Created under app/Http/Requests/Api/V1/Artist/, mirroring the
existing FormRequest pattern (final class, authorize() returns true,
controller-level Gate::authorize). One request per CRUD shape plus the
two domain-specific endpoints:
artists create / update
genres create / update (with org-scoped unique)
stages create / update (with event-scoped unique)
stages/order ReorderStagesRequest — permutation check
engagements create / update — per RFC §10.3, with
ContractRequiresFee + OptionExpiresInFuture
conditional rules wired
performances create / update — per §10.2; cross-FK
engagement.event_id ↔ event_id chain
enforced via withValidator closure;
update is non-placement only (placement
edits go through /timetable/move)
timetable/move per §10.4; resolves target_event_id from
target_stage_id + target_start_at via
stage_days, then reuses StageActiveOnEvent
+ WithinEventBounds for downstream rules
stages/{stage}/days §10.5 matrix replace; each event_id must
equal stage.event_id (flat) or be sub-event
(festival)
Custom error messages in Dutch where user-facing. Cross-FK rules that
span request inputs (engagement vs event-id chain, day matrix sub-event
membership) live in withValidator after-closures so the rule cache is
stable per request.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
2.8 KiB
PHP
63 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Requests\Api\V1\Artist;
|
|
|
|
use App\Enums\Artist\ArtistEngagementStatus;
|
|
use App\Enums\Artist\BumaHandledBy;
|
|
use App\Enums\Artist\FeeType;
|
|
use App\Enums\Artist\PaymentStatus;
|
|
use App\Models\ArtistEngagement;
|
|
use App\Rules\Artist\ContractRequiresFee;
|
|
use App\Rules\Artist\OptionExpiresInFuture;
|
|
use Illuminate\Foundation\Http\FormRequest;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
final class UpdateArtistEngagementRequest extends FormRequest
|
|
{
|
|
public function authorize(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function rules(): array
|
|
{
|
|
$engagement = $this->route('engagement');
|
|
$effectiveStatus = $this->input(
|
|
'booking_status',
|
|
$engagement instanceof ArtistEngagement
|
|
? ($engagement->booking_status?->value ?? null)
|
|
: null,
|
|
);
|
|
|
|
return [
|
|
'booking_status' => ['sometimes', Rule::enum(ArtistEngagementStatus::class)],
|
|
'project_leader_id' => ['sometimes', 'nullable', 'string', 'max:30', 'exists:users,id'],
|
|
'fee_amount' => ['sometimes', 'nullable', 'numeric', 'min:0', 'max:9999999.99', new ContractRequiresFee($effectiveStatus)],
|
|
'fee_currency' => ['sometimes', 'nullable', 'string', 'size:3', Rule::in(['EUR', 'USD', 'GBP'])],
|
|
'fee_type' => ['sometimes', 'nullable', Rule::enum(FeeType::class)],
|
|
'buma_applicable' => ['sometimes', 'boolean'],
|
|
'buma_percentage' => ['sometimes', 'nullable', 'numeric', 'min:0', 'max:100'],
|
|
'buma_handled_by' => ['sometimes', 'nullable', Rule::enum(BumaHandledBy::class)],
|
|
'vat_applicable' => ['sometimes', 'boolean'],
|
|
'vat_percentage' => ['sometimes', 'nullable', 'numeric', 'min:0', 'max:100'],
|
|
'deal_breakdown' => ['sometimes', 'nullable', 'array'],
|
|
'deposit_percentage' => ['sometimes', 'nullable', 'numeric', 'min:0', 'max:100'],
|
|
'deposit_due_date' => ['sometimes', 'nullable', 'date'],
|
|
'balance_due_date' => ['sometimes', 'nullable', 'date'],
|
|
'payment_status' => ['sometimes', 'nullable', Rule::enum(PaymentStatus::class)],
|
|
'crew_count' => ['sometimes', 'nullable', 'integer', 'min:0', 'max:200'],
|
|
'guests_count' => ['sometimes', 'nullable', 'integer', 'min:0', 'max:1000'],
|
|
'requested_at' => ['sometimes', 'nullable', 'date'],
|
|
'option_expires_at' => ['sometimes', 'nullable', 'date', new OptionExpiresInFuture($effectiveStatus)],
|
|
'advance_open_from' => ['sometimes', 'nullable', 'date'],
|
|
'advance_open_to' => ['sometimes', 'nullable', 'date', 'after_or_equal:advance_open_from'],
|
|
'notes' => ['sometimes', 'nullable', 'string', 'max:2000'],
|
|
];
|
|
}
|
|
}
|