Files
api/services/nginx/app/tests/Unit/Invoicing/InvoicingPeriodInvoiceStateTest.php
T

167 lines
5.7 KiB
PHP

<?php
app_require('routes/InvoicingPeriodRoute.php');
use routes\InvoicingPeriodRoute;
function invoicing_period_invoice_state_invoke(string $method, array $args = []): mixed
{
$reflection = new ReflectionClass(InvoicingPeriodRoute::class);
$target = $reflection->getMethod($method);
return $target->invokeArgs(null, $args);
}
final class InvoicingPeriodInvoiceStateFakeResult
{
public int $num_rows;
public function __construct(private array $rows)
{
$this->num_rows = count($rows);
}
public function fetch_assoc(): ?array
{
return array_shift($this->rows);
}
}
final class InvoicingPeriodInvoiceStateFakeDb
{
public int $queryCount = 0;
public string $sql = '';
public function __construct(private array $rows)
{
}
public function query(string $sql): InvoicingPeriodInvoiceStateFakeResult
{
$this->queryCount++;
$this->sql = $sql;
return new InvoicingPeriodInvoiceStateFakeResult($this->rows);
}
}
it('applies canonical invoice collection state precedence', function (): void {
expect(invoicing_period_invoice_state_invoke('periodInvoiceCollectionState', [[
'booked_invoice_id' => 9001,
'processor' => 1,
'external_id' => 'draft-1',
'closed_at' => '2026-06-30 12:00:00',
]]))->toBe('economic_booked');
expect(invoicing_period_invoice_state_invoke('periodInvoiceCollectionState', [[
'booked_invoice_id' => null,
'processor' => 1,
'external_id' => 'draft-2',
'error_message' => null,
'closed_at' => '2026-06-30 12:00:00',
]]))->toBe('economic_draft');
expect(invoicing_period_invoice_state_invoke('periodInvoiceCollectionState', [[
'processor' => 2,
'external_id' => 'stripe-id',
'closed_at' => '2026-06-30 12:00:00',
]]))->toBe('closed');
expect(invoicing_period_invoice_state_invoke('periodInvoiceCollectionState', [[]]))->toBe('open');
});
it('adds transaction states and collection summaries without losing logical orders', function (): void {
$customer = [
'customer_number' => 42424242,
'transactions' => [
['id' => 11, 'amount' => 100.5, 'invoice_collection_id' => 501, 'booked' => false],
['id' => 12, 'amount' => 50.0, 'invoice_collection_id' => 501, 'booked' => false],
['id' => 13, 'amount' => 25.0, 'invoice_collection_id' => 502, 'booked' => false],
['id' => 14, 'amount' => 10.0, 'invoice_collection_id' => null, 'booked' => false, 'completed_at' => '2026-06-30 10:00:00'],
],
'queue' => ['invoice_collection_ids' => []],
'draft' => ['invoice_collection_ids' => [503]],
];
$metadata = [
501 => ['id' => 501, 'invoice_collection_id' => 501, 'customer_number' => 42424242, 'state' => 'economic_booked'],
502 => ['id' => 502, 'invoice_collection_id' => 502, 'customer_number' => 42424242, 'state' => 'closed'],
503 => ['id' => 503, 'invoice_collection_id' => 503, 'customer_number' => 42424242, 'state' => 'economic_draft'],
];
$result = invoicing_period_invoice_state_invoke('applyPeriodInvoiceStateOverlayToCustomer', [$customer, $metadata]);
expect(array_column($result['transactions'], 'invoice_state'))->toBe([
'economic_booked',
'economic_booked',
'closed',
'closed',
]);
expect(array_column($result['transactions'], 'booked'))->toBe([true, true, false, false]);
expect($result['invoice_collections'])->toHaveCount(3);
expect($result['invoice_collections'][0])->toMatchArray([
'id' => 501,
'order_ids' => [11, 12],
'order_count' => 2,
'total_net_amount' => 150.5,
]);
expect($result['invoice_collections'][2])->toMatchArray([
'id' => 503,
'order_ids' => [],
'order_count' => 0,
'total_net_amount' => 0.0,
]);
});
it('loads all referenced invoice collection metadata in one local query', function (): void {
$hadDb = array_key_exists('db', $GLOBALS);
$originalDb = $GLOBALS['db'] ?? null;
$fakeDb = new InvoicingPeriodInvoiceStateFakeDb([
[
'id' => '501',
'customer_number' => '42424242',
'processor' => '1',
'external_id' => 'draft-501',
'booked_invoice_id' => null,
'error_message' => null,
'closed_at' => '2026-06-30 12:00:00',
],
[
'id' => '502',
'customer_number' => '42424242',
'processor' => '1',
'external_id' => 'draft-502',
'booked_invoice_id' => '7002',
'error_message' => null,
'closed_at' => '2026-06-30 12:00:00',
],
]);
$GLOBALS['db'] = $fakeDb;
try {
$metadata = invoicing_period_invoice_state_invoke('getPeriodInvoiceCollectionMetadata', [[
'all' => [[
'customer_number' => 42424242,
'transactions' => [
['invoice_collection_id' => 501],
['invoice_collection_id' => 501],
],
'queue' => ['invoice_collection_ids' => [502]],
'draft' => ['invoice_collection_ids' => []],
]],
'fixed_pricing' => [[
'customer_number' => 42424242,
'transactions' => [['invoice_collection_id' => 502]],
]],
]]);
} finally {
if ($hadDb) {
$GLOBALS['db'] = $originalDb;
} else {
unset($GLOBALS['db']);
}
}
expect($fakeDb->queryCount)->toBe(1);
expect($fakeDb->sql)->toContain('WHERE id IN (501,502)');
expect($metadata[501]['state'])->toBe('economic_draft');
expect($metadata[502]['state'])->toBe('economic_booked');
});