whereDate('order_date', $date) ->where('status', StoreOrderModel::STATUS_PENDING) ->lockForUpdate() ->get(); if ($orders->isEmpty()) { throw new RepositoryException('当日无待汇总订单'); } // 2. 展开明细按商品聚合 $aggregated = []; $orderIds = []; foreach ($orders as $order) { $orderIds[] = $order->id; foreach ($order->items as $item) { $productId = (int) $item->product_id; if (! isset($aggregated[$productId])) { $aggregated[$productId] = [ 'product_id' => $productId, 'product_name' => $item->product_name, 'product_spec' => $item->product_spec, 'quantity' => '0', ]; } $aggregated[$productId]['quantity'] = bcadd( $aggregated[$productId]['quantity'], (string) $item->quantity, 2 ); } } if ($aggregated === []) { throw new RepositoryException('当日待汇总订单均无明细,无法生成采购单'); } $products = ProductModel::withTrashed() ->with('category:id,sort') ->whereIn('id', array_keys($aggregated)) ->get() ->keyBy('id'); // 3. 估算单价 = 最低等级价 $minPrices = ProductPriceModel::query() ->whereIn('product_id', array_keys($aggregated)) ->groupBy('product_id') ->selectRaw('product_id, MIN(price) as min_price') ->pluck('min_price', 'product_id'); // 组装明细行并按「分类 sort → 商品 sort」排序 $rows = []; foreach ($aggregated as $productId => $item) { $product = $products->get($productId); $price = (string) ($minPrices[$productId] ?? '0'); $rows[] = [ 'product_id' => $productId, 'supplier_id' => (int) ($product->supplier_id ?? 0), 'product_name' => $item['product_name'], 'product_spec' => $item['product_spec'], 'price' => $price, 'quantity' => $item['quantity'], 'amount' => bcmul($item['quantity'], $price, 2), 'category_sort' => (int) ($product->category->sort ?? 9999), 'product_sort' => (int) ($product->sort ?? 9999), ]; } usort($rows, static fn (array $a, array $b): int => [$a['category_sort'], $a['product_sort'], $a['product_id']] <=> [$b['category_sort'], $b['product_sort'], $b['product_id']]); $estimateAmount = array_reduce( $rows, static fn (string $carry, array $row): string => bcadd($carry, $row['amount'], 2), '0' ); $totalQuantity = array_reduce( $rows, static fn (string $carry, array $row): string => bcadd($carry, $row['quantity'], 2), '0' ); // 4. 采购单头 $purchase = PurchaseOrderModel::create([ 'purchase_no' => $this->billNumberService->make('PO'), 'purchase_date' => $date, 'status' => PurchaseOrderModel::STATUS_PENDING, 'total_quantity' => $totalQuantity, 'total_weight' => 0, 'estimate_amount' => $estimateAmount, 'actual_amount' => 0, 'operator_id' => $operatorId, ]); // 5. 采购明细(sort 行号) $sort = 1; foreach ($rows as $row) { PurchaseOrderItemModel::create([ 'purchase_id' => $purchase->id, 'product_id' => $row['product_id'], 'supplier_id' => $row['supplier_id'], 'product_name' => $row['product_name'], 'product_spec' => $row['product_spec'], 'price' => $row['price'], 'quantity' => $row['quantity'], 'weight' => 0, 'amount' => $row['amount'], 'sort' => $sort++, 'is_sent' => PurchaseOrderItemModel::NOT_SENT, ]); } // 6. 源订单回写「已汇总」 StoreOrderModel::whereIn('id', $orderIds) ->update(['status' => StoreOrderModel::STATUS_SUMMARIZED]); return $purchase; }); } }