4999]); $this->assertEquals('small', $document->determineAmountTier()); $document->amount = 3000; $this->assertEquals('small', $document->determineAmountTier()); $document->amount = 1; $this->assertEquals('small', $document->determineAmountTier()); } /** @test */ public function it_determines_medium_amount_tier_correctly() { $document = new FinanceDocument(['amount' => 5000]); $this->assertEquals('medium', $document->determineAmountTier()); $document->amount = 25000; $this->assertEquals('medium', $document->determineAmountTier()); $document->amount = 50000; $this->assertEquals('medium', $document->determineAmountTier()); } /** @test */ public function it_determines_large_amount_tier_correctly() { $document = new FinanceDocument(['amount' => 50001]); $this->assertEquals('large', $document->determineAmountTier()); $document->amount = 100000; $this->assertEquals('large', $document->determineAmountTier()); $document->amount = 1000000; $this->assertEquals('large', $document->determineAmountTier()); } /** @test */ public function small_amount_does_not_need_board_meeting() { $document = new FinanceDocument(['amount' => 4999]); $this->assertFalse($document->needsBoardMeetingApproval()); } /** @test */ public function medium_amount_does_not_need_board_meeting() { $document = new FinanceDocument(['amount' => 50000]); $this->assertFalse($document->needsBoardMeetingApproval()); } /** @test */ public function large_amount_needs_board_meeting() { $document = new FinanceDocument(['amount' => 50001]); $this->assertTrue($document->needsBoardMeetingApproval()); } /** @test */ public function small_amount_approval_stage_is_complete_after_secretary() { $document = new FinanceDocument([ 'amount' => 3000, 'amount_tier' => 'small', 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, ]); $this->assertTrue($document->isApprovalComplete()); } /** @test */ public function medium_amount_approval_stage_needs_chair() { $document = new FinanceDocument([ 'amount' => 25000, 'amount_tier' => 'medium', 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, ]); $this->assertFalse($document->isApprovalComplete()); $document->status = FinanceDocument::STATUS_APPROVED_CHAIR; $this->assertTrue($document->isApprovalComplete()); } /** @test */ public function large_amount_approval_stage_needs_chair_and_board() { $document = new FinanceDocument([ 'amount' => 75000, 'amount_tier' => 'large', 'status' => FinanceDocument::STATUS_APPROVED_CHAIR, ]); $this->assertFalse($document->isApprovalComplete()); $document->status = FinanceDocument::STATUS_APPROVED_BOARD; $this->assertTrue($document->isApprovalComplete()); } /** @test */ public function secretary_cannot_approve_own_submission() { $user = User::factory()->create(); $document = new FinanceDocument([ 'submitted_by_user_id' => $user->id, 'status' => FinanceDocument::STATUS_PENDING, ]); $this->assertFalse($document->canBeApprovedBySecretary($user)); } /** @test */ public function secretary_can_approve_others_submission() { $submitter = User::factory()->create(); $secretary = User::factory()->create(); $document = new FinanceDocument([ 'submitted_by_user_id' => $submitter->id, 'status' => FinanceDocument::STATUS_PENDING, ]); $this->assertTrue($document->canBeApprovedBySecretary($secretary)); } /** @test */ public function chair_cannot_approve_before_secretary() { $document = new FinanceDocument([ 'status' => FinanceDocument::STATUS_PENDING, 'amount_tier' => 'medium', ]); $this->assertFalse($document->canBeApprovedByChair()); } /** @test */ public function chair_can_approve_after_secretary() { $document = new FinanceDocument([ 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, 'amount_tier' => 'medium', ]); $this->assertTrue($document->canBeApprovedByChair()); } /** @test */ public function chair_can_approve_after_secretary_for_medium_amounts() { $document = new FinanceDocument([ 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, 'amount_tier' => 'medium', ]); $this->assertTrue($document->canBeApprovedByChair()); } /** @test */ public function payment_order_can_be_created_after_approval_stage() { $document = new FinanceDocument([ 'amount' => 3000, 'amount_tier' => 'small', 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, ]); $this->assertTrue($document->canCreatePaymentOrder()); } /** @test */ public function payment_order_cannot_be_created_before_approval_complete() { $document = new FinanceDocument([ 'status' => FinanceDocument::STATUS_PENDING, 'amount_tier' => 'small', ]); $this->assertFalse($document->canCreatePaymentOrder()); } /** @test */ public function disbursement_requires_dual_confirmation() { $document = new FinanceDocument([ 'amount' => 3000, 'amount_tier' => 'small', 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, ]); $this->assertFalse($document->isDisbursementComplete()); // Only requester confirmed $document->requester_confirmed_at = now(); $this->assertFalse($document->isDisbursementComplete()); // Both confirmed $document->cashier_confirmed_at = now(); $this->assertTrue($document->isDisbursementComplete()); } /** @test */ public function payment_completed_check_works() { $document = new FinanceDocument([ 'payment_order_created_at' => now(), 'payment_verified_at' => now(), 'payment_executed_at' => null, ]); $this->assertFalse($document->isPaymentCompleted()); $document->payment_executed_at = now(); $this->assertTrue($document->isPaymentCompleted()); } /** @test */ public function recording_complete_check_works() { $document = new FinanceDocument([ 'accountant_recorded_at' => null, ]); $this->assertFalse($document->isRecordingComplete()); $document->accountant_recorded_at = now(); $this->assertTrue($document->isRecordingComplete()); } /** @test */ public function reconciled_check_works() { $document = new FinanceDocument([ 'bank_reconciliation_id' => null, ]); $this->assertFalse($document->isReconciled()); $document->bank_reconciliation_id = 1; $this->assertTrue($document->isReconciled()); } /** @test */ public function amount_tier_text_is_correct() { $small = new FinanceDocument(['amount_tier' => 'small']); $this->assertEquals('小額(< 5000)', $small->getAmountTierText()); $medium = new FinanceDocument(['amount_tier' => 'medium']); $this->assertEquals('中額(5000-50000)', $medium->getAmountTierText()); $large = new FinanceDocument(['amount_tier' => 'large']); $this->assertEquals('大額(> 50000)', $large->getAmountTierText()); } /** @test */ public function fully_processed_check_works() { $document = new FinanceDocument([ 'amount' => 3000, 'amount_tier' => 'small', 'status' => FinanceDocument::STATUS_APPROVED_SECRETARY, ]); $this->assertFalse($document->isFullyProcessed()); // Complete disbursement $document->requester_confirmed_at = now(); $document->cashier_confirmed_at = now(); $this->assertFalse($document->isFullyProcessed()); // Complete recording $document->accountant_recorded_at = now(); $this->assertTrue($document->isFullyProcessed()); } }