Files
usher-manage-stack/tests/FINANCIAL_WORKFLOW_TEST_PLAN.md
2025-11-20 23:21:05 +08:00

15 KiB

Financial Workflow System - Test Plan

Overview

This document outlines the complete testing strategy for the financial workflow system implementing the "會計管帳,出納管錢" (Accountant manages books, Cashier manages money) principle.


Test Environment Setup

Prerequisites

# 1. Run setup script
./setup-financial-workflow.sh

# 2. Verify test users created
php artisan tinker
>>> User::where('email', 'like', '%@test.com')->pluck('email', 'name')

# 3. Check permissions
>>> Role::with('permissions')->get()

Test Users

Email Password Role Purpose
cashier@test.com password finance_cashier Test cashier operations
accountant@test.com password finance_accountant Test accountant operations
chair@test.com password finance_chair Test chair approvals
requester@test.com password finance_requester Test document creation

1. Manual Testing Checklist

1.1 Stage 1: Approval Workflow

Small Amount (< 5,000) - Cashier → Accountant

  • Step 1: Login as requester@test.com

    • Navigate to /admin/finance-documents/create
    • Create document with:
      • Title: "小額報銷測試"
      • Amount: 3,000
      • Request Type: expense_reimbursement
      • Upload attachment
    • Verify document created with status "pending"
    • Verify amount_tier automatically set to "small"
  • Step 2: Login as cashier@test.com

    • Navigate to /admin/finance-documents
    • Find pending document
    • Click "Approve"
    • Verify status changed to "approved_cashier"
    • Verify email sent to accountant
  • Step 3: Login as accountant@test.com

    • View document
    • Click "Approve"
    • Verify status changed to "approved_accountant"
    • Verify message shows "小額申請審核完成,可以製作付款單"
    • Verify "Create Payment Order" button appears

Medium Amount (5,000-50,000) - Cashier → Accountant → Chair

  • Step 1: Create document with amount: 25,000
  • Step 2: Cashier approves
  • Step 3: Accountant approves
    • Verify message shows "已送交理事長審核"
  • Step 4: Login as chair@test.com
    • Approve document
    • Verify status changed to "approved_chair"
    • Verify message shows "審核流程完成"

Large Amount (> 50,000) - Cashier → Accountant → Chair → Board

  • Step 1: Create document with amount: 75,000
  • Step 2-4: Complete cashier, accountant, chair approvals
  • Step 5: Verify requires_board_meeting flag is true
  • Step 6: Verify message shows "大額申請仍需理事會核准"

1.2 Stage 2: Payment Workflow

