From 36b90370a840bb0cce224677f8ecabade541a03b Mon Sep 17 00:00:00 2001 From: sky121113 Date: Fri, 6 Mar 2026 14:40:14 +0800 Subject: [PATCH] =?UTF-8?q?[FEAT]=20=E5=84=AA=E5=8C=96=E6=9C=83=E8=A8=88?= =?UTF-8?q?=E5=A0=B1=E8=A1=A8=EF=BC=9A=E6=96=B0=E5=A2=9E=E7=A8=85=E9=A1=8D?= =?UTF-8?q?=E3=80=81=E7=99=BC=E7=A5=A8=E6=97=A5=E6=9C=9F=E8=88=87=E4=BB=98?= =?UTF-8?q?=E6=AC=BE=E6=96=B9=E5=BC=8F=E7=AD=89=E6=9C=83=E8=A8=88=E5=B0=88?= =?UTF-8?q?=E7=94=A8=E6=AC=84=E4=BD=8D=E4=B8=A6=E6=94=AF=E6=8F=B4=20CSV=20?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E5=8C=AF=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AccountingReportController.php | 21 +++++- .../Finance/Services/FinanceService.php | 67 +++++++++++++----- resources/js/Pages/Accounting/Report.tsx | 68 ++++++++++++++++--- 3 files changed, 124 insertions(+), 32 deletions(-) diff --git a/app/Modules/Finance/Controllers/AccountingReportController.php b/app/Modules/Finance/Controllers/AccountingReportController.php index 75452e5..cf202cb 100644 --- a/app/Modules/Finance/Controllers/AccountingReportController.php +++ b/app/Modules/Finance/Controllers/AccountingReportController.php @@ -69,14 +69,25 @@ class AccountingReportController extends Controller } $exportData = $allRecords->map(function ($record) { + $taxAmount = (float)($record['tax_amount'] ?? 0); + $totalAmount = (float)($record['amount'] ?? 0); + $untaxedAmount = $totalAmount - $taxAmount; + return [ $record['date'], $record['source'], $record['category'], $record['item'], $record['reference'], - $record['invoice_number'], - $record['amount'], + $record['invoice_date'] ?? '-', + $record['invoice_number'] ?? '-', + $untaxedAmount, + $taxAmount, + $totalAmount, + $record['payment_method'] ?? '-', + $record['payment_note'] ?? '-', + $record['remarks'] ?? '-', + $record['status'] ?? '-', ]; }); @@ -91,7 +102,11 @@ class AccountingReportController extends Controller // BOM for Excel compatibility with UTF-8 fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF)); - fputcsv($file, ['日期', '來源', '類別', '項目', '參考單號', '發票號碼', '金額']); + fputcsv($file, [ + '日期', '來源', '類別', '項目', '參考單號', + '發票日期', '發票號碼', '未稅金額', '稅額', '總金額', + '付款方式', '付款備註', '內部備註', '狀態' + ]); foreach ($exportData as $row) { fputcsv($file, $row); diff --git a/app/Modules/Finance/Services/FinanceService.php b/app/Modules/Finance/Services/FinanceService.php index 36dffed..4e5eadb 100644 --- a/app/Modules/Finance/Services/FinanceService.php +++ b/app/Modules/Finance/Services/FinanceService.php @@ -19,23 +19,48 @@ class FinanceService implements FinanceServiceInterface public function getAccountingReportData(string $start, string $end): array { - // 1. 獲取採購單資料 - $purchaseOrders = $this->procurementService->getPurchaseOrdersByDate($start, $end) - ->map(function ($po) { - return [ - 'id' => 'PO-' . $po->id, - 'date' => Carbon::parse($po->created_at)->timezone(config('app.timezone'))->toDateString(), - 'source' => '採購單', - 'category' => '進貨支出', - 'item' => $po->vendor->name ?? '未知廠商', - 'reference' => $po->code, - 'invoice_number' => $po->invoice_number, - 'amount' => (float)$po->grand_total, - ]; - }); + // 1. 獲取應付帳款資料 (已付款) + $accountPayables = \App\Modules\Finance\Models\AccountPayable::where('status', \App\Modules\Finance\Models\AccountPayable::STATUS_PAID) + ->whereNotNull('paid_at') + ->whereBetween('paid_at', [$start, $end]) + ->get(); - // 2. 獲取公共事業費 (注意:目前資料表欄位為 transaction_date) - $utilityFees = UtilityFee::whereBetween('transaction_date', [$start, $end]) + // 取得供應商資料 (Manual Hydration) + $vendorIds = $accountPayables->pluck('vendor_id')->unique()->filter()->toArray(); + $vendorsMap = $this->procurementService->getVendorsByIds($vendorIds)->keyBy('id'); + + // 付款方式對映 + $paymentMethodMap = [ + 'cash' => '現金', + 'bank_transfer' => '銀行轉帳', + 'check' => '支票', + 'credit_card' => '信用卡', + ]; + + $payableRecords = $accountPayables->map(function ($ap) use ($vendorsMap, $paymentMethodMap) { + $vendorName = isset($vendorsMap[$ap->vendor_id]) ? $vendorsMap[$ap->vendor_id]->name : '未知廠商'; + $mappedPaymentMethod = $paymentMethodMap[$ap->payment_method] ?? $ap->payment_method; + return [ + 'id' => 'AP-' . $ap->id, + 'date' => Carbon::parse($ap->paid_at)->timezone(config('app.timezone'))->toDateString(), + 'source' => '應付帳款', + 'category' => '進貨支出', + 'item' => $vendorName, + 'reference' => $ap->document_number, + 'invoice_date' => $ap->invoice_date ? $ap->invoice_date->format('Y-m-d') : null, + 'invoice_number' => $ap->invoice_number, + 'amount' => (float)$ap->total_amount, + 'tax_amount' => (float)$ap->tax_amount, + 'status' => $ap->status, + 'payment_method' => $mappedPaymentMethod, + 'payment_note' => $ap->payment_note, + 'remarks' => $ap->remarks, + ]; + }); + + // 2. 獲取公共事業費 (已繳費) + $utilityFees = UtilityFee::where('payment_status', UtilityFee::STATUS_PAID) + ->whereBetween('transaction_date', [$start, $end]) ->get() ->map(function ($fee) { return [ @@ -45,12 +70,18 @@ class FinanceService implements FinanceServiceInterface 'category' => $fee->category, 'item' => $fee->description ?: $fee->category, 'reference' => '-', + 'invoice_date' => null, 'invoice_number' => $fee->invoice_number, 'amount' => (float)$fee->amount, + 'tax_amount' => 0.0, + 'status' => $fee->payment_status, + 'payment_method' => null, + 'payment_note' => null, + 'remarks' => $fee->description, ]; }); - $allRecords = $purchaseOrders->concat($utilityFees) + $allRecords = $payableRecords->concat($utilityFees) ->sortByDesc('date') ->values(); @@ -58,7 +89,7 @@ class FinanceService implements FinanceServiceInterface 'records' => $allRecords, 'summary' => [ 'total_amount' => $allRecords->sum('amount'), - 'purchase_total' => $purchaseOrders->sum('amount'), + 'payable_total' => $payableRecords->sum('amount'), 'utility_total' => $utilityFees->sum('amount'), 'record_count' => $allRecords->count(), ] diff --git a/resources/js/Pages/Accounting/Report.tsx b/resources/js/Pages/Accounting/Report.tsx index 30a7923..879862f 100644 --- a/resources/js/Pages/Accounting/Report.tsx +++ b/resources/js/Pages/Accounting/Report.tsx @@ -8,7 +8,7 @@ import { Calendar, Filter, TrendingDown, - Package, + Wallet, Pocket, RotateCcw, FileText @@ -29,6 +29,7 @@ import Pagination from "@/Components/shared/Pagination"; import { SearchableSelect } from "@/Components/ui/searchable-select"; import { Can } from "@/Components/Permission/Can"; import { Checkbox } from "@/Components/ui/checkbox"; +import { StatusBadge } from "@/Components/shared/StatusBadge"; interface Record { id: string; @@ -37,8 +38,14 @@ interface Record { category: string; item: string; reference: string; - invoice_number?: string; + invoice_date?: string | null; + invoice_number?: string | null; amount: number | string; + tax_amount: number | string; + status?: string; + payment_method?: string | null; + payment_note?: string | null; + remarks?: string | null; } interface PageProps { @@ -52,7 +59,7 @@ interface PageProps { }; summary: { total_amount: number; - purchase_total: number; + payable_total: number; utility_total: number; record_count: number; }; @@ -273,10 +280,10 @@ export default function AccountingReport({ records, summary, filters }: PageProp
- +
- 採購支出 - $ {Number(summary.purchase_total).toLocaleString()} + 應付帳款 + $ {Number(summary.payable_total).toLocaleString()}
@@ -305,13 +312,16 @@ export default function AccountingReport({ records, summary, filters }: PageProp 來源 類別 項目詳細 - 金額 + 付款方式 / 備註 + 狀態 + 稅額 + 總金額 {records.data.length === 0 ? ( - +

此日期區間內無支出紀錄

@@ -333,7 +343,7 @@ export default function AccountingReport({ records, summary, filters }: PageProp @@ -348,11 +358,47 @@ export default function AccountingReport({ records, summary, filters }: PageProp
{record.item} - {record.invoice_number && ( - 發票:{record.invoice_number} + {(record.invoice_number || record.invoice_date) && ( + + 發票:{record.invoice_number || '-'} + {record.invoice_date && ` (${record.invoice_date})`} + + )} + {record.remarks && ( + + 備註:{record.remarks} + )}
+ +
+ {record.payment_method || '-'} + {record.payment_note && ( + + {record.payment_note} + + )} +
+
+ + {record.status === 'paid' ? ( + 已付款 + ) : record.status === 'pending' ? ( + 待付款 + ) : record.status === 'overdue' ? ( + 已逾期 + ) : record.status === 'draft' ? ( + 草稿 + ) : record.status === 'approved' ? ( + 已核准 + ) : ( + {record.status || '-'} + )} + + + {record.tax_amount ? `$ ${Number(record.tax_amount).toLocaleString()}` : '-'} + $ {Number(record.amount).toLocaleString()}