Faza 2 (din continuare): Email notifications

4 Mailables auto-trigger pe model events:
- WorkOrderReadyMail: la WO.status → 'ready', către client.email
  • Atașat PDF fișa lucru (via WorkOrderPdfService)
  • Total/achitat/rest, recomandări (warning box)
- PaymentReceivedMail: la Payment::created, confirmare cu sumă/metodă/ref
- AppointmentConfirmedMail: la Appointment::created status='scheduled'
- ServiceReminderMail: dispatch manual (vehicle, type=itp/oil/general, note)

Layout email branded (resources/views/emails/layout.blade.php):
- Header cu logo tenant + theme_color border-bottom
- Footer cu telefon/email/disclaimer
- Stiluri inline (compatibil tot mail client)

Settings page extins cu 4 toggle:
- 'Mașina e gata de ridicat'
- 'Confirmare plată primită'
- 'Programare confirmată'
- 'Reminder ITP / revizie'
Salvate în companies.settings.notify (JSON), default true.

NotificationDispatcher service centralizat:
- Verifică isEnabled() pe settings.notify[$key]
- Skip dacă client n-are email
- Try/catch + Log::warning pe eșec (nu crapă request-ul)

Mailables folosesc UsesTenantBranding trait pentru context unitar.
Test prin Mailpit: https://mailpit.service.mir.md (capturează toate).
This commit is contained in:
2026-05-07 13:20:19 +00:00
parent bfe58ed286
commit 09fd0bada2
15 changed files with 608 additions and 0 deletions
+14
View File
@@ -108,4 +108,18 @@ class WorkOrder extends Model
->count();
return sprintf('WO-%s-%04d', $year, $count + 1);
}
/** Auto-send 'ready' email when status transitions to 'ready'. */
protected static function booted(): void
{
static::updated(function (self $wo) {
if (
$wo->wasChanged('status')
&& $wo->status === 'ready'
&& $wo->getOriginal('status') !== 'ready'
) {
app(\App\Services\NotificationDispatcher::class)->workOrderReady($wo);
}
});
}
}