2026-01-20 09:44:05 +08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
use App\Models\PurchaseOrder;
|
|
|
|
|
use App\Models\UtilityFee;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Inertia\Inertia;
|
|
|
|
|
use Illuminate\Support\Carbon;
|
2026-01-20 17:45:38 +08:00
|
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
2026-01-20 09:44:05 +08:00
|
|
|
|
|
|
|
|
class AccountingReportController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function index(Request $request)
|
|
|
|
|
{
|
2026-01-20 17:45:38 +08:00
|
|
|
$dateStart = $request->input('date_start', Carbon::now()->toDateString());
|
|
|
|
|
$dateEnd = $request->input('date_end', Carbon::now()->toDateString());
|
2026-01-20 09:44:05 +08:00
|
|
|
|
|
|
|
|
// 1. Get Purchase Orders (Completed or Received that are ready for accounting)
|
|
|
|
|
$purchaseOrders = PurchaseOrder::with(['vendor'])
|
|
|
|
|
->whereIn('status', ['received', 'completed'])
|
|
|
|
|
->whereBetween('created_at', [$dateStart . ' 00:00:00', $dateEnd . ' 23:59:59'])
|
|
|
|
|
->get()
|
|
|
|
|
->map(function ($po) {
|
|
|
|
|
return [
|
|
|
|
|
'id' => 'PO-' . $po->id,
|
2026-01-20 17:45:38 +08:00
|
|
|
'date' => Carbon::parse($po->created_at)->timezone(config('app.timezone'))->toDateString(),
|
2026-01-20 09:44:05 +08:00
|
|
|
'source' => '採購單',
|
|
|
|
|
'category' => '進貨支出',
|
|
|
|
|
'item' => $po->vendor->name ?? '未知廠商',
|
|
|
|
|
'reference' => $po->code,
|
|
|
|
|
'invoice_number' => $po->invoice_number,
|
|
|
|
|
'amount' => $po->grand_total,
|
|
|
|
|
];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 2. Get Utility Fees
|
|
|
|
|
$utilityFees = UtilityFee::whereBetween('transaction_date', [$dateStart, $dateEnd])
|
|
|
|
|
->get()
|
|
|
|
|
->map(function ($fee) {
|
|
|
|
|
return [
|
|
|
|
|
'id' => 'UF-' . $fee->id,
|
2026-01-20 17:45:38 +08:00
|
|
|
'date' => $fee->transaction_date->format('Y-m-d'),
|
2026-01-20 09:44:05 +08:00
|
|
|
'source' => '公共事業費',
|
|
|
|
|
'category' => $fee->category,
|
|
|
|
|
'item' => $fee->description ?: $fee->category,
|
|
|
|
|
'reference' => '-',
|
|
|
|
|
'invoice_number' => $fee->invoice_number,
|
|
|
|
|
'amount' => $fee->amount,
|
|
|
|
|
];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Combine and Sort
|
|
|
|
|
$allRecords = $purchaseOrders->concat($utilityFees)
|
|
|
|
|
->sortByDesc('date')
|
|
|
|
|
->values();
|
|
|
|
|
|
2026-01-20 17:45:38 +08:00
|
|
|
// 3. Manual Pagination
|
|
|
|
|
$perPage = $request->input('per_page', 10);
|
|
|
|
|
$page = $request->input('page', 1);
|
|
|
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
|
|
|
|
|
|
$paginatedRecords = new LengthAwarePaginator(
|
|
|
|
|
$allRecords->slice($offset, $perPage)->values(),
|
|
|
|
|
$allRecords->count(),
|
|
|
|
|
$perPage,
|
|
|
|
|
$page,
|
|
|
|
|
['path' => $request->url(), 'query' => $request->query()]
|
|
|
|
|
);
|
|
|
|
|
|
2026-01-20 09:44:05 +08:00
|
|
|
$summary = [
|
|
|
|
|
'total_amount' => $allRecords->sum('amount'),
|
|
|
|
|
'purchase_total' => $purchaseOrders->sum('amount'),
|
|
|
|
|
'utility_total' => $utilityFees->sum('amount'),
|
|
|
|
|
'record_count' => $allRecords->count(),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return Inertia::render('Accounting/Report', [
|
2026-01-20 17:45:38 +08:00
|
|
|
'records' => $paginatedRecords,
|
2026-01-20 09:44:05 +08:00
|
|
|
'summary' => $summary,
|
|
|
|
|
'filters' => [
|
|
|
|
|
'date_start' => $dateStart,
|
|
|
|
|
'date_end' => $dateEnd,
|
2026-01-20 17:45:38 +08:00
|
|
|
'per_page' => (int)$perPage,
|
2026-01-20 09:44:05 +08:00
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function export(Request $request)
|
|
|
|
|
{
|
2026-01-20 17:45:38 +08:00
|
|
|
$dateStart = $request->input('date_start', Carbon::now()->toDateString());
|
|
|
|
|
$dateEnd = $request->input('date_end', Carbon::now()->toDateString());
|
2026-01-21 16:30:50 +08:00
|
|
|
$selectedIdsParam = $request->input('selected_ids');
|
|
|
|
|
|
|
|
|
|
$purchaseOrdersQuery = PurchaseOrder::with(['vendor'])
|
|
|
|
|
->whereIn('status', ['received', 'completed']);
|
|
|
|
|
|
|
|
|
|
$utilityFeesQuery = UtilityFee::query();
|
|
|
|
|
|
|
|
|
|
if ($selectedIdsParam) {
|
|
|
|
|
$ids = explode(',', $selectedIdsParam);
|
|
|
|
|
$poIds = [];
|
|
|
|
|
$ufIds = [];
|
|
|
|
|
foreach ($ids as $id) {
|
|
|
|
|
if (str_starts_with($id, 'PO-')) {
|
|
|
|
|
$poIds[] = substr($id, 3);
|
|
|
|
|
} elseif (str_starts_with($id, 'UF-')) {
|
|
|
|
|
$ufIds[] = substr($id, 3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$purchaseOrders = $purchaseOrdersQuery->whereIn('id', $poIds)->get();
|
|
|
|
|
$utilityFees = $utilityFeesQuery->whereIn('id', $ufIds)->get();
|
|
|
|
|
} else {
|
|
|
|
|
$purchaseOrders = $purchaseOrdersQuery
|
|
|
|
|
->whereBetween('created_at', [$dateStart . ' 00:00:00', $dateEnd . ' 23:59:59'])
|
|
|
|
|
->get();
|
|
|
|
|
$utilityFees = $utilityFeesQuery
|
|
|
|
|
->whereBetween('transaction_date', [$dateStart, $dateEnd])
|
|
|
|
|
->get();
|
|
|
|
|
}
|
2026-01-20 09:44:05 +08:00
|
|
|
|
|
|
|
|
$allRecords = collect();
|
|
|
|
|
|
|
|
|
|
foreach ($purchaseOrders as $po) {
|
|
|
|
|
$allRecords->push([
|
2026-01-21 16:30:50 +08:00
|
|
|
Carbon::parse($po->created_at)->toDateString(),
|
2026-01-20 09:44:05 +08:00
|
|
|
'採購單',
|
|
|
|
|
'進貨支出',
|
|
|
|
|
$po->vendor->name ?? '',
|
|
|
|
|
$po->code,
|
|
|
|
|
$po->invoice_number,
|
2026-01-21 16:30:50 +08:00
|
|
|
(float)$po->grand_total,
|
2026-01-20 09:44:05 +08:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($utilityFees as $fee) {
|
|
|
|
|
$allRecords->push([
|
2026-01-21 16:30:50 +08:00
|
|
|
Carbon::parse($fee->transaction_date)->toDateString(),
|
2026-01-20 09:44:05 +08:00
|
|
|
'公共事業費',
|
|
|
|
|
$fee->category,
|
|
|
|
|
$fee->description,
|
|
|
|
|
'-',
|
|
|
|
|
$fee->invoice_number,
|
2026-01-21 16:30:50 +08:00
|
|
|
(float)$fee->amount,
|
2026-01-20 09:44:05 +08:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$allRecords = $allRecords->sortByDesc(0);
|
|
|
|
|
|
|
|
|
|
$filename = "accounting_report_{$dateStart}_{$dateEnd}.csv";
|
|
|
|
|
$headers = [
|
|
|
|
|
'Content-Type' => 'text/csv; charset=UTF-8',
|
|
|
|
|
'Content-Disposition' => "attachment; filename=\"{$filename}\"",
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$callback = function () use ($allRecords) {
|
|
|
|
|
$file = fopen('php://output', 'w');
|
|
|
|
|
// BOM for Excel compatibility with UTF-8
|
|
|
|
|
fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
|
|
|
|
|
|
|
|
|
|
fputcsv($file, ['日期', '來源', '類別', '項目', '參考單號', '發票號碼', '金額']);
|
|
|
|
|
|
|
|
|
|
foreach ($allRecords as $row) {
|
|
|
|
|
fputcsv($file, $row);
|
|
|
|
|
}
|
|
|
|
|
fclose($file);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return response()->stream($callback, 200, $headers);
|
|
|
|
|
}
|
|
|
|
|
}
|