242 lines
9.3 KiB
PHP
242 lines
9.3 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('灵珊店铺业绩')
|
|
->assertSeeText('发货明细数据')
|
|
->assertSeeText('顾家报价单')
|
|
->assertSee('data-kuka-posting-subnav', false)
|
|
->assertSee('data-kuka-posting-panel="shipments"', false)
|
|
->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;
|
|
}
|
|
}
|