157 lines
7.5 KiB
PHP
157 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\EcommerceFinance;
|
|
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Modules\EcommerceFinance\Repositories\CostFreightAnomalyRepository;
|
|
use Tests\TestCase;
|
|
|
|
class CostFreightAnomalyRepositoryTest 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::connection('business_mirror')->create('ops_shengsuan_order_profit_rows', function (Blueprint $table): void {
|
|
$table->bigIncrements('id');
|
|
$table->string('shop_name');
|
|
$table->string('online_order_no')->nullable();
|
|
$table->string('internal_order_no')->nullable();
|
|
$table->string('order_key');
|
|
$table->dateTime('ship_time')->nullable();
|
|
$table->decimal('product_cost', 18, 2)->nullable();
|
|
$table->decimal('matched_package_fee', 18, 2)->nullable();
|
|
$table->decimal('matched_freight_fee', 18, 2)->nullable();
|
|
$table->decimal('matched_bill_fee', 18, 2)->nullable();
|
|
$table->longText('source_payload')->nullable();
|
|
$table->dateTime('synced_at')->nullable();
|
|
$table->timestamps();
|
|
});
|
|
Schema::connection('business_mirror')->create('ops_shengsuan_order_profit_detail_snapshots', function (Blueprint $table): void {
|
|
$table->bigIncrements('id');
|
|
$table->string('order_key');
|
|
$table->string('shop_name');
|
|
$table->string('online_order_no')->nullable();
|
|
$table->string('internal_order_no')->nullable();
|
|
$table->unsignedInteger('item_count')->default(0);
|
|
$table->unsignedInteger('after_item_count')->default(0);
|
|
$table->unsignedInteger('missing_cost_item_count')->default(0);
|
|
$table->longText('order_headers')->nullable();
|
|
$table->longText('order_items')->nullable();
|
|
$table->longText('after_headers')->nullable();
|
|
$table->longText('after_items')->nullable();
|
|
$table->longText('missing_cost_barcodes')->nullable();
|
|
$table->dateTime('synced_at')->nullable();
|
|
$table->timestamps();
|
|
});
|
|
|
|
$db = DB::connection('business_mirror');
|
|
foreach ([
|
|
['shop_name' => '异常店', 'online_order_no' => 'NO-1', 'order_key' => '异常店|NO-1', 'ship_time' => '2026-11-05 10:00:00', 'product_cost' => 0, 'matched_freight_fee' => 0],
|
|
['shop_name' => '运费异常店', 'online_order_no' => 'NO-2', 'order_key' => '运费异常店|NO-2', 'ship_time' => '2026-11-05 11:00:00', 'product_cost' => 100, 'matched_freight_fee' => 0],
|
|
['shop_name' => '成本异常店', 'online_order_no' => 'NO-3', 'order_key' => '成本异常店|NO-3', 'ship_time' => '2026-11-06 11:00:00', 'product_cost' => 0, 'matched_freight_fee' => 7],
|
|
['shop_name' => '正常店', 'online_order_no' => 'NO-4', 'order_key' => '正常店|NO-4', 'ship_time' => '2026-11-06 12:00:00', 'product_cost' => 100, 'matched_freight_fee' => 7],
|
|
['shop_name' => '成本缺失店', 'online_order_no' => 'NO-5', 'order_key' => '成本缺失店|NO-5', 'ship_time' => '2026-11-06 13:00:00', 'product_cost' => null, 'matched_freight_fee' => 9],
|
|
] as $row) {
|
|
$db->table('ops_shengsuan_order_profit_rows')->insert($row + [
|
|
'internal_order_no' => null,
|
|
'matched_package_fee' => 0,
|
|
'matched_bill_fee' => 0,
|
|
'source_payload' => json_encode(['c_900901' => $row['shop_name'], 'c_900902' => $row['online_order_no']]),
|
|
'synced_at' => '2026-11-07 10:00:00',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
$db->table('ops_shengsuan_order_profit_detail_snapshots')->insert([
|
|
'order_key' => '异常店|NO-1',
|
|
'shop_name' => '异常店',
|
|
'online_order_no' => 'NO-1',
|
|
'internal_order_no' => null,
|
|
'item_count' => 2,
|
|
'after_item_count' => 0,
|
|
'missing_cost_item_count' => 1,
|
|
'order_headers' => json_encode([
|
|
['fld' => 'c_900904', 'title' => '商品编码'],
|
|
['fld' => 'c_900910', 'title' => '商品名称'],
|
|
['fld' => 'c_900201', 'title' => '商品成本'],
|
|
]),
|
|
'order_items' => json_encode([
|
|
['c_900904' => 'BAR-001', 'c_900910' => '缺成本商品', 'c_900201' => 0],
|
|
['c_900904' => 'BAR-002', 'c_900910' => '正常成本商品', 'c_900201' => 20],
|
|
]),
|
|
'after_headers' => '[]',
|
|
'after_items' => '[]',
|
|
'missing_cost_barcodes' => json_encode(['BAR-001']),
|
|
'synced_at' => '2026-11-09 10:00:00',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
public function test_orders_with_either_cost_or_freight_missing_are_classified_independently(): void
|
|
{
|
|
$result = app(CostFreightAnomalyRepository::class)->paginate([
|
|
'from' => '2026-11-01',
|
|
'to' => '2026-11-07',
|
|
'order_no' => '',
|
|
'shop_names' => [],
|
|
], 1, 25);
|
|
|
|
$rows = array_column($result['rows'], null, 'online_order_no');
|
|
|
|
$this->assertSame(4, $result['total']);
|
|
$this->assertSame(['成本异常', '运费异常'], $rows['NO-1']['anomaly_types']);
|
|
$this->assertSame('成本异常、运费异常', $rows['NO-1']['anomaly_type']);
|
|
$this->assertSame(['运费异常'], $rows['NO-2']['anomaly_types']);
|
|
$this->assertSame(['成本异常'], $rows['NO-3']['anomaly_types']);
|
|
$this->assertSame(['成本异常'], $rows['NO-5']['anomaly_types']);
|
|
$this->assertArrayNotHasKey('NO-4', $rows);
|
|
$this->assertSame(['BAR-001'], $rows['NO-1']['missing_cost_barcodes']);
|
|
$this->assertSame(2, $rows['NO-1']['detail_item_count']);
|
|
$this->assertSame('商品编码', $rows['NO-1']['detail_order_headers'][0]['title']);
|
|
$this->assertSame('异常店', collect($rows['NO-1']['order_fields'])->firstWhere('code', 'c_900901')['value']);
|
|
}
|
|
|
|
public function test_order_and_shop_batch_filters_are_applied(): void
|
|
{
|
|
$repository = app(CostFreightAnomalyRepository::class);
|
|
$result = $repository->paginate([
|
|
'from' => '2026-11-01',
|
|
'to' => '2026-11-07',
|
|
'order_no' => 'NO-1',
|
|
'shop_names' => ['异常店'],
|
|
], 1, 25);
|
|
|
|
$this->assertSame(1, $result['total']);
|
|
$this->assertSame('异常店', $result['rows'][0]['shop_name']);
|
|
}
|
|
|
|
public function test_missing_detail_references_exclude_orders_already_synced(): void
|
|
{
|
|
$references = app(CostFreightAnomalyRepository::class)->references([
|
|
'from' => '2026-11-01',
|
|
'to' => '2026-11-07',
|
|
'order_no' => '',
|
|
'shop_names' => [],
|
|
], true);
|
|
|
|
$orderNumbers = array_column($references, 'order_no');
|
|
$this->assertCount(3, $references);
|
|
$this->assertNotContains('NO-1', $orderNumbers);
|
|
$this->assertContains('NO-2', $orderNumbers);
|
|
$this->assertContains('NO-3', $orderNumbers);
|
|
$this->assertContains('NO-5', $orderNumbers);
|
|
}
|
|
}
|