CaiWuXiTong/tests/Unit/EcommerceFinance/InstallationDetailMirrorRepositoryTest.php

225 lines
12 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\GenericBusinessMirrorRepository;
use Modules\EcommerceFinance\Repositories\InstallationDetailMirrorRepository;
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
use Tests\TestCase;
class InstallationDetailMirrorRepositoryTest extends TestCase
{
private InstallationDetailMirrorRepository $repository;
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');
$definitions=[];
foreach ([InstallationDetailMirrorRepository::DATASET=>'fixture_install',InstallationDetailMirrorRepository::ITEM_DATASET=>'fixture_items'] as $key=>$table) {
$definitions[$key]=['source_key'=>'jiansun','schema'=>'dbo','object'=>explode('.',$key)[2],'target'=>$table,'keys'=>['Id']];
}
$definitions[InstallationDetailMirrorRepository::DATASET]['minimum_datetime'] = [
'column' => 'ShipTIme',
'value' => '2026-01-01 00:00:00',
];
config(['upstream_mirrors.datasets'=>$definitions]);
$schema=Schema::connection('business_mirror');
$schema->create('business_mirror_table_states',function(Blueprint $t){$t->string('dataset_key')->primary();$t->dateTime('initial_completed_at')->nullable();});
$schema->create('fixture_install',function(Blueprint $t){
$t->integer('Id');
foreach(['SourceGuid','OrderNum','ordernum_id','shop_name','SubmitTime','ShipTIme','kfshTime','cwshTime','apistatus','kfsh','cwsh','BidStatus','costtaker','costWay','installComplete','Delete','installCompanyName','TypeDto'] as $c) $t->string($c)->nullable();
$t->integer('StatusId')->nullable()->default(0);
foreach(['installcost','settleAmount','acceptAmount'] as $c) $t->decimal($c,14,2)->default(0);
});
$schema->create('fixture_items',function(Blueprint $t){
$t->integer('Id');
$t->string('guid');
$t->string('shopproid');
$t->string('Sourceno')->nullable();
$t->dateTime('kfshTime')->nullable();
$t->decimal('settleItemAmount',14,2)->default(0);
$t->decimal('acceptItemAmount',14,2)->default(0);
});
foreach(array_keys($definitions) as $key) DB::connection('business_mirror')->table('business_mirror_table_states')->insert(['dataset_key'=>$key,'initial_completed_at'=>'2026-08-01']);
DB::connection('business_mirror')->table('fixture_install')->insert([
['Id'=>1,'SourceGuid'=>'A','OrderNum'=>'OLD','shop_name'=>'旧店','SubmitTime'=>'2026-08-01','ShipTIme'=>'2026-08-02','installcost'=>500],
['Id'=>2,'SourceGuid'=>'A','OrderNum'=>'NEW','shop_name'=>'测试店','SubmitTime'=>'2026-08-20 23:59:59','ShipTIme'=>'2026-08-21','installcost'=>20],
['Id'=>3,'SourceGuid'=>'B','OrderNum'=>'OTHER','shop_name'=>'其他店','SubmitTime'=>'2026-08-20','ShipTIme'=>'2026-08-21','installcost'=>30],
['Id'=>4,'SourceGuid'=>'C','OrderNum'=>'NEXT','shop_name'=>'测试店','SubmitTime'=>'2026-08-21','ShipTIme'=>'2026-08-22','installcost'=>40],
['Id'=>5,'SourceGuid'=>'D','OrderNum'=>'PRE-2026','shop_name'=>'测试店','SubmitTime'=>'2026-08-21','ShipTIme'=>'2025-12-31 23:59:59','installcost'=>999],
['Id'=>6,'SourceGuid'=>'E','OrderNum'=>'NULL-SHIP','shop_name'=>'测试店','SubmitTime'=>'2026-08-21','ShipTIme'=>null,'installcost'=>999],
]);
DB::connection('business_mirror')->table('fixture_items')->insert([
['Id'=>1,'guid'=>'A','shopproid'=>'PRODUCT-A'],['Id'=>2,'guid'=>'A','shopproid'=>'PRODUCT-A'],
]);
$this->repository=new InstallationDetailMirrorRepository(new GenericBusinessMirrorRepository(app('db')),new BusinessMirrorDatasetRegistry());
}
public function test_filters_apply_to_deduplicated_list_count_and_summary(): void
{
$filters=['shop_name'=>'测试店'];
$page=$this->repository->paginate(2,1,$filters);
$this->assertSame(2,$page['total']);
$this->assertSame(2,$page['data'][0]['id']);
$this->assertSame(60.0,$this->repository->summary($filters)['requested']);
$filters['created_to']='2026-08-20';
$this->assertSame(1,$this->repository->paginate(1,50,$filters)['total']);
$this->assertSame(20.0,$this->repository->summary($filters)['requested']);
$this->assertSame(0,$this->repository->paginate(1,50,['shop_name'=>"' OR 1=1 --"])['total']);
}
public function test_product_filter_does_not_multiply_main_rows(): void
{
$filters=['product_id'=>'PRODUCT-A'];
$this->assertSame(1,$this->repository->paginate(1,50,$filters)['total']);
$this->assertSame(20.0,$this->repository->summary($filters)['requested']);
$this->assertSame('NEW',$this->repository->details('A')['data'][0]['transaction_no']);
$this->assertSame([],$this->repository->details('missing')['data']);
}
public function test_shipment_cutoff_hides_old_and_null_records_from_list_summary_and_details(): void
{
DB::connection('business_mirror')->table('fixture_items')->insert([
['Id'=>30,'guid'=>'D','shopproid'=>'OLD-PRODUCT'],
['Id'=>31,'guid'=>'E','shopproid'=>'NULL-PRODUCT'],
]);
$this->assertSame(3,$this->repository->paginate(1,50)['total']);
$this->assertSame(3,$this->repository->summary()['rows']);
$this->assertSame([],$this->repository->details('D')['data']);
$this->assertSame([],$this->repository->details('E')['data']);
}
public function test_status_filters_and_child_limit_are_explicit(): void
{
DB::connection('business_mirror')->table('fixture_install')->where('Id',2)->update(['kfsh'=>'1','Delete'=>'0','StatusId'=>2]);
$this->assertSame(1,$this->repository->paginate(1,50,['customer_review'=>'1','delete_status'=>'0','arranged_status'=>'1'])['total']);
$rows=[];for($i=1;$i<=101;$i++)$rows[]=['Id'=>$i+10,'guid'=>'B','shopproid'=>'P'.$i];
DB::connection('business_mirror')->table('fixture_items')->insert($rows);
$result=$this->repository->details('B');
$this->assertCount(100,$result['data']);
$this->assertTrue($result['has_more']);
}
public function test_unset_status_filters_include_null_values_and_render_explicit_labels(): void
{
DB::connection('business_mirror')->table('fixture_install')->where('Id',3)->update(['StatusId'=>null]);
foreach (['customer_review','finance_review','completed_status','delete_status','arranged_status'] as $key) {
$this->assertSame(3,$this->repository->paginate(1,50,[$key=>'0'])['total'],$key);
}
$other=$this->repository->paginate(1,50,['shop_name'=>'其他店'])['data'][0];
$this->assertSame('未审核',$other['customer_service_review']);
$this->assertSame('未审核',$other['finance_review']);
$this->assertSame('否',$other['completed_status']);
$this->assertSame('未安排',$other['arranged_status']);
DB::connection('business_mirror')->table('fixture_install')->where('Id',2)->update([
'kfsh'=>1,
'cwsh'=>1,
'installComplete'=>1,
'Delete'=>0,
'StatusId'=>1,
]);
foreach (['customer_review','finance_review','completed_status','arranged_status'] as $key) {
$this->assertSame(1,$this->repository->paginate(1,50,[$key=>'1'])['total'],$key);
}
}
public function test_fee_details_use_customer_review_time_and_latest_main_channel(): void
{
DB::connection('business_mirror')->table('fixture_install')->where('Id',1)->update(['installCompanyName'=>'旧公司']);
DB::connection('business_mirror')->table('fixture_install')->where('Id',2)->update(['installCompanyName'=>'万师傅','costWay'=>null]);
DB::connection('business_mirror')->table('fixture_install')->where('Id',3)->update(['installCompanyName'=>'鲁班到家','costWay'=>'银行转账']);
DB::connection('business_mirror')->table('fixture_install')->where('Id',4)->update(['installCompanyName'=>'万师傅']);
DB::connection('business_mirror')->table('fixture_items')->insert([
['Id'=>20,'guid'=>'A','shopproid'=>'P1','Sourceno'=>'A-1','kfshTime'=>'2026-09-01 09:00:00','settleItemAmount'=>60.10,'acceptItemAmount'=>40.00],
['Id'=>21,'guid'=>'A','shopproid'=>'P2','Sourceno'=>'A-1','kfshTime'=>'2026-09-30 18:00:00','settleItemAmount'=>39.90,'acceptItemAmount'=>0.00],
['Id'=>22,'guid'=>'B','shopproid'=>'P3','Sourceno'=>'B-1','kfshTime'=>'2026-09-15 10:00:00','settleItemAmount'=>80.00,'acceptItemAmount'=>70.00],
['Id'=>23,'guid'=>'C','shopproid'=>'P4','Sourceno'=>'C-1','kfshTime'=>'2026-10-01 00:00:00','settleItemAmount'=>500.00,'acceptItemAmount'=>500.00],
['Id'=>24,'guid'=>'D','shopproid'=>'OLD','Sourceno'=>'D-1','kfshTime'=>'2026-09-10 10:00:00','settleItemAmount'=>999.00,'acceptItemAmount'=>999.00],
['Id'=>25,'guid'=>'E','shopproid'=>'NULL','Sourceno'=>'E-1','kfshTime'=>'2026-09-10 10:00:00','settleItemAmount'=>999.00,'acceptItemAmount'=>999.00],
]);
$result=$this->repository->feeDetails([
'customer_review_from'=>'2026-09-01',
'customer_review_to'=>'2026-09-30',
]);
$this->assertTrue($result['ready']);
$this->assertSame([
['platform'=>'其他','store_name'=>'测试店','installation_company'=>'万师傅','payment_channel'=>'包安装','amount'=>100.0],
['platform'=>'其他','store_name'=>'其他店','installation_company'=>'鲁班到家','payment_channel'=>'不包安装','amount'=>80.0],
],$result['rows']);
$this->assertSame(180.0,$result['total']);
}
public function test_fee_details_classifies_platform_by_store_name_rules(): void
{
$stores = [
'阿里巴巴-测试店' => '淘系',
'天猫-测试店' => '淘系',
'新佰广1688' => '淘系',
'淘工厂-测试店' => '淘系',
'淘宝-测试店' => '淘系',
'抖音-测试店' => '抖音',
'拼多多-测试店' => '拼多多',
'唯品会-测试店' => '唯品会',
'京东-测试店' => '京东',
'京喜-测试店' => '京喜',
'其他平台店铺' => '其他',
];
$mainRows = [];
$itemRows = [];
$id = 100;
foreach (array_keys($stores) as $store) {
$guid = 'platform-' . $id;
$mainRows[] = [
'Id' => $id,
'SourceGuid' => $guid,
'OrderNum' => 'ORDER-' . $id,
'shop_name' => $store,
'SubmitTime' => '2026-09-01',
'ShipTIme' => '2026-09-02',
'installCompanyName' => '万师傅',
'costWay' => null,
];
$itemRows[] = [
'Id' => $id,
'guid' => $guid,
'shopproid' => 'PRODUCT-' . $id,
'kfshTime' => '2026-09-15 10:00:00',
'settleItemAmount' => 1,
];
$id++;
}
DB::connection('business_mirror')->table('fixture_install')->insert($mainRows);
DB::connection('business_mirror')->table('fixture_items')->insert($itemRows);
$result = $this->repository->feeDetails([
'customer_review_from' => '2026-09-01',
'customer_review_to' => '2026-09-30',
]);
$actual = collect($result['rows'])->pluck('platform', 'store_name')->all();
foreach ($stores as $store => $platform) {
$this->assertSame($platform, $actual[$store] ?? null, $store);
}
}
public function test_end_date_alone_is_valid_and_invalid_status_is_rejected(): void
{
request()->merge(['created_to'=>'2026-08-20']);
$this->assertSame(['created_to'=>'2026-08-20'],InstallationDetailMirrorRepository::requestFilters());
request()->merge(['arranged_status'=>'unexpected']);
$this->expectException(\Illuminate\Validation\ValidationException::class);
InstallationDetailMirrorRepository::requestFilters();
}
}