diff --git a/app/Console/Commands/SyncStoreReceiptProductCost.php b/app/Console/Commands/SyncStoreReceiptProductCost.php index 351cdfbd..d69aceed 100644 --- a/app/Console/Commands/SyncStoreReceiptProductCost.php +++ b/app/Console/Commands/SyncStoreReceiptProductCost.php @@ -166,7 +166,7 @@ class SyncStoreReceiptProductCost extends Command } $this->info('订单侧候选单号:' . count($candidates)); - /** @var array> $lines */ + /** @var array> $lines */ $lines = []; foreach (self::SOURCES as $source) { try { @@ -174,6 +174,8 @@ class SyncStoreReceiptProductCost extends Command "SELECT o.so_id as so_id, " . $source['warehouse'] . " as warehouse_code, " . " COALESCE(wh.name, '') as warehouse_name, " + . " CASE WHEN o.link_o_id IS NULL OR o.link_o_id = 0 THEN 1 ELSE 0 END as is_main, " + . " CONCAT(o.o_id, '-', i.oi_id) as row_key, " . " i.sku_id as sku_id, i.qty as qty, i.amount as amount FROM tmp_cost_order_nos t STRAIGHT_JOIN {$source['orders']} o ON o.so_id = t.no @@ -221,6 +223,9 @@ class SyncStoreReceiptProductCost extends Command // 组合装明细金额对应整包,拆到组件后不再参与「数量×单价 vs 明细金额」校验。 'amount' => $isComponent ? 0.0 : $amount, 'warehouse' => $warehouse, + // 拆单后子单(link_o_id 非空)才是实际发货行;主单只是原始下单记录。 + 'is_main' => (int) $row->is_main === 1, + 'row_key' => (string) $row->row_key, ]; } } @@ -348,11 +353,15 @@ class SyncStoreReceiptProductCost extends Command } /** - * 拆单去重:聚水潭的拆单子单会把主单的商品行再列一遍,同一批商品只算一次; - * 同一商品在同一订单里既有真实发货仓库行、又有无仓库的占位行时,丢掉占位行, - * 这样「同一单分多仓发货」会按子单的实际仓库分行。 + * 拆单取数:聚水潭拆单后,主单(link_o_id 为空)保留的是原始下单行, + * 子单(link_o_id 指向主单)才是实际发货行。一个订单只要出现子单,就只取子单, + * 否则主单和子单会被各算一遍(主单的原始商品 + 子单的实际发货商品)。 + * 没有子单的订单才用主单行。 * - * @param list $rows + * 同一订单的多条子单(不同仓库 / 分批发货)按「发货仓库 + 商品编码」汇总,数量相加, + * 这样「一个订单有几个发货仓库、商品编码、数量」会分开列。 + * + * @param list $rows * @return list */ private function costLines(array $rows): array @@ -361,25 +370,23 @@ class SyncStoreReceiptProductCost extends Command return []; } - // 1) 完全相同的行(商品 + 数量 + 金额 + 仓库)只保留一条。 + // 1) 先按订单明细主键去掉上游的完全重复行(同一 o_id + oi_id 只算一次)。 $unique = []; foreach ($rows as $row) { - $key = $row['sku'] . "\0" . $row['qty'] . "\0" . $row['amount'] . "\0" . $row['warehouse']; + $key = $row['row_key'] . "\0" . $row['sku']; $unique[$key] = $row; } - // 2) 有真实仓库的同一(商品 + 数量 + 金额),优先于无仓库的占位行。 - $withWarehouse = []; + // 2) 有子单就只取子单(实际发货行);整单没有子单才用主单行。 + $hasSubOrder = false; foreach ($unique as $row) { - if ($row['warehouse'] !== '') { - $withWarehouse[$row['sku'] . "\0" . $row['qty'] . "\0" . $row['amount']] = true; + if (! $row['is_main']) { + $hasSubOrder = true; + break; } } - foreach ($unique as $key => $row) { - if ($row['warehouse'] === '' - && isset($withWarehouse[$row['sku'] . "\0" . $row['qty'] . "\0" . $row['amount']])) { - unset($unique[$key]); - } + if ($hasSubOrder) { + $unique = array_filter($unique, static fn (array $row): bool => ! $row['is_main']); } // 3) 汇总到「发货仓库 + 商品编码」。