CaiWuXiTong/tests/Unit/EcommerceFinance/CourierSurchargeSettlementMirrorRepositoryTest.php
zhoushengchen d3fee64f80 feat: 接入聚水潭附加费结算镜像
(cherry picked from commit 4b79370faea61aefe05dfe058480600b6fe9981e)
2026-10-12 09:19:55 +08:00

199 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Unit\EcommerceFinance;
use App\Models\Business\CourierSurchargeQuote;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\EcommerceFinance\Repositories\CourierSurchargeSettlementMirrorRepository;
use Modules\EcommerceFinance\Repositories\GenericBusinessMirrorRepository;
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
use Tests\TestCase;
class CourierSurchargeSettlementMirrorRepositoryTest extends TestCase
{
private CourierSurchargeSettlementMirrorRepository $repository;
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
'upstream_mirrors.courier_surcharge_settlement.approved_values' => ['通过', '同意', '同意记账'],
'upstream_mirrors.datasets' => [
'jiansun.dbo.ekf_Bookkeeping' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'ekf_Bookkeeping',
'target' => 'js_ekf_bookkeeping_surcharge', 'keys' => ['Id'], 'watermark' => 'CreationTime',
'columns' => ['Id', 'CreationTime', 'OrderNum', 'ExpressNum', 'ExpressName', 'ShopCode', 'Province', 'City', 'District', 'Address', 'SupplierName', 'SkuName', 'SkuCode', 'Result', 'UserName', 'Payfee', 'SecodaryCause', 'ReasonId', 'State', 'Delete', 'IsAgreeReason', 'ProcessingState', 'BookkeepingName', 'PayTime', 'Svolume', 'Skuweight', 'IfReturn', 'IfBuckle', 'FuZhuMain', 'FuZhuMinor', 'ShipExpressName', 'expressCause', 'receivingDepartment', 'returnCost', 'shopproid'],
'fixed_filters' => ['IsAgreeReason' => ['通过', '同意', '同意记账']],
],
'jiansun.dbo.express_Original' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'express_Original',
'target' => 'js_express_original', 'keys' => ['Id'], 'watermark' => 'DeliveryTime',
'columns' => ['Id', 'ExpressName', 'OrderNum', 'DeliveryTime', 'Lenght', 'Width', 'Height', 'Volume', 'SkuCode', 'JiaoYiNum', 'ShopName', 'SetMoney', 'SetWeight', 'shopproid'],
],
'jiansun.dbo.ekf_BookkeepingConfig' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'ekf_BookkeepingConfig',
'target' => 'js_ekf_bookkeeping_config', 'keys' => ['Id'], 'watermark' => 'CreateTime',
'columns' => ['Id', 'Name', 'ParentId', 'CreateTime'],
],
'jiansun.dbo.ekf_rCheck_Piece' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'ekf_rCheck_Piece',
'target' => 'js_ekf_rcheck_piece', 'keys' => ['Id'], 'watermark' => 'UpdateTime',
'columns' => ['Id', 'CheckType', 'ExpressNum', 'OrderNum', 'CheckSDType', 'CheckReason', 'State', 'Accounting', 'ReturnState', 'CreationTime', 'UpdateTime', 'Delete', 'OmsSDName', 'DHLName', 'SkuCode', 'shopproid'],
],
'jiansun.dbo.ekf_rCheckPieceConfig' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'ekf_rCheckPieceConfig',
'target' => 'js_ekf_rcheck_piece_config', 'keys' => ['Id'], 'watermark' => 'CreateTime',
'columns' => ['Id', 'ParentId', 'Name', 'Code', 'Type', 'Delete', 'CreateTime'],
],
'jiansun.dbo.jst_order_express_details' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'jst_order_express_details',
'target' => 'js_jst_order_express_details', 'keys' => ['id'], 'watermark' => 'updatedAt',
'columns' => ['id', 'updatedAt', 'inner_order_id', 'so_id', 'logistic_id', 'logistic_company_name', 'logistic_status_name', 'platform_name', 'shop_name', 'warehouse_name', 'send_time', 'weight', 'f_weight'],
],
'jiansun.dbo.Jst_SendData_View_extension' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'Jst_SendData_View_extension',
'target' => 'js_jst_send_data_view_extension', 'keys' => ['Id'], 'watermark' => null,
'columns' => ['Id', 'io_id', 'qty', 'l', 'w', 'h', 'sku_ids', 'sku_name', 'so_id', 'shop_site', 'shop_name', 'shop_i_id', 'supplier_name', 'receiver_state', 'receiver_city', 'receiver_district', 'expressName', 'wareHoursName', 'volume', 'weight'],
],
'jiansun.dbo.jst_salesOutboundInfo' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'jst_salesOutboundInfo',
'target' => 'js_jst_sales_outbound_info', 'keys' => ['Id'], 'watermark' => 'updateTime',
'columns' => ['Id', 'io_id', 'modified', 'updateTime', 'f_weight', 'weight', 'f_freight', 'freight', 'pay_date', 'volume', 'f_volume'],
],
'jiansun.dbo.ekf_ReturnPickupManagement' => [
'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'ekf_ReturnPickupManagement',
'target' => 'js_ekf_return_pickup_management', 'keys' => ['Id'], 'watermark' => 'updateTime',
'columns' => ['Id', 'orderId', 'returnExpressNo', 'transactionNo', 'updateTime'],
],
],
]);
DB::purge('business_mirror');
$schema = Schema::connection('business_mirror');
$schema->create('business_mirror_table_states', function (Blueprint $table): void {
$table->string('dataset_key')->primary();
$table->dateTime('initial_completed_at')->nullable();
$table->string('status')->nullable();
$table->dateTime('last_succeeded_at')->nullable();
$table->string('last_error_code')->nullable();
});
$this->createMirrorTables($schema);
$this->repository = new CourierSurchargeSettlementMirrorRepository(
app('db'),
new BusinessMirrorDatasetRegistry(),
new GenericBusinessMirrorRepository(app('db')),
);
}
public function test_it_maps_approved_bookkeeping_to_requested_fields_and_uses_outbound_weight(): void
{
$db = DB::connection('business_mirror');
$this->markReady($db);
$db->table('js_ekf_bookkeeping_config')->insert([
['Id' => 10, 'Name' => '退回', 'ParentId' => null],
['Id' => 20, 'Name' => '二级原因', 'ParentId' => 1],
]);
$db->table('js_ekf_rcheck_piece_config')->insert([
['Id' => 1, 'ParentId' => null, 'Name' => '一级查件', 'Code' => null, 'Type' => null, 'Delete' => null],
['Id' => 2, 'ParentId' => 1, 'Name' => '二级查件原因', 'Code' => null, 'Type' => null, 'Delete' => null],
]);
$db->table('js_ekf_bookkeeping_surcharge')->insert([
'Id' => 1, 'CreationTime' => '2026-09-27 10:00:00', 'OrderNum' => 'SO-1', 'ExpressNum' => 'WB-1',
'ExpressName' => '备用快递', 'ShopCode' => '备用店铺', 'SkuCode' => '备用编码', 'Payfee' => 3.2,
'SecodaryCause' => 20, 'ReasonId' => 10, 'IsAgreeReason' => '同意', 'Svolume' => 1.1,
'Skuweight' => 0.9, 'IfReturn' => '已退回', 'IfBuckle' => null, 'ShipExpressName' => null,
'expressCause' => null, 'shopproid' => '备用商品',
]);
$db->table('js_ekf_rcheck_piece')->insert([
'Id' => 1, 'CheckType' => 1, 'ExpressNum' => 'WB-1', 'OrderNum' => 'SO-1', 'CheckReason' => 2,
'OmsSDName' => '查件物流', 'DHLName' => '不作为快递名称', 'SkuCode' => 'CHECK-SKU', 'shopproid' => 'CHECK-ITEM',
]);
$db->table('js_express_original')->insert([
'Id' => 1, 'ExpressName' => '原始物流', 'OrderNum' => 'WB-1', 'Lenght' => 11, 'Width' => 22, 'Height' => 33,
'Volume' => 7260, 'SkuCode' => 'ORIGINAL-SKU', 'JiaoYiNum' => 'TRADE-1', 'ShopName' => '原始店铺',
'SetMoney' => 18.5, 'SetWeight' => 1.75, 'shopproid' => 'ORIGINAL-ITEM',
]);
$db->table('js_jst_order_express_details')->insert([
'id' => 1, 'updatedAt' => '2026-09-27 10:01:00', 'so_id' => 'SO-1', 'logistic_id' => 'WB-1',
'logistic_company_name' => '正式快递', 'shop_name' => '正式店铺', 'f_weight' => 0.8,
]);
$db->table('js_jst_send_data_view_extension')->insert([
'Id' => 1, 'io_id' => 100, 'so_id' => 'SO-1', 'shop_i_id' => 'ITEM-1', 'sku_ids' => 'SKU-1',
'shop_name' => '正式店铺', 'l' => 10, 'w' => 20, 'h' => 30, 'volume' => 6000, 'weight' => 0.7,
]);
$db->table('js_jst_sales_outbound_info')->insert([
'Id' => 1, 'io_id' => 100, 'f_weight' => 1.25, 'weight' => 1.2, 'f_volume' => 0.5, 'volume' => 0.4,
]);
$result = $this->repository->paginate([], 1, 25, collect());
$row = $result['data']->first();
$this->assertSame(1, $result['total']);
$this->assertSame('查件物流', $row['courier_name']);
$this->assertSame('ORIGINAL-ITEM', $row['platform_product_id']);
$this->assertSame('ORIGINAL-SKU', $row['product_code']);
$this->assertSame('原始店铺', $row['shop_name']);
$this->assertSame('TRADE-1', $row['online_order_no']);
$this->assertSame(11.0, $row['length']);
$this->assertSame(22.0, $row['width']);
$this->assertSame(33.0, $row['height']);
$this->assertSame('一级查件', $row['inspection_type']);
$this->assertSame('二级查件原因', $row['inspection_reason']);
$this->assertSame('退回费', $row['surcharge_type']);
$this->assertSame(1.75, $row['settlement_weight']);
$this->assertSame(7260.0, $row['volume']);
$thresholdResult = $this->repository->paginate([], 1, 25, collect([[
'status_code' => CourierSurchargeQuote::STATUS_ACTIVE,
'courier' => '查件物流', 'types' => ['退回费'],
'mode' => '按尺寸/最长边', 'standard' => '最长边 > 30 cm:8 元/单',
]]));
$this->assertSame(8.0, $thresholdResult['data']->first()['settlement_amount']);
$volumeResult = $this->repository->paginate([], 1, 25, collect([[
'status_code' => CourierSurchargeQuote::STATUS_ACTIVE,
'courier' => '查件物流', 'types' => ['退回费'],
'mode' => '按体积', 'standard' => '体积 ≤ 8000 cm³:50 元/单',
]]));
$this->assertSame(50.0, $volumeResult['data']->first()['settlement_amount']);
}
private function createMirrorTables($schema): void
{
$schema->create('js_ekf_bookkeeping_surcharge', function (Blueprint $table): void {
$table->integer('Id'); $table->dateTime('CreationTime')->nullable();
foreach (['OrderNum', 'ExpressNum', 'ExpressName', 'ShopCode', 'Province', 'City', 'District', 'Address', 'SupplierName', 'SkuName', 'SkuCode', 'Result', 'UserName', 'State', 'Delete', 'IsAgreeReason', 'ProcessingState', 'BookkeepingName', 'PayTime', 'IfReturn', 'IfBuckle', 'FuZhuMain', 'FuZhuMinor', 'ShipExpressName', 'expressCause', 'receivingDepartment', 'returnCost', 'shopproid'] as $column) $table->string($column)->nullable();
foreach (['Payfee', 'Svolume', 'Skuweight'] as $column) $table->decimal($column, 20, 4)->nullable();
foreach (['SecodaryCause', 'ReasonId'] as $column) $table->integer($column)->nullable();
});
$schema->create('js_ekf_bookkeeping_config', function (Blueprint $table): void { $table->integer('Id'); $table->string('Name')->nullable(); $table->integer('ParentId')->nullable(); $table->dateTime('CreateTime')->nullable(); });
$schema->create('js_ekf_rcheck_piece', function (Blueprint $table): void { $table->integer('Id'); foreach (['CheckType', 'CheckReason'] as $column) $table->integer($column)->nullable(); foreach (['ExpressNum', 'OrderNum', 'CheckSDType', 'State', 'Accounting', 'ReturnState', 'CreationTime', 'UpdateTime', 'Delete', 'OmsSDName', 'DHLName', 'SkuCode', 'shopproid'] as $column) $table->string($column)->nullable(); });
$schema->create('js_ekf_rcheck_piece_config', function (Blueprint $table): void { $table->integer('Id'); $table->integer('ParentId')->nullable(); foreach (['Name', 'Code', 'Type', 'Delete', 'CreateTime'] as $column) $table->string($column)->nullable(); });
$schema->create('js_jst_order_express_details', function (Blueprint $table): void { $table->integer('id'); foreach (['updatedAt', 'so_id', 'logistic_id', 'logistic_company_name', 'shop_name', 'f_weight'] as $column) $table->string($column)->nullable(); });
$schema->create('js_jst_send_data_view_extension', function (Blueprint $table): void { $table->integer('Id'); $table->integer('io_id')->nullable(); foreach (['so_id', 'shop_i_id', 'sku_ids', 'shop_name', 'l', 'w', 'h', 'volume', 'weight'] as $column) $table->string($column)->nullable(); });
$schema->create('js_jst_sales_outbound_info', function (Blueprint $table): void { $table->integer('Id'); $table->integer('io_id')->nullable(); foreach (['f_weight', 'weight', 'f_volume', 'volume'] as $column) $table->string($column)->nullable(); });
$schema->create('js_ekf_return_pickup_management', function (Blueprint $table): void { $table->integer('Id'); foreach (['orderId', 'returnExpressNo', 'transactionNo', 'updateTime'] as $column) $table->string($column)->nullable(); });
$schema->create('js_express_original', function (Blueprint $table): void { $table->integer('Id'); foreach (['ExpressName', 'OrderNum', 'DeliveryTime', 'Lenght', 'Width', 'Height', 'Volume', 'SkuCode', 'JiaoYiNum', 'ShopName', 'SetMoney', 'SetWeight', 'shopproid'] as $column) $table->string($column)->nullable(); });
}
private function markReady($db): void
{
$now = now()->toDateTimeString();
foreach (array_keys(config('upstream_mirrors.datasets')) as $dataset) {
$db->table('business_mirror_table_states')->insert([
'dataset_key' => $dataset, 'initial_completed_at' => '2026-09-27 09:00:00',
'status' => 'succeeded', 'last_succeeded_at' => $now,
]);
}
}
}