供应商配件运费:外采快递不扣款 #385

Merged
limeng merged 1 commits from fix/limeng-supplier-external-courier into main 2026-10-17 21:49:10 +08:00
4 changed files with 33 additions and 2 deletions

View File

@ -4,7 +4,7 @@
按业务月份读取发货数据,仓库固定为“常州简尚家居有限公司”。沿用配件补发扣款的包裹合并及条形码规则:按 `source_id` 合并运费,按明细顺序取第一个逗号前的条形码,删除最后一个 `-` 及其后缀。
处理后的条形码匹配普通商品资料 `sku_id`,使用商品的 `supplier_name` 精确匹配供应商管理的 `scmname`,读取 `linkerprovience`(联系人省)。山东、山东省统一为山东;河北、河北省统一为河北。只有这两个省份扣除对应包裹全部运费,其他省份不扣,不套用快递扣款比例,也不要求匹配首发快递。
处理后的条形码匹配普通商品资料 `sku_id`,使用商品的 `supplier_name` 精确匹配供应商管理的 `scmname`,读取 `linkerprovience`(联系人省)。山东、山东省统一为山东;河北、河北省统一为河北。只有这两个省份扣除对应包裹全部运费,快递名称1或快递名称2为“外采快递”时优先不扣款,保留已匹配的供应商资料,说明为“外采快递不扣配件运费”。其他省份不扣,不套用快递扣款比例,也不要求匹配首发快递。
未匹配商品、商品未维护供应商、未匹配供应商档案、联系人省缺失、供应商或省份冲突、运费无效,均列为待核实,扣款额为零。同一条形码的多条商品记录若供应商相同,或同一供应商多份档案的省份经上述归一后一致,只计算一笔运费。零运费列为不扣款。金额按整数分汇总,页面和导出口径一致。

View File

@ -7,6 +7,19 @@ class SupplierAccessoryFreightMatcher
public const STATES = ['ready' => '可扣款', 'excluded' => '不扣款', 'pending' => '待核实'];
public function match(array $row, array $products, array $suppliers): array
{
$result = $this->matchSupplier($row, $products, $suppliers);
foreach (['express_name1', 'express_name2'] as $field) {
if (trim((string) ($row[$field] ?? '')) !== '外采快递') continue;
$result['state'] = 'excluded';
$result['deduction_cents'] = 0;
$result['reason'] = '外采快递不扣配件运费';
break;
}
return $result;
}
private function matchSupplier(array $row, array $products, array $suppliers): array
{
$barcode = AccessoryReshipMatcher::barcode((string) ($row['barcode'] ?? ''));
$row = array_merge($row, ['normalized_barcode' => $barcode, 'product_name' => '', 'supplier_name' => '',

View File

@ -21,7 +21,7 @@
<div><span>待核实</span><strong>{{ number_format($freightSummary['pending']) }}</strong></div>
<div><span>可扣运费合计(元)</span><strong>{{ number_format($freightSummary['amount'] / 100, 2) }}</strong></div>
</div>
<details class="sf-rules"><summary>扣款规则与金额口径</summary><p>仅取所选月份仓库为“常州简尚家居有限公司”的发货包裹。取第一个逗号前的条形码,再去掉最后一个“-”及其后缀,用处理后的条形码匹配商品资料条形码,再按商品供应商名称匹配供应商管理。联系人省为山东、山东省、河北、河北省的供应商,扣除对应包裹全部运费。每个发货包裹只计一次;不需要匹配首发单。其他省份不扣,未匹配、资料缺失、供应商或省份冲突、运费异常的记录待核实。此处展示扣除明细,不自动入账。</p></details>
<details class="sf-rules"><summary>扣款规则与金额口径</summary><p>仅取所选月份仓库为“常州简尚家居有限公司”的发货包裹。取第一个逗号前的条形码,再去掉最后一个“-”及其后缀,用处理后的条形码匹配商品资料条形码,再按商品供应商名称匹配供应商管理。联系人省为山东、山东省、河北、河北省的供应商,扣除对应包裹全部运费。每个发货包裹只计一次;不需要匹配首发单。外采快递不扣运费;其他省份不扣,未匹配、资料缺失、供应商或省份冲突、运费异常的记录待核实。此处展示扣除明细,不自动入账。</p></details>
@if($freightError)<div class="sf-error" role="alert">{{ $freightError }}</div>@endif
<section class="sf-panel" aria-label="配件运费扣除明细">
<div class="sf-table-scroll"><table><thead><tr>

View File

@ -60,6 +60,24 @@ class SupplierAccessoryFreightTest extends TestCase
}
}
public function test_external_courier_always_excluded_and_keeps_supplier_details(): void
{
foreach (['express_name1', 'express_name2'] as $field) {
foreach (['12.35', null, '0'] as $freight) {
$row = (new SupplierAccessoryFreightMatcher)->match($this->row([$field => ' 外采快递 ', 'freight' => $freight]), [['name'=>'成品','supplier_name'=>'甲']], [['linkerprovience'=>'河北省']]);
$this->assertSame('excluded', $row['state']);
$this->assertSame(0, $row['deduction_cents']);
$this->assertSame('甲', $row['supplier_name']);
$this->assertSame('外采快递不扣配件运费', $row['reason']);
}
}
$row = (new SupplierAccessoryFreightMatcher)->match($this->row(['express_name1'=>'外采快递']), [], []);
$this->assertSame('excluded', $row['state']);
$this->assertSame(0, $row['deduction_cents']);
$normal = (new SupplierAccessoryFreightMatcher)->match($this->row(), [['name'=>'成品','supplier_name'=>'甲']], [['linkerprovience'=>'河北省']]);
$this->assertSame(1235, $normal['deduction_cents']);
}
public function test_missing_or_ambiguous_product_supplier_province_never_deducts(): void
{
$product = [['name' => '成品', 'supplier_name' => '甲']];