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
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace App\Services;
use App\Jobs\Mail\SendInvoiceMailJob;
use App\Mail\InvoiceNotificationMail;
use App\Models\Invoice;
use App\Models\Notification;
use App\Models\Service;
use App\Support\AutoEmailGate;
class RenewalInvoiceNotifier
{
public function notify(Invoice $invoice, Service $service): void
{
$customer = $service->customer;
if (! $customer) {
return;
}
$name = $service->server_name ?: $service->orderItem?->product?->name ?: 'Dịch vụ';
$invoice->setRelation('order', $service->orderItem?->order);
Notification::notifyCustomer($customer, [
'type' => 'renewal_invoice',
'title' => 'Hóa đơn gia hạn dịch vụ',
'body' => sprintf(
'Dịch vụ %s hết hạn ngày %s. Hóa đơn gia hạn #%s (%s VNĐ) đang chờ thanh toán.',
$name,
$service->expires_at?->format('d/m/Y') ?? '-',
$invoice->invoice_number,
number_format((int) $invoice->total, 0, ',', '.'),
),
'link' => $invoice->clientPath(),
'data' => ['invoice_number' => $invoice->invoice_number, 'service_id' => $service->id],
]);
if ($customer->email && AutoEmailGate::allows(InvoiceNotificationMail::SCENARIO)) {
SendInvoiceMailJob::dispatch($invoice->id, 'renewal', null, null, null, null)->afterCommit();
}
}
}