InventoryForm.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <?php
  2. /**
  3. * InventoryForm.php
  4. * todo 文件描述
  5. * Created on 2024/3/26 13:37
  6. * @author: hankaige
  7. */
  8. namespace app\modules\admin\models;
  9. use app\models\District;
  10. use app\models\GoodsCat;
  11. use app\models\InventiryList;
  12. use app\models\InventoryOrder;
  13. use app\models\InventoryOrderGoods;
  14. use app\models\Order;
  15. use app\models\OrderDetail;
  16. use app\models\SortingList;
  17. use app\models\SortingOrder;
  18. use app\models\SortingOrderGoods;
  19. use app\models\User;
  20. use yii\base\Model;
  21. use yii\db\Exception;
  22. use yii\helpers\ArrayHelper;
  23. class InventoryForm extends Model
  24. {
  25. public $store_id;
  26. public $goods_name;
  27. public $cat_id;
  28. public $order_no;
  29. public $md_id;
  30. public $dateStart;
  31. public $dateEnd;
  32. public $status;
  33. public $id;
  34. public $inventory_id;
  35. public $sorting_id;
  36. public $driver_id;
  37. public $send_time;
  38. public $select_order = [];
  39. public function rules()
  40. {
  41. return [
  42. [
  43. [
  44. 'store_id',
  45. 'cat_id',
  46. 'md_id',
  47. 'status',
  48. 'sorting_id',
  49. 'driver_id',
  50. 'inventory_id'
  51. ],
  52. 'integer'
  53. ],
  54. [['dateStart','dateEnd','goods_name','order_no','send_time'],'string'],
  55. ['select_order','safe']
  56. ];
  57. }
  58. public function handleInventory()
  59. {
  60. // 获取搜索订单结果
  61. $orderList = $this->getOrderData();
  62. $t = \Yii::$app->db->beginTransaction();
  63. try {
  64. // 整理订单数据
  65. $goodsList = $this->handleData($orderList);
  66. if(empty($goodsList)){
  67. $t->rollBack();
  68. return ['code'=>1,'msg'=>'没有可生成备货单的订单'];
  69. }
  70. // 生成备货单 与 备货单订单表 以及 备货单商品表
  71. $inventoryId = $this->createInventory($orderList,$goodsList);
  72. // 生成分拣单
  73. $this->createSorting($orderList,$inventoryId);
  74. $t->commit();
  75. return [
  76. 'code' => 0,
  77. 'msg' => '备货单已生成'
  78. ];
  79. } catch (\Throwable $e) {
  80. $t->rollBack();
  81. \Yii::error('--------create-inventory-error---- ' . $this->store_id . ' ----auto-execute-error--------' . $e->getMessage() . $e->getFile() . $e->getLine());
  82. return [
  83. 'code' => 0,
  84. 'msg' => $e->getMessage()
  85. ];
  86. }
  87. }
  88. /**
  89. * 获取备货单列表
  90. * @return array
  91. * @author: hankaige
  92. * @Time: 2024/3/27 11:13
  93. */
  94. public function getInventoryList()
  95. {
  96. $query = InventiryList::find()->where([
  97. 'store_id' => $this->store_id,
  98. 'is_delete' => 0
  99. ])->orderBy('id desc');
  100. $result = pagination_make($query);
  101. foreach ($result['list'] as &$value) {
  102. $value['created_at'] = date('Y-m-d H:i:s',$value['created_at']);
  103. }
  104. return ['code'=>0,'data'=>$result];
  105. }
  106. /**
  107. * 备货单详情
  108. * @return array
  109. * @author: hankaige
  110. * @Time: 2024/3/27 11:23
  111. */
  112. public function getInventoryDetail()
  113. {
  114. $query = InventoryOrderGoods::find()->with(['goods'])->where([
  115. 'inventory_id'=>$this->id,
  116. 'is_delete'=>0
  117. ])->orderBy('id desc');
  118. $result['list'] = $query->asArray()->all();
  119. foreach ($result['list'] as &$value) {
  120. $value['created_at'] = date('Y-m-d H:i:s',$value['created_at']);
  121. $value['attr'] = json_decode($value['attr'],true);
  122. }
  123. return ['code'=>0,'data'=>$result];
  124. }
  125. /**
  126. * 分拣单列表
  127. * @return array
  128. * @author: hankaige
  129. * @Time: 2024/3/27 11:30
  130. */
  131. public function getSortingList()
  132. {
  133. $query = SortingList::find()->with(['md','driver'])->where([
  134. 'store_id'=>$this->store_id,
  135. 'is_delete'=>0,
  136. 'inventory_id' => $this->inventory_id
  137. ])->orderBy('id desc');
  138. if(!empty($thid->md_id)){
  139. $query->andWhere(['md_id'=>$this->md_id]);
  140. }
  141. switch($this->status){
  142. case 1:
  143. $query->andWhere(['driver_id'=>0]);
  144. break;
  145. case 2:
  146. $query->andWhere(['!=','driver_id',0]);
  147. break;
  148. default:
  149. break;
  150. }
  151. if(!empty($this->md_id)){
  152. $query->andWhere(['md_id'=>$this->md_id]);
  153. }
  154. $result = pagination_make($query);
  155. foreach ($result['list'] as &$value) {
  156. $value['created_at'] = date('Y-m-d H:i:s',$value['created_at']);
  157. $province = District::findOne($value['md']['province']);
  158. $city = District::findOne($value['md']['city']);
  159. $district = District::findOne($value['md']['district']);
  160. if ($province && $city && $district) {
  161. $value['md']['detail_address'] = $province->name . $city->name . $district->name . $value['md']['detail_address'];
  162. }
  163. }
  164. return ['code'=>0,'data'=>$result];
  165. }
  166. /**
  167. * 获取分拣单详情
  168. * @return array
  169. * @author: hankaige
  170. * @Time: 2024/3/27 11:30
  171. */
  172. public function getSortingDetail()
  173. {
  174. $query = SortingOrderGoods::find()->with(['goods'])->where([
  175. 'sorting_id'=>$this->id,
  176. 'is_delete'=>0
  177. ])->orderBy('id desc');
  178. $result = pagination_make($query,TRUE);
  179. foreach ($result['list'] as &$value) {
  180. $value['created_at'] = date('Y-m-d H:i:s',$value['created_at']);
  181. $value['attr'] = json_decode($value['attr'],true);
  182. }
  183. return ['code'=>0,'data'=>$result];
  184. }
  185. /**
  186. * 获取备货单的所有配送单数据
  187. * @return array
  188. * @author: hankaige
  189. * @Time: 2024/3/28 15:18
  190. */
  191. public function getSortingDetailAll()
  192. {
  193. $sortingList = SortingList::find()->with(['md','driver'])->where([
  194. 'store_id'=>$this->store_id,
  195. 'is_delete'=>0,
  196. 'inventory_id' => $this->id
  197. ])->asArray()->all();
  198. foreach($sortingList as &$item){
  199. $detail = SortingOrderGoods::find()->with(['goods'])->where([
  200. 'sorting_id'=>$item['id'],
  201. 'is_delete'=>0
  202. ])->asArray()->all();
  203. foreach ($detail as &$value) {
  204. $value['created_at'] = date('Y-m-d H:i:s',$value['created_at']);
  205. $value['attr'] = json_decode($value['attr'],true);
  206. }
  207. $item['detail'] = $detail;
  208. $province = District::findOne($item['md']['province']);
  209. $city = District::findOne($item['md']['city']);
  210. $district = District::findOne($item['md']['district']);
  211. if ($province && $city && $district) {
  212. $item['md']['detail_address'] = $province->name . $city->name . $district->name . $item['md']['detail_address'];
  213. }
  214. }
  215. return ['code'=>0,'data'=>$sortingList];
  216. }
  217. /**
  218. * 设置配送员
  219. * @return array
  220. * @author: hankaige
  221. * @Time: 2024/3/28 15:18
  222. */
  223. public function setDriver()
  224. {
  225. SortingList::updateAll(['driver_id'=>$this->driver_id],[
  226. 'store_id'=>$this->store_id,
  227. 'is_delete'=>0,
  228. 'id' => $this->sorting_id
  229. ]);
  230. return ['code'=>0,'msg'=>'设置成功'];
  231. }
  232. public function printTicket()
  233. {
  234. $sortingList = SortingList::find()->with(['md','driver','order'])->where([
  235. 'store_id'=>$this->store_id,
  236. 'is_delete'=>0,
  237. 'inventory_id' => $this->inventory_id
  238. ])->asArray()->all();
  239. foreach ($sortingList as &$item){
  240. $item['created_at'] = date('Y-m-d H:i:s',$item['created_at']);
  241. $province = District::findOne($item['md']['province']);
  242. $city = District::findOne($item['md']['city']);
  243. $district = District::findOne($item['md']['district']);
  244. if ($province && $city && $district) {
  245. $item['md']['detail_address'] = $province->name . $city->name . $district->name . $item['md']['detail_address'];
  246. }
  247. }
  248. return ['code'=>0,'data'=>$sortingList];
  249. }
  250. /**
  251. * 根据搜索条件获取订单列表
  252. * @return array|\yii\db\ActiveRecord[]
  253. * @author: hankaige
  254. * @Time: 2024/3/27 10:02
  255. */
  256. private function getOrderData(): array
  257. {
  258. // 先根据搜索条件获取订单
  259. $orderQuery = Order::find()->alias('o')
  260. ->leftJoin(['u' => User::tableName()], 'u.id = o.user_id')
  261. ->leftJoin(['od' => OrderDetail::tableName()], 'od.order_id=o.id')
  262. ->where([
  263. 'o.store_id' => $this->store_id,
  264. 'o.is_delete' => 0,
  265. 'o.is_offline' => 1,
  266. 'o.shop_peisong' => 0,
  267. // 没有生成过配送单的订单
  268. ]);
  269. // 只获取选中的订单
  270. if(!empty($this->select_order)){
  271. $orderQuery->andWhere(['o.id'=>$this->select_order]);
  272. }else{
  273. if (!empty($this->goods_name)) {
  274. $orderQuery->andWhere([
  275. 'like',
  276. 'od.goods_name',
  277. $this->goods_name
  278. ]);
  279. }
  280. if ($this->cat_id > 0) {
  281. // 获取所有绑定这个分类ID的商品ID
  282. $goodsID = GoodsCat::find()->where([
  283. 'is_delete' => 0,
  284. 'cat_id' => $this->cat_id
  285. ])->select('goods_id')->column();
  286. if (!empty($goodsID)) {
  287. $orderQuery->andWhere(['od.goods_id' => $goodsID]);
  288. }
  289. }
  290. // 发货时间
  291. if($this->send_time){
  292. $orderQuery->andWhere(['o.arrival_time' => strtotime($this->send_time)]);
  293. }
  294. if (!empty($this->order_no)) {
  295. $orderQuery->andWhere([
  296. 'like',
  297. 'o.order_no',
  298. $this->order_no
  299. ]);
  300. }
  301. if (!empty($this->md_id)) {
  302. $orderQuery->andWhere(['o.md_id' => $this->md_id]);
  303. }
  304. switch ($this->status) {
  305. case 0:
  306. $orderQuery->andWhere([
  307. '<>',
  308. 'o.trade_status',
  309. Order::ORDER_FLOW_CANCEL
  310. ])->andWhere([
  311. 'o.is_delete' => Order::IS_DELETE_FALSE,
  312. 'o.is_pay' => Order::IS_PAY_FALSE
  313. ]);
  314. break;
  315. case 1:
  316. $orderQuery->andWhere([
  317. 'o.trade_status' => Order::ORDER_FLOW_NO_SEND,
  318. 'o.is_delete' => Order::IS_DELETE_FALSE
  319. ])->andWhere([
  320. 'or',
  321. ['o.is_pay' => Order::IS_PAY_TRUE],
  322. ['o.pay_type' => Order::PAY_TYPE_COD]
  323. ]);
  324. break;
  325. case 2:
  326. $orderQuery->andWhere([
  327. 'o.trade_status' => Order::ORDER_FLOW_SEND,
  328. 'o.is_delete' => Order::IS_DELETE_FALSE
  329. ])->andWhere([
  330. 'or',
  331. ['o.is_pay' => Order::IS_PAY_TRUE],
  332. ['o.pay_type' => Order::PAY_TYPE_COD]
  333. ]);
  334. break;
  335. case 3:
  336. $orderQuery->andWhere([
  337. 'o.trade_status' => Order::ORDER_FLOW_CONFIRM,
  338. 'o.is_delete' => Order::IS_DELETE_FALSE
  339. ])->andWhere([
  340. 'or',
  341. ['o.is_pay' => Order::IS_PAY_TRUE],
  342. ['o.pay_type' => Order::PAY_TYPE_COD]
  343. ]);
  344. break;
  345. case 4:
  346. break;
  347. case 5:
  348. $orderQuery->andWhere([
  349. 'or',
  350. ['o.trade_status' => Order::ORDER_FLOW_CANCEL],
  351. ['o.is_delete' => Order::IS_DELETE_TRUE]
  352. ]);
  353. break;
  354. case 6:
  355. $orderQuery->andWhere([
  356. 'and',
  357. ['o.apply_delete' => Order::ORDER_APPLY_DELETE],
  358. [
  359. '!=',
  360. 'o.trade_status',
  361. Order::ORDER_FLOW_CANCEL
  362. ]
  363. ]);
  364. break;
  365. case 7:
  366. $orderQuery->andWhere([
  367. 'o.apply_delete' => Order::ORDER_APPLY_DELETE,
  368. 'o.trade_status' => Order::ORDER_FLOW_NO_SEND
  369. ]);
  370. break;
  371. default:
  372. if (empty($this->order_id)) {
  373. $orderQuery->andWhere([
  374. 'o.is_delete' => Order::IS_DELETE_FALSE
  375. ]);
  376. }
  377. break;
  378. }
  379. if ($this->dateStart) {
  380. $orderQuery->andWhere([
  381. '>=',
  382. 'o.created_at',
  383. strtotime($this->dateStart)
  384. ]);
  385. }
  386. if ($this->dateEnd) {
  387. $orderQuery->andWhere([
  388. '<=',
  389. 'o.created_at',
  390. strtotime($this->dateEnd)
  391. ]);
  392. }
  393. }
  394. return $orderQuery->select([
  395. 'o.*',
  396. 'u.nickname as de_name',
  397. 'u.platform',
  398. 'u.binding',
  399. 'u.avatar_url',
  400. 'od.delivery_type',
  401. 'od.attr',
  402. 'o.name user_name',
  403. 'o.mobile user_mobile',
  404. 'o.address user_address',
  405. 'od.order_transit_id'
  406. ])->all();
  407. }
  408. /**
  409. * 更具订单信息 汇总商品信息
  410. * @param $orderData
  411. * @return array
  412. * @author: hankaige
  413. * @Time: 2024/3/27 10:02
  414. */
  415. private function handleData($orderData): array
  416. {
  417. $goodsList = [];
  418. foreach ($orderData as $orderItem) {
  419. $goodsList = array_merge($goodsList, ArrayHelper::toArray($orderItem->orderDetail));
  420. }
  421. $result = [];
  422. foreach ($goodsList as $goodsItem) {
  423. $attr = json_decode($goodsItem['attr'], TRUE);
  424. // 不存在这个商品
  425. if (!in_array($goodsItem['goods_id'], array_column($result, 'goods_id'))) {
  426. $attrList = [
  427. 'attr_list' => $attr,
  428. 'num' => $goodsItem['num'],
  429. // 规格对应的数量
  430. ];
  431. $result[] = [
  432. 'goods_id' => $goodsItem['goods_id'],
  433. 'goods_name' => $goodsItem['goods_name'],
  434. 'num' => $goodsItem['num'],
  435. // 商品所有规格的总数量
  436. 'attr_list' => [$attrList]
  437. ];
  438. } else {
  439. // 选中已存在的商品
  440. foreach ($result as $key => $value) {
  441. if ($value['goods_id'] == $goodsItem['goods_id']) {
  442. $result[$key]['num'] += $goodsItem['num'];
  443. // 处理规格
  444. $hasAttr = false;
  445. foreach ($value['attr_list'] as $attrItemKey => $attrItem) {
  446. // 已存在的规格
  447. $oldAttrIds = array_column($attrItem['attr_list'], 'attr_id');
  448. // 当前商品的规格
  449. $attrIds = array_column($attr, 'attr_id');
  450. sort($oldAttrIds);
  451. sort($attrIds);
  452. // 规格存在相同规格
  453. if (!array_diff($oldAttrIds, $attrIds)) {
  454. $result[$key]['attr_list'][$attrItemKey]['num'] += $goodsItem['num'];
  455. $hasAttr = true;
  456. break;
  457. }
  458. }
  459. if(!$hasAttr){
  460. $result[$key]['attr_list'][] = [
  461. 'attr_list' => $attr,
  462. 'num' => $goodsItem['num']
  463. ];
  464. }
  465. }
  466. }
  467. }
  468. }
  469. return $result;
  470. }
  471. /**
  472. * 生成备货单 备货单商品 备货单订单
  473. * @param $orderList
  474. * @param $goodsList
  475. * @return int
  476. * @throws Exception
  477. * @author: hankaige
  478. * @Time: 2024/3/27 10:03
  479. */
  480. private function createInventory($orderList,$goodsList): int
  481. {
  482. // 生成备货单
  483. $inventoryModel = new InventiryList();
  484. $inventoryModel->store_id = $this->store_id;
  485. $inventoryModel->inventory_no = $inventoryModel::createOrderNo();
  486. $inventoryModel->goods_count = count($goodsList);
  487. $inventoryModel->order_count = count($orderList);
  488. if(!$inventoryModel->save()){
  489. throw new Exception('生成备货单失败');
  490. }
  491. // 生成备货商品
  492. $inventoryOrderGoods = new InventoryOrderGoods();
  493. foreach($goodsList as $goodsItem){
  494. $goods = clone $inventoryOrderGoods;
  495. $goods->inventory_id = $inventoryModel->id;
  496. $goods->goods_id = $goodsItem['goods_id'];
  497. $goods->goods_name = $goodsItem['goods_name'];
  498. $goods->attr = json_encode($goodsItem['attr_list']);
  499. $goods->num = $goodsItem['num'];
  500. if(!$goods->save()){
  501. throw new Exception('生成备货商品错误');
  502. }
  503. }
  504. // 生成备货单订单信息
  505. $inventoryOrder = new InventoryOrder();
  506. foreach ($orderList as $orderItem){
  507. $order = clone $inventoryOrder;
  508. $order->inventory_id = $inventoryModel->id;
  509. $order->order_id = $orderItem['id'];
  510. if(!$order->save()){
  511. throw new Exception('生成备货订单关联信息失败');
  512. }
  513. }
  514. // 返货备货单主ID
  515. return $inventoryModel->id;
  516. }
  517. /**
  518. * 处理生成分拣单
  519. * @param $orderList
  520. * @param $inventoryId
  521. * @throws Exception
  522. * @author: hankaige
  523. * @Time: 2024/3/27 10:39
  524. */
  525. private function createSorting($orderList,$inventoryId)
  526. {
  527. $newOrderList = [];
  528. foreach ($orderList as $orderItem){
  529. $newOrderList[$orderItem['md_id']][] = $orderItem;
  530. }
  531. // 循环整理后的订单信息 整理为商品的数据
  532. $newGoodsList = [];
  533. foreach($newOrderList as $mdId => $value){
  534. $newGoodsList[$mdId] = $this->handleData($value);
  535. }
  536. if(empty($newGoodsList)){
  537. throw new \Exception('商品信息出错了');
  538. }
  539. $sortingModel = new SortingList();
  540. $sortingOrderModel = new SortingOrder();
  541. $sortingOrderGoodsModel = new SortingOrderGoods();
  542. foreach($newGoodsList as $mdId => $goods){
  543. $model = clone $sortingModel;
  544. $model->store_id = $this->store_id;
  545. $model->inventory_id = $inventoryId;
  546. $model->sorting_no = $model::createOrderNo();
  547. $model->md_id = $mdId;
  548. $model->goods_count = count($goods);
  549. $model->order_count = count($newOrderList[$mdId]);
  550. if(!$model->save()){
  551. throw new Exception('生成分拣单失败');
  552. }
  553. foreach($newOrderList[$mdId] as $order){
  554. $modelOrder = clone $sortingOrderModel;
  555. $modelOrder->sorting_id = $model->id;
  556. $modelOrder->order_id = $order['id'];
  557. if(!$modelOrder->save()){
  558. throw new Exception('分拣单订单生成失败');
  559. }
  560. }
  561. foreach($goods as $goodsItem){
  562. $modelOrderGoods = clone $sortingOrderGoodsModel;
  563. $modelOrderGoods->sorting_id = $model->id;
  564. $modelOrderGoods->goods_id = $goodsItem['goods_id'];
  565. $modelOrderGoods->goods_name = $goodsItem['goods_name'];
  566. $modelOrderGoods->attr = json_encode($goodsItem['attr_list']);
  567. $modelOrderGoods->num = $goodsItem['num'];
  568. if(!$modelOrderGoods->save()){
  569. throw new Exception('分拣商品错误');
  570. }
  571. }
  572. }
  573. }
  574. }