join('store_order', 'store_order.id', '=', 'store_order_item.order_id') ->where('store_order.status', StoreOrderModel::STATUS_COMPLETED) ->whereNull('store_order.deleted_at') ->whereDate('store_order.order_date', '>=', $recon->period_start) ->whereDate('store_order.order_date', '<=', $recon->period_end) ->select('store_order_item.*'); if ((int) $recon->supplier_id > 0) { $itemQuery->where('store_order_item.supplier_id', $recon->supplier_id); } if ((int) $recon->category_id > 0) { $itemQuery->whereIn( 'store_order_item.category_id', $this->descendantCategoryIds((int) $recon->category_id) ); } $orderItems = $itemQuery->get()->makeVisible('cost_price'); if ($orderItems->isEmpty()) { throw new RepositoryException('周期内无符合筛选条件的已完成订单数据,无法生成对账明细'); } // 2. 先清后建(幂等) ReconciliationItemModel::where('recon_id', $recon->id)->delete(); $publishTotal = '0'; $actualTotal = '0'; $rows = []; $sort = 1; $now = now(); foreach ($orderItems as $orderItem) { $publish = (string) $orderItem->amount; $actual = bcmul((string) $orderItem->quantity, (string) ($orderItem->cost_price ?? '0'), 2); $publishTotal = bcadd($publishTotal, $publish, 2); $actualTotal = bcadd($actualTotal, $actual, 2); $rows[] = [ 'recon_id' => $recon->id, 'store_id' => $orderItem->store_id, 'order_item_id' => $orderItem->id, 'product_id' => $orderItem->product_id, 'product_name' => $orderItem->product_name, 'quantity' => $orderItem->quantity, 'weight' => $orderItem->weight, 'publish_amount' => $publish, 'actual_amount' => $actual, 'diff_amount' => bcsub($publish, $actual, 2), 'is_reconciled' => ReconciliationItemModel::NOT_RECONCILED, 'store_remark' => '', 'sort' => $sort++, 'created_at' => $now, 'updated_at' => $now, ]; } ReconciliationItemModel::insert($rows); // 3. 汇总写回头 + 状态流转 $recon->publish_amount = $publishTotal; $recon->actual_amount = $actualTotal; $recon->diff_amount = bcsub($publishTotal, $actualTotal, 2); $recon->status = ReconciliationModel::STATUS_WORKING; $recon->save(); return count($rows); }); } /** * 分类自身 + 全部子孙分类ID(多级分类下按顶级分类筛选) * * @return array */ private function descendantCategoryIds(int $categoryId): array { $parentMap = ProductCategoryModel::pluck('parent_id', 'id'); $ids = [$categoryId]; $queue = [$categoryId]; while ($queue !== []) { $current = array_shift($queue); foreach ($parentMap as $id => $parentId) { if ((int) $parentId === $current && ! in_array((int) $id, $ids, true)) { $ids[] = (int) $id; $queue[] = (int) $id; } } } return $ids; } }