inspiren-sem-tool/tests/Feature/ClientInvoiceApprovalTest.php
brian-inspiren 221d3f8173
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
feat: sem codebase
2026-05-21 11:28:03 +08:00

228 lines
7.2 KiB
PHP

<?php
use App\Models\Client;
use App\Models\ClientCustomer;
use App\Models\ClientInvoice;
use App\Models\User;
use Illuminate\Support\Facades\Http;
it('auto approves invoices created through the web form', function () {
$user = User::factory()->create();
$client = Client::create([
'name' => 'Acme',
'customer_id' => '1234567890',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
'industry' => 'Marketing',
'sql_acc_code' => 'SEM-001',
]);
$this->actingAs($user)
->post(route('client-invoices.store'), [
'client_id' => $client->id,
'customer_id' => $client->customer_id,
'invoice_no' => 'INV-1001',
'management_fee' => 120,
'media_fee' => 880,
])
->assertRedirect();
$invoice = ClientInvoice::where('invoice_no', 'INV-1001')->first();
expect($invoice)->not->toBeNull();
expect($invoice?->approved_at)->not->toBeNull();
});
it('keeps invoices created through the api pending approval', function () {
$client = Client::create([
'name' => 'Acme',
'customer_id' => '1234567891',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
'industry' => 'Marketing',
'sql_acc_code' => 'SEM-002',
]);
$this->postJson(route('api.customer-invoices.store'), [
'client_id' => $client->id,
'invoice_no' => 'INV-2001',
'management_fee' => 120,
'media_fee' => 880,
])
->assertCreated()
->assertJsonPath('invoice.approved_at', null);
$invoice = ClientInvoice::where('invoice_no', 'INV-2001')->first();
expect($invoice)->not->toBeNull();
expect($invoice?->approved_at)->toBeNull();
});
it('links api invoices to existing clients by sql account code', function () {
$client = Client::create([
'name' => 'Acme',
'customer_id' => '1234567894',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
'industry' => 'Marketing',
'sql_acc_code' => null,
]);
ClientCustomer::create([
'client_id' => $client->id,
'sql_acc_code' => 'SEM-005',
]);
$this->postJson(route('api.customer-invoices.store'), [
'sql_acc_code' => 'SEM-005',
'invoice_no' => 'INV-2002',
'management_fee' => 120,
'media_fee' => 880,
])
->assertCreated()
->assertJsonPath('invoice.client_id', $client->id)
->assertJsonPath('invoice.approved_at', null);
});
it('keeps api invoices pending without a client when sql account code is unknown', function () {
$this->postJson(route('api.customer-invoices.store'), [
'sql_acc_code' => 'SEM-006',
'client_name' => 'New Client',
'invoice_no' => 'INV-2003',
'management_fee' => 120,
'media_fee' => 880,
])
->assertCreated()
->assertJsonPath('invoice.client_id', null)
->assertJsonPath('invoice.pending_sql_acc_code', 'SEM-006')
->assertJsonPath('invoice.pending_client_name', 'New Client');
});
it('lists client invoices pending approval through the api', function () {
$client = Client::create([
'name' => 'Acme',
'customer_id' => '1234567893',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
'industry' => 'Marketing',
'sql_acc_code' => 'SEM-004',
]);
ClientInvoice::create([
'client_id' => $client->id,
'invoice_no' => 'INV-PENDING',
'approved_at' => null,
'management_fee' => 120,
'media_fee' => 880,
]);
ClientInvoice::create([
'client_id' => $client->id,
'invoice_no' => 'INV-APPROVED',
'approved_at' => now(),
'management_fee' => 120,
'media_fee' => 880,
]);
$this->getJson(route('api.customer-invoices.pending'))
->assertOk()
->assertJsonPath('count', 1)
->assertJsonPath('invoices.0.invoice_no', 'INV-PENDING')
->assertJsonMissing(['invoice_no' => 'INV-APPROVED']);
});
it('splits repeated previous payment item amounts by invoice item totals', function () {
config([
'app.billing_url' => 'https://billing.test',
'app.billing_key' => 'secret',
]);
$client = Client::create([
'name' => 'Acme',
'customer_id' => '1234567895',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
'industry' => 'Marketing',
'sql_acc_code' => 'SEM-007',
]);
ClientInvoice::create([
'client_id' => $client->id,
'invoice_no' => 'INV-REPEATED-AMOUNTS',
'payment_no' => 'PAY-CURRENT',
'approved_at' => null,
'management_fee' => 120,
'media_fee' => 880,
]);
Http::fake([
'https://billing.test/customer/invoices/getInvoicePaymentDetailsByInvoiceGoogle*' => Http::response([
'data' => [
[
'payment_number' => 'PAY-PREVIOUS',
'company_name' => 'Acme',
'status' => 'Processed',
'sql_created_at' => '2026-05-18 10:00:00',
'amount' => 1000,
'invoice' => [
'invoice_number' => 'INV-REPEATED-AMOUNTS',
],
'items' => [
[
'amount' => 1000,
'item' => [
'estimated_total' => 880,
'item' => [
'sql_acc_code' => 'G03',
],
],
],
[
'amount' => 1000,
'item' => [
'estimated_total' => 120,
'item' => [
'sql_acc_code' => 'GOOGLE',
],
],
],
],
],
],
]),
]);
$this->getJson(route('api.customer-invoices.pending'))
->assertOk()
->assertJsonPath('invoices.0.previous_payments.0.media_fee', 880.0)
->assertJsonPath('invoices.0.previous_payments.0.management_fee', 120.0)
->assertJsonPath('invoices.0.previous_payments.0.invoice_media_fee', 880.0)
->assertJsonPath('invoices.0.previous_payments.0.invoice_management_fee', 120.0);
});
it('approves a pending invoice from the web approval action', function () {
$user = User::factory()->create();
$client = Client::create([
'name' => 'Acme',
'customer_id' => '1234567892',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
'industry' => 'Marketing',
'sql_acc_code' => 'SEM-003',
]);
$invoice = ClientInvoice::create([
'client_id' => $client->id,
'invoice_no' => 'INV-3001',
'approved_at' => null,
'management_fee' => 120,
'media_fee' => 880,
]);
$this->actingAs($user)
->patch(route('client-invoices.approve', $invoice))
->assertRedirect();
expect($invoice->refresh()->approved_at)->not->toBeNull();
});