541 lines
15 KiB
Markdown
541 lines
15 KiB
Markdown
# 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
|
|
```bash
|
|
# 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
|
|
<?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
|
|
<?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
|
|
|
|
```php
|
|
/** @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
|
|
```php
|
|
/** @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**: ________________
|