160 lines
6.7 KiB
PHP
160 lines
6.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Console;
|
|
|
|
use App\Models\Business\InventoryWarehouseNameMapping;
|
|
use Illuminate\Support\Facades\DB;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class SupplierSummaryInboundImportTest extends FeatureTestCase
|
|
{
|
|
public function test_monthly_import_preserves_identical_rows_and_reports_duplicates_and_failures(): void
|
|
{
|
|
$provider = '测试仓储方-' . uniqid();
|
|
InventoryWarehouseNameMapping::query()->create([
|
|
'company_id' => 1,
|
|
'warehouse_provider' => $provider,
|
|
'warehouse' => $provider . '主仓',
|
|
'warehouse_short_name' => '测试简称',
|
|
]);
|
|
$ambiguousProvider = '测试歧义仓储方-' . uniqid();
|
|
foreach (['甲', '乙'] as $name) {
|
|
InventoryWarehouseNameMapping::query()->create([
|
|
'company_id' => 1,
|
|
'warehouse_provider' => $ambiguousProvider,
|
|
'warehouse' => $ambiguousProvider . $name . '主仓',
|
|
'warehouse_short_name' => '歧义简称' . $name,
|
|
]);
|
|
}
|
|
|
|
$connection = (string) config('upstream_mirrors.connection', 'business_mirror');
|
|
DB::connection($connection)->table('inventory_inbound_detail_rows')->insert([
|
|
'report_month' => '2026-08-01',
|
|
'row_key' => sha1('historic-row'),
|
|
'imported_at' => now(),
|
|
]);
|
|
|
|
$path = $this->temporaryPath('supplier-summary-', '.xlsx');
|
|
$firstResult = $this->temporaryPath('supplier-result-', '.json');
|
|
$secondResult = $this->temporaryPath('supplier-result-', '.json');
|
|
try {
|
|
$this->writeWorkbook($path, $provider, $ambiguousProvider);
|
|
|
|
$this->artisan('inventory:supplier-summary:import', [
|
|
'file' => $path,
|
|
'--month' => '2026-09',
|
|
'--result-json' => $firstResult,
|
|
])
|
|
->expectsOutputToContain('成功=2')
|
|
->expectsOutputToContain('另有 2 条失败原因')
|
|
->assertExitCode(1);
|
|
|
|
$result = json_decode((string) file_get_contents($firstResult), true);
|
|
$this->assertSame('partial', $result['status']);
|
|
$this->assertSame('2026-09-01', $result['report_month']);
|
|
$this->assertSame(2, $result['success']);
|
|
$this->assertSame(0, $result['duplicates']);
|
|
$this->assertSame(12, $result['failed']);
|
|
$this->assertSame(12, $result['failure_reason_count']);
|
|
$this->assertSame(4, $result['failure_reasons'][0]['row']);
|
|
$this->assertStringContainsString('仓库未匹配', $result['failure_reasons'][0]['reason']);
|
|
$this->assertSame(
|
|
'仓库未匹配到唯一仓库简称:' . $ambiguousProvider,
|
|
$result['failure_reasons'][11]['reason'],
|
|
);
|
|
|
|
$this->assertSame(
|
|
1,
|
|
DB::connection($connection)->table('inventory_inbound_detail_rows')
|
|
->where('report_month', '2026-08-01')->count(),
|
|
);
|
|
$september = DB::connection($connection)->table('inventory_inbound_detail_rows')
|
|
->where('report_month', '2026-09-01')
|
|
->get();
|
|
$this->assertCount(2, $september);
|
|
$this->assertSame('BARCODE-1', $september->first()->barcode);
|
|
$this->assertSame('测试简称', $september->first()->warehouse_short_name);
|
|
$this->assertSame('2026-09-03 10:00:00', $september->first()->created_at_raw);
|
|
$this->assertEquals(20, $september->first()->amount);
|
|
$this->assertEquals(22, $september->first()->estimated_amount);
|
|
$this->assertNotSame(
|
|
$september[0]->row_key,
|
|
$september[1]->row_key,
|
|
);
|
|
|
|
$this->artisan('inventory:supplier-summary:import', [
|
|
'file' => $path,
|
|
'--month' => '2026-09',
|
|
'--result-json' => $secondResult,
|
|
])->assertExitCode(1);
|
|
|
|
$second = json_decode((string) file_get_contents($secondResult), true);
|
|
$this->assertSame('failed', $second['status']);
|
|
$this->assertSame(0, $second['success']);
|
|
$this->assertSame(2, $second['duplicates']);
|
|
$this->assertSame(12, $second['failed']);
|
|
$this->assertSame(
|
|
2,
|
|
DB::connection($connection)->table('inventory_inbound_detail_rows')
|
|
->where('report_month', '2026-09-01')->count(),
|
|
);
|
|
} finally {
|
|
foreach ([$path, $firstResult, $secondResult] as $file) {
|
|
@unlink($file);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function writeWorkbook(
|
|
string $path,
|
|
string $provider,
|
|
string $ambiguousProvider,
|
|
): void
|
|
{
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
$sheet->setTitle('明细');
|
|
$sheet->fromArray([
|
|
'来源单号', "单据编号\n(采购单号)", '创建时间', '供应商', '货号', '条码',
|
|
'规格名', '库存变动量', '单价', '金额', '预估单价', '预估金额',
|
|
'供应商条码', '备注', '仓库', '创建时间', '快递名称', '快递单号',
|
|
'未发出', '负责人',
|
|
], null, 'A1');
|
|
|
|
for ($row = 2; $row <= 14; $row++) {
|
|
$warehouse = $row <= 3 ? $provider : '不存在的仓库';
|
|
$sheet->fromArray([
|
|
'SRC-1', 'PO-1', '46245', '测试供应商', 'ITEM-1', 'BARCODE-1',
|
|
'测试规格', 2, 10, null, 11, null, 'SUP-BAR-1', '测试备注',
|
|
$warehouse, '2026-09-03 10:00:00', '顺丰', 'SF123', '否',
|
|
'测试负责人',
|
|
], null, 'A' . $row);
|
|
$sheet->setCellValue('J' . $row, '=H' . $row . '*I' . $row);
|
|
$sheet->setCellValue('L' . $row, '=K' . $row . '*H' . $row);
|
|
}
|
|
|
|
$sheet->fromArray([
|
|
'SRC-2', 'PO-2', '46246', '测试供应商', 'ITEM-2', 'BARCODE-2',
|
|
'测试规格二', 1, 10, null, 10, null, 'SUP-BAR-2', null,
|
|
$ambiguousProvider, '2026-09-04 10:00:00', null, null, null,
|
|
'测试负责人',
|
|
], null, 'A15');
|
|
$sheet->setCellValue('J15', '=H15*I15');
|
|
$sheet->setCellValue('L15', '=K15*H15');
|
|
|
|
(new Xlsx($spreadsheet))->save($path);
|
|
$spreadsheet->disconnectWorksheets();
|
|
}
|
|
|
|
private function temporaryPath(string $prefix, string $suffix): string
|
|
{
|
|
$temporary = tempnam(sys_get_temp_dir(), $prefix);
|
|
$path = $temporary . $suffix;
|
|
rename($temporary, $path);
|
|
|
|
return $path;
|
|
}
|
|
}
|