148 lines
5.8 KiB
PHP
148 lines
5.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Jobs\SendEmailTemplateJob;
|
|
use App\Models\Activity;
|
|
use App\Models\Client;
|
|
use App\Models\ClientCase;
|
|
use App\Models\Contract;
|
|
use App\Models\Decision;
|
|
use App\Models\Email;
|
|
use App\Models\EmailLog;
|
|
use App\Models\EmailLogStatus;
|
|
use App\Models\EmailTemplate;
|
|
use App\Models\MailProfile;
|
|
|
|
class AutoMailDispatcher
|
|
{
|
|
public function __construct(public EmailTemplateRenderer $renderer) {}
|
|
|
|
/**
|
|
* Attempt to queue an auto mail for the given activity based on its decision/template.
|
|
* Returns array with either ['queued' => true, 'log_id' => int] or ['skipped' => 'reason'].
|
|
*/
|
|
public function maybeQueue(Activity $activity, bool $sendFlag = true, array $options = []): array
|
|
{
|
|
$decision = $activity->decision;
|
|
if (! $sendFlag || ! $decision || ! $decision->auto_mail || ! $decision->email_template_id) {
|
|
return ['skipped' => 'disabled'];
|
|
}
|
|
|
|
/** @var EmailTemplate|null $template */
|
|
$template = EmailTemplate::query()->find($decision->email_template_id);
|
|
if (! $template || ! $template->active) {
|
|
return ['skipped' => 'no-template'];
|
|
}
|
|
|
|
// Resolve context
|
|
$clientCase = $activity->clientCase; /** @var ClientCase|null $clientCase */
|
|
$contract = $activity->contract; /** @var Contract|null $contract */
|
|
$client = $clientCase?->client; /** @var Client|null $client */
|
|
$person = $clientCase?->person; /** @var \App\Models\Person\Person|null $person */
|
|
|
|
// Validate required entity types from template
|
|
$required = (array) ($template->entity_types ?? []);
|
|
if (in_array('client', $required, true) && ! $client) {
|
|
return ['skipped' => 'missing-client'];
|
|
}
|
|
if (in_array('client_case', $required, true) && ! $clientCase) {
|
|
return ['skipped' => 'missing-client-case'];
|
|
}
|
|
if (in_array('contract', $required, true) && ! $contract) {
|
|
return ['skipped' => 'missing-contract'];
|
|
}
|
|
if (in_array('person', $required, true) && ! $person) {
|
|
return ['skipped' => 'missing-person'];
|
|
}
|
|
|
|
// Resolve eligible recipients: client's person emails with receive_auto_mails = true
|
|
$recipients = [];
|
|
if ($client && $client->person) {
|
|
$recipients = Email::query()
|
|
->where('person_id', $client->person->id)
|
|
->where('is_active', true)
|
|
->where('receive_auto_mails', true)
|
|
->pluck('value')
|
|
->map(fn ($v) => strtolower(trim((string) $v)))
|
|
->filter(fn ($v) => filter_var($v, FILTER_VALIDATE_EMAIL))
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
}
|
|
if (empty($recipients)) {
|
|
return ['skipped' => 'no-recipients'];
|
|
}
|
|
|
|
// Ensure related names are available without extra queries
|
|
$activity->loadMissing(['action', 'decision']);
|
|
|
|
// Render content
|
|
$rendered = $this->renderer->render([
|
|
'subject' => (string) $template->subject_template,
|
|
'html' => (string) $template->html_template,
|
|
'text' => (string) $template->text_template,
|
|
], [
|
|
'client' => $client,
|
|
'client_case' => $clientCase,
|
|
'contract' => $contract,
|
|
'person' => $person,
|
|
'activity' => $activity,
|
|
'extra' => [],
|
|
]);
|
|
|
|
// Create the log and body
|
|
$log = new EmailLog;
|
|
$log->fill([
|
|
'uuid' => (string) \Str::uuid(),
|
|
'template_id' => $template->id,
|
|
'mail_profile_id' => optional(MailProfile::query()->where('active', true)->orderBy('priority')->orderBy('id')->first())->id,
|
|
'user_id' => auth()->id(),
|
|
'to_email' => (string) ($recipients[0] ?? ''),
|
|
'to_recipients' => $recipients,
|
|
'subject' => (string) ($rendered['subject'] ?? $template->subject_template ?? ''),
|
|
'body_html_hash' => $rendered['html'] ? hash('sha256', $rendered['html']) : null,
|
|
'body_text_preview' => isset($rendered['text']) ? mb_strimwidth((string) $rendered['text'], 0, 4096) : null,
|
|
'embed_mode' => 'base64',
|
|
'status' => EmailLogStatus::Queued,
|
|
'queued_at' => now(),
|
|
'client_id' => $client?->id,
|
|
'client_case_id' => $clientCase?->id,
|
|
'contract_id' => $contract?->id,
|
|
]);
|
|
// If multiple recipients, keep to_email null to avoid implying a single primary recipient
|
|
if (count($recipients) > 1) {
|
|
$log->to_email = null;
|
|
}
|
|
// Resolve and attach selected documents as attachments metadata (id, name, path, mime, size)
|
|
$attachmentIds = collect($options['attachment_ids'] ?? [])->filter()->map(fn ($v) => (int) $v)->values();
|
|
if ($attachmentIds->isNotEmpty()) {
|
|
$docs = \App\Models\Document::query()
|
|
->whereIn('id', $attachmentIds)
|
|
->get(['id', 'disk', 'path', 'original_name', 'name', 'mime_type', 'size']);
|
|
$log->attachments = $docs->map(function ($d) {
|
|
return [
|
|
'id' => $d->id,
|
|
'disk' => $d->disk ?: 'public',
|
|
'path' => $d->path,
|
|
'name' => $d->original_name ?: ($d->name ?: basename($d->path)),
|
|
'mime' => $d->mime_type ?: 'application/octet-stream',
|
|
'size' => $d->size,
|
|
];
|
|
})->values()->all();
|
|
}
|
|
|
|
$log->save();
|
|
|
|
$log->body()->create([
|
|
'body_html' => (string) ($rendered['html'] ?? ''),
|
|
'body_text' => (string) ($rendered['text'] ?? ''),
|
|
'inline_css' => true,
|
|
]);
|
|
|
|
dispatch(new SendEmailTemplateJob($log->id));
|
|
|
|
return ['queued' => true, 'log_id' => $log->id];
|
|
}
|
|
}
|