Initial commit
This commit is contained in:
306
app/Http/Controllers/BankReconciliationController.php
Normal file
306
app/Http/Controllers/BankReconciliationController.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?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->validate([
|
||||
'reconciliation_month' => ['required', 'date_format:Y-m'],
|
||||
'bank_statement_balance' => ['required', 'numeric'],
|
||||
'bank_statement_date' => ['required', 'date'],
|
||||
'bank_statement_file' => ['nullable', 'file', 'max:10240'],
|
||||
'system_book_balance' => ['required', 'numeric'],
|
||||
'outstanding_checks' => ['nullable', 'array'],
|
||||
'outstanding_checks.*.amount' => ['required', 'numeric', 'min:0'],
|
||||
'outstanding_checks.*.check_number' => ['nullable', 'string'],
|
||||
'outstanding_checks.*.description' => ['nullable', 'string'],
|
||||
'deposits_in_transit' => ['nullable', 'array'],
|
||||
'deposits_in_transit.*.amount' => ['required', 'numeric', 'min:0'],
|
||||
'deposits_in_transit.*.date' => ['nullable', 'date'],
|
||||
'deposits_in_transit.*.description' => ['nullable', 'string'],
|
||||
'bank_charges' => ['nullable', 'array'],
|
||||
'bank_charges.*.amount' => ['required', 'numeric', 'min:0'],
|
||||
'bank_charges.*.description' => ['nullable', 'string'],
|
||||
'notes' => ['nullable', 'string'],
|
||||
]);
|
||||
|
||||
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'] . '-01',
|
||||
'bank_statement_balance' => $validated['bank_statement_balance'],
|
||||
'bank_statement_date' => $validated['bank_statement_date'],
|
||||
'bank_statement_file_path' => $statementPath,
|
||||
'system_book_balance' => $validated['system_book_balance'],
|
||||
'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,
|
||||
]);
|
||||
|
||||
// Calculate adjusted balance
|
||||
$reconciliation->adjusted_balance = $reconciliation->calculateAdjustedBalance();
|
||||
|
||||
// Calculate discrepancy
|
||||
$reconciliation->discrepancy_amount = $reconciliation->calculateDiscrepancy();
|
||||
|
||||
// Set status based on discrepancy
|
||||
if ($reconciliation->hasDiscrepancy()) {
|
||||
$reconciliation->reconciliation_status = BankReconciliation::STATUS_DISCREPANCY;
|
||||
} else {
|
||||
$reconciliation->reconciliation_status = BankReconciliation::STATUS_PENDING;
|
||||
}
|
||||
|
||||
$reconciliation->save();
|
||||
|
||||
AuditLogger::log('bank_reconciliation.created', $reconciliation, $validated);
|
||||
|
||||
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(),
|
||||
]);
|
||||
|
||||
AuditLogger::log('bank_reconciliation.reviewed', $bankReconciliation, $validated);
|
||||
|
||||
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,
|
||||
]);
|
||||
|
||||
AuditLogger::log('bank_reconciliation.approved', $bankReconciliation, [
|
||||
'approved_by' => $request->user()->name,
|
||||
'final_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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user