diff --git a/modules/EcommerceFinance/Services/SupplierStatementTemplate.php b/modules/EcommerceFinance/Services/SupplierStatementTemplate.php index 2923e6c6..946d4338 100644 --- a/modules/EcommerceFinance/Services/SupplierStatementTemplate.php +++ b/modules/EcommerceFinance/Services/SupplierStatementTemplate.php @@ -28,8 +28,8 @@ class SupplierStatementTemplate private const MAX_ROWS = 200000; - /** 允许留空的列。 */ - private const OPTIONAL_COLUMNS = ['expressName', 'expressNo', 'purchaseNo']; + /** 允许留空的列:快递信息可缺;单价/金额留空时按「金额÷数量」或「数量×单价」补齐,都空按 0。 */ + private const OPTIONAL_COLUMNS = ['expressName', 'expressNo', 'purchaseNo', 'unitPrice', 'amount']; /** 必须是数字的列。 */ private const NUMERIC_COLUMNS = ['qty', 'unitPrice', 'amount']; @@ -96,6 +96,8 @@ class SupplierStatementTemplate } } + $values = $this->completeAmounts($values); + if ($expectedSupplier !== null && $values['supplier'] !== $expectedSupplier) { throw ValidationException::withMessages([ 'import' => "第 {$rowNumber} 行:供应商必须填写「{$expectedSupplier}」,不能提交其他厂家的数据。", @@ -194,4 +196,24 @@ class SupplierStatementTemplate return mb_strtolower($label); } + + /** 单价/金额留空时互相补算;两者都空按 0(未定价行厂家直接导出时会出现)。 */ + private function completeAmounts(array $values): array + { + $qty = (float) ($values['qty'] ?? 0); + $unitPrice = $values['unitPrice'] === null ? null : (float) $values['unitPrice']; + $amount = $values['amount'] === null ? null : (float) $values['amount']; + + if ($unitPrice === null && $amount !== null && $qty != 0.0) { + $values['unitPrice'] = (string) round($amount / $qty, 4); + } elseif ($unitPrice === null) { + $values['unitPrice'] = '0'; + } + + if ($amount === null) { + $values['amount'] = (string) round($qty * (float) $values['unitPrice'], 2); + } + + return $values; + } } diff --git a/resources/views/business/pages/supplier-reconciliation/supplier-data.blade.php b/resources/views/business/pages/supplier-reconciliation/supplier-data.blade.php index 52a5c2a9..b3c92384 100644 --- a/resources/views/business/pages/supplier-reconciliation/supplier-data.blade.php +++ b/resources/views/business/pages/supplier-reconciliation/supplier-data.blade.php @@ -41,7 +41,8 @@ 下载对账单模板
- 厂家只能按模板上传(表头列名与顺序必须与模板完全一致):快递名称、快递单号、单据编号(采购单号)可以留空,其余列必填; + 厂家只能按模板上传(表头列名与顺序必须与模板完全一致):快递名称、快递单号、单据编号(采购单号)可以留空; + 单价或金额留空时按数量自动补齐(都留空按 0,未定价行可直接导出上传);其余列必填; 不符合模板或数值格式的整份失败并提示具体行号。厂家上传成功后,下面的数据与「财务端数据」的比对结果立即更新。
@if ($selectedSupplier !== '') diff --git a/tests/Feature/Business/SupplierReconciliationTest.php b/tests/Feature/Business/SupplierReconciliationTest.php index 3ecbc46c..29ff7370 100644 --- a/tests/Feature/Business/SupplierReconciliationTest.php +++ b/tests/Feature/Business/SupplierReconciliationTest.php @@ -240,6 +240,24 @@ class SupplierReconciliationTest extends FeatureTestCase } } + public function test_template_completes_missing_unit_price_or_amount(): void + { + $file = UploadedFile::fake()->createWithContent( + 'statement.csv', + "\xEF\xBB\xBF" . self::HEADER . "\n" + . ',,编号-1,2026-09-30,晨丰,ZSJ-1,规格甲,24,,2916' . "\n" // 单价空 → 2916/24 + . ',,编号-2,2026-09-30,晨丰,ZSJ-2,规格甲,3,10,' . "\n", // 金额空 → 3×10 + ); + + $records = app(SupplierStatementTemplate::class)->parse($file, '晨丰'); + + $this->assertCount(2, $records); + $this->assertSame('121.5', $records[0]['unitPrice']); + $this->assertSame('2916', $records[0]['amount']); + $this->assertSame('10', $records[1]['unitPrice']); + $this->assertSame('30', $records[1]['amount']); + } + private function statementRow(string $barcode, string $unitPrice, string $qty, string $amount): array { return [