StoreService.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. <?php
  2. namespace App\Services;
  3. use App\Feature;
  4. use App\Jobs\RejectedPushJob;
  5. use App\Jobs\StoreCreateInstantBill;
  6. use App\Order;
  7. use App\OwnerFeeDetail;
  8. use App\OwnerFeeOperation;
  9. use App\OwnerFeeOperationDetail;
  10. use App\Services\common\BatchUpdateService;
  11. use App\Services\common\DataHandlerService;
  12. use App\Services\common\QueryService;
  13. use App\Store;
  14. use App\StoreRejected;
  15. use App\ValueStore;
  16. use App\Warehouse;
  17. use Carbon\Carbon;
  18. use Illuminate\Support\Facades\Cache;
  19. use App\Traits\ServiceAppAop;
  20. use Illuminate\Support\Facades\DB;
  21. use Illuminate\Support\Facades\Log;
  22. class StoreService
  23. {
  24. use ServiceAppAop;
  25. protected $modelClass = Store::class;
  26. private function conditionQuery(array $params)
  27. {
  28. $stores = Store::query()->with(['storeItems.store', 'warehouse', 'owner'])->where('is_fast_stored', '无')->orderBy('id', 'DESC');
  29. $columnQueryRules = [
  30. 'asn_code' => ['batch' => ''],
  31. 'remark' => ['batch' => '', 'timeLimit' => 15],
  32. 'created_at_start' => ['alias' => 'created_at', 'startDate' => ' 00:00:00'],
  33. 'created_at_end' => ['alias' => 'created_at', 'endDate' => ' 23:59:59'],
  34. 'owner' => ['alias' => 'owner_id', 'multi' => ','],
  35. 'warehouse' => ['alias' => 'warehouse_id', 'multi' => ','],
  36. 'status' => ['multi' => ','],
  37. 'stored_method' => ['multi' => ','],
  38. 'id' => ['multi' => ',']
  39. ];
  40. $stores = app(QueryService::class)->query($params, $stores, $columnQueryRules, 'stores');
  41. return $stores;
  42. }
  43. public function paginate(array $params)
  44. {
  45. return $this->conditionQuery($params)->paginate($params['paginate'] ?? 50);
  46. }
  47. public function create(array $params)
  48. {
  49. return Store::query()->create($params);
  50. }
  51. public function storeCreateByWms()
  52. {
  53. $created_at = config('sync.asn_sync.created_at');
  54. $create_set = config('sync.asn_sync.cache_prefix.create_set');
  55. $create_keys = config('sync.asn_sync.cache_prefix.create_keys');
  56. $create_key = config('sync.asn_sync.cache_prefix.create');
  57. /** @var OracleDocAsnHerderService $oracleDocAsnHerderService */
  58. $oracleDocAsnHerderService = app(OracleDocAsnHerderService::class);
  59. $last_time = $this->getAsnLastSyncAt($created_at, 'create');
  60. $asnHerders = $oracleDocAsnHerderService->getWmsAsnOnStartDateCreate($last_time);
  61. if (count($asnHerders) < 1) return;
  62. try {
  63. $arr = [];
  64. foreach ($asnHerders as $asnHerder) {
  65. array_push($arr, $asnHerder->asnno);
  66. }
  67. RejectedPushJob::dispatch($arr);
  68. } catch (\Exception $e) {
  69. Log::error("退货推送asn集合失败", [$e->getMessage()]);
  70. }
  71. $last_time = $asnHerders->first()['addtime'];
  72. $last_records = $asnHerders->where('addtime', $last_time);
  73. $this->createStore($asnHerders);
  74. $this->createStoreRejected($asnHerders);
  75. $this->deleteCacheKey($create_set, $create_keys);
  76. $this->setLastRecordsByRedis($create_key, $create_set, $create_keys, $last_records);
  77. $this->setAsnLastSyncAt($created_at, $last_time);
  78. }
  79. public function storeUpdateByWms()
  80. {
  81. $updated_at = config('sync.asn_sync.updated_at');
  82. $update_set = config('sync.asn_sync.cache_prefix.update_set');
  83. $update_keys = config('sync.asn_sync.cache_prefix.update_keys');
  84. $update_key = config('sync.asn_sync.cache_prefix.update');
  85. /** @var OracleDocAsnHerderService $oracleDocAsnHerderService */
  86. $oracleDocAsnHerderService = app(OracleDocAsnHerderService::class);
  87. $last_time = $this->getAsnLastSyncAt($updated_at, 'update');
  88. app('LogService')
  89. ->log(__METHOD__, __FUNCTION__, '11 获取上次更新时间:' . $last_time);
  90. $asnHerders = $oracleDocAsnHerderService->getWmsAsnOnStartDateEdit($last_time);
  91. if (count($asnHerders) < 1) return;
  92. try {
  93. $arr = [];
  94. foreach ($asnHerders as $asnHerder) {
  95. array_push($arr, $asnHerder->asnno);
  96. }
  97. RejectedPushJob::dispatch($arr);
  98. } catch (\Exception $e) {
  99. Log::error("退货推送asn集合失败", [$e->getMessage()]);
  100. }
  101. $last_time = $asnHerders->first()['edittime'];
  102. $last_records = $asnHerders->where('edittime', $last_time);
  103. $this->createStore($asnHerders,'update');
  104. $this->updateStore($asnHerders);
  105. $this->createStoreRejected($asnHerders);
  106. $this->deleteCacheKey($update_set, $update_keys);
  107. $this->setLastRecordsByRedis($update_key, $update_set, $update_keys, $last_records);
  108. $this->setAsnLastSyncAt($updated_at, $last_time);
  109. }
  110. public function createStore($asnHerders, $isUpdate = null)
  111. {
  112. if (!$asnHerders) return null;
  113. /** @var OwnerService $ownerService */
  114. $ownerService = app(OwnerService::class);
  115. $owner_codes = [];
  116. $warehouse_codes = [];
  117. foreach ($asnHerders as $asnHerder) {
  118. if (!empty($asnHerder['customerid']))
  119. $owner_codes[$asnHerder['customerid']] = $asnHerder['customerid'];
  120. if (!empty($asnHerder['warehouseid']))
  121. $warehouse_codes[$asnHerder['warehouseid']] = $asnHerder['warehouseid'];
  122. }
  123. $owners = $ownerService->getOwnerByCodes($owner_codes);
  124. $warehouses = Warehouse::query()->get();
  125. $owners_code_map = [];
  126. foreach ($owners as $owner) {
  127. $owners_code_map[$owner->code] = $owner;
  128. }
  129. $warehouses_code_map = [];
  130. foreach ($warehouses as $warehouse) {
  131. $warehouses_code_map[$warehouse->code] = $warehouse;
  132. }
  133. $params = $this->getParamsByAsnHeader($asnHerders, $owners_code_map, $warehouses_code_map);
  134. if (count($params) > 0) $this->insertStore($params);
  135. /** @var StoreItemService $storeItemService */
  136. $storeItemService = app(StoreItemService::class);
  137. $storeItemService->storeItemCreateByWms($asnHerders);
  138. /** @var RejectedBillService $rejectedBillService */
  139. $rejectedBillService = app(RejectedBillService::class);
  140. $rejectedBillService->syncLoadedStatusByAsnHerder($asnHerders);
  141. if (!$isUpdate) $this->pushJob($asnHerders);
  142. if (!$isUpdate) unset($asnHerders);
  143. unset($owners_code_map, $warehouses_code_map);
  144. }
  145. public function getParamsByAsnHeader($asnHerders, $owners_code_map, $warehouses_code_map)
  146. {
  147. $params = [];
  148. $stores = Store::query()->whereIn('asn_code', data_get($asnHerders, '*.asnno'))->get();
  149. $store_asn_code_map = [];
  150. foreach ($stores as $store) {
  151. $store_asn_code_map[$store->asn_code] = $store;
  152. }
  153. foreach ($asnHerders as $asnHerder) {
  154. if ($store_asn_code_map[$asnHerder->asnno] ?? false) continue;
  155. $owner = $owners_code_map[$asnHerder->customerid] ?? null;
  156. $warehouse = $warehouses_code_map[$asnHerder->warehouseid] ?? null;
  157. $status = null;
  158. $fast = '无';
  159. if ($asnHerder->asnStatus && $asnHerder->asnStatus->codename_c == '完全收货') $status = '已入库';
  160. if ($asnHerder->asnStatus && $asnHerder->asnStatus->codename_c == '订单创建') $status = '未入库';
  161. if ($asnHerder->asnType && $asnHerder->asnType->codename_c == '调拨入库') $fast = '快速入库';
  162. $params[] = [
  163. 'asn_code' => $asnHerder->asnno,
  164. 'warehouse_id' => $warehouse->id ?? null,
  165. 'owner_id' => $owner->id ?? null,
  166. 'stored_method' => $asnHerder->asnType ? $asnHerder->asnType->codename_c : '',
  167. 'status' => $status ? $status : $asnHerder->asnStatus->codename_c,
  168. 'remark' => $asnHerder->notes ?? null,
  169. 'created_at' => $asnHerder->addtime ?? null,
  170. 'updated_at' => $asnHerder->edittime ?? null,
  171. 'is_fast_stored' => $fast,
  172. ];
  173. }
  174. return $params;
  175. }
  176. public function insertStore(array $params)
  177. {
  178. if (count($params) === 0) return;
  179. foreach (array_chunk($params, 1000) as $item) {
  180. $bool = $this->insert($item);
  181. if ($bool) {
  182. app('LogService')->log(__METHOD__, __FUNCTION__, "批量创建 store success " . count($item) . ' || ' . json_encode($item));
  183. } else app('LogService')->log(__METHOD__, __FUNCTION__, "批量添加 store FAILED " . ' || ' . json_encode($item));
  184. }
  185. }
  186. public function updateStore($asnHerders)
  187. {
  188. if (!$asnHerders || $asnHerders->count() == 0) return null;
  189. /**
  190. * @var DataHandlerService $dataHandlerService
  191. * @var OwnerService $ownerService
  192. */
  193. $ownerService = app(OwnerService::class);
  194. $dataHandlerService = app(DataHandlerService::class);
  195. $stores = $this->getByWms($asnHerders);
  196. $store_asn_code_map = $dataHandlerService->dataHeader(['asn_code'], $stores);
  197. $owners = $ownerService->getOwnerByCodes(array_unique(data_get($asnHerders, '*.customerid')));
  198. $warehouses = Warehouse::query()->get();
  199. $owner_code_map = $dataHandlerService->dataHeader(['code'], $owners);
  200. $warehouses_map = $dataHandlerService->dataHeader(['code'], $warehouses);
  201. $updateParams = [[
  202. 'id', 'asn_code', 'warehouse_id', 'owner_id', 'stored_method', 'status', 'remark', 'updated_at'
  203. ]];
  204. foreach ($asnHerders as $asnHerder) {
  205. $store = $dataHandlerService->getKeyValue(['asn_code' => $asnHerder->asnno], $store_asn_code_map);
  206. if (!$store) continue;
  207. $owner = $dataHandlerService->getKeyValue(['code' => $asnHerder->customerid], $owner_code_map);
  208. $warehouse = $dataHandlerService->getKeyValue(['code' => $asnHerder->warehouseid], $warehouses_map);
  209. $owner_id = $owner->id ?? null;
  210. $warehouse_id = $warehouse->id ?? null;
  211. $status = null;
  212. $fast = '无';
  213. if ($asnHerder->asnStatus && $asnHerder->asnStatus->codename_c == '完全收货') $status = '已入库';
  214. if ($asnHerder->asnStatus && $asnHerder->asnStatus->codename_c == '订单创建') $status = '未入库';
  215. if ($asnHerder->asnType && $asnHerder->asnType->codename_c == '调拨入库') $fast = '快速入库';
  216. if ($store->asn_code != $asnHerder->asnno ||
  217. $store->warehouse_id != $warehouse_id ||
  218. $store->owner_id != $owner_id ||
  219. $store->stored_method != ($asnHerder->asnType->codename_c ?? '') ||
  220. $store->status != ($asnHerder->asnStatus->codename_c ?? '') ||
  221. $store->remark != $asnHerder->notes ||
  222. $store->updated_at != $asnHerder->edittime) {
  223. $updateParams[] = [
  224. 'id' => $store->id,
  225. 'asn_code' => $asnHerder->asnno,
  226. 'warehouse_id' => $warehouse_id,
  227. 'owner_id' => $owner_id,
  228. 'stored_method' => $asnHerder->asnType ? $asnHerder->asnType->codename_c : '',
  229. 'status' => $status ? $status : $asnHerder->asnStatus->codename_c,
  230. 'remark' => $asnHerder->notes,
  231. 'updated_at' => $asnHerder->edittime,
  232. 'is_fast_stored' => $fast,
  233. ];
  234. }
  235. }
  236. if (count($updateParams) > 1) {
  237. $this->batchUpdate($updateParams);
  238. }
  239. /** @var StoreItemService $storeItemService */
  240. $storeItemService = app(StoreItemService::class);
  241. $storeItemService->storeItemUpdateByWms($asnHerders);
  242. /** @var RejectedBillService $rejectedBillService */
  243. $rejectedBillService = app(RejectedBillService::class);
  244. $rejectedBillService->syncLoadedStatusByAsnHerder($asnHerders);
  245. $this->pushJob($asnHerders);
  246. unset($updateParams, $asnHerders);
  247. }
  248. public function pushJob($asnHerders)
  249. {
  250. dispatch(new StoreCreateInstantBill(Store::query()->with(["storeItems"])->where('status', '已入库')->whereIn('asn_code', data_get($asnHerders, '*.asnno'))->get()));
  251. }
  252. public function insert($params)
  253. {
  254. return Store::query()->insert($params);
  255. }
  256. public function batchUpdate($params)
  257. {
  258. return app(BatchUpdateService::class)->batchUpdate('stores', $params);
  259. }
  260. public function getByWms($asnHerders)
  261. {
  262. if (!$asnHerders) {
  263. return null;
  264. }
  265. $asn_nos = array_unique(data_get($asnHerders, '*.asnno'));
  266. return Store::query()->whereIn('asn_code', $asn_nos)->get();
  267. }
  268. public function getAsnLastSyncAt($key, $type)
  269. {
  270. $last_time = ValueStore::query()->where('name', $key)->value('value');
  271. if ($last_time) return $last_time;
  272. if ($type == 'create') {
  273. $store = Store::query()->orderByDesc('created_at')->first();
  274. if ($store) return $store->created_at;
  275. } else {
  276. $store = Store::query()->orderByDesc('updated_at')->first();
  277. if ($store) return $store->updated_at;
  278. }
  279. return Carbon::now()->subSeconds(65);
  280. }
  281. public function setAsnLastSyncAt($key, $last_time)
  282. {
  283. $asnLastSyncAt = ValueStore::query()->updateOrCreate([
  284. 'name' => $key,
  285. ], [
  286. 'name' => $key,
  287. 'value' => $last_time,
  288. ]);
  289. return $asnLastSyncAt;
  290. }
  291. public function deleteCacheKey($set, $keys)
  292. {
  293. if (Cache::get($set)) {
  294. $cacheKeys = Cache::get($keys);
  295. if (!$cacheKeys) return;
  296. foreach ($cacheKeys as $cacheKey) {
  297. Cache::forget($cacheKey);
  298. }
  299. Cache::forget($keys);
  300. }
  301. }
  302. public function setLastRecordsByRedis($prefixKey, $set, $keys, $last_records)
  303. {
  304. $cacheKeys = [];
  305. foreach ($last_records as $last_record) {
  306. Cache::put($prefixKey . $last_record->customerid . '_' . $last_record->asnno, true);
  307. array_push($cacheKeys, $prefixKey . $last_record->customerid . '_' . $last_record->asnno);
  308. }
  309. Cache::put($keys, $cacheKeys);
  310. Cache::put($set, true);
  311. }
  312. public function createInstantBill(Store $store): bool
  313. {
  314. /** @var \stdClass $store */
  315. if (!$store || $store->status != "已入库") return false;
  316. if (Cache::has("owner_fee_details:stores_" . $store->id)) return true;
  317. $store->loadMissing(["storeItems.commodity", "warehouse"]);
  318. /** @var OwnerPriceOperationService $service */
  319. $service = app("OwnerPriceOperationService");
  320. $workedAt = $store->updated_at;
  321. $month = substr($workedAt, 0, 7);
  322. $GLOBALS["FEE_INFO"] = [];
  323. list($id, $money, $taxFee) = $service->matching($month, $store, Feature::MAPPING["store"], $store->owner_id, "入库");
  324. $amount = 0;
  325. if ($store->storeItems) foreach ($store->storeItems as $item) $amount += $item->amount;
  326. if (!app("OwnerFeeDetailService")->create([
  327. "owner_id" => $store->owner_id,
  328. "worked_at" => $workedAt,
  329. "type" => "收货",
  330. "operation_bill" => $store->asn_code,
  331. "commodity_amount" => $amount,
  332. "work_fee" => $money,
  333. "owner_price_operation_id" => $id,
  334. "created_at" => date('Y-m-d H:i:s'),
  335. "outer_id" => $store->id,
  336. "outer_table_name" => "stores",
  337. "work_tax_fee" => $taxFee,
  338. ])) return false;
  339. $this->setStoreAmount($store->owner_id, $amount, $month);
  340. if ($money > 0) $this->constructFeeInfo([
  341. "worked_at" => $workedAt,
  342. "owner_id" => $store->owner_id,
  343. "model_id" => $id,
  344. "source_number" => null,
  345. "doc_number" => $store->asn_code,
  346. "commodity_id" => 0,
  347. "total_fee" => 0,
  348. "tax_rate" => 0,
  349. "fee_description" => '',
  350. ]);
  351. Cache::put("owner_fee_details:stores_" . $store->id, 1, 86400);
  352. return true;
  353. }
  354. /**
  355. * 构建费用信息
  356. *
  357. * @param array $defaultInfo
  358. *
  359. * @return void
  360. */
  361. public function constructFeeInfo(array $defaultInfo)
  362. {
  363. foreach ($GLOBALS["FEE_INFO"] as $info) {
  364. $operation = $defaultInfo;
  365. foreach ($operation as $key => $val) if (isset($info[$key])) $operation[$key] = $info[$key];
  366. $model = OwnerFeeOperation::query()->create($operation);
  367. if ($info['details']) foreach ($info['details'] as &$detail) $detail["owner_fee_operation_id"] = $model->id;
  368. OwnerFeeOperationDetail::query()->insert($info['details']);
  369. }
  370. }
  371. /**
  372. * 清除费用信息
  373. *
  374. * @param string $docNumber
  375. */
  376. public function clearFeeInfo(string $docNumber)
  377. {
  378. try {
  379. $models = OwnerFeeOperation::query()->where("doc_number", $docNumber)->get();
  380. if ($models->count() == 0) return;
  381. OwnerFeeOperationDetail::query()->whereIn("owner_fee_operation_id", array_column($models->toArray(), "id"))->delete();
  382. OwnerFeeOperation::query()->where("doc_number", $docNumber)->delete();
  383. } catch (\Exception $e) {
  384. Log::warning("清除费用信息发送错误", ["ASN号:{$docNumber}", isset($models) ? $models->toJson() : "查费用信息时被锁定", $e->getMessage()]);
  385. }
  386. }
  387. /**
  388. * @param $asnHerders
  389. * @return null
  390. */
  391. public function createStoreRejected($asnHerders)
  392. {
  393. if (!$asnHerders) return null;
  394. $stores = $this->getByWms($asnHerders);
  395. $orders = Order::query()->whereIn('client_code', data_get($asnHerders, '*.asnreference2'))->get();
  396. $store_rejecteds = StoreRejected::query()->whereIn('order_id', data_get($orders, '*.id'))->whereIn('store_id', data_get($stores, '*.id'))->get();
  397. /** @var DataHandlerService $dataHandlerService */
  398. $dataHandlerService = app(DataHandlerService::class);
  399. $order_map = $dataHandlerService->dataHeader(['client_code'], $orders);
  400. $store_map = $dataHandlerService->dataHeader(['asn_code'], $stores);
  401. $store_rejected_map = $dataHandlerService->dataHeader(['store_id', 'order_id', 'logistic_number_return'], $store_rejecteds);
  402. $insert_param = [];
  403. foreach ($asnHerders as $asnHerder) {
  404. if (!$asnHerder->asnreference2) continue;
  405. $order = $dataHandlerService->getKeyValue(['client_code' => $asnHerder->asnreference2], $order_map);
  406. $store = $dataHandlerService->getKeyValue(['asn_code' => $asnHerder->asnno], $store_map);
  407. if (!$order || !$store) continue;
  408. $store_rejected = $dataHandlerService->getKeyValue(['store_id' => $store->id, 'order_id' => $order->id, 'logistic_number_return' => $asnHerder->asnreference3], $store_rejected_map);
  409. if ($store_rejected) continue;
  410. $insert_param[] = [
  411. 'store_id' => $store->id,
  412. 'order_id' => $order->id,
  413. 'logistic_number_return' => $asnHerder->asnreference3 ?? '',
  414. 'created_at' => Carbon::now()->toDateTimeString(),
  415. ];
  416. }
  417. if (count($insert_param) > 0) StoreRejected::query()->insert($insert_param);
  418. app('LogService')
  419. ->log(__METHOD__, __FUNCTION__, '11 createStoreRejected:' . json_encode($insert_param));
  420. }
  421. /**
  422. * 入库件数丢失补偿逻辑
  423. *
  424. * @param int $owner
  425. * @param string $month
  426. */
  427. private function storeAmountCompensationLogic(int $owner, string $month)
  428. {
  429. $query = DB::raw(<<<sql
  430. SELECT sum(amount) total FROM `store_items` LEFT JOIN stores ON store_items.store_id=stores.id WHERE stores.owner_id = ? AND (stores.status='已入库' OR stores.status='ASN关闭') AND store_items.updated_at LIKE ?
  431. sql
  432. );
  433. $statistics = DB::selectOne($query, [$owner, date("Y-m") . "%"]);
  434. Cache::put($month . "|A|" . $owner, $statistics->total ?: 0, 2764800);
  435. }
  436. /**
  437. * 设置货主下的本月入库件数
  438. *
  439. * @param int $owner
  440. * @param int $amount
  441. * @param string $month
  442. */
  443. public function setStoreAmount(int $owner, int $amount, string $month)
  444. {
  445. $date = date("Y-m");
  446. if (!Cache::has($date . "|A|" . $owner)) $this->storeAmountCompensationLogic($owner, $month);//补偿逻辑
  447. Cache::increment($date . "|A|" . $owner, $amount);
  448. }
  449. /**
  450. * 获取货主下的本月入库件数
  451. *
  452. * @param int $owner
  453. * @param string $date
  454. *
  455. * @return int
  456. */
  457. public function getStoreAmount(int $owner, string $date): int
  458. {
  459. if (!Cache::has($date . "|A|" . $owner)) $this->storeAmountCompensationLogic($owner, $date);
  460. return Cache::get($date . "|A|" . $owner);
  461. }
  462. }