ReserveOrderController.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. <?php
  2. namespace api\modules\v1\controllers\order;
  3. use addons\ReservationService\common\enums\ReservationServiceEnum;
  4. use addons\ReservationService\common\models\ReservationServiceOrder;
  5. use addons\ReservationService\common\models\ReservationServiceUser;
  6. use addons\ReservationService\common\services\OrderService;
  7. use addons\ReservationService\common\services\TechniciansService;
  8. use addons\SecondaryCard\common\service\SecondaryCardOrderService;
  9. use addons\Store\common\models\Store;
  10. use addons\Store\common\models\StoreGoodsReserve;
  11. use addons\Store\common\models\StoreGoodsReserveSku;
  12. use addons\Store\common\models\StoreOrder;
  13. use addons\Store\common\models\StoreOrderReserve;
  14. use addons\Store\common\models\StoreReserveSetting;
  15. use addons\Store\common\services\StoreReserveSettingService;
  16. use api\controllers\OnAuthController;
  17. use common\enums\AddonsEnum;
  18. use common\enums\ApiStatusEnum;
  19. use common\enums\ClerkEnum;
  20. use common\enums\GlobalFuncEnum;
  21. use common\enums\MarketingTypeEnum;
  22. use common\enums\OrderSourceEnum;
  23. use common\enums\OrderStatusEnum;
  24. use common\enums\PayTypeEnum;
  25. use common\enums\RefundReasonEnum;
  26. use common\enums\RefundTypeEnum;
  27. use common\enums\ShippingTypeEnum;
  28. use common\enums\StatusEnum;
  29. use common\globals\GlobalInvoke;
  30. use common\helpers\DateHelper;
  31. use common\models\goods\Goods;
  32. use common\models\goods\GoodsAttr;
  33. use common\models\goods\GoodsReserve;
  34. use common\models\goods\GoodsReserveSku;
  35. use common\models\mall\MallAddons;
  36. use common\models\order\Order;
  37. use common\models\order\OrderDetail;
  38. use common\models\order\OrderMarketing;
  39. use common\models\order\OrderReserve;
  40. use Yii;
  41. use yii\helpers\Json;
  42. /**
  43. * Class OrderController
  44. * @package api\modules\order\controllers
  45. */
  46. class ReserveOrderController extends OnAuthController
  47. {
  48. public $modelClass = '';
  49. /**
  50. * 不用进行登录验证的方法
  51. *
  52. * 例如: ['index', 'update', 'create', 'view', 'delete']
  53. * 默认全部需要验证
  54. *
  55. * @var array
  56. */
  57. protected $authOptional = [];
  58. public function actionList()
  59. {
  60. $params = \Yii::$app->request->get();
  61. $where[] = ['mall_id' => $this->mall_id];
  62. $where[] = ['user_id' => $this->user_id];
  63. $where[] = ['status' => StatusEnum::ENABLED];
  64. // $where[] = ['is_feedback' => OrderStatusEnum::NOT_FEEDBACK];
  65. $where[] = ['order_source' => [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE]];
  66. if (isset($params['order_status']) && $params['order_status'] != 999) {
  67. if ($params['order_status'] == OrderStatusEnum::SING) { // 如果筛选的是已收货的订单那么就将已提货的订单一起取出来
  68. $params['order_status'] = [OrderStatusEnum::SING, OrderStatusEnum::TAKE_FINISH];
  69. }
  70. $where[] = ['order_status' => $params['order_status']];
  71. if ($params['order_status'] == OrderStatusEnum::ACCOMPLISH) $where[] = ['is_comment' => StatusEnum::DISABLED];
  72. }
  73. if (!empty($params['keyword'])) {
  74. $goods_list = Goods::lists(['where' => [
  75. ['mall_id' => $this->mall_id],
  76. ['like', 'goods_name', trim($params['keyword']) . '%', false]
  77. ], 'select' => 'id']);
  78. $goods_ids = array_column($goods_list, 'id');
  79. $order_detail_list = OrderDetail::lists(['where' => [
  80. ['mall_id' => $this->mall_id],
  81. ['in', 'goods_id', $goods_ids]
  82. ], 'select' => 'order_id']);
  83. $order_ids = array_column($order_detail_list, 'order_id');
  84. $where[] = [
  85. 'or',
  86. ['id' => $order_ids],
  87. ];
  88. }
  89. $params['order'] = 'id desc';
  90. if (isset($params['order_status']) && $params['order_status'] != 999) {
  91. if ($params['order_status'] == OrderStatusEnum::SING) {
  92. $where[] = ['order_status' => [OrderStatusEnum::SING, OrderStatusEnum::TAKE_FINISH]];
  93. } else {
  94. $where[] = ['order_status' => $params['order_status']];
  95. }
  96. }
  97. if (!empty($params['keyword'])) {
  98. $goods_list = Goods::lists(['where' => [
  99. ['mall_id' => $this->mall_id],
  100. ['like', 'goods_name', trim($params['keyword']) . '%', false]
  101. ], 'select' => 'id']);
  102. $goods_ids = array_column($goods_list, 'id');
  103. $order_detail_list = OrderDetail::lists(['where' => [
  104. ['mall_id' => $this->mall_id],
  105. ['in', 'goods_id', $goods_ids]
  106. ], 'select' => 'order_id']);
  107. $order_ids = array_column($order_detail_list, 'order_id');
  108. $where[] = [
  109. 'or',
  110. ['id' => $order_ids],
  111. ['like', 'order_no', trim($params['keyword']) . '%', false]
  112. ];
  113. }
  114. // is_comment 是否评价 0为未评价 1为已评价 2为已追评
  115. $select = 'id,pay_money,score,order_status,shipping_type,is_recycle,marketing_type,extra_info,order_source,store_id,mch_id,is_comment,marketing_id,is_feedback';
  116. $params['order'] = 'id desc';
  117. $params['select'] = $select;
  118. $params['where'] = $where;
  119. $params['with'] = [
  120. 'detail' => function ($query) {
  121. $query->select('id,order_id,goods_id,goods_name,pic_url,goods_attr_id,num,price,is_feedback,sign_id,goods_attr_name,extra_info,adjust_money');
  122. },
  123. 'reserve' => function ($query) {
  124. $query->select('id,order_id,reserve_date_time,province_id,city_id,district_id,detail,lat,lng,region_name,reserve_status,reserve_code,service_type,reserve_type,tech_id');
  125. },
  126. ];
  127. $order_list = Order::listPage($params);
  128. $order_list['mall_name'] = $this->mall['name'];
  129. $order_list['mall_logo'] = Yii::$app->services->setting->mall->get($this->mall_id, 'basic.logo');
  130. $score_name = Yii::$app->services->setting->mall->get($this->mall_id, 'score.score_name');
  131. if (!empty($order_list['list'])) {
  132. $store_reserve_setting_list = [];
  133. if (MallAddons::isInstall($this->mall_id, 'Store')) {
  134. $store_ids = array_column($order_list['list'], 'store_id');
  135. $store_reserve_setting_list = (new StoreReserveSettingService(['mall_id' => $this->mall_id]))->list($store_ids);
  136. }
  137. $fields = 'id,main_order_id as order_id,reserve_date_time,province_id,city_id,district_id,detail,lat,lng,region_name,reserve_status,reserve_code,service_type,reserve_type';
  138. foreach ($order_list['list'] as $key => $val) {
  139. array_map(function ($arg) use (&$val) {
  140. if (!isset($val['num'])) $val['num'] = 0;
  141. $val['num'] += $arg['num'];
  142. }, $val['detail']);
  143. // 剩余库存
  144. $stock = [];
  145. if ($val['detail']) {
  146. foreach ($val['detail'] as $k => $v) {
  147. $order_list['list'][$key]['detail'][$k]['price'] = $order_list['list'][$key]['detail'][$k]['price'] + $order_list['list'][$key]['detail'][$k]['adjust_money'];
  148. $order_list['list'][$key]['detail'][$k]['price'] = round($order_list['list'][$key]['detail'][$k]['price'], 2);
  149. $goods_unit = Goods::getOne([['=', 'id', $v['goods_id']]], false, '', '', 'unit')['unit'];
  150. $val['detail'][$k]['unit'] = $goods_unit;
  151. // 当订单关闭时可以直接再次购买
  152. if ($val['order_status'] == OrderStatusEnum::REPEAL) {
  153. // 查询商品是否有货
  154. $stock[] = [
  155. 'num' => $v['num'],
  156. 'store_id' => $v['store_id'] ?? 0,
  157. 'goods_id' => $v['goods_id'],
  158. 'attr_id' => $v['goods_attr_id'],
  159. 'stock' => GoodsAttr::find()->where(['id' => $v['goods_attr_id']])->select('stock')->scalar(),
  160. ];
  161. }
  162. }
  163. }
  164. // 获取支付金额
  165. $order_list['list'][$key] = GlobalInvoke::exec(GlobalInvoke::ORDER_DETAIL, $this->mall_id, [
  166. 'order' => $val,
  167. 'mall_id' => $this->mall_id,
  168. ]) ?: $val;
  169. if (!empty($val['store_id'])) {
  170. $order_list['list'][$key]['reserve'] = StoreOrderReserve::getOne([['main_order_id' => $val['id']]], true, [], 'id asc', $fields);
  171. }
  172. $order_list['list'][$key]['reserve']['employee_selection'] = 1;
  173. $order_list['list'][$key]['reserve']['user_acceptance'] = 0;
  174. if (isset($store_reserve_setting_list[$val['store_id']])) {
  175. $temp_setting = $store_reserve_setting_list[$val['store_id']];
  176. $order_list['list'][$key]['reserve']['employee_selection'] = $temp_setting['employee_selection'] ?? 1;
  177. $order_list['list'][$key]['reserve']['user_acceptance'] = $temp_setting['user_acceptance'] ?? 0;
  178. }
  179. $reservation_service_order = ReservationServiceOrder::find()->where(['order_id' => $val['id']])->orderBy('id desc')->one();
  180. $order_list['list'][$key]['reserve']['reject_reason'] = $reservation_service_order['reject_reason'] ?? '';
  181. $order_list['list'][$key]['reserve']['remark'] = $reservation_service_order['remark'] ?? '';
  182. $order_list['list'][$key]['reserve']['service_status'] = $reservation_service_order['service_status'] ?? '';
  183. $order_list['list'][$key]['reserve']['server_status'] = $reservation_service_order['server_status'] ?? '';
  184. $order_list['list'][$key]['mall_name'] = $this->mall['name'];
  185. $order_list['list'][$key]['mall_logo'] = Yii::$app->services->setting->mall->get($this->mall_id, 'basic.logo');
  186. if (!empty($order_list['list'][$key]['store_id'])) {
  187. $store = Store::findOne(['id' => $order_list['list'][$key]['store_id']]);
  188. $order_list['list'][$key]['mall_name'] = $store->store_name;
  189. $order_list['list'][$key]['mall_logo'] = $store->logo_img;
  190. }
  191. $order_list['list'][$key]['service_type_text'] = $order_list['list'][$key]['reserve']['service_type'] == 1 ? '上门服务' : '到店服务';
  192. $order_list['list'][$key]['reserve_date_time'] = !empty($order_list['list'][$key]['reserve']['reserve_date_time']) ? str_replace(',', '~', $order_list['list'][$key]['reserve']['reserve_date_time']) : "";
  193. // 判断是否可以再次购买
  194. $is_agent = empty($stock) ? 0 : 1;
  195. foreach ($stock as $v) {
  196. // 当库存小于购买数量 不可再次购买
  197. if ($v['stock'] < $v['num']) {
  198. $is_agent = 0;
  199. }
  200. }
  201. $order_list['list'][$key]['is_agent'] = $is_agent;
  202. $order_list['list'][$key]['is_agent_data'] = $is_agent ? $stock : [];
  203. if ($val['marketing_type'] == 'ScoreMall' && !empty($val['score'])) $order_list['list'][$key]['pay_money'] .= '+' . $val['score'] . $score_name;
  204. }
  205. //次卡部分----start
  206. foreach ($order_list['list'] as &$item) {
  207. $item['secondary_card'] = [];
  208. $item['is_secondary_card'] = 0;
  209. }
  210. //次卡部分----end
  211. $order_list['list'] = GlobalInvoke::exec(GlobalFuncEnum::ORDER_LIST, $this->mall_id, [
  212. 'order' => $order_list['list'], 'mall_id' => $this->mall_id, 'reserve' => true
  213. ]) ?: $order_list['list'];
  214. $order_list['shipping_type_text'] = ShippingTypeEnum::getMap();
  215. $order_list['order_status_text'] = OrderStatusEnum::getReserveMap();
  216. $order_list['service_status_text'] = ReservationServiceEnum::RESERVE_ORDER_STATUS_MAP;
  217. $order_list['server_status_text'] = ReservationServiceEnum::SERVER_STATUS_MAP;
  218. $order_list['is_feedback_text'] = OrderStatusEnum::getFeedbackMap();
  219. }
  220. $order_list['pay_type_list'] = \Yii::$app->services->pay->getEnablePayment($this->mall_id, $this->platform);
  221. $order_list['marketing_type_map'] = MarketingTypeEnum::getMap();
  222. $tabsIndex = [999, 0, 1, 2, 3, 4];
  223. foreach ($tabsIndex as $index) {
  224. $badgeQuery = Order::find()
  225. ->where(['mall_id' => $this->mall_id, 'user_id' => $this->user_id,
  226. 'is_feedback' => StatusEnum::DISABLED, 'status' => StatusEnum::ENABLED,
  227. 'order_source' => [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE]]);
  228. $num = StatusEnum::DISABLED;
  229. switch ($index) {
  230. case 0: // 待支付
  231. $num = $badgeQuery->andWhere(['order_status' => StatusEnum::DISABLED])->andWhere(['>=', 'created_at', time() - 86400])->count();
  232. break;
  233. case 1: // 已付款
  234. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::PAY])->andWhere(['>=', 'pay_time', time() - 86400])->count();
  235. break;
  236. case 2: // 待服务
  237. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::SHIPMENTS])->andWhere(['>=', 'shipping_time', time() - 86400])->count();
  238. break;
  239. case 3: // 待验收
  240. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::SING])->andWhere(['>=', 'sign_time', time() - 86400])->count();
  241. break;
  242. case 4: // 待评价
  243. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::ACCOMPLISH])
  244. ->andWhere(['is_comment' => StatusEnum::DISABLED])->andWhere(['>=', 'finish_time', time() - 86400])->count();
  245. break;
  246. }
  247. $order_list['tab_badge'][$index] = (int) $num;
  248. }
  249. // dd($order_list['tab_badge']);
  250. return $this->success('操作成功', $order_list, ApiStatusEnum::CODE_SUCCESS, JSON_BIGINT_AS_STRING);
  251. }
  252. /**
  253. * 订单详情
  254. * @return mixed|void
  255. */
  256. public function actionDetail()
  257. {
  258. $params = \Yii::$app->request->get();
  259. $res = \Yii::$app->services->validate->validate($params, [
  260. [['order_id'], 'required'],
  261. ]);
  262. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  263. $select = 'id,mall_id,store_id,order_no,receiver_name,mobile,pay_money,shipping_money,original_goods_price,coupon_money,goods_price,order_status,pay_status,
  264. shipping_status,review_status,is_feedback,address,created_at,pay_time,region_name,remark,consign_time,finish_time,sign_time,shipping_type,payment_type,
  265. score,score_money,marketing_id,marketing_type,extra_info,order_source,is_comment';
  266. $where = [];
  267. $where[] = ['mall_id' => $this->mall_id];
  268. $where[] = ['user_id' => $this->user_id];
  269. $where[] = ['id' => $params['order_id']];
  270. $order = Order::getOne($where, true, [
  271. 'detail' => function ($query) {
  272. $query->select('id,order_id,goods_id,goods_name,pic_url,num,price,is_feedback,sign_id,goods_attr_name,order_status,adjust_money,extra_info,goods_attr_id,goods_attr_name');
  273. },
  274. 'reserve' => function ($query) {
  275. $query->select('id,order_id,reserve_date_time,province_id,city_id,district_id,detail,lat,lng,region_name,reserve_status,reserve_code,reserve_type,service_type');
  276. },
  277. ], false, $select);
  278. if (empty($order)) return $this->error('该订单不存在', []);
  279. if (empty($order['reserve']) && !empty($order['store_id'])) { // 门店订单
  280. $order['reserve'] = StoreOrderReserve::getOne([['main_order_id' => $order['id']]], true, [], 'id asc',
  281. 'id,main_order_id as order_id,reserve_date_time,province_id,city_id,district_id,detail,lat,lng,region_name,reserve_status,reserve_code,reserve_type,service_type');
  282. }
  283. $marketing = OrderMarketing::getMarketingInfo($order);
  284. $order['order_status_text'] = OrderStatusEnum::getReserveMap();
  285. $order['is_feedback_text'] = OrderStatusEnum::getFeedbackMap();
  286. $order['shipping_type_text'] = ShippingTypeEnum::getMap();
  287. $order['payment_type_text'] = PayTypeEnum::getMap();
  288. $order['pay_type_list'] = \Yii::$app->services->pay->getEnablePayment($this->mall_id, $this->platform);
  289. $order['change_price'] = array_sum(array_column($order['detail'], 'adjust_money')); //预留字段
  290. $order['marketing'] = $marketing;
  291. $order['marketing_type_map'] = MarketingTypeEnum::getMap();
  292. $order_setting = Yii::$app->services->setting->mall->get($this->mall_id, 'order');
  293. $order['payment_expire_time'] = $order_setting['payment_expire_time'] ?? 30;
  294. $order['auto_receiving_time'] = $order_setting['auto_receiving_time'] ?? 14;
  295. //查询商品单位
  296. if ($order['detail']) {
  297. foreach ($order['detail'] as $k => $v) {
  298. $goods_unit = Goods::getOne([['=', 'id', $v['goods_id']]], false, '', '', 'unit')['unit'];
  299. $order['detail'][$k]['unit'] = $goods_unit;
  300. }
  301. }
  302. // 获取订单详情的状态文字描述
  303. $order['status_text'] = OrderStatusEnum::getApiStatus($order['status'], $order['shipping_type']);
  304. $order['status'] == OrderStatusEnum::NOT_PAY && $order['status_text']['desc'] = $order['payment_expire_time'] . $order['status_text']['desc'];
  305. $order['status'] == OrderStatusEnum::SHIPMENTS && $order['status_text']['desc'] = $order['auto_receiving_time'] . $order['status_text']['desc'];
  306. $order['mall_name'] = $this->mall['name'];
  307. $order['mall_logo'] = Yii::$app->services->setting->mall->get($this->mall_id, 'basic.logo');
  308. if (!empty($order['store_id'])) {
  309. $store = Store::findOne(['id' => $order['store_id']]);
  310. $order['mall_name'] = $store->store_name;
  311. $order['mall_logo'] = $store->logo_img;
  312. }
  313. $order['service_type_text'] = $order['reserve']['service_type'] == 1 ? '上门服务' : '到店服务';
  314. $order['reserve_date_time'] = !empty($order['reserve']['reserve_date_time']) ? str_replace(',', '~', $order['reserve']['reserve_date_time']) : "";
  315. $order['tech'] = null;
  316. if ($order['reserve']['reserve_type'] == StatusEnum::ENABLED) { // 预约订单
  317. $order['tech'] = (new OrderService())->getStaffByOrderId($params['order_id'], $order['mall_id']);
  318. }
  319. // 公共调用
  320. $order['is_secondary_card'] = 0;
  321. $order = GlobalInvoke::exec(GlobalInvoke::ORDER_DETAIL, $this->mall_id, [
  322. 'order' => $order,
  323. 'mall_id' => $this->mall_id,
  324. 'reserve' => true
  325. ]) ?: $order;
  326. $order['employee_selection'] = 1;
  327. if (MallAddons::isInstall($this->mall_id, 'Store') && !empty($order['store_id'])) {
  328. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $order['store_id']]))->detail();
  329. $order['reserve']['employee_selection'] = $store_reserve_setting['employee_selection'] ?? 1;
  330. $order['reserve']['user_acceptance'] = $store_reserve_setting['user_acceptance'] ?? 0;
  331. }
  332. return $this->success('操作成功', $order, ApiStatusEnum::CODE_SUCCESS, JSON_BIGINT_AS_STRING);
  333. }
  334. /**
  335. * 获取预约码链接
  336. * @return mixed|void
  337. */
  338. public function actionGetReserveCodeLink()
  339. {
  340. $params = \Yii::$app->request->get();
  341. $res = \Yii::$app->services->validate->validate($params, [
  342. [['order_id'], 'required'],
  343. ]);
  344. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  345. $reserveOrder = OrderReserve::findOne(['order_id' => $params['order_id']]);
  346. if (empty($reserveOrder)) return $this->error('订单未找到');
  347. if (empty($reserveOrder->reserve_code)) return $this->error('订单未生成核销码');
  348. $link = '/v1/order/reserve-order/write-off?code=' . $reserveOrder->reserve_code;
  349. return $this->success('获取成功', ['path' => $link]);
  350. }
  351. /**
  352. * 核销
  353. * @return mixed|void
  354. */
  355. public function actionWriteOff()
  356. {
  357. $params = \Yii::$app->request->get();
  358. $res = \Yii::$app->services->validate->validate($params, [
  359. [['code'], 'required'],
  360. ]);
  361. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  362. $reserveOrder = OrderReserve::findOne(['reserve_code' => $params['code']]);
  363. if (empty($reserveOrder)) return $this->error('订单未找到');
  364. if ($reserveOrder->reserve_status != OrderReserve::RESERVE_STATUS_WAIT) return $this->error('该订单已核销过,请勿重复操作');
  365. $data['id'] = $reserveOrder->order_id;
  366. $data['mall_id'] = $this->mall_id;
  367. $data['user_id'] = $this->user_id;
  368. $data['operator_id'] = $this->user_id;
  369. $data['operator_name'] = $this->user->nickname ?? $this->user->mobile;
  370. $data['from'] = OrderSourceEnum::SOURCE_RESERVE;
  371. $res = Order::takeDelivery($data);
  372. if ($res === false) return $this->error(Goods::getStaticError());
  373. return $this->success();
  374. }
  375. /**
  376. * 验收
  377. * @return mixed|void
  378. */
  379. public function actionAcceptance()
  380. {
  381. $params = \Yii::$app->request->get();
  382. $res = \Yii::$app->services->validate->validate($params, [
  383. [['order_id'], 'required'],
  384. ]);
  385. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  386. $reserveOrder = OrderReserve::find()->where(['order_id' => $params['order_id']])->orderBy('id desc')->one();
  387. if (empty($reserveOrder)) {
  388. $reserveOrder = StoreOrderReserve::find()->where(['main_order_id' => $params['order_id']])->orderBy('id desc')->one();
  389. if (empty($reserveOrder)) {
  390. return $this->error('订单未找到');
  391. }
  392. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $reserveOrder['store_id']]))->detail();
  393. if (isset($store_reserve_setting['user_acceptance']) && empty($store_reserve_setting['user_acceptance'])) {
  394. return $this->error('该门店未开启用户验收');
  395. }
  396. } else {
  397. $reservation_service_order = ReservationServiceOrder::find()->where(['order_id' => $params['order_id']])->orderBy('id desc')->one();
  398. if (!empty($reservation_service_order['store_id'])) {
  399. $store_reserve_setting = (new StoreReserveSettingService(['mall_id' => $this->mall_id, 'store_id' => $reservation_service_order['store_id']]))->detail();
  400. if (isset($store_reserve_setting['user_acceptance']) && empty($store_reserve_setting['user_acceptance'])) {
  401. return $this->error('该门店未开启用户验收');
  402. }
  403. }
  404. }
  405. if ($reserveOrder->reserve_status != OrderReserve::RESERVE_STATUS_PENDDING) return $this->error('该订单还未核销,请先进行核销');
  406. if ($reserveOrder->reserve_status == OrderReserve::RESERVE_STATUS_FINISH) return $this->error('该订单已验收,请勿重复操作');
  407. $data['id'] = $params['order_id'];
  408. $data['mall_id'] = $this->mall_id;
  409. $data['user_id'] = $this->user_id;
  410. $data['operator_id'] = $this->user_id;
  411. $data['operator_name'] = $this->user->nickname ?? $this->user->mobile;
  412. $data['from'] = OrderSourceEnum::SOURCE_RESERVE;
  413. $res = Order::completes($data);
  414. if ($res === false) return $this->error(Goods::getStaticError());
  415. return $this->success();
  416. }
  417. /**
  418. * 预约订单售后选项
  419. * @return mixed|void
  420. */
  421. public function actionToRefundSubmit()
  422. {
  423. $params = \Yii::$app->request->get();
  424. $res = \Yii::$app->services->validate->validate($params, [
  425. [['order_detail_id'], 'required'],
  426. ]);
  427. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  428. $select = 'id,order_id,goods_name,sign_id,goods_attr_name,price,num,pic_url,goods_price,order_status,marketing_type,extra_info,adjust_money,store_id';
  429. $order_detail = OrderDetail::getOne([['id' => $params['order_detail_id']], 'user_id' => $this->user_id, 'mall_id' => $this->mall_id], 1, [], false, $select);
  430. if (empty($order_detail)) return $this->error('订单不存在');
  431. $order = Order::find()->select("extra_info,score,shipping_money,out_trade_no")->where(['id' => $order_detail['order_id']])->asArray()->one();
  432. if ($order_detail['marketing_type'] == AddonsEnum::ADDONS_NAME_SCORE_MALL) {
  433. $order_detail['extra_info'] = $order['extra_info'];
  434. $order_detail['pay_score'] = $order['score'];
  435. }
  436. // 查询预约商品订单
  437. if (empty($order_detail['store_id'])) {
  438. $reserveOrder = OrderReserve::findOne(['order_id' => $order_detail['order_id']]);
  439. } else {
  440. $reserveOrder = StoreOrderReserve::findOne(['main_order_id' => $order_detail['order_id']]);
  441. }
  442. $cancelRule = !empty($reserveOrder->cancel_rule) ? json_decode($reserveOrder->cancel_rule, true) : [];
  443. $startTime = strtotime(explode(',', $reserveOrder->reserve_date_time)[0]);
  444. $cancelFeePercent = 0;
  445. switch ($reserveOrder->cancel_type) {
  446. case OrderReserve::CANCEL_TYPE_NOT:
  447. // 不允许售后
  448. return $this->error('此订单不能申请售后');
  449. break;
  450. case OrderReserve::CANCEL_TYPE_TIME:
  451. // 提前N小时允许售后
  452. $cancelSecond = ($cancelRule['cancel_hour'] ?? 0) * 3600;
  453. if (!empty($startTime) && (time() + $cancelSecond) >= $startTime) {
  454. return $this->error(date('Y-m-d H:i:s',$startTime - $cancelSecond).' 前才可以申请售后');
  455. }
  456. break;
  457. case OrderReserve::CANCEL_TYPE_RATE:
  458. $cancelFeePercent = $cancelRule['cancel_fee_percent'];
  459. break;
  460. }
  461. // 公共调用
  462. $order_detail = GlobalInvoke::exec(GlobalInvoke::ORDER_REFUND_SUBMIT, $this->mall_id, [
  463. 'order_detail' => $order_detail,
  464. 'mall_id' => $this->mall_id,
  465. ]) ?: $order_detail;
  466. //价格等于商品价格加上修改价格
  467. $order_detail['goods_price'] = round($order_detail['goods_price'] + $order_detail['adjust_money'], 2);
  468. $order_detail['cancel_fee_percent'] = $cancelFeePercent;
  469. $data['goods_info'] = $order_detail;
  470. $data['feedback_reason'] = RefundReasonEnum::getMap();
  471. if (MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_RESERVATION_SERVICE)) {
  472. $tags = \Yii::$app->services->setting->addons->get($this->mall_id, ReservationServiceEnum::ADDONS_NAME, 'basic.refund_tags');
  473. $data['feedback_reason'] = !empty($tags) ? Json::decode((string)$tags) : $data['feedback_reason'];
  474. }
  475. $feedback_type = RefundTypeEnum::getArr();
  476. $data['feedback_type'] = [];
  477. $data['feedback_type'][] = $feedback_type[RefundTypeEnum::MONEY];
  478. $data['order_status'] = $order_detail['order_status'];
  479. return $this->success('操作成功', $data);
  480. }
  481. /**
  482. * 预约
  483. * @return mixed|null
  484. */
  485. public function actionReserve()
  486. {
  487. $params = \Yii::$app->request->post();
  488. $res = \Yii::$app->services->validate->validate($params, [
  489. [['order_id', 'select_time'], 'required'],
  490. [['secondary_card_no', 'latitude', 'longitude', 'store_id', 'tech_id', 'service_type'], 'safe'],
  491. ]);
  492. if ($res === false) return $this->error(\Yii::$app->services->validate->getErrorMessage());
  493. if (empty($params['tech_id'])) {
  494. $params['tech_id'] = 0;
  495. }
  496. try {
  497. if(!empty($params['secondary_card_no'])){
  498. if(!MallAddons::isInstall($this->mall_id,'SecondaryCard')){
  499. if (empty($order)) throw new \Exception('次卡插件未安装!');
  500. }
  501. $params['mall_id'] = $this->mall_id;
  502. $params['user_id'] = $this->user_id;
  503. $res = $this->secondaryCardReserve($params);
  504. if(is_string($res)) return $this->error($res);
  505. return $this->success('提交成功');
  506. }
  507. $order = Order::findOne(['id' => $params['order_id'], 'user_id' => $this->user_id]);
  508. if (empty($order)) throw new \Exception('订单不存在!');
  509. if (empty($order->store_id)) { // 平台订单
  510. if (empty($params['tech_id'])) {
  511. return $this->error('请选择技师');
  512. }
  513. $reserveOrder = OrderReserve::findOne(['order_id' => $params['order_id'], 'user_id' => $this->user_id]);
  514. $goods = GoodsReserve::findOne(['goods_id' => OrderDetail::find()->where(['order_id' => $params['order_id']])->select('goods_id')->scalar()]);
  515. } else { // 门店订单
  516. $storeOrder = StoreOrder::findOne(['order_no' => $order->order_no]);
  517. if (empty($storeOrder)) throw new \Exception('订单不存在');
  518. $reserveOrder = StoreOrderReserve::findOne(['order_id' => $storeOrder->id, 'store_id' => $order->store_id]);
  519. $goods = StoreGoodsReserve::findOne(['goods_id' => OrderDetail::find()->where(['order_id' => $params['order_id']])->select('goods_id')->scalar()]);
  520. }
  521. if (empty($reserveOrder)) throw new \Exception('订单未找到');
  522. if ($reserveOrder->reserve_type != StatusEnum::ENABLED) {
  523. throw new \Exception('当前订单不可进行预约');
  524. }
  525. if ($reserveOrder->reserve_status != 0) {
  526. throw new \Exception('当前订单不可进行预约.');
  527. }
  528. // if ($reserveOrder->order_status != OrderStatusEnum::PAY) {
  529. // throw new \Exception('当前订单不可进行预约!');
  530. // }
  531. // 预约时间校验
  532. // 取出商品
  533. // 数据校验
  534. if (!MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_RESERVATION_SERVICE)) {
  535. throw new \Exception('您还未安装当前插件');
  536. }
  537. $time = strtotime($params['select_time']);
  538. if($time <= time()) throw new \Exception('选择时间错误');
  539. if (!empty($goods->reserve_advance_hourse) && $time - time() < ($goods->reserve_advance_hourse * 3600))
  540. throw new \Exception("当前项目需提前 {$goods->reserve_advance_hourse} 小时进行预约");
  541. //检测技师
  542. $orderDetail = OrderDetail::findOne(['order_id' => $order->id]);
  543. if (!empty($params['tech_id'])) {
  544. $check = TechniciansService::checkCanOrder($this->mall_id, $params['tech_id'], $params['select_time'], $orderDetail->goods_id,
  545. $orderDetail->goods_attr_id, $reserveOrder->lat, $reserveOrder->lng, $order->store_id);
  546. if (!$check) throw new \Exception(TechniciansService::getStaticError());
  547. }
  548. // 开始处理
  549. $t = Yii::$app->db->beginTransaction();
  550. try {
  551. $reserve_code = OrderReserve::getClerkCode($order['mall_id'], ClerkEnum::RESERVATION_SERVICE_CLERK_CODE_PRE);
  552. // 处理数据
  553. $start_at = strtotime($params['select_time']);
  554. if (empty($order->store_id)) {
  555. $sku = GoodsReserveSku::findOne(['id' => $orderDetail->goods_attr_id]);
  556. } else {
  557. $sku = StoreGoodsReserveSku::findOne(['id' => $orderDetail->goods_attr_id]);
  558. $reserveOrder->reserve_code = $reserve_code;
  559. }
  560. $reserveOrder->tech_id = $params['tech_id'];
  561. $reserveOrder->reserve_date_time = $params['select_time'];
  562. $reserveOrder->service_start_at = $start_at;
  563. $reserveOrder->service_end_at = $start_at + ($sku->service_min * 60);
  564. $reserveOrder->reserve_time_range_type = 2;
  565. if (!$reserveOrder->save()) throw new \Exception($reserveOrder->getErrorMessage());
  566. $data = [
  567. 'mall_id' => $this->mall_id,
  568. 'store_id' => $order->store_id,
  569. 'user_id' => $reserveOrder->user_id,
  570. 'tech_id' => $params['tech_id'],
  571. 'order_id' => $params['order_id'],
  572. 'order_no' => $order->order_no,
  573. 'service_start_at' => $start_at,
  574. 'service_end_at' => $start_at + ($sku->service_min * 60),
  575. 'remark' => '',
  576. 'commission' => 0,
  577. 'order_detail_id' => $orderDetail->id,
  578. 'goods_id' => $orderDetail->goods_id,
  579. 'lng' => $reserveOrder->lng,
  580. 'lat' => $reserveOrder->lat,
  581. 'goods_price' => $orderDetail->goods_price,
  582. 'service_type' => $reserveOrder->service_type,
  583. 'goods_reserve_sku_id' => $sku['id'],
  584. ];
  585. if (!empty($order->store_id)) {
  586. $data['server_status'] = ReservationServiceEnum::ALREADY_BOOKED;
  587. $data['service_status'] = ReservationServiceEnum::RESERVE_ORDER_STATUS_TALKING;
  588. $main_order = Order::findOne(['order_no' => $order['order_no']]);
  589. $main_order->order_status = OrderStatusEnum::SHIPMENTS;
  590. $main_order->shipping_time = time();
  591. $main_order->save();
  592. $store_order = StoreOrder::findOne(['order_no' => $order['order_no']]);
  593. $store_order->order_status = OrderStatusEnum::SHIPMENTS;
  594. $store_order->shipping_time = time();
  595. $store_order->clerk_code = $reserveOrder->reserve_code;
  596. $store_order->save();
  597. }
  598. $reservation_service_order = (new OrderService())->save($data);
  599. OrderReserve::updateAll(['reservation_service_order_id' => $reservation_service_order['id']], ['order_id' => $order['id']]);
  600. StoreOrderReserve::updateAll(['reservation_service_order_id' => $reservation_service_order['id']], ['main_order_id' => $order['id']]);
  601. $t->commit();
  602. } catch (\Exception $e) {
  603. $t->rollBack();
  604. throw new \Exception($e->getMessage());
  605. }
  606. } catch (\Exception $e) {
  607. return $this->error($e->getMessage());
  608. }
  609. return $this->success('提交成功');
  610. }
  611. /**
  612. * 次卡预约
  613. * @Author:hua
  614. * @Date:2024-03-05 16:55
  615. * @Copyright:copyright(c)2024 广东七件事集团
  616. * @param $params
  617. * @return bool
  618. */
  619. protected function secondaryCardReserve($params){
  620. return SecondaryCardOrderService::secondaryCardReserve($params);
  621. }
  622. public function actionNumberIcons()
  623. {
  624. $tabsIndex = [999, 0, 1, 2, 3, 4];
  625. foreach ($tabsIndex as $index) {
  626. $badgeQuery = Order::find()
  627. ->where(['mall_id' => $this->mall_id, 'user_id' => $this->user_id,
  628. 'is_feedback' => StatusEnum::DISABLED, 'status' => StatusEnum::ENABLED,
  629. 'order_source' => [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE]]);
  630. $num = StatusEnum::DISABLED;
  631. switch ($index) {
  632. case 0: // 待支付
  633. $num = $badgeQuery->andWhere(['order_status' => StatusEnum::DISABLED])->andWhere(['>=', 'created_at', time() - 86400])->count();
  634. break;
  635. case 1: // 已付款
  636. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::PAY])->andWhere(['>=', 'pay_time', time() - 86400])->count();
  637. break;
  638. case 2: // 待服务
  639. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::SHIPMENTS])->andWhere(['>=', 'shipping_time', time() - 86400])->count();
  640. break;
  641. case 3: // 待验收
  642. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::SING])->andWhere(['>=', 'sign_time', time() - 86400])->count();
  643. break;
  644. case 4: // 待评价
  645. $num = $badgeQuery->andWhere(['order_status' => OrderStatusEnum::ACCOMPLISH])
  646. ->andWhere(['is_comment' => StatusEnum::DISABLED])->andWhere(['>=', 'finish_time', time() - 86400])->count();
  647. break;
  648. }
  649. $order_list['tab_badge'][$index] = (int) $num;
  650. }
  651. return $this->success('操作成功', $order_list);
  652. }
  653. }