Files
usher-manage-stack/tests/Feature/CashierLedger/CashierLedgerTest.php
Gbanyan 642b879dd4 Add membership fee system with disability discount and fix document permissions
Features:
- Implement two fee types: entrance fee and annual fee (both NT$1,000)
- Add 50% discount for disability certificate holders
- Add disability certificate upload in member profile
- Integrate disability verification into cashier approval workflow
- Add membership fee settings in system admin

Document permissions:
- Fix hard-coded role logic in Document model
- Use permission-based authorization instead of role checks

Additional features:
- Add announcements, general ledger, and trial balance modules
- Add income management and accounting entries
- Add comprehensive test suite with factories
- Update UI translations to Traditional Chinese

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 09:56:01 +08:00

260 lines
6.6 KiB
PHP

<?php
namespace Tests\Feature\CashierLedger;
use App\Models\CashierLedger;
use App\Models\PaymentOrder;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
use Tests\Traits\CreatesFinanceData;
use Tests\Traits\SeedsRolesAndPermissions;
/**
* Cashier Ledger Tests
*
* Tests cashier ledger entries in the 4-stage finance workflow.
*/
class CashierLedgerTest extends TestCase
{
use RefreshDatabase, SeedsRolesAndPermissions, CreatesFinanceData;
protected function setUp(): void
{
parent::setUp();
Storage::fake('local');
$this->seedRolesAndPermissions();
}
/**
* Test can view cashier ledger
*/
public function test_can_view_cashier_ledger(): void
{
$cashier = $this->createCashier();
$response = $this->actingAs($cashier)->get(
route('admin.cashier-ledger.index')
);
$response->assertStatus(200);
}
/**
* Test ledger entry created from payment order
*/
public function test_ledger_entry_created_from_payment_order(): void
{
$cashier = $this->createCashier();
$order = $this->createPaymentOrder([
'status' => PaymentOrder::STATUS_COMPLETED,
]);
$response = $this->actingAs($cashier)->post(
route('admin.cashier-ledger.store'),
[
'payment_order_id' => $order->id,
'entry_type' => 'expense',
'entry_date' => now()->toDateString(),
]
);
$response->assertRedirect();
$this->assertDatabaseHas('cashier_ledgers', [
'payment_order_id' => $order->id,
]);
}
/**
* Test ledger tracks income entries
*/
public function test_ledger_tracks_income_entries(): void
{
$cashier = $this->createCashier();
$response = $this->actingAs($cashier)->post(
route('admin.cashier-ledger.store'),
[
'entry_type' => 'income',
'amount' => 50000,
'description' => '會員繳費收入',
'entry_date' => now()->toDateString(),
]
);
$this->assertDatabaseHas('cashier_ledgers', [
'entry_type' => 'income',
'amount' => 50000,
]);
}
/**
* Test ledger tracks expense entries
*/
public function test_ledger_tracks_expense_entries(): void
{
$cashier = $this->createCashier();
$order = $this->createPaymentOrder([
'status' => PaymentOrder::STATUS_COMPLETED,
]);
$entry = $this->createCashierLedgerEntry([
'payment_order_id' => $order->id,
'entry_type' => 'expense',
]);
$this->assertEquals('expense', $entry->entry_type);
}
/**
* Test ledger balance calculation
*/
public function test_ledger_balance_calculation(): void
{
$cashier = $this->createCashier();
// Create income
$this->createCashierLedgerEntry([
'entry_type' => 'income',
'amount' => 100000,
]);
// Create expense
$this->createCashierLedgerEntry([
'entry_type' => 'expense',
'amount' => 30000,
]);
$balance = CashierLedger::calculateBalance();
$this->assertEquals(70000, $balance);
}
/**
* Test ledger date range filter
*/
public function test_ledger_date_range_filter(): void
{
$cashier = $this->createCashier();
$this->createCashierLedgerEntry([
'entry_date' => now()->subMonth(),
]);
$this->createCashierLedgerEntry([
'entry_date' => now(),
]);
$response = $this->actingAs($cashier)->get(
route('admin.cashier-ledger.index', [
'start_date' => now()->startOfMonth()->toDateString(),
'end_date' => now()->endOfMonth()->toDateString(),
])
);
$response->assertStatus(200);
}
/**
* Test ledger entry validation
*/
public function test_ledger_entry_validation(): void
{
$cashier = $this->createCashier();
$response = $this->actingAs($cashier)->post(
route('admin.cashier-ledger.store'),
[
'entry_type' => 'income',
'amount' => -1000, // Invalid negative amount
'entry_date' => now()->toDateString(),
]
);
$response->assertSessionHasErrors('amount');
}
/**
* Test ledger entry requires date
*/
public function test_ledger_entry_requires_date(): void
{
$cashier = $this->createCashier();
$response = $this->actingAs($cashier)->post(
route('admin.cashier-ledger.store'),
[
'entry_type' => 'income',
'amount' => 5000,
// Missing entry_date
]
);
$response->assertSessionHasErrors('entry_date');
}
/**
* Test ledger monthly summary
*/
public function test_ledger_monthly_summary(): void
{
$cashier = $this->createCashier();
$this->createCashierLedgerEntry([
'entry_type' => 'income',
'amount' => 100000,
'entry_date' => now(),
]);
$this->createCashierLedgerEntry([
'entry_type' => 'expense',
'amount' => 50000,
'entry_date' => now(),
]);
$response = $this->actingAs($cashier)->get(
route('admin.cashier-ledger.summary', [
'year' => now()->year,
'month' => now()->month,
])
);
$response->assertStatus(200);
}
/**
* Test ledger export
*/
public function test_ledger_export(): void
{
$cashier = $this->createCashier();
$this->createCashierLedgerEntry();
$this->createCashierLedgerEntry();
$response = $this->actingAs($cashier)->get(
route('admin.cashier-ledger.export', ['format' => 'csv'])
);
$response->assertStatus(200);
}
/**
* Test ledger entry cannot be edited after reconciliation
*/
public function test_ledger_entry_cannot_be_edited_after_reconciliation(): void
{
$cashier = $this->createCashier();
$entry = $this->createCashierLedgerEntry([
'is_reconciled' => true,
]);
$response = $this->actingAs($cashier)->patch(
route('admin.cashier-ledger.update', $entry),
['amount' => 99999]
);
$response->assertSessionHasErrors();
}
}