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:
215
tests/Feature/Roles/RolePermissionTest.php
Normal file
215
tests/Feature/Roles/RolePermissionTest.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Roles;
|
||||
|
||||
use App\Models\FinanceDocument;
|
||||
use App\Models\Member;
|
||||
use App\Models\MembershipPayment;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Tests\TestCase;
|
||||
use Tests\Traits\CreatesFinanceData;
|
||||
use Tests\Traits\CreatesMemberData;
|
||||
use Tests\Traits\SeedsRolesAndPermissions;
|
||||
|
||||
/**
|
||||
* Role Permission Tests
|
||||
*
|
||||
* Tests role-based access control and permissions.
|
||||
*/
|
||||
class RolePermissionTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase, SeedsRolesAndPermissions, CreatesMemberData, CreatesFinanceData;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Storage::fake('private');
|
||||
Storage::fake('local');
|
||||
$this->seedRolesAndPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test admin can access admin dashboard
|
||||
*/
|
||||
public function test_admin_can_access_admin_dashboard(): void
|
||||
{
|
||||
$admin = $this->createAdmin();
|
||||
|
||||
$response = $this->actingAs($admin)->get(route('admin.dashboard'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test member cannot access admin dashboard
|
||||
*/
|
||||
public function test_member_cannot_access_admin_dashboard(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole('member');
|
||||
|
||||
$response = $this->actingAs($user)->get(route('admin.dashboard'));
|
||||
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cashier can approve payments
|
||||
*/
|
||||
public function test_cashier_can_approve_payments(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$data = $this->createMemberWithPendingPayment();
|
||||
|
||||
$response = $this->actingAs($cashier)->post(
|
||||
route('admin.membership-payments.approve', $data['payment'])
|
||||
);
|
||||
|
||||
$data['payment']->refresh();
|
||||
$this->assertEquals(MembershipPayment::STATUS_APPROVED_CASHIER, $data['payment']->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test accountant cannot approve pending payment directly
|
||||
*/
|
||||
public function test_accountant_cannot_approve_pending_payment_directly(): void
|
||||
{
|
||||
$accountant = $this->createAccountant();
|
||||
$data = $this->createMemberWithPendingPayment();
|
||||
|
||||
$response = $this->actingAs($accountant)->post(
|
||||
route('admin.membership-payments.approve', $data['payment'])
|
||||
);
|
||||
|
||||
// Should be forbidden or redirect with error
|
||||
$data['payment']->refresh();
|
||||
$this->assertNotEquals(MembershipPayment::STATUS_APPROVED_ACCOUNTANT, $data['payment']->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test chair can approve after accountant
|
||||
*/
|
||||
public function test_chair_can_approve_after_accountant(): void
|
||||
{
|
||||
$chair = $this->createChair();
|
||||
$data = $this->createMemberWithPaymentAtStage('accountant_approved');
|
||||
|
||||
$response = $this->actingAs($chair)->post(
|
||||
route('admin.membership-payments.approve', $data['payment'])
|
||||
);
|
||||
|
||||
$data['payment']->refresh();
|
||||
$this->assertEquals(MembershipPayment::STATUS_APPROVED_CHAIR, $data['payment']->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test finance_cashier can approve finance documents
|
||||
*/
|
||||
public function test_finance_cashier_can_approve_finance_documents(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$document = $this->createFinanceDocument([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($cashier)->post(
|
||||
route('admin.finance-documents.approve', $document)
|
||||
);
|
||||
|
||||
$document->refresh();
|
||||
$this->assertEquals(FinanceDocument::STATUS_APPROVED_CASHIER, $document->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test unauthorized user cannot approve
|
||||
*/
|
||||
public function test_unauthorized_user_cannot_approve(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$data = $this->createMemberWithPendingPayment();
|
||||
|
||||
$response = $this->actingAs($user)->post(
|
||||
route('admin.membership-payments.approve', $data['payment'])
|
||||
);
|
||||
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test role can be assigned to user
|
||||
*/
|
||||
public function test_role_can_be_assigned_to_user(): void
|
||||
{
|
||||
$admin = $this->createAdmin();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($admin)->post(
|
||||
route('admin.users.assign-role', $user),
|
||||
['role' => 'finance_cashier']
|
||||
);
|
||||
|
||||
$this->assertTrue($user->hasRole('finance_cashier'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test role can be removed from user
|
||||
*/
|
||||
public function test_role_can_be_removed_from_user(): void
|
||||
{
|
||||
$admin = $this->createAdmin();
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole('finance_cashier');
|
||||
|
||||
$response = $this->actingAs($admin)->post(
|
||||
route('admin.users.remove-role', $user),
|
||||
['role' => 'finance_cashier']
|
||||
);
|
||||
|
||||
$this->assertFalse($user->hasRole('finance_cashier'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test permission check for member management
|
||||
*/
|
||||
public function test_permission_check_for_member_management(): void
|
||||
{
|
||||
$admin = $this->createAdmin();
|
||||
$member = $this->createPendingMember();
|
||||
|
||||
$response = $this->actingAs($admin)->patch(
|
||||
route('admin.members.update-status', $member),
|
||||
['membership_status' => Member::STATUS_ACTIVE]
|
||||
);
|
||||
|
||||
$member->refresh();
|
||||
$this->assertEquals(Member::STATUS_ACTIVE, $member->membership_status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test super admin has all permissions
|
||||
*/
|
||||
public function test_super_admin_has_all_permissions(): void
|
||||
{
|
||||
$superAdmin = User::factory()->create();
|
||||
$superAdmin->assignRole('super_admin');
|
||||
|
||||
$this->assertTrue($superAdmin->can('manage-members'));
|
||||
$this->assertTrue($superAdmin->can('approve-payments'));
|
||||
$this->assertTrue($superAdmin->can('manage-finance'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test role hierarchy for approvals
|
||||
*/
|
||||
public function test_role_hierarchy_for_approvals(): void
|
||||
{
|
||||
// Chair should be able to do everything accountant can
|
||||
$chair = $this->createChair();
|
||||
|
||||
$this->assertTrue($chair->hasRole('finance_chair'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user