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>
This commit is contained in:
243
tests/Browser/FinanceWorkflowBrowserTest.php
Normal file
243
tests/Browser/FinanceWorkflowBrowserTest.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Browser;
|
||||
|
||||
use App\Models\FinanceDocument;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
/**
|
||||
* Finance Workflow Browser Tests
|
||||
*
|
||||
* Tests the finance workflow user interface and user experience.
|
||||
*/
|
||||
class FinanceWorkflowBrowserTest extends DuskTestCase
|
||||
{
|
||||
use DatabaseMigrations;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->artisan('db:seed', ['--class' => 'FinancialWorkflowPermissionsSeeder']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cashier user
|
||||
*/
|
||||
protected function createCashier(): User
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'email' => 'cashier@test.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
$user->assignRole('finance_cashier');
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an accountant user
|
||||
*/
|
||||
protected function createAccountant(): User
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'email' => 'accountant@test.com',
|
||||
'password' => Hash::make('password'),
|
||||
]);
|
||||
$user->assignRole('finance_accountant');
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test finance dashboard loads
|
||||
*/
|
||||
public function test_finance_dashboard_loads(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance.index'))
|
||||
->assertSee('財務管理');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test can create finance document
|
||||
*/
|
||||
public function test_can_create_finance_document(): void
|
||||
{
|
||||
$accountant = $this->createAccountant();
|
||||
|
||||
$this->browse(function (Browser $browser) use ($accountant) {
|
||||
$browser->loginAs($accountant)
|
||||
->visit(route('admin.finance-documents.create'))
|
||||
->assertSee('新增財務單據')
|
||||
->assertPresent('input[name="title"]')
|
||||
->assertPresent('input[name="amount"]');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test finance document list shows status
|
||||
*/
|
||||
public function test_finance_document_list_shows_status(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
'title' => '測試單據',
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.index'))
|
||||
->assertSee('測試單據')
|
||||
->assertSee('待審核');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cashier can see approve button
|
||||
*/
|
||||
public function test_cashier_can_see_approve_button(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$document = FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier, $document) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.show', $document))
|
||||
->assertSee('核准');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cashier can see reject button
|
||||
*/
|
||||
public function test_cashier_can_see_reject_button(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$document = FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier, $document) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.show', $document))
|
||||
->assertSee('退回');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test approval requires confirmation
|
||||
*/
|
||||
public function test_approval_requires_confirmation(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$document = FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier, $document) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.show', $document))
|
||||
->press('核准')
|
||||
->assertDialogOpened();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test document amount is formatted
|
||||
*/
|
||||
public function test_document_amount_is_formatted(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
'amount' => 15000,
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.index'))
|
||||
->assertSee('15,000');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test filter by status works
|
||||
*/
|
||||
public function test_filter_by_status_works(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
'title' => '待審核單據',
|
||||
]);
|
||||
|
||||
FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_APPROVED_CASHIER,
|
||||
'title' => '已核准單據',
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.index'))
|
||||
->select('status', FinanceDocument::STATUS_PENDING)
|
||||
->press('篩選')
|
||||
->assertSee('待審核單據')
|
||||
->assertDontSee('已核准單據');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test rejection requires reason
|
||||
*/
|
||||
public function test_rejection_requires_reason(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$document = FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier, $document) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.show', $document))
|
||||
->press('退回')
|
||||
->waitFor('.modal')
|
||||
->assertSee('退回原因');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test document history is visible
|
||||
*/
|
||||
public function test_document_history_is_visible(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$document = FinanceDocument::factory()->create([
|
||||
'status' => FinanceDocument::STATUS_APPROVED_CASHIER,
|
||||
]);
|
||||
|
||||
$this->browse(function (Browser $browser) use ($cashier, $document) {
|
||||
$browser->loginAs($cashier)
|
||||
->visit(route('admin.finance-documents.show', $document))
|
||||
->assertSee('審核歷程');
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user