- Created stubs for JavaScript and SASS assets. - Added stubs for channel, command, component, controller, event, factory, job, listener, mail, middleware, migration, model, notification, observer, policy, request, resource, route provider, seeder, and validation rule. - Implemented scaffolding for configuration, service provider, and Vite integration. - Included example views for module structure. - Introduced a module loader for Vite to handle asset paths dynamically.
318 lines
12 KiB
PHP
318 lines
12 KiB
PHP
@php
|
|
$companyLogo = $companyLogo ?? '';
|
|
$companyName = $companyName ?? '';
|
|
$companyAddress = $companyAddress ?? '';
|
|
$companyPhone = $companyPhone ?? '';
|
|
$companyEmail = $companyEmail ?? '';
|
|
$companyTaxCode = $companyTaxCode ?? '';
|
|
$brandColor = $brandColor ?? '#1a56db';
|
|
$cfg = array_merge([
|
|
'accent_color' => $brandColor,
|
|
'show_logo' => true,
|
|
'show_company_info' => true,
|
|
'show_customer_info' => true,
|
|
'show_order_info' => true,
|
|
'show_items_table' => true,
|
|
'show_summary' => true,
|
|
'show_note' => true,
|
|
'footer_text' => 'Cảm ơn quý khách đã sử dụng dịch vụ.',
|
|
'footer_note' => 'Đây là hóa đơn điện tử được tạo tự động, không cần chữ ký số.',
|
|
], $cfg ?? []);
|
|
@endphp
|
|
<!DOCTYPE html>
|
|
<html lang="vi">
|
|
<head>
|
|
<meta charset="UTF-8"/>
|
|
<title>Hóa đơn #{{ $invoice->invoice_number }}</title>
|
|
<style>
|
|
@page {
|
|
size: A4;
|
|
margin: 17mm 18mm;
|
|
}
|
|
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
html, body {
|
|
width: 100%;
|
|
}
|
|
|
|
body {
|
|
font-family: "DejaVu Sans", "Times New Roman", serif;
|
|
color: #000;
|
|
font-size: 11px;
|
|
line-height: 1.45;
|
|
background: #fff;
|
|
}
|
|
|
|
.page {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Header */
|
|
.top-table { width: 100%; border-collapse: collapse; margin-bottom: 7px; table-layout: fixed; }
|
|
.top-table td { vertical-align: top; }
|
|
.logo-cell { width: 31mm; }
|
|
.logo-cell img { width: 29mm; max-height: 15mm; }
|
|
.title-cell { text-align: center; padding: 0 10px; }
|
|
.title-cell h1 { font-size: 16px; font-weight: bold; text-transform: uppercase; letter-spacing: .4px; margin-bottom: 2px; }
|
|
.title-cell h2 { font-size: 11px; font-style: italic; font-weight: normal; margin-bottom: 3px; }
|
|
.title-cell .date-line { font-size: 11px; }
|
|
.meta-cell { width: 43mm; font-size: 10px; line-height: 1.45; vertical-align: top; padding-top: 1px; }
|
|
.meta-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
|
.meta-table td { padding: 2px 0 2px 3px; vertical-align: top; }
|
|
.meta-table .meta-label { width: 22mm; font-weight: bold; }
|
|
.meta-table .meta-value { word-wrap: break-word; }
|
|
|
|
.divider { border: none; border-top: 1px solid #999; margin: 7px 0; }
|
|
|
|
/* Company */
|
|
.company-table { width: 100%; border-collapse: collapse; }
|
|
.company-main { vertical-align: top; padding: 2px 8px 2px 0; }
|
|
.company-main h3 { font-size: 13px; text-transform: uppercase; font-weight: bold; margin-bottom: 5px; }
|
|
.company-qr { width: 22mm; vertical-align: top; text-align: right; padding: 2px 0 2px 8px; }
|
|
.qr-box { width: 20mm; height: 20mm; border: 1px solid #ccc; background: #f9f9f9; }
|
|
|
|
.info-line { margin-bottom: 4px; font-size: 11px; line-height: 1.45; }
|
|
.lbl { font-weight: bold; }
|
|
|
|
/* Buyer */
|
|
.buyer-section { margin-top: 8px; padding: 1px 0; }
|
|
.payment-table { width: 100%; border-collapse: collapse; }
|
|
.payment-table td { vertical-align: top; padding: 2px 8px 2px 0; }
|
|
|
|
/* Items table */
|
|
.items-table { width: 100%; border-collapse: collapse; margin-top: 9px; font-size: 10px; line-height: 1.4; table-layout: fixed; page-break-inside: auto; }
|
|
.items-table th, .items-table td { border: 1px solid #333; padding: 6px 7px; vertical-align: top; overflow-wrap: break-word; word-wrap: break-word; }
|
|
.items-table thead th { font-weight: bold; text-align: center; }
|
|
.items-table tr { page-break-inside: avoid; page-break-after: auto; }
|
|
.items-table td.center { text-align: center; }
|
|
.items-table td.right { text-align: right; }
|
|
.items-table tr.sum td { font-weight: bold; }
|
|
.items-table tr.total-row td { font-weight: bold; font-size: 11px; background: #fafafa; }
|
|
|
|
/* Amount words */
|
|
.amount-words {
|
|
border: 1px solid #333; border-top: none;
|
|
padding: 7px 9px; font-size: 11px; line-height: 1.45;
|
|
}
|
|
|
|
/* Signature */
|
|
.sign-table { width: 100%; border-collapse: collapse; margin-top: 11mm; text-align: center; page-break-inside: avoid; }
|
|
.sign-table td { vertical-align: top; padding: 0 10px; }
|
|
.sign-table h4 { font-size: 11px; margin-bottom: 2px; }
|
|
.sign-table p { font-size: 10px; font-style: italic; color: #555; }
|
|
.sign-spacer { height: 24px; }
|
|
|
|
/* Note */
|
|
.note-box { margin-top: 8px; border-left: 3px solid #e6a817; background: #fefce8; padding: 7px 10px; font-size: 10px; line-height: 1.45; page-break-inside: avoid; }
|
|
|
|
/* Footer */
|
|
.footer { margin-top: 10px; text-align: center; font-size: 10px; line-height: 1.5; color: #444; border-top: 1px solid #ddd; padding-top: 7px; page-break-inside: avoid; }
|
|
|
|
.status-paid { color: #065f46; font-weight: bold; }
|
|
.status-unpaid { color: #991b1b; font-weight: bold; }
|
|
.status-cancelled { color: #6b7280; font-weight: bold; }
|
|
|
|
.accent { color: {{ $cfg['accent_color'] ?? '#1a56db' }}; }
|
|
.items-table thead th { background: {{ $cfg['accent_color'] ?? '#1a56db' }}; color: #fff; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="page">
|
|
|
|
{{-- ══ HEADER ══ --}}
|
|
<table class="top-table">
|
|
<tr>
|
|
<td class="logo-cell">
|
|
@if(($cfg['show_logo'] ?? true) && $companyLogo)
|
|
<img src="{{ $companyLogo }}" alt="{{ $companyName }}"/>
|
|
@endif
|
|
</td>
|
|
<td class="title-cell">
|
|
<h1>HÓA ĐƠN ĐẶT HÀNG</h1>
|
|
<h2>(INVOICE ORDER)</h2>
|
|
<div class="date-line">
|
|
Ngày <strong>{{ $invoice->created_at?->format('d') ?? '—' }}</strong>
|
|
tháng <strong>{{ $invoice->created_at?->format('m') ?? '—' }}</strong>
|
|
năm <strong>{{ $invoice->created_at?->format('Y') ?? '—' }}</strong>
|
|
</div>
|
|
</td>
|
|
<td class="meta-cell">
|
|
<table class="meta-table">
|
|
<tr>
|
|
<td class="meta-label">Mã hóa đơn:</td>
|
|
<td class="meta-value">{{ $invoice->invoice_number }}</td>
|
|
</tr>
|
|
@if($order)
|
|
<tr>
|
|
<td class="meta-label">Mã đơn hàng:</td>
|
|
<td class="meta-value">{{ $order->order_num }}</td>
|
|
</tr>
|
|
@endif
|
|
<tr>
|
|
<td class="meta-label">Hạn thanh toán:</td>
|
|
<td class="meta-value">{{ $invoice->due_date ? \Carbon\Carbon::parse($invoice->due_date)->format('d/m/Y') : '—' }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="meta-label">Trạng thái:</td>
|
|
<td class="meta-value">
|
|
@if($invoice->status == 2)<span class="status-paid">Đã thanh toán</span>
|
|
@elseif($invoice->status == 1)<span class="status-unpaid">Chưa thanh toán</span>
|
|
@else<span class="status-cancelled">Đã hủy</span>@endif
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<hr class="divider"/>
|
|
|
|
{{-- ══ COMPANY ══ --}}
|
|
@if($cfg['show_company_info'] ?? true)
|
|
<table class="company-table">
|
|
<tr>
|
|
<td class="company-main">
|
|
<h3>{{ $companyName }}</h3>
|
|
@if($companyTaxCode)
|
|
<div class="info-line"><span class="lbl">Mã số thuế (Tax code):</span> {{ $companyTaxCode }}</div>
|
|
@endif
|
|
@if($companyAddress)
|
|
<div class="info-line"><span class="lbl">Địa chỉ (Address):</span> {{ $companyAddress }}</div>
|
|
@endif
|
|
@if($companyPhone)
|
|
<div class="info-line"><span class="lbl">Điện thoại (Tel):</span> {{ $companyPhone }}</div>
|
|
@endif
|
|
@if($companyEmail)
|
|
<div class="info-line"><span class="lbl">Email:</span> {{ $companyEmail }}</div>
|
|
@endif
|
|
</td>
|
|
<td class="company-qr"><div class="qr-box"></div></td>
|
|
</tr>
|
|
</table>
|
|
<hr class="divider"/>
|
|
@endif
|
|
|
|
{{-- ══ BUYER ══ --}}
|
|
@if($cfg['show_customer_info'] ?? true)
|
|
<div class="buyer-section">
|
|
<div class="info-line">
|
|
<span class="lbl">Họ tên người mua hàng (Buyer):</span> {{ $customer?->fullname ?? '—' }}
|
|
</div>
|
|
@if($customer?->company)
|
|
<div class="info-line"><span class="lbl">Tên đơn vị:</span> {{ $customer->company }}</div>
|
|
@endif
|
|
@if($customer?->identity_number)
|
|
<div class="info-line"><span class="lbl">MST / CMND:</span> {{ $customer->identity_number }}</div>
|
|
@endif
|
|
@if($customer?->address)
|
|
<div class="info-line"><span class="lbl">Địa chỉ (Address):</span> {{ $customer->address }}</div>
|
|
@endif
|
|
<table class="payment-table">
|
|
<tr>
|
|
<td style="width:55%">
|
|
<div class="info-line">
|
|
<span class="lbl">Hình thức thanh toán:</span>
|
|
{{ $invoice->payment_method ? strtoupper($invoice->payment_method) : 'TM/CK' }}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="info-line"><span class="lbl">Email:</span> {{ $customer?->email ?? '—' }}</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- ══ ITEMS TABLE ══ --}}
|
|
@if($cfg['show_items_table'] ?? true)
|
|
<table class="items-table">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:9mm">STT</th>
|
|
<th>Tên hàng hóa, dịch vụ</th>
|
|
<th style="width:19mm">Đơn vị tính</th>
|
|
<th style="width:17mm">Số lượng</th>
|
|
<th style="width:27mm">Đơn giá (đ)</th>
|
|
<th style="width:29mm">Thành tiền (đ)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($invoiceItems as $i => $item)
|
|
<tr>
|
|
<td class="center">{{ $i + 1 }}</td>
|
|
<td>{{ $item->description ?? ($item->product?->name ?? 'Dịch vụ') }}</td>
|
|
<td class="center">{{ ($item->months && $item->months > 0) ? 'Tháng' : 'Lần' }}</td>
|
|
<td class="center">{{ number_format($item->quantity ?? 1, 2, ',', '.') }}</td>
|
|
<td class="right">{{ number_format($item->unit_price, 0, ',', '.') }}</td>
|
|
<td class="right">{{ number_format($item->total_price, 0, ',', '.') }}</td>
|
|
</tr>
|
|
@endforeach
|
|
|
|
@if($cfg['show_summary'] ?? true)
|
|
{{-- Sub-total --}}
|
|
<tr class="sum">
|
|
<td colspan="5" class="right">Cộng tiền hàng (Total amount excl. VAT)</td>
|
|
<td class="right">{{ number_format($invoice->sub_total, 0, ',', '.') }}</td>
|
|
</tr>
|
|
@if($invoice->discount_amount > 0)
|
|
<tr class="sum">
|
|
<td colspan="5" class="right">Giảm giá (Discount)</td>
|
|
<td class="right">-{{ number_format($invoice->discount_amount, 0, ',', '.') }}</td>
|
|
</tr>
|
|
@endif
|
|
@php $taxRate = ($invoice->total > 0 && $invoice->tax > 0) ? round($invoice->tax / ($invoice->total - $invoice->tax) * 100) : 0; @endphp
|
|
<tr class="sum">
|
|
<td colspan="3">Thuế suất GTGT (VAT rate): {{ $taxRate > 0 ? $taxRate.'%' : 'Không áp dụng' }}</td>
|
|
<td colspan="2" class="right">Tiền thuế GTGT (VAT amount)</td>
|
|
<td class="right">{{ number_format($invoice->tax, 0, ',', '.') }}</td>
|
|
</tr>
|
|
<tr class="sum total-row">
|
|
<td colspan="5" class="right">Tổng tiền thanh toán (Total amount)</td>
|
|
<td class="right">{{ number_format($invoice->total, 0, ',', '.') }}</td>
|
|
</tr>
|
|
@endif
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="amount-words">
|
|
<strong>Số tiền viết bằng chữ (Total amount in words):</strong>
|
|
{{ \App\Helpers\NumberToWords::convert((int) $invoice->total) }}.
|
|
</div>
|
|
@endif
|
|
|
|
{{-- ══ NOTE ══ --}}
|
|
@if(($cfg['show_note'] ?? true) && $invoice->note)
|
|
<div class="note-box"><strong>Ghi chú:</strong> {{ $invoice->note }}</div>
|
|
@endif
|
|
|
|
{{-- ══ SIGNATURE ══ --}}
|
|
<table class="sign-table">
|
|
<tr>
|
|
<td>
|
|
<h4>Người mua hàng<br/><em style="font-size:11px">(Buyer's signature)</em></h4>
|
|
<p>Ký, ghi rõ họ tên</p>
|
|
<div class="sign-spacer"></div>
|
|
<p>{{ $customer?->fullname ?? '' }}</p>
|
|
</td>
|
|
<td></td>
|
|
<td>
|
|
<h4>Người bán hàng<br/><em style="font-size:11px">(Seller's signature)</em></h4>
|
|
<p>Ký, đóng dấu, ghi rõ họ tên</p>
|
|
<div class="sign-spacer"></div>
|
|
<p>{{ $companyName }}</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
{{-- ══ FOOTER ══ --}}
|
|
<div class="footer">
|
|
@if($cfg['footer_text'] ?? ''){{ $cfg['footer_text'] }}<br/>@endif
|
|
@if($cfg['footer_note'] ?? ''){{ $cfg['footer_note'] }}<br/>@endif
|
|
Mã tra cứu: <strong>{{ $invoice->invoice_number }}</strong>
|
|
</div>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|