renderFromTemplate('invoice_notification', $this->vars())['subject'], ); } public function content(): Content { return new Content( htmlString: $this->renderFromTemplate('invoice_notification', $this->vars())['html'], ); } public function attachments(): array { if ($this->invoicePdf !== null && $this->invoicePdfFileName !== null) { return [ Attachment::fromData(fn() => $this->invoicePdf, $this->invoicePdfFileName) ->withMime('application/pdf'), ]; } return []; } private function vars(): array { $isRenewal = $this->type === 'renewal'; return [ 'title' => $isRenewal ? 'Thông báo gia hạn cước' : 'Thông báo hóa đơn', 'company_name' => $this->companyName, 'customer_name' => $this->customerName, 'body_message' => $this->message, 'invoice_number' => $this->invoice->invoice_number ?? $this->invoice->order?->order_num ?? '', 'created_date' => $this->formatDate($this->invoice->date ?? $this->invoice->created_at), 'due_date' => $this->formatDate($this->invoice->due_date), 'total' => number_format((int) $this->invoice->total, 0, ',', '.'), 'status_label' => $this->statusLabel(), 'outstanding_row' => $this->outstandingRow(), 'intro_note' => $isRenewal ? 'File PDF hóa đơn gia hạn đã được đính kèm trong email này. Vui lòng hoàn tất thanh toán trước hạn để dịch vụ không bị gián đoạn.' : 'File PDF hóa đơn đã được đính kèm trong email này để Quý khách tiện lưu trữ và đối chiếu.', 'pay_button' => $this->payButton(), 'attachments_block' => $this->attachmentsBlock(), 'note_block' => $this->noteBlock(), ]; } private function formatDate(mixed $value): string { if (empty($value)) { return '—'; } if ($value instanceof \Carbon\CarbonInterface) { return $value->format('d/m/Y'); } if (is_numeric($value)) { return date('d/m/Y', (int) $value); } try { return Carbon::parse($value)->format('d/m/Y'); } catch (\Throwable) { return '—'; } } private function statusLabel(): string { return match ((int) $this->invoice->status) { InvoiceStatus::PAID->value => 'Đã thanh toán', InvoiceStatus::UNPAID->value => 'Chưa thanh toán', InvoiceStatus::OVERDUE->value => 'Quá hạn', InvoiceStatus::REFUNDED->value => 'Đã hoàn tiền', default => 'Đã hủy', }; } /** Chỉ hiện khi khách đã trả được một phần nhưng chưa đủ. */ private function outstandingRow(): string { $paid = (int) $this->invoice->amount_paid; $outstanding = InvoicePaymentService::outstanding($this->invoice); if ($paid <= 0 || $outstanding <= 0) { return ''; } return '
| ' . 'THANH TOÁN HÓA ĐƠN' . ' |
Hoặc sao chép liên kết thanh toán: ' . $url . '
'; } private function attachmentsBlock(): string { if ($this->invoiceAttachments->isEmpty()) { return ''; } $items = $this->invoiceAttachments ->map(fn($file) => 'File đính kèm
' . '| ' . 'Ghi chú: ' . htmlspecialchars((string) $this->invoice->note, ENT_QUOTES, 'UTF-8') . ' |