CaiWuXiTong/tests/Feature/Business/OnlineDaifaPricePageTest.php
limeng 43390be327 feat(线上代发价格): 识别改版明细表格 + 新增报销成本核查(多报销) (#841)
1) 钉钉「线上代发业务报销」有 23 张已通过审批单的商品明细表格不叫“收款费用明细”(新版模板表格名为空、旧单叫“费用明细”),
   同步时整单只剩一行空明细。按名称找不到时改取行内含“商品ID”列的明细表格;料号兼容新版“商品编码”。上线后对该数据集全量重同步。
2) 新增报销成本核查:按聚水潭订单由本行商品编码拆出的子商品,取普通商品资料成本价×发货数量求和为系统成本,
   与代发订单金额比较,高出 0.01 元以上判“多报销”,另有“成本缺失”“无发货明细”;页面新增筛选、计数、多报销金额汇总及四列,导出同步。

涉及:金额核查展示(只读)、镜像全量重同步;不涉及迁移、权限和上游写入。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VANWYsNUbu2piMtCKymoyd
2026-11-03 17:13:56 +08:00

297 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\Feature\Business;
use App\Models\Auth\Permission;
use Mockery;
use Modules\EcommerceFinance\Repositories\DingOnlineDaifaPriceMirrorRepository;
use Tests\Feature\FeatureTestCase;
class OnlineDaifaPricePageTest extends FeatureTestCase
{
public function test_online_daifa_price_page_renders_matched_product_and_amounts(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingOnlineDaifaPriceMirrorRepository::class);
$repository->shouldReceive('paginate')->once()->andReturn([
'data' => [[
'process_instance_id' => 'DEMO-6927049845766979458',
'detail_index' => 0,
'product_id' => '3821829253650055444',
'daifa_trade_no' => '6927049845766979458',
'shop_trade_no' => '6927049845766979458',
'shop_name' => '抖音-HX旗舰店101',
'payee_name' => '宁波顾家望业家居有限公司',
'tag' => '刘萧代发',
'supplier_name' => '',
'material_no' => '',
'ship_qty' => 1,
'paid_amount' => 616.61,
'daifa_amount' => 375.0,
'price_diff' => 241.61,
'workflow' => [
'businessId' => '20260901001',
'title' => '线上代发业务报销',
'status' => 'COMPLETED',
'result' => 'agree',
'originatorUserName' => '扬灵',
'createAt' => '2026-09-01 10:00:00',
],
'scheme' => [['name' => '商品ID', 'value' => '3821829253650055444']],
'tasks' => [],
'status_key' => 'COMPLETED',
'status_label' => '已完成',
'matched_product' => ['matched' => true, 'name' => '演示商品', 'sku_id' => null, 'i_id' => null],
'workflow_created_at' => '2026-09-01 10:00:00',
]],
'total' => 1,
'status' => 'succeeded',
'last_synced_at' => '2026-09-08 00:00:05',
'summary' => [
'count' => 1,
'matched_count' => 1,
'daifa_amount' => 375.0,
'paid_amount' => 616.61,
'price_diff' => 241.61,
],
]);
$this->app->instance(DingOnlineDaifaPriceMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'product-inventory',
'page' => 'online-daifa-price',
]))->assertOk()
->assertSee('线上代发价格')
->assertSee('数据来源:钉钉线上代发审批、聚水潭采购单管理(已确认/已完成)')
->assertDontSee('固定收款人:')
->assertSee('宁波顾家望业家居有限公司')
->assertSee('镜像状态:succeeded')
->assertSee('3821829253650055444')
->assertSee('演示商品')
->assertSee('抖音-HX旗舰店101')
->assertSee('6927049845766979458')
->assertSee('已完成')
->assertSee('扬灵')
->assertSee('标签')
->assertSee('刘萧代发')
->assertSee('已匹配商品目录')
->assertDontSee('原始表单')
->assertDontSee('展开表单');
}
public function test_online_daifa_price_page_places_purchase_fields_in_existing_columns(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingOnlineDaifaPriceMirrorRepository::class);
$repository->shouldReceive('paginate')->once()->andReturn([
'data' => [[
'source_type' => 'purchase',
'process_instance_id' => '907329',
'detail_index' => 1,
'product_id' => '',
'daifa_trade_no' => '',
'shop_trade_no' => '110212448119316609',
'shop_name' => '得物-米客米家家居旗舰店',
'payee_name' => '',
'tag' => '采购单管理',
'supplier_name' => '霸州市亚锦五金制品厂',
'material_no' => 'DF-C1ZDY000202',
'ship_qty' => 1,
'paid_amount' => null,
'daifa_amount' => 19.3,
'price_diff' => null,
'workflow' => ['businessId' => '907329', 'title' => '采购单管理'],
'status_label' => '已确认',
'matched_product' => ['matched' => false, 'name' => null],
'order_matched' => true,
'workflow_created_at' => '2026-09-22 16:30:03',
]],
'total' => 1,
'status' => 'available',
'last_synced_at' => '2026-09-22 17:35:11',
'summary' => ['count' => 1, 'purchase_count' => 1, 'matchable_count' => 0],
'source_statuses' => [
'purchase' => ['label' => '聚水潭采购单管理', 'ready' => true, 'status' => 'succeeded'],
],
]);
$this->app->instance(DingOnlineDaifaPriceMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'product-inventory',
'page' => 'online-daifa-price',
]))->assertOk()
->assertSee('110212448119316609')
->assertSee('得物-米客米家家居旗舰店')
->assertSee('霸州市亚锦五金制品厂')
->assertSee('DF-C1ZDY000202')
->assertSee('19.30')
->assertSee('采购单管理')
->assertSee('已确认')
->assertDontSee('(未匹配)');
}
public function test_online_daifa_price_page_keeps_an_unavailable_mirror_readable(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingOnlineDaifaPriceMirrorRepository::class);
$repository->shouldReceive('paginate')->andThrow(new \RuntimeException('not ready'));
$this->app->instance(DingOnlineDaifaPriceMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'product-inventory',
'page' => 'online-daifa-price',
]))->assertOk()
->assertSee('线上代发价格镜像尚未完成同步,请稍后重试。')
->assertSee('镜像读取失败,请稍后重试。');
}
public function test_online_daifa_price_page_passes_filters_to_repository(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingOnlineDaifaPriceMirrorRepository::class);
$repository->shouldReceive('paginate')->once()->with(
[
'keyword' => '6927049845766979458',
'product_id' => '3821829253650055444',
'shop_name' => '抖音-HX旗舰店101',
'supplier_name' => '新北供应商',
'material_no' => 'MAT-001',
'status' => '',
'audit' => '',
'created_from' => '2026-09-01',
'created_to' => '2026-09-07',
],
1,
Mockery::type('int'),
)->andReturn([
'data' => [],
'total' => 0,
'status' => 'succeeded',
'last_synced_at' => '2026-09-08 00:00:05',
'summary' => [],
]);
$this->app->instance(DingOnlineDaifaPriceMirrorRepository::class, $repository);
$this->loginAs()->get(route('business.pages.show', [
'section' => 'product-inventory',
'page' => 'online-daifa-price',
'keyword' => '6927049845766979458',
'product_id' => '3821829253650055444',
'shop_name' => '抖音-HX旗舰店101',
'supplier_name' => '新北供应商',
'material_no' => 'MAT-001',
'created_from' => '2026-09-01',
'created_to' => '2026-09-07',
]))->assertOk()
->assertSee('商品ID')
->assertSee('店铺名称')
->assertSee('供应商名称')
->assertSee('新北供应商')
->assertSee('商品编码')
->assertSee('MAT-001')
->assertSee('2026-09-01')
->assertSee('2026-09-07');
}
public function test_online_daifa_price_export_streams_all_currently_filtered_rows(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$filters = [
'keyword' => 'DAIFA-001',
'product_id' => 'PRODUCT-001',
'shop_name' => '抖音旗舰店',
'supplier_name' => '新北供应商',
'material_no' => 'MAT-001',
'status' => 'COMPLETED',
'audit' => '',
'created_from' => '2026-09-01',
'created_to' => '2026-09-07',
];
$repository = Mockery::mock(DingOnlineDaifaPriceMirrorRepository::class);
$repository->shouldReceive('ready')->once()->andReturnTrue();
$repository->shouldReceive('exportRows')->once()->with($filters)->andReturn((static function (): \Generator {
yield [
'process_instance_id' => 'PROCESS-001',
'product_id' => '3821829253650055444',
'daifa_trade_no' => '6927049845766979458',
'shop_trade_no' => '6927049845766979459',
'shop_name' => '抖音旗舰店',
'payee_name' => '宁波顾家望业家居有限公司',
'tag' => '刘萧代发',
'supplier_name' => '@新北供应商',
'material_no' => 'MAT-001',
'ship_qty' => 2,
'paid_amount' => 616.61,
'daifa_amount' => 375.0,
'price_diff' => 241.61,
'workflow' => [
'businessId' => '=APPROVAL-001',
'title' => '线上代发业务报销',
'originatorUserName' => '+扬灵',
],
'status_label' => '已完成',
'matched_product' => ['matched' => true, 'name' => '演示商品'],
'ship_skus' => 'VTYZ010102×1, GDYRB020102×1',
'jst_labels' => '京东供销订单,京喜',
'workflow_created_at' => '2026-09-01 10:00:00',
];
})());
$this->app->instance(DingOnlineDaifaPriceMirrorRepository::class, $repository);
$response = $this->loginAs()->get(route('business.product-inventory.online-daifa-price.export', $filters));
$response->assertOk();
$this->assertStringContainsString('attachment;', (string) $response->headers->get('content-disposition'));
$this->assertStringContainsString('text/csv', (string) $response->headers->get('content-type'));
$content = $response->streamedContent();
$this->assertStringStartsWith(chr(0xEF) . chr(0xBB) . chr(0xBF), $content);
$this->assertStringContainsString('供应商名称', $content);
$this->assertStringContainsString('商品编码', $content);
$this->assertStringContainsString('标签', $content);
$this->assertStringContainsString('刘萧代发', $content);
$this->assertStringContainsString("'=APPROVAL-001", $content);
$this->assertStringContainsString("'+扬灵", $content);
$this->assertStringContainsString("'@新北供应商", $content);
$this->assertStringContainsString("'3821829253650055444", $content);
$this->assertStringContainsString("'6927049845766979458", $content);
$this->assertStringContainsString("'6927049845766979459", $content);
$this->assertStringContainsString('MAT-001', $content);
$this->assertStringContainsString('实际发货商品编码', $content);
$this->assertStringContainsString('聚水潭标记|多标签', $content);
$this->assertStringContainsString('VTYZ010102×1, GDYRB020102×1', $content);
$this->assertStringContainsString('京东供销订单,京喜', $content);
}
public function test_online_daifa_price_export_requires_a_ready_mirror(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(DingOnlineDaifaPriceMirrorRepository::class);
$repository->shouldReceive('ready')->once()->andReturnFalse();
$repository->shouldNotReceive('exportRows');
$this->app->instance(DingOnlineDaifaPriceMirrorRepository::class, $repository);
$this->withExceptionHandling()
->loginAs()
->get(route('business.product-inventory.online-daifa-price.export'))
->assertServiceUnavailable();
}
public function test_online_daifa_price_export_rejects_users_without_sidebar_permission(): void
{
$subject = 'user_' . $this->user->id;
$this->user->roles()->sync([\App\Models\Auth\Role::where('name', 'admin')->firstOrFail()->id]);
setting()->set('system.sidebar_permissions', json_encode([
'configured' => [$subject],
'subjects' => [
$subject => ['company-management/basic-information'],
],
], JSON_UNESCAPED_UNICODE));
$this->withExceptionHandling()
->loginAs()
->get(route('business.product-inventory.online-daifa-price.export'))
->assertForbidden();
}
}