inspiren-sem-tool/tests/Feature/UpdateLatestClientInvoicePaymentItemSpendingTest.php

120 lines
4.1 KiB
PHP

<?php
use App\Models\BillingItemType;
use App\Models\Client;
use App\Models\ClientInvoice;
use App\Models\ClientInvoicePayment;
use App\Models\ClientInvoicePaymentItem;
use App\Services\GoogleAdsService;
use Illuminate\Support\Carbon;
function createInvoiceItem(
Client $client,
int $billingItemTypeId,
?string $startDate,
?string $endDate,
float $spending = 0,
): ClientInvoicePaymentItem {
$invoice = ClientInvoice::query()->forceCreate([
'client_id' => $client->id,
'invoice_no' => fake()->unique()->numerify('INV-####'),
]);
$payment = ClientInvoicePayment::query()->create([
'client_invoice_id' => $invoice->id,
]);
return ClientInvoicePaymentItem::query()->create([
'client_invoice_payment_id' => $payment->id,
'billing_item_types_id' => $billingItemTypeId,
'start_date' => $startDate,
'end_date' => $endDate,
'spending' => $spending,
]);
}
test('it updates only the latest eligible invoice payment item for each client', function () {
Carbon::setTestNow('2026-06-25 10:00:00');
BillingItemType::query()->insert([
['id' => 1, 'name' => 'Media'],
['id' => 2, 'name' => 'Management'],
]);
$firstClient = Client::factory()->create([
'customer_id' => '1111111111',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
]);
$secondClient = Client::factory()->create([
'customer_id' => '2222222222',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
]);
$olderItem = createInvoiceItem($firstClient, 1, '2026-05-01', '2026-05-31', 10);
$latestItem = createInvoiceItem($firstClient, 1, '2026-06-01', null);
$otherTypeItem = createInvoiceItem($firstClient, 2, '2026-06-01', null, 20);
$secondClientItem = createInvoiceItem($secondClient, 1, '2026-06-10', '2026-06-20');
$adsService = Mockery::mock(GoogleAdsService::class);
$adsService->shouldReceive('listCampaigns')
->once()
->with('1111111111')
->andReturn([['id' => 101], ['id' => 102]]);
$adsService->shouldReceive('listCampaignsMetricsById')
->once()
->with('1111111111', '101', '2026-06-01', '2026-06-25')
->andReturn([['actual_spend' => 12.25]]);
$adsService->shouldReceive('listCampaignsMetricsById')
->once()
->with('1111111111', '102', '2026-06-01', '2026-06-25')
->andReturn([['actual_spend' => 7.75]]);
$adsService->shouldReceive('listCampaigns')
->once()
->with('2222222222')
->andReturn([['id' => 201]]);
$adsService->shouldReceive('listCampaignsMetricsById')
->once()
->with('2222222222', '201', '2026-06-10', '2026-06-20')
->andReturn([['actual_spend' => 5.5]]);
app()->instance(GoogleAdsService::class, $adsService);
$this->artisan('customer:update-latest-invoice-item-spending')
->expectsOutputToContain('Done. 2 calculated, 0 skipped, 0 failed.')
->assertSuccessful();
expect((float) $olderItem->fresh()->spending)->toBe(10.0)
->and((float) $latestItem->fresh()->spending)->toBe(20.0)
->and((float) $otherTypeItem->fresh()->spending)->toBe(20.0)
->and((float) $secondClientItem->fresh()->spending)->toBe(5.5);
});
test('dry run calculates spending without updating the item', function () {
BillingItemType::query()->insert([
'id' => 1,
'name' => 'Media',
]);
$client = Client::factory()->create([
'customer_id' => '3333333333',
'status' => 'ENABLED',
'time_zone' => 'Asia/Kuala_Lumpur',
]);
$item = createInvoiceItem($client, 1, '2026-06-01', '2026-06-15', 8);
$adsService = Mockery::mock(GoogleAdsService::class);
$adsService->shouldReceive('listCampaigns')
->once()
->with('3333333333')
->andReturn([]);
app()->instance(GoogleAdsService::class, $adsService);
$this->artisan('customer:update-latest-invoice-item-spending', ['--dry-run' => true])
->assertSuccessful();
expect((float) $item->fresh()->spending)->toBe(8.0);
});