Files
crewli/api/app/Exceptions/Artist/CrossTenantEngagementException.php
bert.hausmans 85ad45c7e9 feat(timetable): observers — engagement denorm/guard + performance version bump
ArtistEngagementObserver:
- creating: auto-fills organisation_id from parent Artist (RFC v0.2 D10
  denormalisation), asserts artist.organisation_id == event.organisation_id;
  cross-tenant linkage throws CrossTenantEngagementException (extends
  DomainException, included in this commit).
- saving: no-op marker reserved for Session 2 state-machine validation.
- deleted: cascades soft-delete to Performance children, hard-deletes
  AdvanceSection children. AdvanceSubmission rows are immutable per
  RFC §5.4 and remain attached.

PerformanceObserver:
- saving: increments version by 1 on UPDATE only (D14 optimistic lock).
  MoveTimetablePerformanceRequest in Session 2 uses this for concurrent-
  edit detection.

Both observers registered in AppServiceProvider::boot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:01:42 +02:00

30 lines
970 B
PHP

<?php
declare(strict_types=1);
namespace App\Exceptions\Artist;
use App\Models\ArtistEngagement;
use DomainException;
/**
* Raised when an ArtistEngagement is being created with an artist and
* event that belong to different organisations. The engagement's
* `organisation_id` is denormalised from the artist (RFC v0.2 D10);
* the event must match. Cross-tenant linkage is a hard error — fail
* loud rather than silently denormalise the wrong tenant.
*/
final class CrossTenantEngagementException extends DomainException
{
public static function forEngagement(ArtistEngagement $engagement): self
{
return new self(sprintf(
'ArtistEngagement cross-tenant: artist=%s (org=%s) vs event=%s (org=%s).',
$engagement->artist_id ?? 'null',
$engagement->artist?->organisation_id ?? 'null',
$engagement->event_id ?? 'null',
$engagement->event?->organisation_id ?? 'null',
));
}
}