OrderStatus.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. <?php
  2. namespace app\admin\model\shopro\order\traits;
  3. use app\admin\model\shopro\order\Order;
  4. use app\admin\model\shopro\order\OrderItem;
  5. use app\admin\model\shopro\activity\Groupon;
  6. use app\admin\model\shopro\Config;
  7. trait OrderStatus
  8. {
  9. protected function getStatus($data, $type)
  10. {
  11. $btns = []; // 前端订单操作按钮
  12. $backendBtns = [];
  13. $status_text = '';
  14. $status_desc = '';
  15. $ext = $this->ext;
  16. switch ($this->status_code) {
  17. case 'cancel':
  18. $status_text = '已取消';
  19. $status_desc = '买家已取消';
  20. $btns[] = 'delete'; // 删除订单
  21. break;
  22. case 'closed':
  23. $status_text = '交易关闭';
  24. if (isset($ext['closed_type']) && $ext['closed_type'] == 'refuse') {
  25. $status_desc = '买家拒绝收货';
  26. } else {
  27. $status_desc = '买家未在规定时间内付款';
  28. }
  29. $btns[] = 'delete'; // 删除订单
  30. break;
  31. case 'unpaid':
  32. $status_text = '待付款';
  33. $status_desc = '等待买家付款';
  34. $btns[] = 'cancel'; // 取消订单
  35. $btns[] = 'pay'; // 支付
  36. $backendBtns[] = 'change_fee'; // 订单改价
  37. break;
  38. // 已支付的
  39. case 'apply_refund':
  40. $status_text = '申请退款中';
  41. $status_desc = '等待卖家处理退款';
  42. $backendBtns[] = 'apply_refund_oper'; // 处理申请退款按钮
  43. $backendBtns[] = 'refund'; // 只能退款,或者在列表上拒绝申请退款
  44. break;
  45. case 'commented':
  46. $status_text = '已评价';
  47. $status_desc = '订单已评价';
  48. $dispatchType = $this->getItemDispatchTypes();
  49. if (in_array('express', $dispatchType)) {
  50. //$btns[] = 'express'; // 查看物流, 放到详情里去了
  51. }
  52. $backendBtns[] = 'refund';
  53. break;
  54. case 'nocomment':
  55. $status_text = '待评价';
  56. $status_desc = '等待买家评价';
  57. $dispatchType = $this->getItemDispatchTypes();
  58. if (in_array('express', $dispatchType)) {
  59. //$btns[] = 'express'; // 查看物流, 放到详情里去了
  60. }
  61. $btns[] = 'comment';
  62. //应该申请售后
  63. // $btns[] = 'aftersale';
  64. $backendBtns[] = 'refund';
  65. break;
  66. case 'noget':
  67. $status_text = '待收货';
  68. $status_desc = '等待买家收货';
  69. $dispatchType = $this->getItemDispatchTypes();
  70. if (in_array('express', $dispatchType)) {
  71. //$btns[] = 'express'; // 查看物流, 放到详情里去了
  72. }
  73. if ($this->isOffline($data)) {
  74. $status_desc = '卖家已发货,等待包裹运达';
  75. // 用户可以拒收,后台确认收货
  76. $btns[] = 'refuse'; // 用户拒收
  77. $backendBtns[] = 'confirm';
  78. }else {
  79. // 计算自动确认收货时间
  80. $send_time = $ext['send_time'] ?? 0;
  81. $auto_confirm = Config::getConfigField('shop.order.auto_confirm');
  82. $auto_confirm_unix = $auto_confirm * 86400;
  83. if ($send_time && $auto_confirm_unix) {
  84. $auto_confirm_time = $send_time + $auto_confirm_unix;
  85. $status_desc .= ',还剩' . diff_in_time($auto_confirm_time, null, true, true) . '自动确认';
  86. }
  87. $btns[] = 'confirm'; // 确认收货
  88. //应该申请售后
  89. // $btns[] = 'aftersale';
  90. $backendBtns[] = 'refund';
  91. }
  92. break;
  93. case 'nosend':
  94. $status_text = '待发货';
  95. $status_desc = '等待卖家发货';
  96. $statusCodes = $this->getItemStatusCode();
  97. if (in_array('noget', $statusCodes)) { // 只要存在待收货的item
  98. $btns[] = 'confirm'; // 确认收货 (部分发货时也可以收货)
  99. }
  100. if ($this->isOffline($data)) {
  101. // 发货之前用户可以取消
  102. $btns[] = 'cancel'; // 用户取消订单
  103. } else {
  104. $backendBtns[] = 'refund';
  105. }
  106. $backendBtns[] = 'send';
  107. if (!isset($ext['need_address']) || $ext['need_address']) { // 自动发货这些不需要收货地址的,没有 edit_consignee
  108. $backendBtns[] = 'edit_consignee'; //修改收货地址
  109. }
  110. //应该申请全额退款,有了
  111. break;
  112. case 'refund_completed':
  113. $status_text = '退款完成';
  114. $status_desc = '订单退款完成';
  115. break;
  116. case 'refund_agree':
  117. $status_text = '退款完成';
  118. $status_desc = '订单退款完成';
  119. break;
  120. case 'groupon_ing':
  121. $status_text = '等待成团';
  122. $status_desc = '等待拼团成功';
  123. if ($this->isOffline($data)) {
  124. // 货到付款未付款,不能退款,等待拼团时还未发货,用户可取消订单
  125. $btns[] = 'cancel'; // 用户取消订单
  126. } else {
  127. $backendBtns[] = 'refund'; // 全部退款 直接不申请退款
  128. }
  129. //应该团购,有了
  130. //应该申请全额退款,有了
  131. break;
  132. case 'groupon_invalid':
  133. $status_text = '拼团失败';
  134. $status_desc = '拼团失败';
  135. break;
  136. // 已支付的结束
  137. case 'completed':
  138. $status_text = '交易完成';
  139. $status_desc = '交易已完成';
  140. // $btns[] = 'delete'; // 删除订单
  141. break;
  142. }
  143. // 有活动
  144. if (in_array($data['activity_type'], ['groupon', 'groupon_ladder', 'groupon_lucky'])) {
  145. // 是拼团订单
  146. if (isset($ext['groupon_id']) && $ext['groupon_id']) {
  147. $btns[] = 'groupon'; // 拼团详情
  148. }
  149. }
  150. if (in_array($this->status_code, ['nosend', 'groupon_ing']) && !$this->isOffline($data)) { // 线下付款订单,不可申请全额退款
  151. if (in_array($data['apply_refund_status'], [Order::APPLY_REFUND_STATUS_NOAPPLY, Order::APPLY_REFUND_STATUS_REFUSE])) {
  152. // 获取所有的 item 状态
  153. $statusCodes = $this->getItemStatusCode();
  154. if (count($statusCodes) == 1 && current($statusCodes) == 'nosend') {
  155. // items 只有 未发货,就显示 申请退款按钮
  156. if ($data['apply_refund_status'] == Order::APPLY_REFUND_STATUS_REFUSE) {
  157. $btns[] = 're_apply_refund'; // 重新申请全额退款
  158. } else {
  159. $btns[] = 'apply_refund'; // 申请全额退款
  160. }
  161. }
  162. }
  163. }
  164. //申请售后
  165. if (in_array($this->status_code, ['noget', 'nocomment']) && !$this->isOffline($data)) { // 线下付款订单,不可申请全额退款
  166. // 获取所有的 item 状态
  167. $statusCodes = $this->getItemStatusCode('item');
  168. if(!in_array('refund_agree',$statusCodes) && !in_array('refund_completed',$statusCodes)){
  169. $aftersale_times = count($statusCodes);//默认能申请N次
  170. foreach($statusCodes as $key => $statusCode){
  171. if(strpos($statusCode,'aftersale_ing') !== false || strpos($statusCode,'aftersale_completed') !== false){
  172. $aftersale_times--; //减1次
  173. }
  174. }
  175. //dump($aftersale_times);
  176. if($aftersale_times != 0){
  177. $btns[] = 'aftersale'; // 申请售后
  178. }
  179. }
  180. }
  181. if ($data['invoice_status'] == 1) {
  182. $btns[] = 'invoice'; // 查看发票
  183. }
  184. $return = null;
  185. switch ($type) {
  186. case 'status_text':
  187. $return = $status_text;
  188. break;
  189. case 'btns':
  190. $return = $btns;
  191. break;
  192. case 'status_desc':
  193. $return = $status_desc;
  194. break;
  195. case 'backend_btns':
  196. if (in_array('refund', $backendBtns)) {
  197. // 判断是否有退款,如果存在退款就移除 refund 按钮
  198. $refundCount = OrderItem::where('order_id', $data['id'])->where('refund_status', '<>', OrderItem::REFUND_STATUS_NOREFUND)->count();
  199. if ($refundCount && ($key = array_search('refund', $backendBtns))) {
  200. unset($backendBtns[$key]);
  201. }
  202. $backendBtns = array_values($backendBtns);
  203. }
  204. $return = $backendBtns;
  205. break;
  206. }
  207. return $return;
  208. }
  209. /**
  210. * 获取支付成功之后的子状态
  211. */
  212. public function getPayedStatusCode($data)
  213. {
  214. $status_code = '';
  215. // 获取所有的 item 状态
  216. $statusCodes = $this->getItemStatusCode();
  217. if (in_array('nosend', $statusCodes)) {
  218. // 存在待发货就是待发货
  219. $status_code = 'nosend';
  220. } else if (in_array('noget', $statusCodes)) {
  221. // 存在待收货,就是待收货
  222. $status_code = 'noget';
  223. } else if (in_array('nocomment', $statusCodes)) {
  224. // 存在待评价,就是待评价
  225. $status_code = 'nocomment';
  226. } else if (in_array('commented', $statusCodes)) {
  227. // 存在已评价,就是已评价
  228. $status_code = 'commented';
  229. } else if (in_array('refund_completed', $statusCodes)) {
  230. // 所有商品退款完成,或者退款中(不可能存在待发货或者收货的商品,上面已判断过)
  231. $status_code = 'refund_completed';
  232. } else if (in_array('refund_agree', $statusCodes)) {
  233. // 所有商品都同意退款了 (不可能存在待发货或者收货的商品,上面已判断过)
  234. $status_code = 'refund_agree';
  235. } // 售后都不在总状态显示
  236. if ($data['apply_refund_status'] == Order::APPLY_REFUND_STATUS_APPLY && !in_array($status_code, ['refund_completed', 'refund_agree'])) {
  237. return $status_code = 'apply_refund'; // 申请退款中,并且还没退款
  238. }
  239. $ext = $this->ext;
  240. // 是拼团订单
  241. if (
  242. in_array($data['activity_type'], ['groupon', 'groupon_ladder', 'groupon_lucky']) &&
  243. isset($ext['groupon_id']) && $ext['groupon_id']
  244. ) {
  245. $groupon = Groupon::where('id', $ext['groupon_id'])->find();
  246. if ($groupon) {
  247. if ($groupon['status'] == 'ing') {
  248. // 尚未成团
  249. $status_code = $statusCodes[0] ?? ''; // 拼团订单只能有一个商品
  250. $status_code = in_array($status_code, ['refund_agree', 'refund_completed']) ? $status_code : 'groupon_ing'; // 如果订单已退款,则是退款状态,不显示拼团中
  251. } else if ($groupon['status'] == 'invalid') {
  252. $status_code = 'groupon_invalid';
  253. }
  254. }
  255. }
  256. return $status_code;
  257. }
  258. /**
  259. * 获取订单items状态
  260. *
  261. * @param string $type
  262. * @return array
  263. */
  264. private function getItemStatusCode($type = 'order')
  265. {
  266. // 循环判断 item 状态
  267. $itemStatusCode = [];
  268. foreach ($this->items as $key => $item) {
  269. // 获取 item status
  270. $itemStatusCode[] = (new OrderItem)->getBaseStatusCode($item, $type);
  271. }
  272. // 取出不重复不为空的 status_code
  273. $statusCodes = array_values(array_unique(array_filter($itemStatusCode)));
  274. return $statusCodes;
  275. }
  276. private function getItemDispatchTypes()
  277. {
  278. $dispatchType = [];
  279. foreach ($this->items as $key => $item) {
  280. // 获取 item status
  281. $dispatchType[] = $item['dispatch_type'];
  282. }
  283. $dispatchType = array_unique(array_filter($dispatchType)); // 过滤重复,过滤空值
  284. return $dispatchType;
  285. }
  286. public function getStatusTextAttr($value, $data)
  287. {
  288. return $this->getStatus($data, 'status_text');
  289. }
  290. public function getStatusDescAttr($value, $data)
  291. {
  292. return $this->getStatus($data, 'status_desc');
  293. }
  294. public function getBtnsAttr($value, $data)
  295. {
  296. $btn_name = strpos(request()->url(), 'addons/shopro') !== false ? 'btns' : 'backend_btns';
  297. return $this->getStatus($data, $btn_name);
  298. }
  299. // 获取订单状态
  300. //订单状态:closed=交易关闭,cancel=已取消,unpaid=未支付,paid=已支付,completed=已完成,pending=待定
  301. public function getStatusCodeAttr($value, $data)
  302. {
  303. $status_code = '';
  304. switch ($data['status']) {
  305. case Order::STATUS_CLOSED:
  306. $status_code = 'closed'; // 订单交易关闭
  307. break;
  308. case Order::STATUS_CANCEL:
  309. $status_code = 'cancel'; // 订单已取消
  310. break;
  311. case Order::STATUS_UNPAID:
  312. $status_code = 'unpaid'; // 订单等待支付
  313. break;
  314. case Order::STATUS_PENDING:
  315. $status_code = $this->getPayedStatusCode($data); // 订单线下付款
  316. break;
  317. case Order::STATUS_PAID:
  318. // 根据 item 获取支付后的状态信息
  319. $status_code = $this->getPayedStatusCode($data);
  320. break;
  321. case Order::STATUS_COMPLETED:
  322. $status_code = 'completed';
  323. break;
  324. }
  325. return $status_code;
  326. }
  327. /**
  328. * 处理未支付 item status_code
  329. * 查询列表 item status_code 不关联订单表,使用这个方法进行处理
  330. */
  331. public function setOrderItemStatusByOrder($order)
  332. {
  333. $order = $order instanceof \think\Model ? $order->toArray() : $order;
  334. foreach ($order['items'] as $key => &$item) {
  335. if ((!in_array($order['status'], [Order::STATUS_PAID, Order::STATUS_COMPLETED]) && !$this->isOffline($order)) // 没有支付,并且也不是货到付款
  336. || $order['apply_refund_status'] == Order::APPLY_REFUND_STATUS_APPLY) {
  337. // 未支付,status_code = 订单的 status_code
  338. $item['status_code'] = $order['status_code'];
  339. $item['status_text'] = '';
  340. $item['status_desc'] = '';
  341. $item['btns'] = [];
  342. } else {
  343. if (strpos(request()->url(), 'addons/shopro') !== false) {
  344. // 前端
  345. if (strpos($item['status_code'], 'nosend') !== false) {
  346. if (!$this->isOffline($order) && !array_intersect(['re_apply_refund', 'apply_refund'], $order['btns'])) {
  347. // 不能申请全额退款了(有部分发货,或者退款)的待发货的 item 要显示申请售后的按钮
  348. $aftersale_id = (isset($item['ext']['aftersale_id']) && !empty($item['ext']['aftersale_id'])) ? $item['ext']['aftersale_id'] : 0;
  349. if (strpos($item['status_code'], 'aftersale_ing') === false && strpos($item['status_code'], 'aftersale_completed') === false) {
  350. // 不是售后中,也不是售后完成
  351. if (strpos($item['status_code'], 'aftersale_refuse') !== false && $aftersale_id) {
  352. // 如果申请过退款,被拒绝了,则为重新申请售后
  353. $item['btns'][] = 're_aftersale';
  354. } else {
  355. // 取消售后是 re_aftersale ,未申请过是 aftersale
  356. $item['btns'][] = $aftersale_id ? 're_aftersale' : 'aftersale'; // 售后,售后取消会有 aftersale_id
  357. }
  358. }
  359. }
  360. } else if (strpos($item['status_code'], 'noget') !== false) {
  361. // 如果是货到付款的 待收货
  362. if ($this->isOffline($order)) {
  363. foreach($item['btns'] as $btnk => $btn) {
  364. if (in_array($btn, ['re_aftersale', 'aftersale'])) {
  365. unset($item['btns'][$btnk]);
  366. }
  367. }
  368. }
  369. }
  370. } else {
  371. // 后端,如果是货到付款, 并且还没收货,不显示退款按钮
  372. if ($this->isOffline($order) && (strpos($item['status_code'], 'nosend') !== false || strpos($item['status_code'], 'noget') !== false)) {
  373. $refund_key = array_search('refund', $item['btns']);
  374. if ($refund_key !== false) {
  375. unset($item['btns'][$refund_key]);
  376. }
  377. }
  378. }
  379. }
  380. }
  381. return $order;
  382. }
  383. }