100000, 'outstanding_checks' => [ ['check_number' => 'CHK001', 'amount' => 3000], ['check_number' => 'CHK002', 'amount' => 2000], ], 'deposits_in_transit' => [ ['date' => '2024-01-15', 'amount' => 5000], ['date' => '2024-01-16', 'amount' => 3000], ], 'bank_charges' => [ ['amount' => 500], ['amount' => 200], ], ]); // Adjusted = 100000 + (5000 + 3000) - (3000 + 2000) - (500 + 200) = 102300 $adjusted = $reconciliation->calculateAdjustedBalance(); $this->assertEquals(102300, $adjusted); } /** @test */ public function it_calculates_adjusted_balance_with_no_items() { $reconciliation = new BankReconciliation([ 'system_book_balance' => 50000, 'outstanding_checks' => null, 'deposits_in_transit' => null, 'bank_charges' => null, ]); $adjusted = $reconciliation->calculateAdjustedBalance(); $this->assertEquals(50000, $adjusted); } /** @test */ public function it_calculates_adjusted_balance_with_empty_arrays() { $reconciliation = new BankReconciliation([ 'system_book_balance' => 50000, 'outstanding_checks' => [], 'deposits_in_transit' => [], 'bank_charges' => [], ]); $adjusted = $reconciliation->calculateAdjustedBalance(); $this->assertEquals(50000, $adjusted); } /** @test */ public function it_calculates_discrepancy_correctly() { $reconciliation = new BankReconciliation([ 'bank_statement_balance' => 100000, 'system_book_balance' => 95000, 'outstanding_checks' => [ ['amount' => 3000], ], 'deposits_in_transit' => [ ['amount' => 5000], ], 'bank_charges' => [ ['amount' => 500], ], ]); // Adjusted = 95000 + 5000 - 3000 - 500 = 96500 // Discrepancy = |100000 - 96500| = 3500 $discrepancy = $reconciliation->calculateDiscrepancy(); $this->assertEquals(3500, $discrepancy); } /** @test */ public function it_detects_discrepancy_above_tolerance() { $reconciliation = new BankReconciliation([ 'bank_statement_balance' => 100000, 'system_book_balance' => 95000, 'outstanding_checks' => [], 'deposits_in_transit' => [], 'bank_charges' => [], ]); // Discrepancy = 5000, which is > 0.01 $this->assertTrue($reconciliation->hasDiscrepancy()); } /** @test */ public function it_allows_small_discrepancy_within_tolerance() { $reconciliation = new BankReconciliation([ 'bank_statement_balance' => 100000.00, 'system_book_balance' => 100000.00, 'outstanding_checks' => [], 'deposits_in_transit' => [], 'bank_charges' => [], ]); // Discrepancy = 0, which is <= 0.01 $this->assertFalse($reconciliation->hasDiscrepancy()); } /** @test */ public function it_generates_outstanding_items_summary_correctly() { $reconciliation = new BankReconciliation([ 'outstanding_checks' => [ ['check_number' => 'CHK001', 'amount' => 3000], ['check_number' => 'CHK002', 'amount' => 2000], ['check_number' => 'CHK003', 'amount' => 1500], ], 'deposits_in_transit' => [ ['date' => '2024-01-15', 'amount' => 5000], ['date' => '2024-01-16', 'amount' => 3000], ], 'bank_charges' => [ ['amount' => 500], ['amount' => 200], ['amount' => 100], ], ]); $summary = $reconciliation->getOutstandingItemsSummary(); $this->assertEquals(6500, $summary['total_outstanding_checks']); $this->assertEquals(3, $summary['outstanding_checks_count']); $this->assertEquals(8000, $summary['total_deposits_in_transit']); $this->assertEquals(2, $summary['deposits_in_transit_count']); $this->assertEquals(800, $summary['total_bank_charges']); $this->assertEquals(3, $summary['bank_charges_count']); } /** @test */ public function it_handles_null_outstanding_items_in_summary() { $reconciliation = new BankReconciliation([ 'outstanding_checks' => null, 'deposits_in_transit' => null, 'bank_charges' => null, ]); $summary = $reconciliation->getOutstandingItemsSummary(); $this->assertEquals(0, $summary['total_outstanding_checks']); $this->assertEquals(0, $summary['outstanding_checks_count']); $this->assertEquals(0, $summary['total_deposits_in_transit']); $this->assertEquals(0, $summary['deposits_in_transit_count']); $this->assertEquals(0, $summary['total_bank_charges']); $this->assertEquals(0, $summary['bank_charges_count']); } /** @test */ public function it_can_be_reviewed_when_pending() { $reconciliation = new BankReconciliation([ 'reconciliation_status' => 'pending', 'reviewed_at' => null, ]); $this->assertTrue($reconciliation->canBeReviewed()); } /** @test */ public function it_cannot_be_reviewed_when_already_reviewed() { $reconciliation = new BankReconciliation([ 'reconciliation_status' => 'pending', 'reviewed_at' => now(), ]); $this->assertFalse($reconciliation->canBeReviewed()); } /** @test */ public function it_can_be_approved_when_reviewed() { $reconciliation = new BankReconciliation([ 'reconciliation_status' => 'pending', 'reviewed_at' => now(), 'approved_at' => null, ]); $this->assertTrue($reconciliation->canBeApproved()); } /** @test */ public function it_cannot_be_approved_when_not_reviewed() { $reconciliation = new BankReconciliation([ 'reconciliation_status' => 'pending', 'reviewed_at' => null, 'approved_at' => null, ]); $this->assertFalse($reconciliation->canBeApproved()); } /** @test */ public function it_cannot_be_approved_when_already_approved() { $reconciliation = new BankReconciliation([ 'reconciliation_status' => 'completed', 'reviewed_at' => now(), 'approved_at' => now(), ]); $this->assertFalse($reconciliation->canBeApproved()); } /** @test */ public function it_detects_pending_status() { $pending = new BankReconciliation(['reconciliation_status' => 'pending']); $this->assertTrue($pending->isPending()); $completed = new BankReconciliation(['reconciliation_status' => 'completed']); $this->assertFalse($completed->isPending()); } /** @test */ public function it_detects_completed_status() { $completed = new BankReconciliation(['reconciliation_status' => 'completed']); $this->assertTrue($completed->isCompleted()); $pending = new BankReconciliation(['reconciliation_status' => 'pending']); $this->assertFalse($pending->isCompleted()); } /** @test */ public function it_detects_unresolved_discrepancy() { $withDiscrepancy = new BankReconciliation([ 'reconciliation_status' => 'discrepancy', ]); $this->assertTrue($withDiscrepancy->hasUnresolvedDiscrepancy()); $completed = new BankReconciliation([ 'reconciliation_status' => 'completed', ]); $this->assertFalse($completed->hasUnresolvedDiscrepancy()); } /** @test */ public function status_text_is_correct() { $pending = new BankReconciliation(['reconciliation_status' => 'pending']); $this->assertEquals('待覆核', $pending->getStatusText()); $completed = new BankReconciliation(['reconciliation_status' => 'completed']); $this->assertEquals('已完成', $completed->getStatusText()); $discrepancy = new BankReconciliation(['reconciliation_status' => 'discrepancy']); $this->assertEquals('有差異', $discrepancy->getStatusText()); } /** @test */ public function it_handles_missing_amounts_in_outstanding_items() { $reconciliation = new BankReconciliation([ 'system_book_balance' => 100000, 'outstanding_checks' => [ ['check_number' => 'CHK001'], // Missing amount ['check_number' => 'CHK002', 'amount' => 2000], ], 'deposits_in_transit' => [ ['date' => '2024-01-15'], // Missing amount ['date' => '2024-01-16', 'amount' => 3000], ], 'bank_charges' => [ ['description' => 'Fee'], // Missing amount ['amount' => 200], ], ]); // Should handle missing amounts gracefully (treat as 0) $adjusted = $reconciliation->calculateAdjustedBalance(); // 100000 + 3000 - 2000 - 200 = 100800 $this->assertEquals(100800, $adjusted); } }