log = new LogModel([ 'module' => 'goods', 'action' => $request->getMethod(), 'target_type' => 'goods_sku', ]); } public function index(Request $request) { $fields = implode(',', [ 'shop_id', 'external_sku_id', 'SUM(goods_number) - SUM(already_cancel_number) as number', ]); $orderRestTime = DeveloperConfig::query() ->where('key', DeveloperConfig::$ORDER_RESET_TIME) ->value('value'); if (is_null($orderRestTime)) { $orderRestTime = date('Y-m-d 07:00:00'); } $businessOrderItems = BusinessOrderItem::query() ->select(DB::raw($fields)) ->with([ 'shop:id,name', 'goodsSku:id,external_sku_id' ]) ->where('created_at', '>', $orderRestTime) ->where('external_sku_id', '<>', '') ->groupBy(['shop_id', 'external_sku_id']) ->orderByDesc('number') ->get() ->toArray(); $ids = $externals = []; foreach ($businessOrderItems as $businessOrderItem) { if (is_null($businessOrderItem['goods_sku'])) { continue; } $id = $businessOrderItem['goods_sku']['id']; if (isset($ids[$id])) { $ids[$id] += (int)$businessOrderItem['number']; } else { $ids[$id] = (int)$businessOrderItem['number']; } $externals[$businessOrderItem['external_sku_id']][] = $businessOrderItem; } arsort($ids); $builder = GoodsSku::query(); $this->preparQueryGoodsSkus($request, $builder); $day = DateTimeUtils::getToday(); $goodsSkus = (clone $builder)->filter() ->where('is_combination', 0) ->orderByDesc('stock') ->pluck('stock', 'id') ->toArray(); $finalIds = []; foreach ($ids as $id => $number) { if (isset($goodsSkus[$id])) { $finalIds[] = $id; unset($goodsSkus[$id]); } } $finalIds = array_merge($finalIds, array_keys($goodsSkus)); $idField = implode(',', $finalIds); $goodsSkus = (clone $builder)->with(['goods' => function ($query) { $query->with(['type:id,name', 'brand:id,name']); }]) ->with(['daily' => function ($query) use ($day) { $query->where('day', $day); }]) ->whereIn('id', $finalIds) ->orderByRaw("FIELD(id, {$idField})") ->paginate($request->get('per_page')); $rolesName = $request->user()->getRoleNames()->toArray(); foreach ($goodsSkus as &$sku) { $lastInventoryTime = $sku['daily']['inventory_time'] ?: date('Y-m-d 07:00:00'); if (isset($externals[$sku['external_sku_id']])) { $sku['order_detail'] = $externals[$sku['external_sku_id']]; $sku['order_goods_num'] = array_sum(array_column($externals[$sku['external_sku_id']], 'number')); } else { $sku['order_detail'] = []; $sku['order_goods_num'] = 0; } $sku['inventory_time'] = $lastInventoryTime; if ('销售' === $rolesName[0]) { $sku['cost'] = 0; } } return GoodsSkuResource::collection($goodsSkus); } private function preparQueryGoodsSkus(Request $request, &$builder) { if ($request->get('keyword_type') && $request->get('keyword_value')) { $skuIds = Log::query()->where('target_type', 'goods_sku') ->where('target_field', $request->keyword_type) ->whereBetween('created_at', explode(' - ', $request->keyword_value)) ->pluck('target_id') ->toArray(); $builder->whereIn('id', $skuIds); } if ($request->get('goods_title') || $request->get('type_id') || $request->get('brand_id')) { $goodsIds = Goods::query()->filter()->pluck('id')->toArray(); $builder->whereIn('goods_id', $goodsIds); } } public function show($id) { $sku = GoodsSku::query() ->with(['goods' => function ($query) { $query->with(['type:id,name', 'brand:id,name']); }]) ->find($id) ->toArray(); $shops = Shop::query()->get(['id', 'name'])->toArray(); if (empty($sku['thumb_url'])) { foreach ($shops as $shop) { $sku['thumb_url'][] = [ 'shop_id' => $shop['id'], 'shop_name' => $shop['name'], 'img_url' => '', ]; } } return ['data' => $sku]; } public function update($id, Request $request) { $goodsRules = (new GoodsRequest())->arrayRules('goods.'); $skuRules = (new GoodsSkuRequest())->arrayRules('sku.'); $validator = Validator::make($request->all(), array_merge($goodsRules, $skuRules)); if ($validator->fails()) { $this->setValidatorFailResponse($validator->getMessageBag()->getMessages()); return response($this->res, $this->res['httpCode']); } DB::beginTransaction(); try { // 商品规格更新 $sku = GoodsSku::query()->find($id); $this->setBeforeUpdateForLog($sku->toArray()); $skuInfo = $request->sku; $skuInfo['external_sku_id'] = $request->goods['goods_code'] . '_' . $request->sku['sku_code']; $sku->update($skuInfo); $this->setAfterUpdateForLog($sku->toArray()); $this->addLog($id, 'update'); // 商品更新 $goods = Goods::query()->find($sku->goods_id); $this->log = new LogModel([ 'module' => 'goods', 'action' => $request->getMethod(), 'target_type' => 'goods', ]); $this->setBeforeUpdateForLog($goods->toArray()); $goods->update($request->goods); $this->setAfterUpdateForLog($goods->toArray()); $this->addLog($sku->goods_id, 'update'); DB::commit(); } catch (\Exception $exception) { DB::rollBack(); $this->res = [ 'httpCode' => 400, 'errorCode' => 400416, 'errorMessage' => $exception->getMessage(), ]; } return response($this->res, $this->res['httpCode']); } public function batchUpdate(Request $request) { $appendRules = [ 'updateType' => ['required', 'string', Rule::in(['newest', 'inventory', 'stock'])], 'skus' => ['required', 'array'], 'skus.*.id' => [ 'required', Rule::exists('goods_skus', 'id'), ], ]; $skuRules = (new GoodsSkuRequest())->arrayRules('skus.*.'); $validator = Validator::make($request->all(), array_merge($appendRules, $skuRules)); if ($validator->fails()) { $this->setValidatorFailResponse($validator->getMessageBag()->getMessages()); return response($this->res, $this->res['httpCode']); } $function = $request->updateType; return $this->$function($request); } /** * 上新 * * @param $request * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response */ private function newest($request) { DB::beginTransaction(); try { $logs = []; foreach ($request->skus as $sku) { $costLog = $arrivedLog = [ 'module' => 'goods', 'action' => $request->getMethod(), 'target_type' => 'goods_sku', 'target_id' => $sku['id'], 'user_id' => $request->user()->id ]; // 成本 $goodsSku = GoodsSku::query()->where('id', $sku['id'])->first(['id', 'cost', 'stock', 'num']); $costLog['target_field'] = 'cost'; $costLog['before_update'] = $goodsSku->cost; $goodsSku->cost = $sku['cost']; $goodsSku->reference_price = $sku['cost'] * 1.5; $goodsSku->stock += $sku['arrived_today_num']; $goodsSku->num += $sku['arrived_today_num']; $goodsSku->save(); $costLog['after_update'] = $goodsSku->cost; $logs[] = $costLog; // 今日到货 $record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', DateTimeUtils::getToday())->first(['id', 'arrived_today_num']); $arrivedLog['target_field'] = 'arrived_today_num'; $arrivedLog['before_update'] = $record->arrived_today_num; $record->arrived_today_num += $sku['arrived_today_num']; $record->save(); $arrivedLog['after_update'] = $record->arrived_today_num; $logs[] = $arrivedLog; } $log = new LogModel(); $log->batchInsert($logs); DB::commit(); // 批量更新 event(new BatchStockUpdateEvent(array_column($request->skus, 'id'))); } catch (\Exception $exception) { DB::rollBack(); $this->res = [ 'httpCode' => 400, 'errorCode' => 400500, 'errorMessage' => $exception->getMessage(), ]; } return response($this->res, $this->res['httpCode']); } /** * 库存盘点 * * @param $request * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response */ private function inventory($request) { DB::beginTransaction(); try { $logs = []; $requestSkus = ArrayUtils::index($request->skus, 'id'); $skus = GoodsSku::query() ->whereIn('id', array_keys($requestSkus)) ->with(['goods:id,goods_code']) ->get(['id', 'goods_id', 'sku_code']) ->toArray(); $today = DateTimeUtils::getToday(); $nextDay = DateTimeUtils::getNextDay(); $dateTime = date('Y-m-d H:i:s'); foreach ($skus as $sku) { if (!isset($requestSkus[$sku['id']])) { continue; } // 更新今天 $record = DailyStockRecord::query()->where('sku_id', $sku['id'])->where('day', $today)->first(); // 日志记录 $inventoryLog = [ 'module' => 'goods', 'action' => $request->getMethod(), 'target_type' => 'goods_sku', 'target_id' => $sku['id'], 'user_id' => $request->user()->id, 'target_field' => 'inventory', 'before_update' => $record->inventory, 'after_update' => $requestSkus[$sku['id']]['inventory'] ]; $externalSkuId = $sku['goods']['goods_code'] . '_' . $sku['sku_code']; // 自上一次盘点过后有订单发生的数量(包含退单) $lastInventoryTime = $record->inventory_time; $addOrderGoodsNum = BusinessOrderItem::query() ->where('external_sku_id', $externalSkuId) ->when($lastInventoryTime, function ($query) use ($lastInventoryTime) { $query->where('updated_at', '>', $lastInventoryTime); }) ->sum('goods_number'); $reduceOrderGoodsNum = BusinessOrderItem::query() ->where('external_sku_id', $externalSkuId) ->when($lastInventoryTime, function ($query) use ($lastInventoryTime) { $query->where('updated_at', '>', $lastInventoryTime); }) ->sum('already_cancel_number'); $orderGoodsNum = $addOrderGoodsNum - $reduceOrderGoodsNum; $record->inventory = $requestSkus[$sku['id']]['inventory']; $record->inventory_time = $dateTime; $record->order_goods_num += $orderGoodsNum; $record->save(); $inventoryLog['message'] = '盘点时订单商品数量: ' . $orderGoodsNum; $logs[] = $inventoryLog; // 更新明天 DailyStockRecord::updateOrCreate( ['sku_id' => $sku['id'], 'day' => $nextDay], [ 'inventory' => $record->inventory, 'inventory_time' => $dateTime, ] ); } $log = new LogModel(); $log->batchInsert($logs); DB::commit(); } catch (\Exception $exception) { DB::rollBack(); $this->res = [ 'httpCode' => 400, 'errorCode' => 400500, 'errorMessage' => $exception->getMessage(), ]; } return response($this->res, $this->res['httpCode']); } /** * 库存修改 * * @param $request * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response */ private function stock($request) { $skus = $request->skus; $update = reset($skus); DB::beginTransaction(); try { $sku = GoodsSku::query()->where('id', $update['id'])->with(['goods:id,goods_code'])->first(); $record = DailyStockRecord::query() ->where('sku_id', $sku->id) ->where('day', DateTimeUtils::getToday()) ->first(); $this->setBeforeUpdateForLog([ 'two_days_ago_num' => $sku->two_days_ago_num, 'yesterday_num' => $sku->yesterday_num, 'arrived_today_num' => $record->arrived_today_num, 'num' => $sku->num, 'stock' => $sku->stock, ]); $num = $update['two_days_ago_num'] + $update['yesterday_num'] + $update['arrived_today_num']; $sku->num = $num; $sku->stock += ($num - $record->arrived_today_num - $sku->two_days_ago_num - $sku->yesterday_num); $sku->two_days_ago_num = $update['two_days_ago_num']; $sku->yesterday_num = $update['yesterday_num']; $sku->save(); $record->arrived_today_num = $update['arrived_today_num']; $record->save(); $this->setAfterUpdateForLog([ 'two_days_ago_num' => $sku->two_days_ago_num, 'yesterday_num' => $sku->yesterday_num, 'arrived_today_num' => $record->arrived_today_num, 'num' => $sku->num, 'stock' => $sku->stock, ]); $this->addLog($sku->id, 'stock'); DB::commit(); event(new StockUpdateEvent($sku)); } catch (\Exception $exception) { DB::rollBack(); $this->res = [ 'httpCode' => 400, 'errorCode' => 400416, 'errorMessage' => $exception->getMessage(), ]; } return response($this->res, $this->res['httpCode']); } public function updateField($id, Request $request) { $rules = [ 'updateField' => [ 'required', Rule::in(['reference_price', 'reserve', 'loss_num', 'status']) ], 'reference_price' => [ 'sometimes', 'numeric', 'gt:0' ], 'reserve' => [ 'sometimes', 'integer', ], 'loss_num' => [ 'sometimes', 'integer', ], 'reason' => [ 'sometimes', 'required', 'string' ], 'status' => [ 'sometimes', 'required', 'integer', Rule::in([0, 1, 2])], ]; $validator = Validator::make($request->all(), $rules); if ($validator->fails()) { $this->setValidatorFailResponse($validator->getMessageBag()->getMessages()); goto end; } $updateField = \request('updateField'); $sku = GoodsSku::query()->find($id); if ('loss_num' === $updateField) { $record = DailyStockRecord::query() ->where('sku_id', $id) ->where('day', DateTimeUtils::getToday()) ->first(['id', 'loss_num']); $this->log->message = $request->get('reason'); $this->setBeforeUpdateForLog($record->loss_num); $record->loss_num += $request->loss_num; $record->save(); $this->setAfterUpdateForLog($record->loss_num); $sku->stock -= $request->loss_num; $sku->save(); } else { $this->setBeforeUpdateForLog($sku->$updateField); if ('reserve' === $updateField) { $changeNum = $sku->reserve - $request->reserve; if (0 > $changeNum + $sku->stock) { $this->setValidatorFailResponse('预留量超过库存数量'); goto end; } $sku->stock += $changeNum; } $sku->$updateField = $request->$updateField; $sku->save(); $this->setAfterUpdateForLog($sku->$updateField); } if (in_array($updateField, ['reserve', 'loss_num'])) { event(new StockUpdateEvent($sku)); } $this->addLog($id, $updateField); end: return response($this->res, $this->res['httpCode']); } public function store(Request $request) { if (!$request->hasFile('goodsSkus')) { $this->res = [ 'httpCode' => 404, 'errorCode' => 404404, 'errorMessage' => 'not found goodsSkus file', ]; } try { $import = new GoodsSkusImport(); $path = $request->file('goodsSkus'); Excel::import($import, $path); $this->addLog(0, 'import'); } catch (ValidationException $exception) { $this->setValidatorFailResponse($exception->validator->getMessageBag()->getMessages()); } return response($this->res, $this->res['httpCode']); } public function export(Request $request) { $type = $request->get('exportType'); ob_end_clean(); if ('week_data' === $type) { return Excel::download(new WeekDataExport(), $type . '.xlsx'); } return Excel::download(new GoodsSkusExport($type), $type . '.xlsx'); } public function newSetImport(Request $request) { if (!$request->hasFile('newSetFile')) { $this->res = [ 'httpCode' => 404, 'errorCode' => 404404, 'errorMessage' => 'not found new set file', ]; } try { $import = new NewSetImport(); $path = $request->file('newSetFile'); Excel::import($import, $path); $this->addLog(0, 'import', 'newSetFile'); } catch (ValidationException $exception) { $this->setValidatorFailResponse($exception->validator->getMessageBag()->getMessages()); } return response($this->res, $this->res['httpCode']); } public function inventoryImport(Request $request) { if (!$request->hasFile('inventoryFile')) { $this->res = [ 'httpCode' => 404, 'errorCode' => 404404, 'errorMessage' => 'not found inventory file', ]; } try { $import = new InventoryImport(); $path = $request->file('inventoryFile'); Excel::import($import, $path); $this->addLog(0, 'import', 'inventory'); } catch (ValidationException $exception) { $this->setValidatorFailResponse($exception->validator->getMessageBag()->getMessages()); } return response($this->res, $this->res['httpCode']); } public function stockNum() { $skus = GoodsSku::query() ->where('is_combination', 0) ->where('status', '>', 0) ->pluck('stock', 'external_sku_id') ->toArray(); $roseNum = $otherNum = 0; foreach ($skus as $externalSkuId => $stock) { // 玫瑰 if (false !== strpos($externalSkuId, 'M')) { $roseNum += $stock; continue; } // 多头玫瑰 if (false !== strpos($externalSkuId, 'D')) { $roseNum += $stock; continue; } // 草花剔除A开头 if (false !== strpos($externalSkuId, 'A')) { continue; } // 剔除Z开头组合 if (false !== strpos($externalSkuId, 'Z')) { continue; } // 剔除测试CS if (false !== strpos($externalSkuId, 'CS')) { continue; } // 剔除N开头年宵花 // if (false !== strpos($externalSkuId, 'N')) { // continue; // } $otherNum += $stock; } return response()->json([ 'rose_num' => $roseNum, 'other_num' => $otherNum, ]); } public function goodsSkusList(Request $request) { $title = $request->input('title'); $data = []; $goodsSkus = GoodsSku::query() ->with('goods:id,title') ->whereHas('goods', function ($query) use ($title) { $query->where('title', 'like', '%' . $title . '%'); }) ->get(['title', 'external_sku_id', 'goods_id']); foreach ($goodsSkus as $goodsSku) { $data[] = [ 'external_sku_id' => $goodsSku['external_sku_id'], 'title' => $goodsSku['goods']['title'] . ' ' . $goodsSku['title'] ]; } return $data; } }