Three rows of task 13 were storing data nothing ever read. `preferred_day_parts` was saved and displayed but never applied when matching. It was deferred because "evening" has no fixed meaning — but branches already carry a timezone (DoctorAddress::getTimezone), so the boundaries can be pinned: morning [6,12), afternoon [12,17), evening [17,22), in the branch's local hour. The list is now closed and validated; an unknown part is a 422 rather than a preference that silently matches nothing. The filter runs *before* the cut to ten recipients — otherwise the first ten slots go to people who did not want that hour and the real eleventh person is never told. `markConverted()` was dead code: nothing called it. It now runs off the AppointmentBooked domain event rather than from inside BookingService, because converting is a side effect of booking — inside the booking transaction a waitlist error could roll back the patient's actual appointment. The match is deliberately narrow (same patient, same service, start inside the window); a loose match closes a row the patient is still waiting on. It is idempotent, so redelivery is harmless. Expiry now exists as a service, a daily scheduled message and `app:waitlist:expire`. Expired rows were already excluded from matching, so this is display hygiene, not a behaviour fix: without it the waitlist page fills with dead entries and the operator cannot tell which are still live. It sets a status rather than deleting — who waited and never got a slot is data. Also: a waitlist window is capped at 90 days, matching the booking horizon. An unbounded window is a row that never expires and shows up in every match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
37 lines
1.6 KiB
YAML
37 lines
1.6 KiB
YAML
framework:
|
|
messenger:
|
|
failure_transport: failed
|
|
|
|
transports:
|
|
async:
|
|
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
retry_strategy:
|
|
max_retries: 3
|
|
delay: 5000
|
|
multiplier: 2
|
|
failed: 'doctrine://default?queue_name=failed'
|
|
sync: 'sync://'
|
|
scheduler_default:
|
|
dsn: 'schedule://default'
|
|
# SchedulerTransport cannot send(); any retry re-send crashes the
|
|
# worker with "cannot send messages". Disable retry — a transient
|
|
# handler failure (e.g. DB blip) is simply retried on the next tick.
|
|
retry_strategy:
|
|
max_retries: 0
|
|
|
|
routing:
|
|
'App\Sms\Message\SendSmsMessage': async
|
|
'App\Appointment\Message\ExpireAppointmentsMessage': scheduler_default
|
|
'App\Shared\Logging\Message\PruneLogsMessage': scheduler_default
|
|
'App\Shared\Event\Message\PublishDomainEventsMessage': scheduler_default
|
|
'App\Waitlist\Message\ExpireWaitlistMessage': scheduler_default
|
|
# مصرفکنندهٔ رویداد async است، وگرنه یک consumer کند خودِ تخلیهٔ صندوق را
|
|
# کند میکند و شکستش ردیفی را «ناموفق» علامت میزند که در واقع تحویل شده بود.
|
|
'App\Shared\Event\Message\DomainEventMessage': async
|
|
|
|
when@test:
|
|
framework:
|
|
messenger:
|
|
transports:
|
|
async: 'in-memory://'
|