Create Payment Order (Accountant)

  • Step 1: Login as accountant@test.com
  • Step 2: Navigate to approved document
  • Step 3: Click "製作付款單"
  • Step 4: Fill payment order form:
    • Payee Name: "Test Vendor"
    • Payment Method: "bank_transfer"
    • Bank Name: "Test Bank"
    • Bank Code: "007"
    • Account Number: "1234567890"
    • Amount: (auto-filled from document)
    • Notes: "測試付款單"
  • Step 5: Submit form
  • Step 6: Verify:
    • Payment order created with unique number (PO-YYYYMMDD-####)
    • Status is "pending_verification"
    • finance_document updated with payment_order_created_at
    • Redirect to payment order show page

Verify Payment Order (Cashier)

  • Step 1: Login as cashier@test.com
  • Step 2: Navigate to /admin/payment-orders
  • Step 3: Find pending payment order
  • Step 4: Click to view details
  • Step 5: Review payment information
  • Step 6: Option A - Approve:
    • Enter verification notes
    • Click "通過覆核"
    • Verify status changed to "verified"
    • Verify execution form appears
  • Step 7: Option B - Reject:
    • Enter rejection reason
    • Click "駁回"
    • Verify status changed to "cancelled"

Execute Payment (Cashier)

  • Step 1: With verified payment order
  • Step 2: Fill execution form:
    • Transaction Reference: "TXN-2025-001"
    • Upload payment receipt (PDF/image)
    • Execution notes: "已完成轉帳"
  • Step 3: Click "確認執行付款"
  • Step 4: Verify:
    • Status changed to "executed"
    • Execution status is "completed"
    • Receipt can be downloaded
    • finance_document updated with payment_executed_at

1.3 Stage 3: Recording Workflow

Cashier Ledger Entry

  • Step 1: Login as cashier@test.com
  • Step 2: Navigate to /admin/cashier-ledger/create
  • Step 3: Fill form:
    • Finance Document: (select executed payment)
    • Entry Date: (today)
    • Entry Type: "payment"
    • Payment Method: "bank_transfer"
    • Bank Account: "Main Account"
    • Amount: (from payment order)
    • Receipt Number: "RCP-001"
    • Transaction Reference: (from payment order)
    • Notes: "記錄付款"
  • Step 4: Submit form
  • Step 5: Verify:
    • Entry created
    • Balance_before calculated from previous entry
    • Balance_after = balance_before - amount
    • finance_document updated with cashier_ledger_entry_id

Accounting Transaction (Accountant)

  • Step 1: Login as accountant@test.com
  • Step 2: Navigate to /admin/transactions/create
  • Step 3: Create accounting entry with debit/credit
  • Step 4: Link to finance document
  • Step 5: Verify transaction recorded

1.4 Stage 4: Reconciliation Workflow

Prepare Bank Reconciliation (Cashier)

  • Step 1: Login as cashier@test.com
  • Step 2: Navigate to /admin/bank-reconciliations/create
  • Step 3: Fill reconciliation form:
    • Reconciliation Month: "2025-11"
    • Bank Statement Balance: 500,000
    • Bank Statement Date: 2025-11-30
    • Upload bank statement (PDF)
    • System Book Balance: (auto-calculated from ledger)
  • Step 4: Add outstanding items:
    • Outstanding checks: [{"amount": 5000, "check_number": "CHK-001"}]
    • Deposits in transit: [{"amount": 10000, "date": "2025-11-29"}]
    • Bank charges: [{"amount": 50, "description": "Service fee"}]
  • Step 5: Submit form
  • Step 6: Verify:
    • Reconciliation created
    • Adjusted balance calculated correctly
    • Discrepancy detected if amounts don't match
    • Status based on discrepancy

Review Bank Reconciliation (Accountant)

  • Step 1: Login as accountant@test.com
  • Step 2: Navigate to pending reconciliation
  • Step 3: Review outstanding items
  • Step 4: Click "Review"
  • Step 5: Verify reviewed_at timestamp set

Approve Bank Reconciliation (Chair)

  • Step 1: Login as chair@test.com
  • Step 2: Navigate to reviewed reconciliation
  • Step 3: Click "Approve"
  • Step 4: Verify:
    • Status changed to "completed" or "discrepancy"
    • Approved_at timestamp set

2. Automated Tests

2.1 Feature Tests

Create file: tests/Feature/FinancialWorkflowTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\User;
use App\Models\FinanceDocument;
use App\Models\PaymentOrder;
use App\Models\CashierLedgerEntry;
use App\Models\BankReconciliation;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;

class FinancialWorkflowTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed(FinancialWorkflowPermissionsSeeder::class);
    }

    /** @test */
    public function small_amount_workflow_completes_without_chair()
    {
        // Create users
        $cashier = User::factory()->create();
        $accountant = User::factory()->create();
        $requester = User::factory()->create();

        $cashier->assignRole('finance_cashier');
        $accountant->assignRole('finance_accountant');
        $requester->assignRole('finance_requester');

        // Step 1: Requester submits
        $document = FinanceDocument::create([
            'submitted_by_user_id' => $requester->id,
            'title' => 'Small Expense',
            'amount' => 3000,
            'request_type' => 'expense_reimbursement',
            'status' => 'pending',
            'submitted_at' => now(),
        ]);

        $document->amount_tier = $document->determineAmountTier();
        $document->save();

        $this->assertEquals('small', $document->amount_tier);

        // Step 2: Cashier approves
        $this->actingAs($cashier)
            ->post(route('admin.finance.approve', $document))
            ->assertRedirect();

        $document->refresh();
        $this->assertEquals('approved_cashier', $document->status);

        // Step 3: Accountant approves
        $this->actingAs($accountant)
            ->post(route('admin.finance.approve', $document))
            ->assertRedirect();

        $document->refresh();
        $this->assertEquals('approved_accountant', $document->status);
        $this->assertTrue($document->isApprovalStageComplete());
    }

    /** @test */
    public function medium_amount_requires_chair_approval()
    {
        // Similar test for medium amount...
    }

    /** @test */
    public function accountant_can_create_payment_order()
    {
        // Test payment order creation...
    }

    /** @test */
    public function cashier_can_verify_payment_order()
    {
        // Test payment verification...
    }

    /** @test */
    public function cashier_can_execute_payment()
    {
        // Test payment execution...
    }

    /** @test */
    public function cashier_ledger_calculates_balance_correctly()
    {
        // Test balance calculation...
    }

    /** @test */
    public function bank_reconciliation_detects_discrepancy()
    {
        // Test discrepancy detection...
    }
}

2.2 Unit Tests

Create file: tests/Unit/PaymentOrderTest.php

<?php

namespace Tests\Unit;

