Files
usher-manage-stack/tests/Unit/FinanceDocumentTest.php
2025-11-20 23:21:05 +08:00

313 lines
9.5 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Tests\Unit;
use App\Models\FinanceDocument;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
/**
* Finance Document Model Unit Tests
*
* Tests business logic methods in FinanceDocument model
*/
class FinanceDocumentTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function it_determines_small_amount_tier_correctly()
{
$document = new FinanceDocument(['amount' => 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_accountant()
{
$document = new FinanceDocument([
'amount' => 3000,
'amount_tier' => 'small',
'status' => FinanceDocument::STATUS_APPROVED_ACCOUNTANT,
]);
$this->assertTrue($document->isApprovalStageComplete());
}
/** @test */
public function medium_amount_approval_stage_needs_chair()
{
$document = new FinanceDocument([
'amount' => 25000,
'amount_tier' => 'medium',
'status' => FinanceDocument::STATUS_APPROVED_ACCOUNTANT,
]);
$this->assertFalse($document->isApprovalStageComplete());
$document->status = FinanceDocument::STATUS_APPROVED_CHAIR;
$this->assertTrue($document->isApprovalStageComplete());
}
/** @test */
public function large_amount_approval_stage_needs_chair_and_board()
{
$document = new FinanceDocument([
'amount' => 75000,
'amount_tier' => 'large',
'status' => FinanceDocument::STATUS_APPROVED_CHAIR,
'board_meeting_approved_at' => null,
]);
$this->assertFalse($document->isApprovalStageComplete());
$document->board_meeting_approved_at = now();
$this->assertTrue($document->isApprovalStageComplete());
}
/** @test */
public function cashier_cannot_approve_own_submission()
{
$user = User::factory()->create();
$document = new FinanceDocument([
'submitted_by_id' => $user->id,
'status' => 'pending',
]);
$this->assertFalse($document->canBeApprovedByCashier($user));
}
/** @test */
public function cashier_can_approve_others_submission()
{
$submitter = User::factory()->create();
$cashier = User::factory()->create();
$document = new FinanceDocument([
'submitted_by_id' => $submitter->id,
'status' => 'pending',
]);
$this->assertTrue($document->canBeApprovedByCashier($cashier));
}
/** @test */
public function accountant_cannot_approve_before_cashier()
{
$document = new FinanceDocument([
'status' => 'pending',
]);
$this->assertFalse($document->canBeApprovedByAccountant());
}
/** @test */
public function accountant_can_approve_after_cashier()
{
$document = new FinanceDocument([
'status' => FinanceDocument::STATUS_APPROVED_CASHIER,
]);
$this->assertTrue($document->canBeApprovedByAccountant());
}
/** @test */
public function chair_cannot_approve_before_accountant()
{
$document = new FinanceDocument([
'status' => FinanceDocument::STATUS_APPROVED_CASHIER,
'amount_tier' => 'medium',
]);
$this->assertFalse($document->canBeApprovedByChair());
}
/** @test */
public function chair_can_approve_after_accountant_for_medium_amounts()
{
$document = new FinanceDocument([
'status' => FinanceDocument::STATUS_APPROVED_ACCOUNTANT,
'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_ACCOUNTANT,
]);
$this->assertTrue($document->canCreatePaymentOrder());
}
/** @test */
public function payment_order_cannot_be_created_before_approval_complete()
{
$document = new FinanceDocument([
'status' => FinanceDocument::STATUS_APPROVED_CASHIER,
]);
$this->assertFalse($document->canCreatePaymentOrder());
}
/** @test */
public function workflow_stages_are_correctly_identified()
{
$document = new FinanceDocument([
'status' => 'pending',
'amount_tier' => 'small',
]);
// Stage 1: Approval
$this->assertEquals('approval', $document->getCurrentWorkflowStage());
// Stage 2: Payment
$document->status = FinanceDocument::STATUS_APPROVED_ACCOUNTANT;
$document->cashier_approved_at = now();
$document->accountant_approved_at = now();
$document->payment_order_created_at = now();
$this->assertEquals('payment', $document->getCurrentWorkflowStage());
// Stage 3: Recording
$document->payment_executed_at = now();
$this->assertEquals('payment', $document->getCurrentWorkflowStage());
$document->cashier_recorded_at = now();
$this->assertEquals('recording', $document->getCurrentWorkflowStage());
// Stage 4: Reconciliation
$document->bank_reconciliation_id = 1;
$this->assertEquals('completed', $document->getCurrentWorkflowStage());
}
/** @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([
'cashier_recorded_at' => null,
]);
$this->assertFalse($document->isRecordingComplete());
$document->cashier_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 request_type_text_is_correct()
{
$doc1 = new FinanceDocument(['request_type' => 'expense_reimbursement']);
$this->assertEquals('費用報銷', $doc1->getRequestTypeText());
$doc2 = new FinanceDocument(['request_type' => 'advance_payment']);
$this->assertEquals('預支款項', $doc2->getRequestTypeText());
$doc3 = new FinanceDocument(['request_type' => 'purchase_request']);
$this->assertEquals('採購申請', $doc3->getRequestTypeText());
$doc4 = new FinanceDocument(['request_type' => 'petty_cash']);
$this->assertEquals('零用金', $doc4->getRequestTypeText());
}
/** @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());
}
}