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:
2025-12-01 09:56:01 +08:00
parent 83ce1f7fc8
commit 642b879dd4
207 changed files with 19487 additions and 3048 deletions

View File

@@ -0,0 +1,201 @@
<?php
namespace Tests\Browser;
use App\Models\FinanceDocument;
use App\Models\Issue;
use App\Models\Member;
use App\Models\MembershipPayment;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Support\Facades\Hash;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
/**
* Admin Dashboard Browser Tests
*
* Tests the admin dashboard user interface and user experience.
*/
class AdminDashboardBrowserTest extends DuskTestCase
{
use DatabaseMigrations;
protected function setUp(): void
{
parent::setUp();
$this->artisan('db:seed', ['--class' => 'FinancialWorkflowPermissionsSeeder']);
}
/**
* Create an admin user for testing
*/
protected function createAdminUser(): User
{
$user = User::factory()->create([
'email' => 'admin@test.com',
'password' => Hash::make('password'),
]);
$user->assignRole('admin');
return $user;
}
/**
* Test admin can view dashboard
*/
public function test_admin_can_view_dashboard(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->assertSee('管理儀表板');
});
}
/**
* Test dashboard shows statistics
*/
public function test_dashboard_shows_statistics(): void
{
$admin = $this->createAdminUser();
// Create some data
Member::factory()->count(5)->create();
Issue::factory()->count(3)->create(['created_by_user_id' => $admin->id]);
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->assertPresent('.stats-card');
});
}
/**
* Test admin can navigate to members page
*/
public function test_admin_can_navigate_to_members_page(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->clickLink('會員管理')
->assertPathIs('/admin/members');
});
}
/**
* Test admin can navigate to finance page
*/
public function test_admin_can_navigate_to_finance_page(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->clickLink('財務管理')
->assertPathIs('/admin/finance*');
});
}
/**
* Test admin can navigate to issues page
*/
public function test_admin_can_navigate_to_issues_page(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->clickLink('議題追蹤')
->assertPathIs('/admin/issues');
});
}
/**
* Test sidebar navigation works
*/
public function test_sidebar_navigation_works(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->assertPresent('.sidebar')
->assertPresent('.nav-link');
});
}
/**
* Test pending approvals widget
*/
public function test_pending_approvals_widget(): void
{
$admin = $this->createAdminUser();
MembershipPayment::factory()->count(3)->create([
'status' => MembershipPayment::STATUS_PENDING,
]);
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->assertSee('待審核');
});
}
/**
* Test recent activity section
*/
public function test_recent_activity_section(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.dashboard'))
->assertPresent('.activity-feed');
});
}
/**
* Test admin can search members
*/
public function test_admin_can_search_members(): void
{
$admin = $this->createAdminUser();
Member::factory()->create(['full_name' => '測試搜尋會員']);
$this->browse(function (Browser $browser) use ($admin) {
$browser->loginAs($admin)
->visit(route('admin.members.index'))
->type('search', '測試搜尋')
->press('搜尋')
->assertSee('測試搜尋會員');
});
}
/**
* Test responsive sidebar collapse
*/
public function test_responsive_sidebar_collapse(): void
{
$admin = $this->createAdminUser();
$this->browse(function (Browser $browser) use ($admin) {
// Mobile view
$browser->loginAs($admin)
->resize(375, 667)
->visit(route('admin.dashboard'))
->assertPresent('.sidebar-toggle');
});
}
}