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:
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Mail\AppointmentConfirmedMail;
|
||||
use App\Mail\PaymentReceivedMail;
|
||||
use App\Mail\ServiceReminderMail;
|
||||
use App\Mail\WorkOrderReadyMail;
|
||||
use App\Models\Central\Company;
|
||||
use App\Models\Tenant\Appointment;
|
||||
use App\Models\Tenant\Payment;
|
||||
use App\Models\Tenant\Vehicle;
|
||||
use App\Models\Tenant\WorkOrder;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
/**
|
||||
* Centralizes outbound email notifications, with per-tenant feature toggles
|
||||
* stored in companies.settings.notify (e.g., 'wo_ready' => true).
|
||||
*
|
||||
* Usage:
|
||||
* app(NotificationDispatcher::class)->workOrderReady($workOrder);
|
||||
*/
|
||||
class NotificationDispatcher
|
||||
{
|
||||
public function workOrderReady(WorkOrder $wo): bool
|
||||
{
|
||||
$company = $this->companyFor($wo);
|
||||
if (! $this->isEnabled($company, 'wo_ready')) return false;
|
||||
|
||||
$email = $wo->client?->email;
|
||||
if (! $email) return false;
|
||||
|
||||
try {
|
||||
Mail::to($email)->send(new WorkOrderReadyMail($wo, $company));
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('workOrderReady mail failed', ['wo' => $wo->id, 'err' => $e->getMessage()]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function paymentReceived(Payment $payment): bool
|
||||
{
|
||||
$company = $this->companyFor($payment);
|
||||
if (! $this->isEnabled($company, 'payment')) return false;
|
||||
|
||||
$email = $payment->client?->email;
|
||||
if (! $email) return false;
|
||||
|
||||
try {
|
||||
Mail::to($email)->send(new PaymentReceivedMail($payment, $company));
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('paymentReceived mail failed', ['payment' => $payment->id, 'err' => $e->getMessage()]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function appointmentConfirmed(Appointment $a): bool
|
||||
{
|
||||
$company = $this->companyFor($a);
|
||||
if (! $this->isEnabled($company, 'appointment')) return false;
|
||||
|
||||
$email = $a->client?->email;
|
||||
if (! $email) return false;
|
||||
|
||||
try {
|
||||
Mail::to($email)->send(new AppointmentConfirmedMail($a, $company));
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('appointmentConfirmed mail failed', ['appt' => $a->id, 'err' => $e->getMessage()]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function serviceReminder(Vehicle $v, string $type = 'general', ?string $note = null): bool
|
||||
{
|
||||
$company = $this->companyFor($v);
|
||||
if (! $this->isEnabled($company, 'reminder')) return false;
|
||||
|
||||
$email = $v->client?->email;
|
||||
if (! $email) return false;
|
||||
|
||||
try {
|
||||
Mail::to($email)->send(new ServiceReminderMail($v, $type, $note, $company));
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('serviceReminder mail failed', ['vehicle' => $v->id, 'err' => $e->getMessage()]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function companyFor($model): Company
|
||||
{
|
||||
return Company::withoutGlobalScopes()->findOrFail($model->company_id);
|
||||
}
|
||||
|
||||
protected function isEnabled(Company $company, string $key): bool
|
||||
{
|
||||
$settings = (array) ($company->settings ?? []);
|
||||
$notify = (array) ($settings['notify'] ?? []);
|
||||
// default: enabled (toate notificările active by default)
|
||||
return ($notify[$key] ?? true) === true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user