use Tests\TestCase;
use App\Models\PaymentOrder;
use Illuminate\Foundation\Testing\RefreshDatabase;

class PaymentOrderTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function generates_unique_payment_order_number()
    {
        $number1 = PaymentOrder::generatePaymentOrderNumber();
        $number2 = PaymentOrder::generatePaymentOrderNumber();

        $this->assertStringStartsWith('PO-', $number1);
        $this->assertStringStartsWith('PO-', $number2);
        $this->assertNotEquals($number1, $number2);
    }

    /** @test */
    public function can_be_verified_when_pending()
    {
        $order = PaymentOrder::factory()->create([
            'status' => 'pending_verification',
            'verification_status' => 'pending',
        ]);

        $this->assertTrue($order->canBeVerifiedByCashier());
    }

    /** @test */
    public function cannot_be_verified_when_already_verified()
    {
        $order = PaymentOrder::factory()->create([
            'status' => 'verified',
            'verification_status' => 'approved',
        ]);

        $this->assertFalse($order->canBeVerifiedByCashier());
    }

    /** @test */
    public function can_be_executed_when_verified_and_approved()
    {
        $order = PaymentOrder::factory()->create([
            'status' => 'verified',
            'verification_status' => 'approved',
            'execution_status' => 'pending',
        ]);

        $this->assertTrue($order->canBeExecuted());
    }
}

2.3 Integration Tests

/** @test */
public function complete_workflow_from_document_to_reconciliation()
{
    // 1. Create and approve document
    // 2. Create payment order
    // 3. Verify payment order
    // 4. Execute payment
    // 5. Record in cashier ledger
    // 6. Create accounting transaction
    // 7. Perform bank reconciliation
    // Assert all steps completed successfully
}

3. Permission Tests

Test Permission Enforcement

/** @test */
public function non_cashier_cannot_verify_payment()
{
    $accountant = User::factory()->create();
    $accountant->assignRole('finance_accountant');

    $order = PaymentOrder::factory()->create([
        'status' => 'pending_verification',
    ]);

    $this->actingAs($accountant)
        ->post(route('admin.payment-orders.verify', $order))
        ->assertForbidden();
}

/** @test */
public function non_accountant_cannot_create_payment_order()
{
    $cashier = User::factory()->create();
    $cashier->assignRole('finance_cashier');

    $document = FinanceDocument::factory()->create([
        'status' => 'approved_accountant',
    ]);

    $this->actingAs($cashier)
        ->post(route('admin.payment-orders.store', $document), [
            'payee_name' => 'Test',
            'payment_amount' => 1000,
            'payment_method' => 'cash',
        ])
        ->assertForbidden();
}

4. Edge Cases and Error Handling

Test Cases

  • Cannot approve already approved document
  • Cannot verify already verified payment order
  • Cannot execute payment without verification
  • Cannot create payment order for unapproved document
  • Balance calculation with negative balance
  • Bank reconciliation with exact match (no discrepancy)
  • Bank reconciliation with large discrepancy
  • File upload size limits
  • Invalid file types
  • Missing required fields
  • Concurrent access to same document
  • Cancelling executed payment order (should fail)

5. Performance Tests

Load Testing Checklist

  • Create 1,000 finance documents
  • Create 1,000 payment orders
  • Create 10,000 ledger entries
  • Test pagination performance
  • Test search/filter performance
  • Test balance calculation with large datasets
  • Test bank reconciliation with many outstanding items

6. Security Tests

Security Checklist

  • SQL injection in search filters
  • XSS in notes fields
  • CSRF token validation
  • File upload security (malicious files)
  • Path traversal in file downloads
  • Authorization bypass attempts
  • Rate limiting on sensitive operations

7. User Acceptance Testing (UAT)

UAT Checklist

  • UI is intuitive and easy to navigate
  • Error messages are clear and helpful
  • Success messages provide adequate feedback
  • Forms validate input properly
  • Tables display data correctly
  • Pagination works smoothly
  • Filters work as expected
  • File uploads work reliably
  • Downloads work correctly
  • Email notifications are received
  • Mobile responsiveness (if applicable)

8. Regression Testing

After Each Change

  • Run all automated tests
  • Test complete workflow manually
  • Verify no existing functionality broken
  • Check database integrity
  • Verify audit logs still working

Test Execution Log

Date Tester Test Section Status Notes

Bugs/Issues Tracker

ID Priority Description Steps to Reproduce Status Fixed By

Sign-off

  • All manual tests passed
  • All automated tests passing
  • Performance acceptable
  • Security verified
  • UAT completed
  • Documentation complete

Tested by: ________________ Date: ________________ Approved by: ________________ Date: ________________