240 lines
9.7 KiB
PHP
240 lines
9.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Business;
|
|
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Tests\Feature\FeatureTestCase;
|
|
use ZipArchive;
|
|
|
|
class KukaPostingQuotationPageTest extends FeatureTestCase
|
|
{
|
|
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');
|
|
|
|
$schema = Schema::connection('business_mirror');
|
|
$schema->create('kuka_posting_quotation_imports', function (Blueprint $table): void {
|
|
$table->increments('id');
|
|
$table->unsignedBigInteger('company_id');
|
|
$table->unsignedInteger('row_count')->default(0);
|
|
$table->unsignedInteger('material_count')->default(0);
|
|
$table->text('status_counts')->nullable();
|
|
$table->string('file_name');
|
|
$table->char('file_sha256', 64);
|
|
$table->string('source_sheet');
|
|
$table->timestamp('completed_at')->nullable();
|
|
$table->timestamps();
|
|
$table->unique('company_id');
|
|
});
|
|
$schema->create('kuka_posting_quotation_rows', function (Blueprint $table): void {
|
|
$table->increments('id');
|
|
$table->unsignedBigInteger('company_id');
|
|
$table->string('material_number');
|
|
$table->string('material_desc')->nullable();
|
|
$table->string('supplier_account')->nullable();
|
|
$table->string('supplier_name')->nullable();
|
|
$table->string('record_status')->nullable();
|
|
$table->decimal('price_including_tax', 20, 4)->nullable();
|
|
$table->decimal('price_excluding_tax', 20, 4)->nullable();
|
|
$table->dateTime('price_valid_date')->nullable();
|
|
$table->dateTime('price_expiry_date')->nullable();
|
|
$table->char('row_hash', 64);
|
|
$table->text('source_values');
|
|
$table->unsignedInteger('source_row_number');
|
|
$table->timestamp('imported_at');
|
|
$table->unique(['company_id', 'material_number']);
|
|
});
|
|
}
|
|
|
|
public function test_imports_quotation_workbook_and_renders_paginated_price_rows(): void
|
|
{
|
|
$path = $this->workbook();
|
|
|
|
$this->artisan('business-mirror:import-kuka-posting-quotation', [
|
|
'path' => $path,
|
|
'--company' => (string) $this->company->id,
|
|
])->assertExitCode(0);
|
|
|
|
$connection = DB::connection('business_mirror');
|
|
$this->assertSame(2, $connection->table('kuka_posting_quotation_rows')->count());
|
|
$this->assertSame(2, $connection->table('kuka_posting_quotation_rows')->distinct()->count('material_number'));
|
|
$this->assertDatabaseHas('kuka_posting_quotation_imports', [
|
|
'company_id' => $this->company->id,
|
|
'row_count' => 2,
|
|
'material_count' => 2,
|
|
'source_sheet' => 'Sheet1',
|
|
], 'business_mirror');
|
|
|
|
$row = $connection->table('kuka_posting_quotation_rows')
|
|
->where('material_number', '91.C52798.01')
|
|
->first();
|
|
$values = json_decode((string) $row->source_values, true);
|
|
$this->assertSame('DS5145BG-G192~升降桌-柚木色-1.4米', $values['material_desc']);
|
|
$this->assertSame('常州欧伯娜家居有限公司', $values['supplier_name']);
|
|
$this->assertSame('生效', $values['record_status']);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'posting-data',
|
|
'page' => 'kuka-basic-data',
|
|
'submenu' => 'quotation',
|
|
]))
|
|
->assertOk()
|
|
->assertDontSeeText('顾家报价单 · 价格明细')
|
|
->assertDontSeeText('来源文件:')
|
|
->assertDontSeeText('已从共享业务镜像读取 2 条数据')
|
|
->assertDontSeeText('当前页面为只读展示')
|
|
->assertSeeText('91.C52798.01')
|
|
->assertSeeText('升降桌-柚木色-1.4米')
|
|
->assertSeeText('常州欧伯娜家居有限公司')
|
|
->assertSeeText('989.10')
|
|
->assertSeeText('生效')
|
|
->assertSee('data-directory-table-key="kuka-quotation"', false);
|
|
|
|
@unlink($path);
|
|
}
|
|
|
|
private function workbook(): string
|
|
{
|
|
$fields = config('kuka_posting.quotation_fields');
|
|
$headers = array_column($fields, 'source');
|
|
$rows = [
|
|
$headers,
|
|
array_map(static fn (array $field): string => $field['label'], $fields),
|
|
$this->row([
|
|
'Fbk1' => 'DS5145BG-G192',
|
|
'MaterialNumber' => '91.C52798.01',
|
|
'MaterialDesc' => 'DS5145BG-G192~升降桌-柚木色-1.4米',
|
|
'ToElsAccount' => '435421',
|
|
'ToElsAccountName' => '常州欧伯娜家居有限公司',
|
|
'OrderUnit' => 'PC',
|
|
'Currency' => 'CNY',
|
|
'TaxRate' => '13',
|
|
'PriceIncludingTax' => '989.10',
|
|
'PriceExcludingTax' => '875.31',
|
|
'PurchaseOrganization' => '1090',
|
|
'Factory' => '1090',
|
|
'PurchaseGroup' => 'G32',
|
|
'PriceValidDate' => '2026-06-24 12:00:00',
|
|
'PriceExpiryDate' => '2027-06-16 12:00:00',
|
|
'CreateDate' => '2026-06-24 02:00:42',
|
|
'SourceType' => 'Excel',
|
|
'OverTolerance' => '0',
|
|
'LackTolerance' => '0',
|
|
'RecordType' => '标准',
|
|
'Fbk6' => '0004',
|
|
'Fbk14' => '1',
|
|
'RecordStatus' => '生效',
|
|
'Fbk3' => 'Z9',
|
|
'CreateUser' => 'config-center_系统账号',
|
|
'LastUpdateUser' => 'config-center_系统账号',
|
|
'LastUpdateDate' => '2026-06-24 02:00:42',
|
|
]),
|
|
$this->row([
|
|
'Fbk1' => 'DS5166BG-B4',
|
|
'MaterialNumber' => '91.C52597.01',
|
|
'MaterialDesc' => 'DS5166BG-B4~妆台-白色-0.28米',
|
|
'ToElsAccount' => '435421',
|
|
'ToElsAccountName' => '常州欧伯娜家居有限公司',
|
|
'OrderUnit' => 'PC',
|
|
'Currency' => 'CNY',
|
|
'TaxRate' => '13',
|
|
'PriceIncludingTax' => '199.50',
|
|
'PriceExcludingTax' => '176.55',
|
|
'PurchaseOrganization' => '1090',
|
|
'Factory' => '1090',
|
|
'PurchaseGroup' => 'G32',
|
|
'PriceValidDate' => '2026-06-24 12:00:00',
|
|
'PriceExpiryDate' => '2027-06-16 12:00:00',
|
|
'CreateDate' => '2026-06-24 02:00:42',
|
|
'SourceType' => 'Excel',
|
|
'RecordType' => '标准',
|
|
'Fbk6' => '0004',
|
|
'Fbk14' => '1',
|
|
'RecordStatus' => '生效',
|
|
'Fbk3' => 'Z9',
|
|
'CreateUser' => 'config-center_系统账号',
|
|
'LastUpdateUser' => 'config-center_系统账号',
|
|
'LastUpdateDate' => '2026-06-24 02:00:42',
|
|
]),
|
|
];
|
|
|
|
$path = tempnam(sys_get_temp_dir(), 'kuka-quotation-') . '.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="Sheet1" sheetId="1" r:id="rId1"/>
|
|
</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"/>
|
|
</Relationships>
|
|
XML);
|
|
$zip->addFromString('xl/worksheets/sheet1.xml', $this->worksheetXml($rows, $headers));
|
|
$zip->close();
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $headers
|
|
*/
|
|
private function row(array $values): array
|
|
{
|
|
return array_map(
|
|
static fn (string $header): string => (string) ($values[$header] ?? ''),
|
|
array_column(config('kuka_posting.quotation_fields'), 'source'),
|
|
);
|
|
}
|
|
|
|
private function worksheetXml(array $rows, array $headers): string
|
|
{
|
|
$xmlRows = [];
|
|
foreach ($rows as $rowIndex => $row) {
|
|
$cells = [];
|
|
foreach ($row 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>';
|
|
}
|
|
|
|
private function columnName(int $index): string
|
|
{
|
|
$name = '';
|
|
while ($index > 0) {
|
|
$index--;
|
|
$name = chr(65 + ($index % 26)) . $name;
|
|
$index = intdiv($index, 26);
|
|
}
|
|
|
|
return $name;
|
|
}
|
|
}
|