create([ 'client_invoice_id' => $invoice->id, ]); return ClientInvoicePaymentItem::query()->create(array_merge([ 'client_invoice_payment_id' => $payment->id, 'billing_item_types_id' => $billingItemTypeId, ], $attributes)); } beforeEach(function () { BillingItemType::query()->insert([ [ 'id' => 1, 'name' => ClientInvoicePaymentSyncService::MEDIA_SEARCH_NAME, 'nett_contribution' => true, 'fee_type' => 'Media', ], [ 'id' => 2, 'name' => ClientInvoicePaymentSyncService::MANAGEMENT_SEARCH_NAME, 'nett_contribution' => false, 'fee_type' => 'Management', ], ]); }); afterEach(function () { Carbon::setTestNow(); }); test('it treats google search management spending on credit card invoices as credit card media spend', function () { $client = Client::factory()->create(); $payToUsInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-PAY-TO-US', 'is_credit_card' => false, ]); createRemainingAmountPaymentItem($payToUsInvoice, 1, [ 'final_net_amount' => 200, 'spending' => 0, 'is_creditcard' => false, ]); $creditCardInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-CREDIT-CARD', 'is_credit_card' => true, ]); createRemainingAmountPaymentItem($creditCardInvoice, 2, [ 'spending' => 125, 'is_creditcard' => false, ]); expect($client->latestRemainingAmount())->toBe('200.00'); }); test('it keeps google search management spending billable on non credit card invoices', function () { $client = Client::factory()->create(); $invoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-PAY-TO-US', 'is_credit_card' => false, ]); createRemainingAmountPaymentItem($invoice, 1, [ 'final_net_amount' => 200, 'spending' => 0, 'is_creditcard' => false, ]); createRemainingAmountPaymentItem($invoice, 2, [ 'spending' => 125, 'is_creditcard' => false, ]); expect($client->latestRemainingAmount())->toBe('75.00'); }); test('it allows negative remaining amount when billable spending exceeds invoice net amount', function () { $client = Client::factory()->create(); $invoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-OVERSPEND', 'is_credit_card' => false, ]); createRemainingAmountPaymentItem($invoice, 1, [ 'final_net_amount' => 100, 'spending' => 175, 'is_creditcard' => false, ]); expect($client->latestRemainingAmount())->toBe('-75.00'); }); test('it uses linked invoice payment dates when they are the latest range', function () { Carbon::setTestNow('2026-06-25 10:00:00'); $client = Client::factory()->create(); $olderInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-OLDER', ]); createRemainingAmountPaymentItem($olderInvoice, 1, [ 'start_date' => '2026-06-01', 'end_date' => '2026-06-30', ]); $linkedInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-LINKED-SOURCE', ]); createRemainingAmountPaymentItem($linkedInvoice, 2, [ 'start_date' => '2026-08-01', 'end_date' => '2026-08-31', ]); $linkedPaymentInvoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-LINKED-PAYMENT', 'linked_invoice_id' => $linkedInvoice->id, ]); createRemainingAmountPaymentItem($linkedPaymentInvoice, 2, [ 'start_date' => '2026-09-01', 'end_date' => '2026-09-30', ]); expect($client->latestPaymentDateRange())->toBe([ 'start_date' => '2026-09-01', 'end_date' => '2026-09-30', ])->and($client->latestRemainingDays())->toBe(29); }); test('it counts remaining days from today within the latest range', function () { Carbon::setTestNow('2026-06-25 10:00:00'); $client = Client::factory()->create(); $invoice = ClientInvoice::query()->forceCreate([ 'client_id' => $client->id, 'invoice_no' => 'INV-CURRENT', ]); createRemainingAmountPaymentItem($invoice, 1, [ 'start_date' => '2026-06-01', 'end_date' => '2026-06-30', ]); expect($client->latestRemainingDays())->toBe(5); });