handleSkusWithCombinationGoods($skusWithCombinationGoods); //传进来的sku可能包含组合商品 所以这里需要事先计算好数据 $inventoryKeyBySkuIdMap = collect($skusWithCombinationGoods)->where('is_combination', "=", 0) ->pluck("real_stock", "id")->toArray(); Log::info("库存原始操作map", $inventoryKeyBySkuIdMap); //计算组合商品 foreach ($skusWithCombinationGoods as $sku) { if (!empty($sku['is_combination'])) { foreach ($sku['combination_goods'] as $combinationGoods) { if (!isset($inventoryKeyBySkuIdMap[$combinationGoods["item_id"]])) { //没有盘点到的sku需要在原先的sku库存 $inventoryKeyBySkuIdMap[$combinationGoods["item_id"]] = 0; } $inventoryKeyBySkuIdMap[$combinationGoods["item_id"]] += $combinationGoods['item_num'] * $sku['real_stock']; } } } $today = DateTimeUtils::getToday(); $dateTime = date('Y-m-d H:i:s'); $updateIds = []; Log::info("库存盘点前完整信息", $skusWithCombinationGoods); Log::info("需要操作的库存数据", $inventoryKeyBySkuIdMap); $batchNumber = GeneratorUtils::generateBatchNumber("import"); DB::beginTransaction(); try { foreach ($skusWithCombinationGoods as $sku) { // 更新每日数据 DailyStockRecord::query()->updateOrCreate([ 'sku_id' => $sku['id'], 'day' => $today, ], [ "inventory" => $sku['inventory'], "inventory_time" => $dateTime, "batch_number" => $batchNumber ]); } $costLogs = []; //更新库存并记录数据 foreach ($inventoryKeyBySkuIdMap as $skuId => $realStock) { //库存修改 盘点是直接覆盖 $skuData = GoodsSku::query()->where('id', $skuId)->first(); if (!empty($skuData)) { $costLogs[] = static::addStockLog($skuData , TargetTypeEnum::INVENTORY, ['stock' => $realStock]); $skuData->stock = $realStock; $skuData->save(); $updateIds[] = $skuId; } } $log = new LogModel(); $log->batchInsert($costLogs); DB::commit(); } catch (\Exception $exception) { DB::rollBack(); Log::error("库存盘点异常", ["error" => $exception->getMessage()]); } // 批量更新 event(new BatchStockUpdateEvent($updateIds)); } /** * 库存盘点 需要扣减未发货未取消的订单数据 * 目前只查询了7日内的数据 * @param array $skusWithCombinationGoods * @return \Illuminate\Support\Collection */ public function handleSkusWithCombinationGoods(array $skusWithCombinationGoods) { //查询sku当前未发货的数量 需要扣减 $externalSkuIds = collect($skusWithCombinationGoods)->pluck("external_sku_id")->toArray(); $skuIds = collect($skusWithCombinationGoods)->pluck("id")->toArray(); //关联的组合数据 $combinationGoods = CombinationGood::query()->with("goodsSku:id,external_sku_id")->where('item_id', $skuIds)->get()->toArray(); $externalSkuIds = collect($combinationGoods)->pluck("goods_sku.external_sku_id")->merge(collect($externalSkuIds))->toArray(); //默认只查15天内未发货的数据 $startTime = Carbon::now()->subDays(15)->startOfDay()->toDateTimeString(); $unshippedDataCollect = BusinessOrderItem::query() ->with([ 'shop:id,name', 'goodsSku:id,external_sku_id,is_combination', 'goodsSku.combinationGoods:id,goods_sku_id,item_id,item_num' ]) ->leftJoin("business_orders as b", "business_order_id", "=", "b.id") ->select("external_sku_id", DB::raw("SUM(goods_number) - SUM(already_cancel_number) as number")) ->whereIn("external_sku_id", $externalSkuIds) ->where("b.shipping_status", "=", BusinessOrderShippingStatus::UNSHIP) ->where("business_order_items.created_at", ">=", $startTime)->where("b.cancel_status", "=", 0) ->groupBy('external_sku_id')->get()->toArray(); Log::info("盘点未发货数据".implode(",",$externalSkuIds),[$unshippedDataCollect]); //重组订单 foreach ($unshippedDataCollect as $businessOrderItem) { if (is_null($businessOrderItem['goods_sku'])) { continue; } $id = $businessOrderItem['goods_sku']['id']; if ($businessOrderItem['goods_sku']['is_combination']) { foreach ($businessOrderItem['goods_sku']['combination_goods'] ?? [] as $combinationGoods) { $ids[$combinationGoods['item_id']] = ($ids[$combinationGoods['item_id']] ?? 0) + ((int)$businessOrderItem['number']) * $combinationGoods['item_num']; } } if (isset($ids[$id])) { $ids[$id] += (int)$businessOrderItem['number']; } else { $ids[$id] = (int)$businessOrderItem['number']; } } return collect($skusWithCombinationGoods)->map(function ($v) use ($ids) { $v['real_stock'] = $v['inventory'] ?? null; if (!empty($ids[$v['id']]) && isset($v['inventory'])) { $v['real_stock'] = $v['inventory'] - $ids[$v['id']]; } return $v; })->toArray(); } public static function computeSkuStock(array $goodsSkuItem, array $changeData, $targetType = TargetTypeEnum::LOSS) { $updateIds = []; $updateParams = []; //添加系统日志 $costLogs = []; // 成本 if (empty($goodsSkuItem['is_combination'])) { $updateParam = [ 'stock' => $goodsSkuItem['stock'] + $changeData['num'], 'sale_stock' => max($goodsSkuItem['sale_stock'] + $changeData['num'], 0), ]; if ($targetType == TargetTypeEnum::PURCHASE) { $updateParam['cost'] = $changeData['cost']; } if ($updateParam['sale_stock'] <= 0) { $updateParam['status'] = GoodsSku::$STATUS_DOWN; } GoodsSku::query()->where('external_sku_id', "=", $goodsSkuItem['external_sku_id']) ->update($updateParam); $updateIds[] = $goodsSkuItem['id']; $updateParams[] = $updateParam; $costLogs[] = static::addStockLog($goodsSkuItem, $targetType, $updateParam); } else { $combinationGood = CombinationGood::query()->with('goodsSkuItem:id,stock,sale_stock') ->where('goods_sku_id', $goodsSkuItem['id'])->get(); foreach ($combinationGood as $item) { $updateParam = [ 'stock' => $item['goodsSkuItem']['stock'] + $changeData['num'] * $item['item_num'], 'sale_stock' => max($item['goodsSkuItem']['sale_stock'] + $changeData['num'] * $item['item_num'], 0), ]; if ($updateParam['sale_stock'] <= 0) { $updateParam['status'] = GoodsSku::$STATUS_DOWN; } GoodsSku::query()->where('id', $item['goodsSkuItem']['id'])->update($updateParam); $updateIds[] = $item['goodsSkuItem']['id']; $updateParams[] = $updateParam; $costLogs[] = static::addStockLog($item['goodsSkuItem'], $targetType, $updateParam); } } $log = new LogModel(); $log->batchInsert($costLogs); Log::info("本次请求更新参数", $updateParams); return $updateIds; } public static function addStockLog($goodsSkuItem, $targetType = TargetTypeEnum::LOSS, $updateParam) { $userId = Auth::id(); $costLog = [ 'module' => 'goods', 'action' => "POST", 'target_type' => $targetType, 'target_id' => $goodsSkuItem['id'] ?? 0, 'user_id' => $userId ?? 999, "target_field" => "stock" ]; $costLog['before_update'] = json_encode($goodsSkuItem); $costLog['after_update'] = json_encode($updateParam); return $costLog; } }