feat(form-builder): add FormSubmissionActionFailure model + apply_status casts (WS-6)

- FormSubmissionActionFailure: audit model, no organisation_id (FK-chain
  tenancy per RFC V3), open/resolved/dismissed scopes, canBeRetried()
  helper. Morph alias 'form_submission_action_failure' registered for
  future activity-log subject references.
- FormSubmission: apply_status (ApplyStatus enum cast),
  apply_completed_at (datetime), actionFailures() HasMany,
  scopePendingApply().

Refs: RFC-WS-6.md §3 (Q5), §4 (V3)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 22:47:06 +02:00
parent 0dd991c688
commit c5b0210ae7
6 changed files with 372 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Database\Factories\FormBuilder;
use App\Models\FormBuilder\FormSubmission;
use App\Models\FormBuilder\FormSubmissionActionFailure;
use Illuminate\Database\Eloquent\Factories\Factory;
/** @extends Factory<FormSubmissionActionFailure> */
final class FormSubmissionActionFailureFactory extends Factory
{
protected $model = FormSubmissionActionFailure::class;
/** @return array<model-property<FormSubmissionActionFailure>, mixed> */
public function definition(): array
{
return [
'form_submission_id' => FormSubmission::factory(),
'listener_class' => 'App\\Listeners\\FormBuilder\\ApplyBindingsOnFormSubmit',
'binding_id' => null,
'failed_at' => now(),
'exception_class' => \RuntimeException::class,
'exception_message' => 'Simulated apply failure',
'context' => [
'target_entity' => 'person',
'target_attribute' => 'email',
],
'retry_count' => 0,
'resolved_at' => null,
'resolved_by_user_id' => null,
'resolved_note' => null,
'dismissed_at' => null,
'dismissed_by_user_id' => null,
'dismissed_reason_type' => null,
'dismissed_reason_note' => null,
];
}
public function resolved(): static
{
return $this->state(fn (): array => [
'resolved_at' => now(),
'resolved_note' => 'Resolved via direct edit',
]);
}
public function dismissed(): static
{
return $this->state(fn (): array => [
'dismissed_at' => now(),
'dismissed_reason_type' => \App\Enums\FormBuilder\DismissalReasonType::SCHEMA_DELETED,
]);
}
}