feat: email gửi tay/nhắc gia hạn, gói hosting, sửa dữ liệu sản phẩm

- Gửi email theo template cho 1 khách, tab lịch sử email, nhắc gia hạn dịch vụ hàng loạt
- Mail lưu from/bcc/sent_by; mẫu MARKETING và mẫu cũ trong TemplateSeeder
- Gói hosting: giới hạn, trạng thái, lấy plan Plesk qua CLI gateway
- Backfill payment_method và ngày đơn hàng còn thiếu
- Bổ sung field CPU/RAM/Disk/Băng thông cho loại sản phẩm vps và cloud

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
baodev0
2026-10-09 11:32:05 +07:00
co-authored by Claude Opus 5.5
parent 8651dde6b1
commit 30898ea46f
59 changed files with 1771 additions and 108 deletions
+10 -3
View File
@@ -145,12 +145,13 @@ class InvoicePaymentService
?int $adminId = null,
string $method = 'admin',
?int $receivedAmount = null,
string $note = '',
): Invoice {
if ((int) $invoice->status === InvoiceStatus::PAID->value) {
return $invoice->fresh(['order.orderItems.product', 'services', 'customer']);
}
return DB::transaction(function () use ($invoice, $adminId, $method, $receivedAmount) {
return DB::transaction(function () use ($invoice, $adminId, $method, $receivedAmount, $note) {
$invoice = Invoice::where('id', $invoice->id)->lockForUpdate()->firstOrFail();
if ((int) $invoice->status === InvoiceStatus::PAID->value) {
@@ -174,13 +175,19 @@ class InvoicePaymentService
'status' => $settled ? InvoiceStatus::PAID->value : $invoice->status,
'paid_at' => $settled ? now() : null,
'payment_method' => $invoice->payment_method ?: $method,
'note' => $note !== '' ? $note : $invoice->note,
]);
$transactionNote = 'Admin xác nhận thanh toán hóa đơn #' . $invoice->invoice_number
. ($settled ? '' : sprintf(' (một phần: %s/%s VNĐ)', number_format($paidSoFar), number_format((int) $invoice->total)));
if ($note !== '') {
$transactionNote .= '. Ghi chú: ' . $note;
}
$this->recordTransaction(
$invoice,
$method,
'Admin xác nhận thanh toán hóa đơn #' . $invoice->invoice_number
. ($settled ? '' : sprintf(' (một phần: %s/%s VNĐ)', number_format($paidSoFar), number_format((int) $invoice->total))),
$transactionNote,
$adminId,
amount: $received,
sequence: $settled && $received === (int) $invoice->total ? null : $paidSoFar,