CaiWuXiTong/tests/Unit/EcommerceFinance/SupplierStatementMatcherTest.php
苏木 a9c49c85cb fix: 修复厂家对账异常日期导致批量误报
Squash merge PR #961.
Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
2026-11-06 16:51:02 +08:00

309 lines
10 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Modules\EcommerceFinance\Services\SupplierStatementMatcher;
use Tests\TestCase;
class SupplierStatementMatcherTest extends TestCase
{
public function test_seven_day_date_difference_matches_when_purchase_barcode_quantity_and_amount_match(): void
{
$mine = [[
'purchaseNo' => '879735',
'date' => '2026-09-10',
'skuCode' => 'DNZ033609',
'productCode' => 'DNZ033609N1178V01',
'unitPrice' => '100',
'qty' => '20',
'amount' => '2000',
]];
$theirs = [[
'purchaseNo' => '879735',
'createdAt' => '2026-09-17',
'barcode' => 'DNZ033609N1097V01',
'unitPrice' => '100',
'qty' => '20',
'amount' => '2000',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(1, $result['summary']['match']);
$this->assertSame(0, $result['summary']['supplierExtra']);
$this->assertSame(0, $result['summary']['supplierMissing']);
$this->assertSame(0, $result['summary']['mismatch']);
$this->assertSame(0.0, $result['summary']['differenceAmount']);
$this->assertSame(2000.0, $result['summary']['settleAmount']);
$this->assertSame(7, $result['rows'][0]['dateToleranceDays']);
$this->assertStringContainsString('日期相差 7 天', $result['rows'][0]['note']);
}
public function test_date_difference_over_seven_days_remains_a_difference(): void
{
$mine = [[
'purchaseNo' => '879735',
'date' => '2026-09-10',
'skuCode' => 'DNZ033609',
'qty' => '20',
'amount' => '2000',
]];
$theirs = [[
'purchaseNo' => '879735',
'createdAt' => '2026-09-18',
'barcode' => 'DNZ033609N1097V01',
'qty' => '20',
'amount' => '2000',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(0, $result['summary']['match']);
$this->assertSame(1, $result['summary']['supplierExtra']);
$this->assertSame(1, $result['summary']['supplierMissing']);
}
public function test_date_tolerance_still_requires_equal_quantity_and_amount(): void
{
$mine = [[
'purchaseNo' => '879735',
'date' => '2026-09-10',
'skuCode' => 'DNZ033609',
'qty' => '20',
'amount' => '2000',
]];
$theirs = [[
'purchaseNo' => '879735',
'createdAt' => '2026-09-12',
'barcode' => 'DNZ033609N1097V01',
'qty' => '19',
'amount' => '1900',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(0, $result['summary']['match']);
$this->assertSame(1, $result['summary']['supplierExtra']);
$this->assertSame(1, $result['summary']['supplierMissing']);
}
public function test_unrecognized_supplier_date_matches_unique_purchase_and_barcode(): void
{
$mine = [[
'purchaseNo' => '879735',
'date' => '2026-10-10',
'skuCode' => 'SJ009363',
'productCode' => 'SJ009363N160V01',
'qty' => '130',
'amount' => '12350',
]];
$theirs = [[
'purchaseNo' => '879735',
'createdAt' => '2026.1',
'barcode' => 'SJ009363N160V01',
'qty' => '130',
'amount' => '12350',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(1, $result['summary']['match']);
$this->assertSame(0, $result['summary']['supplierExtra']);
$this->assertSame(0, $result['summary']['supplierMissing']);
$this->assertSame(0, $result['summary']['mismatch']);
$this->assertSame('2026-10-10', $result['rows'][0]['date']);
$this->assertStringContainsString('厂家创建时间无法识别', $result['rows'][0]['note']);
}
public function test_unrecognized_supplier_date_does_not_guess_when_purchase_and_barcode_are_ambiguous(): void
{
$mine = [
[
'purchaseNo' => '879735',
'date' => '2026-10-10',
'skuCode' => 'SJ009363',
'qty' => '130',
'amount' => '12350',
],
[
'purchaseNo' => '879735',
'date' => '2026-10-12',
'skuCode' => 'SJ009363',
'qty' => '130',
'amount' => '12350',
],
];
$theirs = [[
'purchaseNo' => '879735',
'createdAt' => '2026.1',
'barcode' => 'SJ009363N160V01',
'qty' => '130',
'amount' => '12350',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(0, $result['summary']['match']);
$this->assertSame(1, $result['summary']['supplierExtra']);
$this->assertSame(2, $result['summary']['supplierMissing']);
}
public function test_unrecognized_supplier_date_matches_mine_rows_aggregated_across_dates(): void
{
$mine = [
[
'purchaseNo' => '923923',
'date' => '2026-10-03',
'skuCode' => 'CBG011611',
'qty' => '36',
'amount' => '2466',
],
[
'purchaseNo' => '923923',
'date' => '2026-10-04',
'skuCode' => 'CBG011611',
'qty' => '44',
'amount' => '3014',
],
];
$theirs = [[
'purchaseNo' => '923923',
'createdAt' => '2026.1',
'barcode' => 'CBG011611N160V01',
'qty' => '80',
'amount' => '5480',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(1, $result['summary']['match']);
$this->assertSame(0, $result['summary']['supplierExtra']);
$this->assertSame(0, $result['summary']['supplierMissing']);
$this->assertSame(80.0, $result['rows'][0]['mineQty']);
$this->assertSame(5480.0, $result['rows'][0]['mineAmount']);
$this->assertSame(2, $result['rows'][0]['dateCount']);
$this->assertStringContainsString('汇总我方 2 个日期', $result['rows'][0]['note']);
}
public function test_duplicate_mine_rows_keep_the_purchase_number_present_in_supplier_data(): void
{
$mine = [
[
'purchaseNo' => '911299',
'date' => '2026-10-10',
'skuCode' => 'XG007322',
'productCode' => 'XG007322N160V01',
'qty' => '30',
'amount' => '4530',
],
[
'purchaseNo' => '941858',
'date' => '2026-10-10',
'skuCode' => 'XG007322',
'productCode' => 'XG007322N160V01',
'qty' => '30',
'amount' => '4530',
],
];
$theirs = [[
'purchaseNo' => '941858',
'createdAt' => '2026.1',
'barcode' => 'XG007322N160V01',
'qty' => '30',
'amount' => '4530',
]];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$this->assertSame(1, $result['summary']['match']);
$this->assertSame(0, $result['summary']['supplierExtra']);
$this->assertSame(0, $result['summary']['supplierMissing']);
$this->assertSame('941858', $result['rows'][0]['purchaseNo']);
}
public function test_regression_keeps_only_the_real_supplier_extra_line(): void
{
$mine = [
[
'purchaseNo' => '911299',
'date' => '2026-10-10',
'skuCode' => 'XG007322',
'qty' => '30',
'amount' => '4530',
],
[
'purchaseNo' => '941858',
'date' => '2026-10-10',
'skuCode' => 'XG007322',
'qty' => '30',
'amount' => '4530',
],
[
'purchaseNo' => '923923',
'date' => '2026-10-03',
'skuCode' => 'CBG011611',
'qty' => '36',
'amount' => '2466',
],
[
'purchaseNo' => '923923',
'date' => '2026-10-04',
'skuCode' => 'CBG011611',
'qty' => '44',
'amount' => '3014',
],
[
'purchaseNo' => '937779',
'date' => '2026-10-20',
'skuCode' => 'SJ009363',
'qty' => '130',
'amount' => '12350',
],
];
$theirs = [
[
'purchaseNo' => '941858',
'createdAt' => '2026.1',
'barcode' => 'XG007322N160V01',
'qty' => '30',
'amount' => '4530',
],
[
'purchaseNo' => '923923',
'createdAt' => '2026.1',
'barcode' => 'CBG011611N160V01',
'qty' => '80',
'amount' => '5480',
],
[
'purchaseNo' => '937779',
'createdAt' => '2026.1',
'barcode' => 'SJ009363N160V01',
'qty' => '130',
'amount' => '12350',
],
[
'purchaseNo' => '922003',
'createdAt' => '2026.1',
'barcode' => 'SJ009363N160V01',
'qty' => '20',
'amount' => '0',
],
];
$result = (new SupplierStatementMatcher())->compare($mine, $theirs);
$differences = array_values(array_filter(
$result['rows'],
static fn (array $row): bool => $row['type'] !== SupplierStatementMatcher::TYPE_MATCH,
));
$this->assertSame(3, $result['summary']['match']);
$this->assertSame(1, $result['summary']['supplierExtra']);
$this->assertSame(0, $result['summary']['supplierMissing']);
$this->assertCount(1, $differences);
$this->assertSame('922003', $differences[0]['purchaseNo']);
$this->assertSame('SJ009363N160V01', $differences[0]['barcode']);
$this->assertSame(20.0, $differences[0]['qtyDifference']);
}
}