136 lines
4.8 KiB
PHP
136 lines
4.8 KiB
PHP
<?php
|
|
|
|
app_require('classes/economic_v2_compare_engine.php');
|
|
|
|
use classes\economic_v2_compare_engine;
|
|
|
|
function economic_v2_test_invoice(array $overrides = []): array
|
|
{
|
|
$base = [
|
|
'source' => 'internal',
|
|
'totals' => [
|
|
'net_total' => 100.0,
|
|
'line_net_total' => 100.0,
|
|
'line_count' => 1,
|
|
'billable_line_count' => 1,
|
|
],
|
|
'departments' => [
|
|
'75' => 100.0,
|
|
],
|
|
'lines' => [
|
|
[
|
|
'source' => 'internal',
|
|
'source_line_id' => 1,
|
|
'line_type' => 'product',
|
|
'billable' => true,
|
|
'product_number' => '1',
|
|
'description' => 'Trakker',
|
|
'reference' => '',
|
|
'quantity' => 1.0,
|
|
'unit_net_price' => 100.0,
|
|
'line_net_amount' => 100.0,
|
|
'department_distribution' => ['75' => 100.0],
|
|
'match_key' => 'product:1|ref:',
|
|
],
|
|
],
|
|
'warnings' => [],
|
|
];
|
|
|
|
return array_replace_recursive($base, $overrides);
|
|
}
|
|
|
|
it('returns exact_match when totals lines and departments are identical', function (): void {
|
|
$internal = economic_v2_test_invoice();
|
|
$draft = economic_v2_test_invoice(['source' => 'draft']);
|
|
$booked = economic_v2_test_invoice(['source' => 'booked']);
|
|
|
|
$result = economic_v2_compare_engine::compare($internal, $draft, $booked);
|
|
|
|
expect($result['targets']['draft']['status'])->toBe('exact_match');
|
|
expect($result['targets']['booked']['status'])->toBe('exact_match');
|
|
expect($result['targets']['draft']['overall_match'])->toBeTrue();
|
|
expect($result['targets']['booked']['overall_match'])->toBeTrue();
|
|
});
|
|
|
|
it('returns total_mismatch when only totals differ', function (): void {
|
|
$internal = economic_v2_test_invoice();
|
|
$draft = economic_v2_test_invoice([
|
|
'source' => 'draft',
|
|
'totals' => ['net_total' => 125.0],
|
|
]);
|
|
|
|
$result = economic_v2_compare_engine::compareTarget($internal, $draft, 'draft');
|
|
|
|
expect($result['status'])->toBe('total_mismatch');
|
|
expect($result['totals']['matches'])->toBeFalse();
|
|
expect($result['mismatch_reasons'])->toContain('total_mismatch');
|
|
});
|
|
|
|
it('detects line-level mismatches for quantity and price', function (): void {
|
|
$internal = economic_v2_test_invoice();
|
|
$draft = economic_v2_test_invoice([
|
|
'source' => 'draft',
|
|
'lines' => [[
|
|
'source' => 'draft',
|
|
'source_line_id' => 1,
|
|
'line_type' => 'product',
|
|
'billable' => true,
|
|
'product_number' => '1',
|
|
'description' => 'Trakker',
|
|
'reference' => '',
|
|
'quantity' => 2.0,
|
|
'unit_net_price' => 95.0,
|
|
'line_net_amount' => 190.0,
|
|
'department_distribution' => ['75' => 100.0],
|
|
'match_key' => 'product:1|ref:',
|
|
]],
|
|
'totals' => ['net_total' => 100.0],
|
|
'departments' => ['75' => 100.0],
|
|
]);
|
|
|
|
$result = economic_v2_compare_engine::compareTarget($internal, $draft, 'draft');
|
|
$reasons = $result['lines']['diff'][0]['reasons'] ?? [];
|
|
|
|
expect($result['lines']['summary']['mismatch_count'])->toBeGreaterThan(0);
|
|
expect($reasons)->toContain('quantity_mismatch');
|
|
expect($reasons)->toContain('unit_price_mismatch');
|
|
});
|
|
|
|
it('detects departmental distribution mismatches', function (): void {
|
|
$internal = economic_v2_test_invoice();
|
|
$draft = economic_v2_test_invoice([
|
|
'source' => 'draft',
|
|
'lines' => [[
|
|
'source' => 'draft',
|
|
'source_line_id' => 1,
|
|
'line_type' => 'product',
|
|
'billable' => true,
|
|
'product_number' => '1',
|
|
'description' => 'Trakker',
|
|
'reference' => '',
|
|
'quantity' => 1.0,
|
|
'unit_net_price' => 100.0,
|
|
'line_net_amount' => 100.0,
|
|
'department_distribution' => ['10' => 100.0],
|
|
'match_key' => 'product:1|ref:',
|
|
]],
|
|
'departments' => ['10' => 100.0],
|
|
]);
|
|
|
|
$result = economic_v2_compare_engine::compareTarget($internal, $draft, 'draft');
|
|
$lineReasons = $result['lines']['diff'][0]['reasons'] ?? [];
|
|
|
|
expect($lineReasons)->toContain('departmental_distribution_mismatch');
|
|
expect($result['departments']['matches'])->toBeFalse();
|
|
expect($result['mismatch_reasons'])->toContain('department_total_mismatch');
|
|
});
|
|
|
|
it('returns missing_target when draft or booked target is unavailable', function (): void {
|
|
$internal = economic_v2_test_invoice();
|
|
$result = economic_v2_compare_engine::compareTarget($internal, null, 'booked');
|
|
|
|
expect($result['status'])->toBe('missing_target');
|
|
expect($result['overall_match'])->toBeFalse();
|
|
expect($result['mismatch_reasons'])->toContain('missing_target');
|
|
});
|