with(['financeDocument', 'recordedByCashier']) ->orderByDesc('entry_date') ->orderByDesc('id'); // Filter by entry type if ($request->filled('entry_type')) { $query->where('entry_type', $request->entry_type); } // Filter by payment method if ($request->filled('payment_method')) { $query->where('payment_method', $request->payment_method); } // Filter by bank account if ($request->filled('bank_account')) { $query->where('bank_account', $request->bank_account); } // Filter by date range if ($request->filled('date_from')) { $query->where('entry_date', '>=', $request->date_from); } if ($request->filled('date_to')) { $query->where('entry_date', '<=', $request->date_to); } $entries = $query->paginate(20); // Get latest balance for each bank account $balances = DB::table('cashier_ledger_entries') ->select('bank_account', DB::raw('MAX(id) as latest_id')) ->groupBy('bank_account') ->get() ->mapWithKeys(function ($item) { $latest = CashierLedgerEntry::find($item->latest_id); return [$item->bank_account => $latest->balance_after ?? 0]; }); return view('admin.cashier-ledger.index', [ 'entries' => $entries, 'balances' => $balances, ]); } /** * Show the form for creating a new ledger entry */ public function create(Request $request) { // Get finance document if specified $financeDocument = null; if ($request->filled('finance_document_id')) { $financeDocument = FinanceDocument::with('paymentOrder')->findOrFail($request->finance_document_id); } return view('admin.cashier-ledger.create', [ 'financeDocument' => $financeDocument, ]); } /** * Store a newly created ledger entry */ public function store(Request $request) { $validated = $request->all(); // Get latest balance for the bank account $bankAccount = $validated['bank_account'] ?? 'default'; $balanceBefore = CashierLedgerEntry::getLatestBalance($bankAccount); $entryType = $validated['entry_type'] ?? CashierLedgerEntry::ENTRY_TYPE_RECEIPT; $amount = (float) ($validated['amount'] ?? 0); $entry = CashierLedgerEntry::create([ 'finance_document_id' => $validated['finance_document_id'] ?? null, 'entry_date' => $validated['entry_date'] ?? now(), 'entry_type' => $entryType, 'payment_method' => $validated['payment_method'] ?? CashierLedgerEntry::PAYMENT_METHOD_CASH, 'bank_account' => $bankAccount, 'amount' => $amount, 'balance_before' => $balanceBefore, 'balance_after' => $entryType === CashierLedgerEntry::ENTRY_TYPE_PAYMENT ? $balanceBefore - $amount : $balanceBefore + $amount, 'receipt_number' => $validated['receipt_number'] ?? null, 'transaction_reference' => $validated['transaction_reference'] ?? null, 'recorded_by_cashier_id' => $request->user()->id, 'recorded_at' => now(), 'notes' => $validated['notes'] ?? null, ]); if (!empty($validated['finance_document_id'])) { $financeDocument = FinanceDocument::find($validated['finance_document_id']); if ($financeDocument) { $financeDocument->update([ 'cashier_ledger_entry_id' => $entry->id, 'cashier_recorded_at' => now(), ]); } } return redirect() ->route('admin.cashier-ledger.show', $entry) ->with('status', '現金簿記錄已建立。'); } /** * Display the specified ledger entry */ public function show(CashierLedgerEntry $cashierLedgerEntry) { $cashierLedgerEntry->load([ 'financeDocument.member', 'financeDocument.paymentOrder', 'recordedByCashier' ]); return view('admin.cashier-ledger.show', [ 'entry' => $cashierLedgerEntry, ]); } /** * Show ledger balance report */ public function balanceReport(Request $request) { // Check authorization $this->authorize('view_cashier_ledger'); // Get all bank accounts with their latest balances $accounts = DB::table('cashier_ledger_entries') ->select('bank_account') ->distinct() ->get() ->map(function ($account) { $latest = CashierLedgerEntry::where('bank_account', $account->bank_account) ->orderBy('entry_date', 'desc') ->orderBy('id', 'desc') ->first(); return [ 'bank_account' => $account->bank_account, 'balance' => $latest->balance_after ?? 0, 'last_updated' => $latest->entry_date ?? null, ]; }); // Get transaction summary for current month $startOfMonth = now()->startOfMonth(); $endOfMonth = now()->endOfMonth(); $monthlySummary = [ 'receipts' => CashierLedgerEntry::where('entry_type', CashierLedgerEntry::ENTRY_TYPE_RECEIPT) ->whereBetween('entry_date', [$startOfMonth, $endOfMonth]) ->sum('amount'), 'payments' => CashierLedgerEntry::where('entry_type', CashierLedgerEntry::ENTRY_TYPE_PAYMENT) ->whereBetween('entry_date', [$startOfMonth, $endOfMonth]) ->sum('amount'), ]; return view('admin.cashier-ledger.balance-report', [ 'accounts' => $accounts, 'monthlySummary' => $monthlySummary, ]); } /** * Export ledger entries to CSV */ public function export(Request $request) { // Check authorization $this->authorize('view_cashier_ledger'); $query = CashierLedgerEntry::query() ->with(['financeDocument', 'recordedByCashier']) ->orderBy('entry_date') ->orderBy('id'); // Apply filters if ($request->filled('date_from')) { $query->where('entry_date', '>=', $request->date_from); } if ($request->filled('date_to')) { $query->where('entry_date', '<=', $request->date_to); } if ($request->filled('bank_account')) { $query->where('bank_account', $request->bank_account); } $entries = $query->get(); $filename = 'cashier_ledger_' . now()->format('Ymd_His') . '.csv'; $headers = [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Content-Disposition' => "attachment; filename=\"{$filename}\"", ]; $callback = function() use ($entries) { $file = fopen('php://output', 'w'); // Add BOM for UTF-8 fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF)); // Header row fputcsv($file, [ '記帳日期', '類型', '付款方式', '銀行帳戶', '金額', '交易前餘額', '交易後餘額', '收據編號', '交易參考號', '記錄人', '備註', ]); // Data rows foreach ($entries as $entry) { fputcsv($file, [ $entry->entry_date->format('Y-m-d'), $entry->getEntryTypeText(), $entry->getPaymentMethodText(), $entry->bank_account ?? '', $entry->amount, $entry->balance_before, $entry->balance_after, $entry->receipt_number ?? '', $entry->transaction_reference ?? '', $entry->recordedByCashier->name ?? '', $entry->notes ?? '', ]); } fclose($file); }; return response()->stream($callback, 200, $headers); } }