15 KiB
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
| 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"
- Navigate to
-
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
- Navigate to
-
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_meetingflag 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: ________________