seedRolesAndPermissions(); } /** * Test can view bank reconciliation page */ public function test_can_view_bank_reconciliation_page(): void { $accountant = $this->createAccountant(); $response = $this->actingAs($accountant)->get( route('admin.bank-reconciliation.index') ); $response->assertStatus(200); } /** * Test can create reconciliation */ public function test_can_create_reconciliation(): void { $accountant = $this->createAccountant(); $response = $this->actingAs($accountant)->post( route('admin.bank-reconciliation.store'), [ 'reconciliation_date' => now()->toDateString(), 'bank_statement_balance' => 500000, 'ledger_balance' => 500000, 'notes' => '月末對帳', ] ); $response->assertRedirect(); $this->assertDatabaseHas('bank_reconciliations', [ 'bank_statement_balance' => 500000, ]); } /** * Test reconciliation detects discrepancy */ public function test_reconciliation_detects_discrepancy(): void { $accountant = $this->createAccountant(); $reconciliation = $this->createBankReconciliation([ 'bank_statement_balance' => 500000, 'ledger_balance' => 480000, ]); $this->assertNotEquals( $reconciliation->bank_statement_balance, $reconciliation->ledger_balance ); $discrepancy = $reconciliation->bank_statement_balance - $reconciliation->ledger_balance; $this->assertEquals(20000, $discrepancy); } /** * Test can upload bank statement */ public function test_can_upload_bank_statement(): void { $accountant = $this->createAccountant(); $file = UploadedFile::fake()->create('bank_statement.pdf', 1024); $response = $this->actingAs($accountant)->post( route('admin.bank-reconciliation.store'), [ 'reconciliation_date' => now()->toDateString(), 'bank_statement_balance' => 500000, 'ledger_balance' => 500000, 'bank_statement_file' => $file, ] ); $response->assertRedirect(); } /** * Test reconciliation marks ledger entries */ public function test_reconciliation_marks_ledger_entries(): void { $accountant = $this->createAccountant(); $entry1 = $this->createCashierLedgerEntry(['is_reconciled' => false]); $entry2 = $this->createCashierLedgerEntry(['is_reconciled' => false]); $response = $this->actingAs($accountant)->post( route('admin.bank-reconciliation.store'), [ 'reconciliation_date' => now()->toDateString(), 'bank_statement_balance' => 100000, 'ledger_balance' => 100000, 'ledger_entry_ids' => [$entry1->id, $entry2->id], ] ); $entry1->refresh(); $entry2->refresh(); $this->assertTrue($entry1->is_reconciled); $this->assertTrue($entry2->is_reconciled); } /** * Test reconciliation status tracking */ public function test_reconciliation_status_tracking(): void { $reconciliation = $this->createBankReconciliation([ 'status' => BankReconciliation::STATUS_PENDING, ]); $this->assertEquals(BankReconciliation::STATUS_PENDING, $reconciliation->status); } /** * Test reconciliation approval */ public function test_reconciliation_approval(): void { $chair = $this->createChair(); $reconciliation = $this->createBankReconciliation([ 'status' => BankReconciliation::STATUS_PENDING, ]); $response = $this->actingAs($chair)->post( route('admin.bank-reconciliation.approve', $reconciliation) ); $reconciliation->refresh(); $this->assertEquals(BankReconciliation::STATUS_APPROVED, $reconciliation->status); } /** * Test reconciliation date filter */ public function test_reconciliation_date_filter(): void { $accountant = $this->createAccountant(); $this->createBankReconciliation([ 'reconciliation_date' => now()->subMonth(), ]); $this->createBankReconciliation([ 'reconciliation_date' => now(), ]); $response = $this->actingAs($accountant)->get( route('admin.bank-reconciliation.index', [ 'start_date' => now()->startOfMonth()->toDateString(), 'end_date' => now()->endOfMonth()->toDateString(), ]) ); $response->assertStatus(200); } /** * Test reconciliation requires matching balances warning */ public function test_reconciliation_requires_matching_balances_warning(): void { $accountant = $this->createAccountant(); $response = $this->actingAs($accountant)->post( route('admin.bank-reconciliation.store'), [ 'reconciliation_date' => now()->toDateString(), 'bank_statement_balance' => 500000, 'ledger_balance' => 400000, ] ); // Should still create but flag discrepancy $this->assertDatabaseHas('bank_reconciliations', [ 'has_discrepancy' => true, ]); } /** * Test reconciliation history */ public function test_reconciliation_history(): void { $accountant = $this->createAccountant(); for ($i = 0; $i < 3; $i++) { $this->createBankReconciliation([ 'reconciliation_date' => now()->subMonths($i), ]); } $response = $this->actingAs($accountant)->get( route('admin.bank-reconciliation.history') ); $response->assertStatus(200); } /** * Test only authorized users can reconcile */ public function test_only_authorized_users_can_reconcile(): void { $regularUser = User::factory()->create(); $response = $this->actingAs($regularUser)->get( route('admin.bank-reconciliation.index') ); $response->assertStatus(403); } }