CaiWuXiTong/tests/Unit/Business/KukaPostingMaterialMapSynchronizerTest.php
chenxi 27021ce55a feat: 顾家发货明细补齐物料与成本 (#1117)
按货号同步唯一物料编号,并从顾家报价单带出物料描述和含税成本,按实发数量计算发货金额。
2026-11-11 20:08:21 +08:00

129 lines
5.2 KiB
PHP

<?php
namespace Tests\Unit\Business;
use App\Business\KukaPostingBase\KukaPostingMaterialMapSynchronizer;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Mockery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
use Tests\TestCase;
class KukaPostingMaterialMapSynchronizerTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
]);
DB::purge('business_mirror');
$schema = Schema::connection('business_mirror');
$schema->create('kuka_posting_base_rows', function (Blueprint $table): void {
$table->bigIncrements('id');
$table->unsignedBigInteger('company_id');
$table->string('period', 7);
$table->longText('source_values');
});
$schema->create('kuka_posting_quotation_rows', function (Blueprint $table): void {
$table->bigIncrements('id');
$table->unsignedBigInteger('company_id');
$table->string('material_number', 191);
$table->string('record_status', 64)->nullable();
});
$schema->create('kuka_posting_material_map_rows', function (Blueprint $table): void {
$table->bigIncrements('id');
$table->unsignedBigInteger('company_id');
$table->string('item_number', 191);
$table->string('material_number', 191)->nullable();
$table->string('match_status', 24);
$table->string('match_source', 24)->nullable();
$table->unsignedInteger('candidate_count')->default(0);
$table->longText('candidate_materials')->nullable();
$table->timestamp('synced_at');
});
}
public function test_only_a_unique_active_quotation_match_is_selected(): void
{
$connection = DB::connection('business_mirror');
foreach (['A', 'B', 'C', 'D', 'E'] as $itemNumber) {
$connection->table('kuka_posting_base_rows')->insert([
'company_id' => 1,
'period' => '2026-10',
'source_values' => json_encode(['货号' => 'ITEM-'.$itemNumber], JSON_THROW_ON_ERROR),
]);
}
foreach (['Q-A', 'Q-B', 'Q-C1', 'Q-C2', 'Q-E'] as $materialNumber) {
$connection->table('kuka_posting_quotation_rows')->insert([
'company_id' => 1,
'material_number' => $materialNumber,
'record_status' => '生效',
]);
}
$query = Mockery::mock(UpstreamReadOnlyQuery::class);
$query->shouldReceive('select')
->twice()
->andReturnUsing(static function (string $sql): array {
if (str_contains($sql, 'BarcodePrintImportRecord')) {
return [
['item_number' => 'ITEM-A', 'material_number' => 'Q-A'],
['item_number' => 'ITEM-B', 'material_number' => 'Q-B'],
['item_number' => 'ITEM-C', 'material_number' => 'Q-C-OLD'],
['item_number' => 'ITEM-E', 'material_number' => 'Q-E-OLD'],
];
}
return [
['item_number' => 'ITEM-A', 'material_number' => 'Q-A'],
['item_number' => 'ITEM-B', 'material_number' => 'Q-B-ARCHIVE'],
['item_number' => 'ITEM-C', 'material_number' => 'Q-C1'],
['item_number' => 'ITEM-C', 'material_number' => 'Q-C2'],
['item_number' => 'ITEM-E', 'material_number' => 'Q-E'],
];
});
$factory = Mockery::mock(UpstreamReadOnlyQueryFactory::class);
$factory->shouldReceive('forSourceWithTimeout')
->once()
->with(KukaPostingMaterialMapSynchronizer::SOURCE_KEY, 15)
->andReturn($query);
$result = (new KukaPostingMaterialMapSynchronizer(app('db'), $factory))
->sync(1, '2026-10');
$this->assertSame([
'dry_run' => false,
'items' => 5,
'matched' => 3,
'unmatched' => 1,
'ambiguous' => 1,
'direct' => 2,
'archive' => 1,
], $result);
$rows = $connection->table('kuka_posting_material_map_rows')
->orderBy('item_number')
->get()
->keyBy('item_number');
$this->assertSame('Q-A', $rows['ITEM-A']->material_number);
$this->assertSame('barcode_material_no', $rows['ITEM-A']->match_source);
$this->assertSame('Q-B', $rows['ITEM-B']->material_number);
$this->assertSame('Q-E', $rows['ITEM-E']->material_number);
$this->assertSame('archive_barcode', $rows['ITEM-E']->match_source);
$this->assertSame('ambiguous', $rows['ITEM-C']->match_status);
$this->assertNull($rows['ITEM-C']->material_number);
$this->assertSame('unmatched', $rows['ITEM-D']->match_status);
}
}