CaiWuXiTong/tests/Feature/Business/KukaPostingBasePageTest.php
chenxi 6e6e3038a1 fix: 精简顾家过账发货明细页面 (#1058)
fix: 精简顾家过账发货明细页面 (#1058)
2026-11-10 19:00:14 +08:00

245 lines
9.5 KiB
PHP

<?php
namespace Tests\Feature\Business;
use Illuminate\Support\Facades\DB;
use Tests\Feature\FeatureTestCase;
use ZipArchive;
class KukaPostingBasePageTest extends FeatureTestCase
{
public function test_imports_only_requested_stores_and_renders_paginated_base_data(): void
{
$file = $this->workbook();
$this->artisan('business-mirror:import-kuka-posting-base', [
'path' => $file,
'--month' => '2026-10',
'--company' => (string) $this->company->id,
])->assertExitCode(0);
$connection = DB::connection('business_mirror');
$this->assertSame(12, $connection->table('kuka_posting_base_rows')->count());
$this->assertDatabaseHas('kuka_posting_base_imports', [
'company_id' => $this->company->id,
'period' => '2026-10',
'row_count' => 12,
'store_count' => 12,
], 'business_mirror');
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'posting-data',
'page' => 'kuka-basic-data',
'store_name' => '天猫-HX旗舰店101',
]))
->assertOk()
->assertSeeText('内部订单号')
->assertSeeText('发货金额')
->assertSeeText('灵珊店铺业绩')
->assertSeeText('发货明细数据')
->assertSeeText('顾家报价单')
->assertSee('data-kuka-posting-subnav', false)
->assertSee('data-kuka-posting-panel="shipments"', false)
->assertDontSeeText('顾家过账 · 基础数据')
->assertDontSeeText('聚水潭共享镜像')
->assertDontSeeText('来源文件:')
->assertDontSeeText('基础数据 · 字段结构')
->assertDontSeeText('已从共享业务镜像读取 12 条数据')
->assertSeeText('KUKA-00')
->assertSeeText('SKU-00')
->assertDontSeeText('KUKA-01')
->assertDontSeeText('SKU-01');
@unlink($file);
}
public function test_kuka_posting_submenu_can_open_directly_without_a_second_sidebar_entry(): void
{
$file = $this->workbook();
$this->artisan('business-mirror:import-kuka-posting-base', [
'path' => $file,
'--month' => '2026-10',
'--company' => (string) $this->company->id,
])->assertExitCode(0);
$this->loginAs()
->get(route('business.pages.show', [
'section' => 'posting-data',
'page' => 'kuka-basic-data',
'submenu' => 'performance',
]))
->assertOk()
->assertSee('id="kuka-posting-tab-performance"', false)
->assertSee('class="kuka-posting-subnav__tab is-active"', false)
->assertSee('data-kuka-posting-panel="performance"', false)
->assertSeeText('后续接入灵珊店铺业绩数据后');
$navigation = collect(\App\Utilities\SidebarPermissions::navigationFor($this->user))
->firstWhere('slug', 'posting-data');
$kuka = collect($navigation['pages'])->firstWhere('slug', 'kuka-posting');
$this->assertSame(['kuka-basic-data'], array_column($kuka['children'], 'slug'));
$this->assertSame(['基础数据'], array_column($kuka['children'], 'title'));
@unlink($file);
}
public function test_incomplete_store_export_keeps_the_previous_period_data(): void
{
$complete = $this->workbook();
$this->artisan('business-mirror:import-kuka-posting-base', [
'path' => $complete,
'--month' => '2026-10',
'--company' => (string) $this->company->id,
])->assertExitCode(0);
$incomplete = $this->workbook(skipFirstStore: true);
$this->artisan('business-mirror:import-kuka-posting-base', [
'path' => $incomplete,
'--month' => '2026-10',
'--company' => (string) $this->company->id,
])->assertExitCode(1);
$this->assertSame(
12,
DB::connection('business_mirror')->table('kuka_posting_base_rows')
->where('period', '2026-10')
->count(),
);
@unlink($complete);
@unlink($incomplete);
}
private function workbook(bool $skipFirstStore = false): string
{
$fields = config('kuka_posting.fields');
$headers = array_column($fields, 'source');
$stores = array_values(config('kuka_posting.stores'));
$rows = [];
foreach ($stores as $index => $store) {
if ($skipFirstStore && $index === 0) {
continue;
}
$rows[] = [
'内部订单号' => sprintf('KUKA-%02d', $index),
'标记多标签' => '测试标签',
'售后单号' => '',
'订单类型' => '普通订单',
'线上订单号' => sprintf('ONLINE-%02d', $index),
'订单状态' => '已发货',
'发货仓' => '测试仓',
'运营云仓' => '测试云仓',
'分销商' => '',
'货主分销' => '',
'店铺' => $store,
'买家账号' => 'buyer' . $index,
'卖家备注' => '测试备注',
'订单日期' => '2026-10-01 10:00:00',
'发货日期' => '2026-10-02 11:00:00',
'付款日期' => '2026-10-01 09:30:00',
'确认收货日期' => '2026-10-05 12:00:00',
'省' => '江苏省',
'市' => '常州市',
'区县' => '新北区',
'订单快递公司' => '测试快递',
'订单快递单号' => 'EXP-' . $index,
'售后快递公司' => '',
'售后快递单号' => '',
'收货人' => '测试收货人',
'售后登记日期' => '',
'售后确认日期' => '',
'收货仓' => '',
'售后分类' => '',
'问题类型' => '',
'商品编码' => sprintf('SKU-%02d', $index),
'原始线上订单号' => sprintf('ONLINE-%02d', $index),
'款式编码' => sprintf('STYLE-%02d', $index),
'货号' => sprintf('ITEM-%02d', $index),
'物料编号' => sprintf('MAT-%02d', $index),
'型号' => '测试型号',
'成本' => '100.00',
'产品材质' => '板材',
'实发数量' => '1',
'发货金额' => '199.00',
];
}
$path = tempnam(sys_get_temp_dir(), 'kuka-posting-base-') . '.xlsx';
$zip = new ZipArchive();
$zip->open($path, ZipArchive::CREATE | ZipArchive::OVERWRITE);
$zip->addFromString('xl/workbook.xml', <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets>
<sheet name="汇总" sheetId="1" r:id="rId1"/>
<sheet name="发货明细表" sheetId="2" r:id="rId2"/>
</sheets>
</workbook>
XML);
$zip->addFromString('xl/_rels/workbook.xml.rels', <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/>
</Relationships>
XML);
$zip->addFromString('xl/worksheets/sheet1.xml', $this->worksheetXml([['汇总']]));
$zip->addFromString('xl/worksheets/sheet2.xml', $this->worksheetXml(array_merge([$headers], $rows)));
$zip->close();
return $path;
}
/**
* @param list<array<int, string>|array<string, string>> $rows
*/
private function worksheetXml(array $rows): string
{
$xmlRows = [];
foreach ($rows as $rowIndex => $row) {
$cells = [];
$values = array_is_list($row) ? $row : $this->rowValues($row);
foreach ($values as $columnIndex => $value) {
$reference = $this->columnName($columnIndex + 1) . ($rowIndex + 1);
$cells[] = '<c r="' . $reference . '" t="inlineStr"><is><t>'
. htmlspecialchars((string) $value, ENT_XML1 | ENT_QUOTES, 'UTF-8')
. '</t></is></c>';
}
$xmlRows[] = '<row r="' . ($rowIndex + 1) . '">' . implode('', $cells) . '</row>';
}
return '<?xml version="1.0" encoding="UTF-8"?>'
. '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
. '<sheetData>' . implode('', $xmlRows) . '</sheetData></worksheet>';
}
/**
* @param array<string, string> $row
* @return list<string>
*/
private function rowValues(array $row): array
{
return array_map(
static fn (array $field): string => (string) ($row[$field['source']] ?? ''),
config('kuka_posting.fields'),
);
}
private function columnName(int $index): string
{
$name = '';
while ($index > 0) {
$index--;
$name = chr(65 + ($index % 26)) . $name;
$index = intdiv($index, 26);
}
return $name;
}
}