StoreService.php 20 KB

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