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:
221
tests/Feature/PaymentOrder/PaymentOrderTest.php
Normal file
221
tests/Feature/PaymentOrder/PaymentOrderTest.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\PaymentOrder;
|
||||
|
||||
use App\Models\FinanceDocument;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Payment Order Tests
|
||||
*
|
||||
* Tests payment order creation and processing in the 4-stage finance workflow.
|
||||
*/
|
||||
class PaymentOrderTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase, SeedsRolesAndPermissions, CreatesFinanceData;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Storage::fake('local');
|
||||
$this->seedRolesAndPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test can view payment orders list
|
||||
*/
|
||||
public function test_can_view_payment_orders_list(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$response = $this->actingAs($cashier)->get(
|
||||
route('admin.payment-orders.index')
|
||||
);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order created from approved document
|
||||
*/
|
||||
public function test_payment_order_created_from_approved_document(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$document = $this->createDocumentAtStage('chair_approved');
|
||||
|
||||
$response = $this->actingAs($cashier)->post(
|
||||
route('admin.payment-orders.store'),
|
||||
[
|
||||
'finance_document_id' => $document->id,
|
||||
'payment_method' => 'bank_transfer',
|
||||
'bank_account' => '012-345678',
|
||||
]
|
||||
);
|
||||
|
||||
$response->assertRedirect();
|
||||
$this->assertDatabaseHas('payment_orders', [
|
||||
'finance_document_id' => $document->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order requires approved document
|
||||
*/
|
||||
public function test_payment_order_requires_approved_document(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$document = $this->createFinanceDocument([
|
||||
'status' => FinanceDocument::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($cashier)->post(
|
||||
route('admin.payment-orders.store'),
|
||||
[
|
||||
'finance_document_id' => $document->id,
|
||||
'payment_method' => 'bank_transfer',
|
||||
]
|
||||
);
|
||||
|
||||
$response->assertSessionHasErrors('finance_document_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order has unique number
|
||||
*/
|
||||
public function test_payment_order_has_unique_number(): void
|
||||
{
|
||||
$orders = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$orders[] = $this->createPaymentOrder();
|
||||
}
|
||||
|
||||
$orderNumbers = array_map(fn ($o) => $o->order_number, $orders);
|
||||
$this->assertEquals(count($orderNumbers), count(array_unique($orderNumbers)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test can update payment order status
|
||||
*/
|
||||
public function test_can_update_payment_order_status(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$order = $this->createPaymentOrder([
|
||||
'status' => PaymentOrder::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($cashier)->patch(
|
||||
route('admin.payment-orders.update-status', $order),
|
||||
['status' => PaymentOrder::STATUS_PROCESSING]
|
||||
);
|
||||
|
||||
$order->refresh();
|
||||
$this->assertEquals(PaymentOrder::STATUS_PROCESSING, $order->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order completion
|
||||
*/
|
||||
public function test_payment_order_completion(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$order = $this->createPaymentOrder([
|
||||
'status' => PaymentOrder::STATUS_PROCESSING,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($cashier)->post(
|
||||
route('admin.payment-orders.complete', $order),
|
||||
[
|
||||
'payment_date' => now()->toDateString(),
|
||||
'reference_number' => 'REF-12345',
|
||||
]
|
||||
);
|
||||
|
||||
$order->refresh();
|
||||
$this->assertEquals(PaymentOrder::STATUS_COMPLETED, $order->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order cancellation
|
||||
*/
|
||||
public function test_payment_order_cancellation(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$order = $this->createPaymentOrder([
|
||||
'status' => PaymentOrder::STATUS_PENDING,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($cashier)->post(
|
||||
route('admin.payment-orders.cancel', $order),
|
||||
['cancellation_reason' => '文件有誤']
|
||||
);
|
||||
|
||||
$order->refresh();
|
||||
$this->assertEquals(PaymentOrder::STATUS_CANCELLED, $order->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order filter by status
|
||||
*/
|
||||
public function test_payment_order_filter_by_status(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
|
||||
$this->createPaymentOrder(['status' => PaymentOrder::STATUS_PENDING]);
|
||||
$this->createPaymentOrder(['status' => PaymentOrder::STATUS_COMPLETED]);
|
||||
|
||||
$response = $this->actingAs($cashier)->get(
|
||||
route('admin.payment-orders.index', ['status' => PaymentOrder::STATUS_PENDING])
|
||||
);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order amount matches document
|
||||
*/
|
||||
public function test_payment_order_amount_matches_document(): void
|
||||
{
|
||||
$document = $this->createDocumentAtStage('chair_approved');
|
||||
$order = $this->createPaymentOrder([
|
||||
'finance_document_id' => $document->id,
|
||||
]);
|
||||
|
||||
$this->assertEquals($document->amount, $order->amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test payment order tracks payment method
|
||||
*/
|
||||
public function test_payment_order_tracks_payment_method(): void
|
||||
{
|
||||
$order = $this->createPaymentOrder([
|
||||
'payment_method' => 'bank_transfer',
|
||||
]);
|
||||
|
||||
$this->assertEquals('bank_transfer', $order->payment_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test completed order cannot be modified
|
||||
*/
|
||||
public function test_completed_order_cannot_be_modified(): void
|
||||
{
|
||||
$cashier = $this->createCashier();
|
||||
$order = $this->createPaymentOrder([
|
||||
'status' => PaymentOrder::STATUS_COMPLETED,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($cashier)->patch(
|
||||
route('admin.payment-orders.update', $order),
|
||||
['payment_method' => 'cash']
|
||||
);
|
||||
|
||||
$response->assertSessionHasErrors();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user