Files
usher-manage-stack/app/Http/Controllers/BankReconciliationController.php
T

273 lines
9.0 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\BankReconciliation;
use App\Models\CashierLedgerEntry;
use App\Support\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class BankReconciliationController extends Controller
{
/**
* Display a listing of bank reconciliations
*/
public function index(Request $request)
{
$query = BankReconciliation::query()
->with([
'preparedByCashier',
'reviewedByAccountant',
'approvedByManager'
])
->orderByDesc('reconciliation_month');
// Filter by status
if ($request->filled('reconciliation_status')) {
$query->where('reconciliation_status', $request->reconciliation_status);
}
// Filter by month
if ($request->filled('month')) {
$query->whereYear('reconciliation_month', '=', substr($request->month, 0, 4))
->whereMonth('reconciliation_month', '=', substr($request->month, 5, 2));
}
$reconciliations = $query->paginate(15);
return view('admin.bank-reconciliations.index', [
'reconciliations' => $reconciliations,
]);
}
/**
* Show the form for creating a new bank reconciliation
*/
public function create(Request $request)
{
// Check authorization
$this->authorize('prepare_bank_reconciliation');
// Default to current month
$month = $request->input('month', now()->format('Y-m'));
// Get system book balance from cashier ledger
$systemBalance = CashierLedgerEntry::getLatestBalance();
return view('admin.bank-reconciliations.create', [
'month' => $month,
'systemBalance' => $systemBalance,
]);
}
/**
* Store a newly created bank reconciliation
*/
public function store(Request $request)
{
// Check authorization
$this->authorize('prepare_bank_reconciliation');
$validated = $request->all();
DB::beginTransaction();
try {
// Handle bank statement file upload
$statementPath = null;
if ($request->hasFile('bank_statement_file')) {
$statementPath = $request->file('bank_statement_file')->store('bank-statements', 'local');
}
// Create reconciliation record
$reconciliation = new BankReconciliation([
'reconciliation_month' => ($validated['reconciliation_month'] ?? now()->format('Y-m')) . '-01',
'bank_statement_balance' => $validated['bank_statement_balance'] ?? 0,
'bank_statement_date' => $validated['bank_statement_date'] ?? now()->format('Y-m-d'),
'bank_statement_file_path' => $statementPath,
'system_book_balance' => $validated['system_book_balance'] ?? 0,
'outstanding_checks' => $validated['outstanding_checks'] ?? [],
'deposits_in_transit' => $validated['deposits_in_transit'] ?? [],
'bank_charges' => $validated['bank_charges'] ?? [],
'prepared_by_cashier_id' => $request->user()->id,
'prepared_at' => now(),
'notes' => $validated['notes'] ?? null,
]);
// Ensure required numeric fields
$adjusted = (float) $reconciliation->calculateAdjustedBalance();
$reconciliation->adjusted_balance = $adjusted ?: 0;
$reconciliation->discrepancy_amount = (float) $reconciliation->calculateDiscrepancy();
$reconciliation->reconciliation_status = BankReconciliation::STATUS_PENDING;
$reconciliation->save();
DB::commit();
$message = '銀行調節表已建立。';
if ($reconciliation->hasDiscrepancy()) {
$message .= ' 發現差異金額:NT$ ' . number_format($reconciliation->discrepancy_amount, 2);
}
return redirect()
->route('admin.bank-reconciliations.show', $reconciliation)
->with('status', $message);
} catch (\Exception $e) {
DB::rollBack();
return redirect()
->back()
->withInput()
->with('error', '建立銀行調節表時發生錯誤:' . $e->getMessage());
}
}
/**
* Display the specified bank reconciliation
*/
public function show(BankReconciliation $bankReconciliation)
{
$bankReconciliation->load([
'preparedByCashier',
'reviewedByAccountant',
'approvedByManager'
]);
// Get outstanding items summary
$summary = $bankReconciliation->getOutstandingItemsSummary();
return view('admin.bank-reconciliations.show', [
'reconciliation' => $bankReconciliation,
'summary' => $summary,
]);
}
/**
* Accountant reviews the bank reconciliation
*/
public function review(Request $request, BankReconciliation $bankReconciliation)
{
// Check authorization
$this->authorize('review_bank_reconciliation');
// Check if can be reviewed
if (!$bankReconciliation->canBeReviewed()) {
return redirect()
->route('admin.bank-reconciliations.show', $bankReconciliation)
->with('error', '此銀行調節表無法覆核。');
}
$validated = $request->validate([
'review_notes' => ['nullable', 'string'],
]);
DB::beginTransaction();
try {
$bankReconciliation->update([
'reviewed_by_accountant_id' => $request->user()->id,
'reviewed_at' => now(),
]);
DB::commit();
return redirect()
->route('admin.bank-reconciliations.show', $bankReconciliation)
->with('status', '銀行調節表已完成會計覆核。');
} catch (\Exception $e) {
DB::rollBack();
return redirect()
->back()
->with('error', '覆核銀行調節表時發生錯誤:' . $e->getMessage());
}
}
/**
* Manager approves the bank reconciliation
*/
public function approve(Request $request, BankReconciliation $bankReconciliation)
{
// Check authorization
$this->authorize('approve_bank_reconciliation');
// Check if can be approved
if (!$bankReconciliation->canBeApproved()) {
return redirect()
->route('admin.bank-reconciliations.show', $bankReconciliation)
->with('error', '此銀行調節表無法核准。');
}
DB::beginTransaction();
try {
// Determine final status
$finalStatus = $bankReconciliation->hasDiscrepancy()
? BankReconciliation::STATUS_DISCREPANCY
: BankReconciliation::STATUS_COMPLETED;
$bankReconciliation->update([
'approved_by_manager_id' => $request->user()->id,
'approved_at' => now(),
'reconciliation_status' => $finalStatus,
]);
DB::commit();
$message = '銀行調節表已核准。';
if ($finalStatus === BankReconciliation::STATUS_DISCREPANCY) {
$message .= ' 請注意:仍有差異需要處理。';
}
return redirect()
->route('admin.bank-reconciliations.show', $bankReconciliation)
->with('status', $message);
} catch (\Exception $e) {
DB::rollBack();
return redirect()
->back()
->with('error', '核准銀行調節表時發生錯誤:' . $e->getMessage());
}
}
/**
* Download bank statement file
*/
public function downloadStatement(BankReconciliation $bankReconciliation)
{
if (!$bankReconciliation->bank_statement_file_path) {
abort(404, '找不到銀行對帳單檔案');
}
if (!Storage::disk('local')->exists($bankReconciliation->bank_statement_file_path)) {
abort(404, '銀行對帳單檔案不存在');
}
return Storage::disk('local')->download($bankReconciliation->bank_statement_file_path);
}
/**
* Export reconciliation to PDF
*/
public function exportPdf(BankReconciliation $bankReconciliation)
{
// Check authorization
$this->authorize('view_cashier_ledger');
$bankReconciliation->load([
'preparedByCashier',
'reviewedByAccountant',
'approvedByManager'
]);
$summary = $bankReconciliation->getOutstandingItemsSummary();
// Generate PDF (you would need to implement PDF generation library like DomPDF or TCPDF)
// For now, return a view that can be printed
return view('admin.bank-reconciliations.pdf', [
'reconciliation' => $bankReconciliation,
'summary' => $summary,
]);
}
}