find($orderId); if ($order === null) { return; } $order->total_quantity = (int) $order->items()->sum('quantity'); $order->total_weight = $order->items()->sum('weight'); $order->total_amount = $order->items()->sum('amount'); $order->save(); } /** * 重算采购单汇总(明细增删改后调用) */ public function recalcPurchase(int $purchaseId): void { $purchase = PurchaseOrderModel::query()->find($purchaseId); if ($purchase === null) { return; } $purchase->total_quantity = (int) StoreOrderItemModel::query() ->where('purchase_id', $purchaseId)->sum('quantity'); $purchase->estimate_amount = StoreOrderItemModel::query() ->where('purchase_id', $purchaseId)->sum(DB::raw('quantity * cost_price')); $purchase->total_weight = StoreOrderItemModel::query() ->where('purchase_id', $purchaseId)->sum('weight'); $purchase->save(); } /** * 采购单内指定门店的采购汇总行(按商品聚合,单价=加权平均 Σ金额÷Σ数量, * 排序:分类 sort → 商品 sort,与明细矩阵同序) * * @return array> */ public function storeSummaryRows(int $purchaseId, int $storeId): array { $items = $this->purchaseItems($purchaseId) ->where('store_id', $storeId) ->sortBy('id'); $rows = []; foreach ($items->groupBy('product_id') as $productId => $group) { $first = $group->first(); $quantity = 0; $amount = '0'; $weight = '0'; foreach ($group as $item) { $weight = bcadd($weight, (string) $item->weight, 3); $quantity += (int) $item->quantity; $amount = bcadd($amount, (string) $item->amount, 2); } $rows[] = [ 'product_id' => (int) $productId, 'product_name' => $first->product_name, 'product_spec' => $first->product_spec, 'unit' => $first->unit, 'market' => (string) data_get($first, 'market', ''), // 加权平均单价(保证 单价×数量=预计金额) 'price' => $quantity > 0 ? bcdiv($amount, (string) $quantity, 2) : (string) $first->price, 'quantity' => $quantity, 'amount' => $amount, 'weight' => $weight, 'category_sort' => (int) data_get($first, 'category_sort', 9999), 'product_sort' => (int) data_get($first, 'product_sort', 9999), ]; } return $this->sortRows($rows); } /** * 采购单内指定供应商的商品聚合行(成本口径:金额=Σ数量×成本价, * 排序:分类 sort → 商品 sort) * * @return array> */ public function supplierRows(int $purchaseId, int $supplierId): array { $items = $this->purchaseItems($purchaseId) ->where('supplier_id', $supplierId) ->sortBy('id'); $rows = []; foreach ($items->groupBy('product_id') as $productId => $group) { $first = $group->first(); $quantity = 0; $weight = '0'; $amount = '0'; foreach ($group as $item) { $quantity += (int) $item->quantity; $weight = bcadd($weight, (string) $item->weight, 3); $amount = bcadd($amount, bcmul((string) $item->quantity, (string) $item->cost_price, 2), 2); } $rows[] = [ 'product_id' => (int) $productId, 'product_name' => $first->product_name, 'product_spec' => $first->product_spec, 'unit' => $first->unit, 'market' => (string) data_get($first, 'market', ''), 'cost_price' => (string) $first->cost_price, 'quantity' => $quantity, 'weight' => $weight, 'amount' => $amount, 'category_sort' => (int) data_get($first, 'category_sort', 9999), 'product_sort' => (int) data_get($first, 'product_sort', 9999), ]; } return $this->sortRows($rows); } /** * 供应商采购明细矩阵(导出用):该供应商在采购单内的商品行按市场分组, * 行附「汇总数量 + 各门店数量」;门店列=本采购单内有该供应商明细的门店(按ID排序) * * @return array{ * stores: array, * markets: array>> * } */ public function supplierMarketMatrix(int $purchaseId, int $supplierId): array { $items = $this->purchaseItems($purchaseId) ->where('supplier_id', $supplierId) ->sortBy('id'); $storeIds = $items->pluck('store_id') ->map(static fn ($id): int => (int) $id) ->unique()->sort()->values()->all(); $stores = StoreModel::withTrashed() ->whereIn('id', $storeIds) ->orderBy('id') ->pluck('name', 'id') ->toArray(); $rows = []; foreach ($items->groupBy('product_id') as $productId => $group) { $first = $group->first(); $quantity = 0; $storeQuantities = array_fill_keys(array_map('intval', array_keys($stores)), 0); foreach ($group as $item) { $quantity += (int) $item->quantity; $storeQuantities[(int) $item->store_id] += (int) $item->quantity; } $rows[] = [ 'product_id' => (int) $productId, 'product_name' => $first->product_name, 'market' => (string) data_get($first, 'market', ''), 'quantity' => $quantity, 'store_quantities' => $storeQuantities, 'category_sort' => (int) data_get($first, 'category_sort', 9999), 'product_sort' => (int) data_get($first, 'product_sort', 9999), ]; } // 排序后按市场分组(组间/组内均保持 分类sort → 商品sort 顺序) $markets = []; foreach ($this->sortRows($rows) as $row) { $markets[$row['market']][] = $row; } return ['stores' => $stores, 'markets' => $markets]; } /** * 采购单订货明细(关联订单过滤软删、成本价可见、附分类/商品排序键) * * @return Collection */ private function purchaseItems(int $purchaseId): Collection { $items = StoreOrderItemModel::query() ->join('store_order', 'store_order.id', '=', 'store_order_item.order_id') ->where('store_order_item.purchase_id', $purchaseId) ->whereNull('store_order.deleted_at') ->select('store_order_item.*') ->get() ->makeVisible('cost_price'); // 排序键:商品分类 sort → 商品 sort;市场取商品档案(商品含软删除,保证历史单据可导出) $products = ProductModel::withTrashed() ->with('category:id,sort') ->whereIn('id', $items->pluck('product_id')->unique()) ->get(['id', 'category_id', 'sort', 'market']) ->keyBy('id'); foreach ($items as $item) { $product = $products->get((int) $item->product_id); $item->setAttribute('category_sort', (int) ($product?->category?->sort ?? 9999)); $item->setAttribute('product_sort', (int) ($product?->sort ?? 9999)); $item->setAttribute('market', (string) ($product?->market ?? '')); } return $items; } /** * 排序并剥离排序键 * * @param array> $rows * @return array> */ private function sortRows(array $rows): array { 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']]); return array_map(static function (array $row): array { unset($row['category_sort'], $row['product_sort']); return $row; }, $rows); } }