49 lines
2.9 KiB
PHP
49 lines
2.9 KiB
PHP
<?php
|
|
|
|
use Tempo\Purchasing\Economic\ProposalScoring;
|
|
use Tempo\Purchasing\Economic\QuotationPricing;
|
|
|
|
require_once __DIR__.'/../../packages/purchasing/bootstrap.php';
|
|
|
|
function economicRow(int $id, int $price, int $quality = 8): array
|
|
{
|
|
return ['id' => $id, 'eligible' => true, 'subtotal_minor' => $price, 'delivery_days' => 2,
|
|
'weights' => ['price' => 50, 'quality' => 50, 'delivery' => 0, 'history' => 0, 'payment' => 0],
|
|
'evaluation_count' => 2, 'qualified_evaluations' => array_fill(0, 2, ['quality' => (string) $quality, 'history' => '8', 'payment' => '8'])];
|
|
}
|
|
|
|
test('shared unit pricing preserves discounts and half up net unit cents', function () {
|
|
$pricing = new QuotationPricing;
|
|
expect($pricing->unit('19.99', 'percent', '12.50'))->toBe(['gross_unit_minor' => 1999, 'discount_type' => 'percent', 'discount_value' => '12.50', 'net_unit_minor' => 1749])
|
|
->and($pricing->unit('0.03', 'percent', '50')['net_unit_minor'])->toBe(2)
|
|
->and($pricing->unit('100', 'amount', '7.50')['net_unit_minor'])->toBe(9250)
|
|
->and($pricing->unit('100')['net_unit_minor'])->toBe(10000);
|
|
});
|
|
|
|
test('shared pricing rejects invalid discounts without Laravel or a database', function () {
|
|
foreach ([['0', 'none', '0'], ['100', 'percent', '101'], ['100', 'amount', '100'], ['100', 'none', '1'], ['100', 'unknown', '0']] as $input) {
|
|
expect(fn () => (new QuotationPricing)->unit(...$input))->toThrow(InvalidArgumentException::class);
|
|
}
|
|
});
|
|
|
|
test('shared scoring preserves exact weighted results all ties and host supplied ineligibility', function () {
|
|
$rows = [economicRow(1, 10000), economicRow(2, 20000, 10), economicRow(3, 10000), [...economicRow(4, 1, 10), 'eligible' => false]];
|
|
expect((new ProposalScoring)->rank($rows, 2))->toBe(['scores' => [1 => 900, 2 => 750, 3 => 900], 'top_ids' => [1, 3]]);
|
|
});
|
|
|
|
test('shared scoring retains the existing reference basis when eligible rows lack sufficient evaluations', function () {
|
|
$pending = economicRow(5, 5000);
|
|
$pending['qualified_evaluations'] = [$pending['qualified_evaluations'][0]];
|
|
$pending['evaluation_count'] = 1;
|
|
expect((new ProposalScoring)->rank([economicRow(1, 10000), economicRow(2, 20000, 10), $pending], 2))->toBe(['scores' => [1 => 650, 2 => 625], 'top_ids' => [1]]);
|
|
expect((new ProposalScoring)->rank([], 2))->toBe(['scores' => [], 'top_ids' => []]);
|
|
});
|
|
|
|
test('shared scoring refuses inconsistent projected inputs rather than inventing a result', function () {
|
|
$row = economicRow(1, 10000);
|
|
foreach ([[$row, $row], [[...$row, 'subtotal_minor' => 0]], [[...$row, 'evaluation_count' => 3]], [[...$row, 'weights' => ['price' => 100]]]] as $rows) {
|
|
expect(fn () => (new ProposalScoring)->rank($rows, 2))->toThrow(InvalidArgumentException::class);
|
|
}
|
|
expect(fn () => (new ProposalScoring)->rank([$row], 0))->toThrow(InvalidArgumentException::class);
|
|
});
|