OrderExpress.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. <?php
  2. namespace common\models\order;
  3. use common\enums\OrderSourceEnum;
  4. use common\enums\OrderStatusEnum;
  5. use common\enums\ShippingStatusEnum;
  6. use common\enums\ShippingTypeEnum;
  7. use common\enums\StatusEnum;
  8. use common\events\order\OrderCancelShipmentEvent;
  9. use common\events\order\OrderDeliveryEvent;
  10. use common\events\order\ShippingValidateEvent;
  11. use Exception;
  12. use Yii;
  13. use yii\db\Expression;
  14. use addons\CloudStock\common\events\order\AgentOrderPaidEvent;
  15. /**
  16. * This is the model class for table "{{%order_express}}".
  17. *
  18. *
  19. * @property int $id
  20. * @property int $order_id 订单id
  21. * @property string|null $order_detail_ids 产品列表
  22. * @property int|null $shipping_type 发货方式1 快递 0其他
  23. * @property int|null $express_id 快递公司id
  24. * @property string|null $express_name 物流公司名称
  25. * @property string|null $express_no 运单编号
  26. * @property int|null $buyer_id 买家id
  27. * @property string|null $buyer_name 买家信息
  28. * @property int|null $operator_id 发货人用户id
  29. * @property string|null $operator_username 发货人用户名
  30. * @property string|null $memo 备注
  31. * @property int|null $status 状态[-1:删除;0:禁用;1启用]
  32. * @property int|null $created_at 创建时间
  33. * @property int|null $updated_at 更新时间
  34. * @property int $cs_order_id 更新时间
  35. * @property array $nums 发货数量
  36. */
  37. class OrderExpress extends \common\models\base\BaseActiveRecord
  38. {
  39. /**
  40. * {@inheritdoc}
  41. */
  42. public static function tableName()
  43. {
  44. return '{{%order_express}}';
  45. }
  46. /**
  47. * {@inheritdoc}
  48. */
  49. public function rules()
  50. {
  51. return [
  52. [['order_id'], 'required'],
  53. [['order_id', 'shipping_type', 'express_id', 'buyer_id', 'operator_id', 'status', 'created_at', 'updated_at', 'cs_order_id'], 'integer'],
  54. [['order_detail_ids', 'nums'], 'safe'],
  55. [['express_name', 'memo', 'express_no'], 'string', 'max' => 255],
  56. [['operator_username'], 'string', 'max' => 50],
  57. [['buyer_name'], 'string', 'max' => 100],
  58. ];
  59. }
  60. /**
  61. * {@inheritdoc}
  62. */
  63. public function attributeLabels()
  64. {
  65. return [
  66. 'id' => 'ID',
  67. 'order_id' => '订单id',
  68. 'order_detail_ids' => '产品列表',
  69. 'shipping_type' => '发货方式1 快递 0其他',
  70. 'express_id' => '快递公司id',
  71. 'express_name' => '物流公司名称',
  72. 'express_no' => '运单编号',
  73. 'buyer_id' => '买家id',
  74. 'buyer_name' => '买家信息',
  75. 'operator_id' => '发货人用户id',
  76. 'operator_username' => '发货人用户名',
  77. 'memo' => '备注',
  78. 'status' => '状态[-1:删除;0:禁用;1启用]',
  79. 'created_at' => '创建时间',
  80. 'updated_at' => '更新时间',
  81. 'nums' => '发货数量',
  82. ];
  83. }
  84. public function beforeSave($insert)
  85. {
  86. if ($this->shipping_type == StatusEnum::DISABLED) {
  87. $this->express_id = 0;
  88. $this->express_name = '';
  89. $this->express_no = '';
  90. }
  91. return parent::beforeSave($insert);
  92. }
  93. /**
  94. * 关联订单详情表
  95. * @return \yii\db\ActiveQuery
  96. * @throws \yii\base\InvalidConfigException
  97. */
  98. public function getOrderDetail()
  99. {
  100. return $this->hasMany(OrderDetail::class, ['id' => 'order_detail_id'])
  101. ->where(['status' => StatusEnum::ENABLED]);
  102. }
  103. /**
  104. * 发货
  105. * @Author bing
  106. * @DateTime 2021-09-16 19:55:46
  107. * @copyright: Copyright (c) 2020 广东七件事集团
  108. * @param [type] $order_id
  109. * @param [type] $params
  110. * @param [type] $trigger_event
  111. * @return void
  112. */
  113. public static function delivery($params,$trigger_event = true){
  114. try{
  115. if (empty($params['cs_order_id'])) {
  116. $order = Order::getOne([['mall_id' => $params['mall_id'], 'id' => $params['id'],'status'=>[StatusEnum::ENABLED]]]);
  117. if(empty($order)) throw new Exception('订单不存在或已删除');
  118. if ($order->order_status == OrderStatusEnum::SHIPMENTS) throw new Exception('订单已经发货');
  119. // 是否允许发货
  120. $shippingValidateEventEvent = new ShippingValidateEvent($params['mall_id']);
  121. $shippingValidateEventEvent->setOrderIds([$params['id']]);
  122. Yii::$app->services->events->dispatch($shippingValidateEventEvent);
  123. ['isAllow' => $isAllow, 'reason' => $reason] = $shippingValidateEventEvent->getIsAllow($params['id']);
  124. if (!$isAllow) {
  125. throw new Exception($reason);
  126. }
  127. $params['order_id'] = $order['id'];
  128. $trans = Yii::$app->db->beginTransaction();
  129. $params['buyer_id'] = $order['user_id'];
  130. $params['buyer_name'] = $order['receiver_name'];
  131. $orderDetailsListByIds = OrderDetail::lists([
  132. 'where' => [
  133. ['in', 'id', $params['order_detail_ids']],
  134. ['=', 'shipping_status', ShippingStatusEnum::UN_SHIPPED],
  135. ['mall_id' => $params['mall_id'], 'status' => StatusEnum::ENABLED, 'order_status' => OrderStatusEnum::PAY],
  136. ],
  137. 'select' => 'id, shipped_num, num, order_status, shipping_status',
  138. 'index' => 'id'
  139. ]);
  140. if (empty($orderDetailsListByIds)) {
  141. throw new Exception('参数错误');
  142. }
  143. $params['order_detail_ids'] = array_column($orderDetailsListByIds, 'id');
  144. $params['send_nums'] = $params['send_nums'] ?? [];
  145. $emptySendNums = true;
  146. if (!empty($params['send_nums'])) {
  147. $emptySendNums = false;
  148. $params['send_nums'] = array_column($params['send_nums'], 'num', 'id');
  149. }
  150. foreach ($params['order_detail_ids'] as $order_detail_id) {
  151. if (!isset($orderDetailsListByIds[$order_detail_id])) {
  152. throw new Exception('参数错误');
  153. }
  154. $orderDetail = &$orderDetailsListByIds[$order_detail_id];
  155. $orderDetail['unshipped_num'] = $orderDetail['num'] - $orderDetail['shipped_num'];
  156. if ($orderDetail['unshipped_num'] <= 0) {
  157. throw new Exception('订单已经发货');
  158. }
  159. if ($emptySendNums) {
  160. $params['send_nums'][$order_detail_id] = $orderDetail['unshipped_num'];
  161. } else {
  162. if (empty($params['send_nums'][$order_detail_id])) {
  163. throw new Exception('参数错误');
  164. }
  165. if ($orderDetail['unshipped_num'] < $params['send_nums'][$order_detail_id]) {
  166. throw new Exception('发货数量大于待发货数量');
  167. }
  168. }
  169. }
  170. } else { // 云库存
  171. }
  172. // YT7478017428791,YT7478018128005,YT7478018445276,YT7478019373473
  173. $express_no = $params['express_no'];
  174. if (strstr($params['express_no'], ',')) {
  175. $express_no_list = explode(",", $express_no);
  176. } else if (strstr($params['express_no'], '、')) {
  177. $express_no_list = explode("、", $express_no);
  178. } else {
  179. $express_no_list = [$express_no];
  180. }
  181. $expressNoCount = count($express_no_list);
  182. foreach (($express_no_list ?? []) as $item) {
  183. if ($expressNoCount === 1) {
  184. // 相同订单 相同物流号合并发货信息
  185. $orderExpress = OrderExpress::findOne([
  186. 'order_id' => $params['order_id'],
  187. 'express_no' => $item,
  188. 'status' => StatusEnum::ENABLED
  189. ]);
  190. // 判断如果是同一运单号,则将之前已经发货的合并。增加原来数据的发货数量。
  191. if (!empty($orderExpress)) {
  192. foreach ($params['order_detail_ids'] as $order_detail_id) {
  193. if (!in_array($order_detail_id, $orderExpress->order_detail_ids)) {
  194. $orderDetailIds = $orderExpress->order_detail_ids;
  195. $orderDetailIds[] = $order_detail_id;
  196. $orderExpress->order_detail_ids = $orderDetailIds;
  197. }
  198. $nums = $orderExpress->nums;
  199. $nums[$order_detail_id] = ($nums[$order_detail_id] ?? 0) + $params['send_nums'][$order_detail_id];
  200. $orderExpress->nums = $nums;
  201. }
  202. $orderExpress->save();
  203. continue;
  204. }
  205. } else {
  206. // 每个订单里面只能使用同一个快递单号一次
  207. if (!empty(OrderExpressLog::findOne(['order_id' => $params['order_id'], 'express_no' => $item]))) continue;
  208. }
  209. $model = new OrderExpress();
  210. $params['operator_username'] = $params['operator_name'];
  211. $params['express_no'] = $item;
  212. $params['nums'] = $params['send_nums'];
  213. if(!$model->load($params,'') || !$model->save()) throw new \Exception($model->getErrorMessage());
  214. //保存物流轨迹记录
  215. $params['order_express_id'] = $model->id;
  216. $res = OrderExpressLog::saveLog($params);
  217. if ($res == false) throw new Exception(OrderExpressLog::getStaticError());
  218. }
  219. if (empty($params['cs_order_id'])) {
  220. $condition['where'] = [['in', 'id', $params['order_detail_ids']], ['is_feedback' => [OrderStatusEnum::FEEDBACKING, OrderStatusEnum::FEEDBACKED]]];
  221. $res = OrderDetail::lists($condition);
  222. if (!empty($res)) throw new \Exception('订单申请售后中,请先处理售后问题');
  223. // 扣待发货数量
  224. foreach ($params['order_detail_ids'] as $order_detail_id) {
  225. $updateData = [
  226. 'shipped_num' => new Expression('shipped_num + :num', [':num' => $params['send_nums'][$order_detail_id]])
  227. ];
  228. // 订单商品的物流状态改变 如果全部发完则改为已发货
  229. if ($orderDetailsListByIds[$order_detail_id]['unshipped_num'] - $params['send_nums'][$order_detail_id] <= 0) {
  230. $updateData['order_status'] = OrderStatusEnum::SHIPMENTS;
  231. $updateData['shipping_status'] = StatusEnum::ENABLED;
  232. }
  233. OrderDetail::updateAll($updateData, ['=', 'id', $order_detail_id]);
  234. }
  235. // 自动维护订单状态
  236. $res = Order::autoUpdateStatus($order);
  237. if ($res === false) throw new \Exception(Order::getStaticError());
  238. if ($order->order_source === OrderSourceEnum::SOURCE_RESERVE) { // 如果为预约订单那么就需要处理
  239. // 执行发货
  240. $order->shipping_status = StatusEnum::ENABLED; // 全部发货状态改为已发货
  241. // 如果是技师,那么就为已支付
  242. $order->order_status = OrderStatusEnum::SHIPMENTS;
  243. $order->consign_time = time();
  244. $order->save();
  245. OrderReserve::syncOrderStatus($order, 0);
  246. }
  247. $trans->commit();
  248. if ($trigger_event) {
  249. // 发货成功触发事件,事务提交完成后触发
  250. $event = new OrderDeliveryEvent($params['mall_id']);
  251. $data['id'] = $model->id;
  252. $data['operator_id'] = $params['operator_id'];
  253. $data['operator_name'] = $params['operator_name'];
  254. $data['is_admin'] = (int)!empty($params['is_admin']);
  255. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  256. }
  257. }else{
  258. // 发货成功触发事件,事务提交完成后触发
  259. $event = new AgentOrderPaidEvent($params['mall_id']);
  260. $data['id'] = $params['cs_order_id'];;
  261. $data['operator_id'] = $params['operator_id'];
  262. $data['operator_name'] = $params['operator_name'];
  263. $data['is_admin'] = (int)!empty($params['is_admin']);
  264. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  265. }
  266. return $model;
  267. }catch(Exception $e){
  268. if(isset($trans)) $trans->rollback();
  269. self::$static_error = $e->getMessage();
  270. return false;
  271. }
  272. }
  273. /**
  274. * 修改物流
  275. * @Author lun
  276. * @DateTime 2021-09-24 19:55:46
  277. * @copyright: Copyright (c) 2020 广东七件事集团
  278. * @param [type] $params
  279. * @return void
  280. */
  281. public static function shippingChange($params){
  282. try{
  283. $order_express = OrderExpress::findOne(['id' => $params['id']]);
  284. if (empty($order_express)) throw new Exception('物流不存在或已删除');
  285. $trans = Yii::$app->db->beginTransaction();
  286. if(!$order_express->load($params,'') || !$order_express->save()) throw new \Exception($order_express->getErrorMessage());
  287. OrderExpressLog::saveLog($params,true);
  288. $trans->commit();
  289. // 发货成功触发事件,事务提交完成后触发
  290. $event = new OrderDeliveryEvent($params['mall_id']);
  291. $data['id'] = $params['id'];
  292. $data['operator_id'] = $params['operator_id'];
  293. $data['operator_name'] = $params['operator_name'];
  294. $data['shipping_change'] = 1;
  295. $data['is_admin'] = (int)!empty($params['is_admin']);
  296. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  297. return true;
  298. }catch(Exception $e){
  299. if(isset($trans)) $trans->rollback();
  300. self::$static_error = $e->getMessage();
  301. return false;
  302. }
  303. }
  304. /**
  305. * 获取订单物流
  306. * @Author bing
  307. * @DateTime 2021-08-26 13:00:11
  308. * @copyright: Copyright (c) 2020 广东七件事集团
  309. * @param array $cond
  310. * @param array $with
  311. * @param integer $is_array
  312. * @return array|null
  313. */
  314. public static function getOrderExpresses(array $cond=[],array $with=[],bool $is_array=true,string $select='*',$index=''){
  315. $default_cond = [['<>','status',StatusEnum::DELETE]];
  316. $cond = array_merge($default_cond,$cond);
  317. $query = self::find()->select($select);
  318. self::paramPack(['where'=>$cond,'with'=>$with],$query);
  319. if(!empty($index)) $query->indexBy($index);
  320. $labels = $query->asArray($is_array)->all();
  321. return $labels;
  322. }
  323. /**
  324. * @notes:取消发货删除物流信息
  325. * @param $order_id
  326. * @return bool
  327. * @author: lun
  328. * @Time: 2022/10/27 10:33
  329. */
  330. public static function cancelExpress($params, $isDispatchEvent = false)
  331. {
  332. $trans = Yii::$app->db->beginTransaction();
  333. try {
  334. $express = self::findOne(['id' => $params['id'],'order_id' => $params['order_id'], 'status' => StatusEnum::ENABLED]);
  335. if (!empty($express)) {
  336. $express->status = StatusEnum::DELETE;
  337. if (!$express->save()) throw new \yii\db\Exception($express->getErrorMessage());
  338. // 物流轨迹修改
  339. $express_log = OrderExpressLog::findOne(['order_express_id' => $express->id]);
  340. if (!empty($express_log)) {
  341. $express_log->status = StatusEnum::DELETE;
  342. if (!$express_log->save()) throw new \yii\db\Exception($express_log->getErrorMessage());
  343. }
  344. // 修改订单状态
  345. $res = Order::cancelShipment([
  346. 'order_id' => $express->order_id,
  347. 'order_detail_ids' => $express->order_detail_ids,
  348. 'nums' => $express->nums ?? [],
  349. ]);
  350. if ($res === false) throw new Exception(Order::getStaticError());
  351. }
  352. $trans->commit();
  353. // 取消发货事件
  354. if ($isDispatchEvent) {
  355. $event = new OrderCancelShipmentEvent($params['mall_id']);
  356. $data['order_id'] = $params['order_id'];
  357. $data['id'] = $params['id'];
  358. $data['operator_id'] = $params['operator_id'];
  359. $data['operator_name'] = $params['operator_name'];
  360. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  361. }
  362. return true;
  363. } catch (Exception $e) {
  364. $trans->rollBack();
  365. self::setStaticError($e->getMessage());
  366. return false;
  367. }
  368. }
  369. public static function setData($params)
  370. {
  371. try {
  372. if (!empty($params['id'])) {
  373. $query = self::find()->where(['id' => $params['id']])->andWhere(['<>', 'status', StatusEnum::DELETE]);
  374. $model = $query->one();
  375. if (empty($model)) {
  376. throw new \Exception('数据不存在');
  377. }
  378. } else {
  379. $model = new self();
  380. $model->loadDefaultValues();
  381. }
  382. if (!$model->load($params, '') || !$model->save()) {
  383. throw new \Exception($model->getErrorMessage());
  384. }
  385. return $model;
  386. } catch (\Exception $e) {
  387. self::$static_error = $e->getMessage();
  388. return false;
  389. }
  390. }
  391. }