feat(billing): renewals, coupons, VAT numbering, billing cycles and invoice payment tracking

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-19 16:37:56 +07:00
co-authored by Claude Sonnet 5.5
parent ea9a53f742
commit 25f23d541e
134 changed files with 4926 additions and 185 deletions
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Support;
use App\Models\Template;
final class EmailHealth
{
public const LIFECYCLE_SCENARIOS = [
'service_expiry',
'service_renewal_reminder',
'invoice_notification',
'invoice_payment_reminder',
'auto_renew_failed',
];
public static function report(): array
{
$templates = Template::query()->get(['key', 'scenario', 'status']);
$disabled = AutoEmailGate::disabledScenarios();
$mailEnabled = AutoEmailGate::mailEnabled();
$scenarios = array_map(function (string $key) use ($templates, $disabled, $mailEnabled) {
$matches = $templates->filter(fn ($template) => $template->key === $key || $template->scenario === $key);
$hasTemplate = $matches->isNotEmpty();
$templateActive = $matches->contains(fn ($template) => (int) $template->status === 1);
$autoEnabled = ! in_array($key, $disabled, true);
return [
'key' => $key,
'label' => EmailScenarioRegistry::SCENARIOS[$key]['label'],
'has_template' => $hasTemplate,
'template_active' => $templateActive,
'auto_enabled' => $autoEnabled,
'ready' => $mailEnabled && $hasTemplate && $templateActive && $autoEnabled,
];
}, self::LIFECYCLE_SCENARIOS);
$smtpConfigured = MailSettings::isConfigured();
$smtpDelivering = MailSettings::isDelivering();
return [
'mail_enabled' => $mailEnabled,
'smtp_configured' => $smtpConfigured,
'smtp_delivering' => $smtpDelivering,
'scenarios' => $scenarios,
'ready' => $mailEnabled && $smtpConfigured && $smtpDelivering && ! in_array(false, array_column($scenarios, 'ready'), true),
];
}
}