OrderServices.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\erp;
  13. use app\jobs\order\OrderSyncJob;
  14. use app\services\order\StoreOrderCartInfoServices;
  15. use app\services\order\StoreOrderCreateServices;
  16. use app\services\order\StoreOrderDeliveryServices;
  17. use app\services\order\StoreOrderRefundServices;
  18. use app\services\order\StoreOrderServices;
  19. use crmeb\exceptions\AdminException;
  20. use crmeb\services\erp\Erp as erpServices;
  21. use think\facade\Log;
  22. /**
  23. * Class OrderServices
  24. * @package app\services\erp
  25. */
  26. class OrderServices
  27. {
  28. protected $services;
  29. /**
  30. * OrderServices constructor.
  31. * @param erpServices $services
  32. */
  33. public function __construct(erpServices $services)
  34. {
  35. $this->services = $services;
  36. }
  37. /**
  38. * 订单上传
  39. * @param int $oid
  40. * @return bool
  41. * @throws \Exception
  42. */
  43. public function upload(int $oid): bool
  44. {
  45. /** @var StoreOrderServices $orderServices */
  46. $orderServices = app()->make(StoreOrderServices::class);
  47. $order = $orderServices->get($oid, ['*'], ['store']);
  48. if ($order['id'] < 1) {
  49. throw new AdminException('订单信息不能为空!');
  50. }
  51. // 过滤已退款订单和虚拟商品
  52. if ($order['refund_status'] == 2 || $order['product_type'] > 0) {
  53. return true;
  54. }
  55. $biz = [
  56. 'shop_id' => $this->getShopId($order->store->erp_shop_id ?? 0), // 店铺编号
  57. 'so_id' => $this->getErpOrderId($order['order_id'], $order['erp_order_id']), // 线上订单号, 长度 <= 50
  58. 'shop_status' => 'WAIT_SELLER_SEND_GOODS', // 订单状态 待发货
  59. 'buyer_message' => $order['mark'], // 买家留言
  60. 'shop_buyer_id' => (string)$order['uid'], // 买家帐号
  61. 'receiver_name' => $order['real_name'], // 收件人
  62. 'receiver_mobile' => $order['user_phone'], // 联系手机
  63. 'pay_amount' => (float)$order['pay_price'], // 应付金额,保留两位小数,单位元)
  64. 'freight' => (float)$order['freight_price'], // 运费
  65. 'order_date' => date('Y-m-d H:i:s', $order['add_time']), // 订单日期
  66. 'shop_modified' => date('Y-m-d H:i:s', $order['add_time']), // 订单修改日期
  67. 'items' => $this->getItems($oid, (int)$order['uid'], $order['unique']),
  68. 'pay' => [
  69. 'outer_pay_id' => 'xxx', // 外部支付单号,最大50
  70. 'pay_date' => date('Y-m-d H:i:s', $order['pay_time']), // 支付日期
  71. 'payment' => $order['pay_type'], // 支付方式,最大20
  72. 'seller_account' => 'seller', // 卖家支付账号,最大 50
  73. 'buyer_account' => 'buyer', // 买家支付账号,最大 200
  74. 'amount' => (float)$order['pay_price'] // 支付总额
  75. ]
  76. ];
  77. $biz = array_merge($biz, $this->getAddress($order['user_address']));
  78. $num = 3;
  79. for ($i = $num; $i >= 0; $i--) {
  80. if ($i <= 0) {
  81. Log::error(['msg' => 'ERP订单上传失败,调用均为异常', 'oid' => $oid]);
  82. return false;
  83. }
  84. try {
  85. $result = $this->services->serviceDriver('order')->ordersUpload([$biz]);
  86. if ($result['datas'][0]['issuccess'] && $order['erp_order_id'] != $biz['so_id']) {
  87. $order->save(['erp_id' => $result['datas'][0]['o_id'], 'erp_order_id' => $biz['so_id']]);
  88. }
  89. break;
  90. } catch (\Exception $e) {
  91. usleep(1000 * 50);
  92. Log::error('ERP订单上传失败,原因:' . $e->getMessage());
  93. }
  94. }
  95. return true;
  96. }
  97. /**
  98. * 收货地址
  99. * @param string $userAddress
  100. * @return array
  101. */
  102. public function getAddress(string $userAddress): array
  103. {
  104. $receiver_state = $receiver_city = $receiver_district = $receiver_address = '';
  105. if (!empty($userAddress)) {
  106. $address = explode(' ', $userAddress);
  107. $receiver_state = $address[0] ?? ''; // 收货省份
  108. $receiver_city = $address[1] ?? ''; // 收货市
  109. $receiver_district = $address[2] ?? ''; // 收货区
  110. $receiver_address = $address[3] ?? ''; // 收货街道
  111. $receiver_address .= $address[4] ?? ''; // 街道
  112. }
  113. return compact('receiver_state', 'receiver_city', 'receiver_district', 'receiver_address');
  114. }
  115. /**
  116. * 获取erp指定订单商品详情
  117. * @param int $oid
  118. * @param int $uid
  119. * @param string $unique
  120. * @return array
  121. */
  122. public function getItems(int $oid, int $uid, string $unique): array
  123. {
  124. /** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
  125. $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
  126. $cartInfo = $storeOrderCartInfoServices->getOrderCartInfo($oid);
  127. if (empty($cartInfo)) {
  128. /** @var StoreOrderServices $orderServices */
  129. $orderServices = app()->make(StoreOrderServices::class);
  130. //同步查询订单商品为查询到 查询缓存信息
  131. $orderInfo = $orderServices->getCacheOrderInfo($uid, $unique);
  132. $cartInfo = $orderInfo['cartInfo'] ?? [];
  133. }
  134. $items = [];
  135. foreach ($cartInfo as $cart) {
  136. $cart = $cart['cart_info'] ?? $cart;
  137. $attrInfo = $cart['productInfo']['attrInfo'];
  138. $items[] = [
  139. 'sku_id' => $attrInfo['code'],
  140. 'shop_sku_id' => $attrInfo['code'],
  141. 'base_price' => (float)bcdiv((string)$cart['truePrice'], (string)$cart['cart_num'], 2),
  142. 'amount' => (float)$cart['truePrice'],
  143. 'qty' => $cart['cart_num'],
  144. 'pic' => $cart['productInfo']['image'],
  145. 'name' => $cart['productInfo']['store_name'],
  146. 'properties_value' => $attrInfo['suk'],
  147. 'outer_oi_id' => $cart['product_attr_unique'],
  148. ];
  149. }
  150. return $items;
  151. }
  152. /**
  153. * 取消回调
  154. * @param array $cancel
  155. * @return bool
  156. * @throws \Psr\SimpleCache\InvalidArgumentException
  157. */
  158. public function cancelCallback(array $cancel): bool
  159. {
  160. try {
  161. /** @var StoreOrderServices $orderServices */
  162. $orderServices = app()->make(StoreOrderServices::class);
  163. $orders = explode('_', $cancel['so_id']);
  164. $cancel['order_id'] = $orders[0] ?? '';
  165. $order = $orderServices->getOne(['order_id' => $cancel['order_id']], '*');
  166. if (!$order) {
  167. throw new AdminException('订单信息不能为空!');
  168. }
  169. $order = is_object($order) ? $order->toArray() : $order;
  170. $ids = [$order['erp_order_id']];
  171. // 获取子订单号
  172. $orders = $orderServices->getColumn(['pid' => $order['id']], 'erp_order_id', 'id');
  173. if ($orders) {
  174. $ids = array_values(array_unique(array_filter(array_merge($ids, $orders))));
  175. }
  176. $result = $this->services->serviceDriver('order')->ordersSingleQuery(['so_ids' => $ids]);
  177. // 取消申请退货
  178. $this->checkRefundApply((int)$order['id']);
  179. // 部分退货
  180. if (count($result['orders']) > 1) {
  181. $this->applyPartRefund($cancel, $order, $result['orders'], $orderServices);
  182. } else {
  183. $this->applyRefund($cancel, $order, $orderServices);
  184. }
  185. } catch (\Exception $e) {
  186. Log::error(['msg' => '订单取消失败,原因:' . $e->getMessage(), 'data' => $cancel]);
  187. }
  188. return true;
  189. }
  190. /**
  191. * 订单申请退款
  192. * @param array $cancel
  193. * @param array $order
  194. * @param StoreOrderServices $orderServices
  195. * @return void
  196. */
  197. protected function applyRefund(array $cancel, array $order, StoreOrderServices $orderServices)
  198. {
  199. try {
  200. $data['refund_reason_wap_explain'] = $cancel['remark'];
  201. $data['order_id'] = $cancel['order_id'];
  202. /** @var StoreOrderRefundServices $storeOrderRefundServices */
  203. $storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
  204. if ($storeOrderRefundServices->count(['store_order_id' => $order['id'], 'refund_type' => [0, 1, 2, 4, 5], 'is_cancel' => 0, 'is_del' => 1])) {
  205. throw new AdminException('请先处理售后申请!');
  206. }
  207. //0元退款
  208. if ($order['pay_price'] == 0 && in_array($order['refund_status'], [0, 1])) {
  209. $refund_price = 0;
  210. } else {
  211. if ($order['pay_price'] == $order['refund_price']) {
  212. throw new AdminException('已退完支付金额!不能再退款了!');
  213. }
  214. $refund_price = bcsub((string)$order['pay_price'], $order['refund_price'], 2);
  215. }
  216. $data['refund_status'] = 2;
  217. $data['refund_type'] = 6;
  218. $refund_data['pay_price'] = $order['pay_price'];
  219. $refund_data['refund_price'] = $refund_price;
  220. if ($order['refund_price'] > 0) {
  221. mt_srand();
  222. $refund_data['refund_id'] = $order['order_id'] . rand(100, 999);
  223. }
  224. /** @var StoreOrderCreateServices $service */
  225. $storeOrderCreateServices = app()->make(StoreOrderCreateServices::class);
  226. //生成退款订单
  227. $refundOrderData['uid'] = $order['uid'];
  228. $refundOrderData['store_id'] = $order['store_id'];
  229. $refundOrderData['store_order_id'] = $order['id'];
  230. $refundOrderData['refund_num'] = $order['total_num'];
  231. $refundOrderData['refund_type'] = $data['refund_type'];
  232. $refundOrderData['refund_price'] = $order['pay_price'];
  233. $refundOrderData['refunded_price'] = $refund_price;
  234. $refundOrderData['refund_reason'] = $cancel['remark'];
  235. $refundOrderData['order_id'] = $storeOrderCreateServices->getNewOrderId('');
  236. $refundOrderData['refunded_time'] = time();
  237. $refundOrderData['add_time'] = time();
  238. /** @var StoreOrderCartInfoServices $orderInfoServices */
  239. $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
  240. $cartInfos = $storeOrderCartInfoServices->getCartColunm(['oid' => $order['id']], 'id,cart_id,cart_num,cart_info');
  241. foreach ($cartInfos as &$cartInfo) {
  242. $cartInfo['cart_info'] = is_string($cartInfo['cart_info']) ? json_decode($cartInfo['cart_info'], true) : $cartInfo['cart_info'];
  243. }
  244. $refundOrderData['cart_info'] = json_encode(array_column($cartInfos, 'cart_info'));
  245. $res = $storeOrderRefundServices->save($refundOrderData);
  246. //修改订单退款状态
  247. if ($storeOrderRefundServices->agreeRefund((int)$res->id, $refund_data)) {
  248. //主动退款清楚原本退款单
  249. $storeOrderRefundServices->delete(['store_order_id' => $order['id']]);
  250. $orderServices->update($order['id'], $data);
  251. } else {
  252. $storeOrderRefundServices->storeProductOrderRefundYFasle((int)$order['id'], $refund_price);
  253. }
  254. } catch (\Exception $e) {
  255. Log::error('订单申请退款失败,原因:' . $e->getMessage());
  256. }
  257. }
  258. /**
  259. * 部分退款申请
  260. * @param array $cancel
  261. * @param array $order
  262. * @param array $orders
  263. * @param StoreOrderServices $orderServices
  264. * @return bool|void
  265. * @throws \Psr\SimpleCache\InvalidArgumentException
  266. */
  267. protected function applyPartRefund(array $cancel, array $order, array $orders, StoreOrderServices $orderServices)
  268. {
  269. try {
  270. $cartWhere = ['oid' => $order['id']];
  271. // 获取拆单信息
  272. if ($order['pid'] < 0) {
  273. $oIds = $orderServices->Value([['pid', '=', $order['id']], ['refund_status', '<', 2], ['status', '=', 0]], 'GROUP_CONCAT(id)');
  274. if (!empty($oIds)) {
  275. $oIds = array_filter(explode(',', $oIds));
  276. }
  277. $cartWhere = ['oid' => $oIds, 'split_status' => [0, 1]];
  278. }
  279. /** @var StoreOrderCartInfoServices $orderInfoServices */
  280. $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
  281. $cartInfos = $storeOrderCartInfoServices->getCartColunm($cartWhere, 'id,oid,cart_id,cart_num,split_surplus_num,cart_info');
  282. foreach ($cartInfos as &$cartInfo) {
  283. $cartInfo['cart_info'] = is_string($cartInfo['cart_info']) ? json_decode($cartInfo['cart_info'], true) : $cartInfo['cart_info'];
  284. }
  285. // 过滤已退商品
  286. $cancelled = $this->filter($orders, $order['id']);
  287. if (empty($cancelled)) {
  288. return true;
  289. }
  290. $cartOid = 0;
  291. $cartIds = [];
  292. foreach ($cartInfos as $cart) {
  293. $num = $cancelled[$cart['cart_info']['productInfo']['attrInfo']['code']] ?? 0;
  294. if ($num > 0) {
  295. $allow = $num <= $cart['split_surplus_num'] ? $num : $num - $cart['split_surplus_num'];
  296. $cartId = (string)$cart['cart_id'];
  297. if (isset($cartIds[$cartId])) {
  298. $cartIds[$cartId]['cart_num'] += $allow;
  299. } else {
  300. $cartIds[$cartId] = ['cart_id' => $cartId, 'cart_num' => $allow, 'oid' => $cart['oid']];
  301. $cartOid = $cart['oid'];
  302. }
  303. }
  304. }
  305. if ($order['pid'] < 0) {
  306. $oid = $orderServices->Value([['pid', '=', $order['id']], ['refund_status', '<', 2], ['status', '=', 0]], 'id');
  307. if ($oid < 1) {
  308. return true;
  309. }
  310. $order = $orderServices->getOne(['id' => $oid], '*');
  311. if (!$order) {
  312. throw new AdminException('订单信息不能为空!');
  313. }
  314. }
  315. /** @var StoreOrderRefundServices $storeOrderRefundServices */
  316. $storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
  317. $refundData = ['refund_reason' => $cancel['remark'], 'refund_explain' => 'ERP取消订单', 'refund_img' => json_encode([])];
  318. $refundId = $storeOrderRefundServices->applyRefund($cartOid, $order['uid'], $order, array_values($cartIds), 1, 0.00, $refundData, 1, false);
  319. if ($refundId) {
  320. $refund = $storeOrderRefundServices->get($refundId);
  321. // 立即退款
  322. $this->receivedCallback(['outer_as_id' => $refund['order_id']], 1);
  323. }
  324. } catch (\Exception $e) {
  325. Log::error('订单部分退款申请失败,原因:' . $e->getMessage());
  326. }
  327. }
  328. /**
  329. * 订单发货
  330. * @param array $data
  331. * @return bool
  332. */
  333. public function deliverCallback(array $data): bool
  334. {
  335. try {
  336. /** @var StoreOrderServices $orderServices */
  337. $orderServices = app()->make(StoreOrderServices::class);
  338. /** @var StoreOrderDeliveryServices $orderDeliveryServices */
  339. $orderDeliveryServices = app()->make(StoreOrderDeliveryServices::class);
  340. $order = $orderServices->getOne(['erp_id' => $data['o_id'], 'status' => 0], '*');
  341. $deliver = [
  342. "type" => 1, // 订单统一使用快递配送
  343. "delivery_name" => $data['logistics_company'], // 快递公司名称
  344. "delivery_id" => $data['l_id'], // 快递公司单号
  345. "delivery_code" => $data['lc_id'], // 快递公司编码
  346. "express_record_type" => "1", // 快递
  347. 'express_temp_id' => '',
  348. 'expressTemp' => [],
  349. "to_name" => '',
  350. "to_tel" => '',
  351. "to_addr" => '',
  352. "sh_delivery_name" => "",
  353. "sh_delivery_id" => "",
  354. "sh_delivery_uid" => "",
  355. "fictitious_content" => "",
  356. "export_open" => true,
  357. 'erp_id' => (int)$data['o_id'] // 内部订单编号
  358. ];
  359. // 单个订单发货
  360. if (!empty($order)) {
  361. // 取消申请退货
  362. $this->checkRefundApply($order['id']);
  363. $other = ["to_name" => $order['real_name'], "to_tel" => $order['user_phone'], "to_addr" => $order['user_address']];
  364. $orderDeliveryServices->delivery($order['id'], array_merge($deliver, $other));
  365. return true;
  366. }
  367. // 根据订单ID查找为空则订单异常
  368. $order = $orderServices->getOne(['erp_order_id' => $data['so_id']], '*');
  369. if (!$order) {
  370. throw new AdminException('订单信息获取异常,不能进行自动发货!');
  371. }
  372. // 拆单发货
  373. /** @var StoreOrderCartInfoServices $cartInfoServices */
  374. $cartInfoServices = app()->make(StoreOrderCartInfoServices::class);
  375. $list = $cartInfoServices->getSplitCartList($order['id']);
  376. if ($order['pid'] < 0 && empty($list)) {
  377. $oIds = $orderServices->Value([['pid', '=', $order['id']], ['status', '=', 0], ['refund_status', '<', 2]], 'GROUP_CONCAT(id)');
  378. if ($oIds) {
  379. $oIds = array_filter(explode(',', $oIds));
  380. // 获取拆单信息进行匹配是否拆单
  381. $cartWhere = ['oid' => $oIds, 'split_status' => [0, 1]];
  382. $list = $cartInfoServices->getCartColunm($cartWhere, 'id,oid,cart_id,cart_num,split_surplus_num,cart_info');
  383. foreach ($list as &$cartInfo) {
  384. $cartInfo['cart_info'] = is_string($cartInfo['cart_info']) ? json_decode($cartInfo['cart_info'], true) : $cartInfo['cart_info'];
  385. }
  386. }
  387. }
  388. // 获取发货数据
  389. [$deliver['cart_ids'], $oid] = $this->generateCart($data['items'], $list);
  390. if (empty($deliver['cart_ids'])) {
  391. return true;
  392. }
  393. // 取消申请退货
  394. $this->checkRefundApply($oid);
  395. $other = ["to_name" => $order['real_name'], "to_tel" => $order['user_phone'], "to_addr" => $order['user_address']];
  396. $orderDeliveryServices->splitDelivery($oid, array_merge($deliver, $other));
  397. } catch (\Exception $e) {
  398. Log::error('发货回调失败, 原因:' . $e->getMessage());
  399. }
  400. return true;
  401. }
  402. /**
  403. * 订单拆分后的数据
  404. * @param array $items
  405. * @param array $list
  406. * @return array
  407. * @throws Exception
  408. */
  409. public function generateCart(array $items, array $list): array
  410. {
  411. $carts = [];
  412. $oid = 0;
  413. foreach ($items as $item) {
  414. foreach ($list as $cart) {
  415. if ($item['sku_id'] == $cart['cart_info']['productInfo']['attrInfo']['code']) {
  416. $carts[] = ['cart_id' => $cart['cart_id'], 'cart_num' => (int)$item['qty']];
  417. $oid = $cart['oid'];
  418. }
  419. }
  420. }
  421. return [$carts, $oid];
  422. }
  423. /**
  424. * 售后单上传
  425. * @param array $refundIds
  426. * @return bool
  427. */
  428. public function refundOrderUpload(array $refundIds, string $shopStatus = 'WAIT_SELLER_CONFIRM_GOODS')
  429. {
  430. try {
  431. /** @var StoreOrderServices $orderServices */
  432. $orderServices = app()->make(StoreOrderServices::class);
  433. /** @var StoreOrderRefundServices $storeOrderRefundServices */
  434. $storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
  435. $list = [];
  436. foreach ($refundIds as $refundId) {
  437. $refund = $storeOrderRefundServices->getOne(['id' => $refundId], '*', ['order']);
  438. if ($refund->order['pid'] > 0) {
  439. $order = $orderServices->get($refund->order['pid']);
  440. if (!$order) {
  441. throw new AdminException('父级订单信息不能为空!');
  442. }
  443. $soId = $order->erp_order_id;
  444. } else {
  445. $soId = $refund->order->erp_order_id;
  446. }
  447. $refundData = [
  448. 'shop_id' => $this->getShopId($refund->order->store->erp_shop_id ?? 0), // 店铺编号
  449. 'outer_as_id' => $refund['order_id'], // 退货退款单号,平台唯一
  450. 'so_id' => $soId, // 平台订单号
  451. 'type' => '普通退货', // 售后类型
  452. 'shop_status' => $shopStatus, // WAIT_SELLER_CONFIRM_GOODS:等待卖家确认收货 CLOSED:退款关闭
  453. 'good_status' => 'BUYER_RETURNED_GOODS', // 买家已退货
  454. 'question_type' => null,
  455. 'remark' => $refund['refund_reason'], // 问题类型
  456. 'total_amount' => (float)$refund->order['pay_price'], // 原单据总金额
  457. 'refund' => (float)$refund['refund_price'], // 卖家应退金额
  458. 'payment' => 0.00 // 买家应补偿金额
  459. ];
  460. $items = [];
  461. foreach ($refund['cart_info'] as $cart) {
  462. $items[] = [
  463. 'sku_id' => $cart['productInfo']['attrInfo']['code'], // 商家商品编码
  464. 'qty' => (int)$cart['cart_num'], // 退货数量
  465. 'amount' => bcadd(bcmul((string)($cart['truePrice'] ?? 0), (string)$cart['cart_num'], 4), (string)($cart['postage_price'] ?? 0), 2), // SKU退款金额
  466. 'type' => '退货',
  467. 'pic' => $cart['productInfo']['attrInfo']['image']
  468. ];
  469. }
  470. $refundData['items'] = $items;
  471. $list[] = $refundData;
  472. }
  473. $result = $this->services->serviceDriver('order')->afterSaleUpload($list);
  474. if ($result['datas'][0]['issuccess'] != true) {
  475. Log::error('退货单:' . implode(',', $refundIds) . ' 发送失败,原因:' . $result['datas'][0]['msg']);
  476. }
  477. } catch (\Exception $e) {
  478. Log::error(['msg' => '售后单上传失败,原因:' . $e->getMessage(), 'data' => ['refundIds' => $refundIds, 'shopStatus' => $shopStatus]]);
  479. }
  480. }
  481. /**
  482. * 获取店铺编号
  483. * @param int $erpShopId
  484. * @return int
  485. */
  486. public function getShopId(int $erpShopId): int
  487. {
  488. if ($erpShopId < 1) {
  489. $erpShopId = sys_config('jst_default_shopid');
  490. }
  491. return (int)$erpShopId;
  492. }
  493. /**
  494. * ERP订单取消
  495. * @param string $orderId
  496. * @param array $erpOrderId
  497. * @return void
  498. */
  499. public function cancelOrder(string $orderId, array $erpOrderId = [])
  500. {
  501. $erpOrderId[] = $orderId;
  502. $result = $this->services->serviceDriver('order')->ordersSingleQuery(['so_ids' => array_filter($erpOrderId), 'status' => 'WaitConfirm']);
  503. if (!$erpIds = array_column($result['orders'], 'o_id')) {
  504. Log::error(['msg' => 'ERP订单取消失败,原因:未找到待发货订单', 'data' => [$orderId, $erpOrderId]]);
  505. return false;
  506. }
  507. $cancelData = ['o_ids' => $erpIds, 'cancel_type' => '用户申请退货, 重新生成订单'];
  508. $num = 3;
  509. for ($i = $num; $i >= 0; $i--) {
  510. if ($i <= 0) {
  511. Log::error(['msg' => 'ERP订单取消失败,调用均为异常', 'data' => [$orderId, $erpOrderId]]);
  512. return false;
  513. }
  514. try {
  515. $this->services->serviceDriver('order')->orderByOIdCancel($cancelData);
  516. return true;
  517. } catch (\Exception $e) {
  518. usleep(1000 * 50);
  519. Log::error('ERP订单取消失败,原因:' . $e->getMessage());
  520. }
  521. }
  522. return false;
  523. }
  524. /**
  525. * 售后收货回调
  526. * @param array $refund
  527. * @param int $type
  528. * @param bool $isUpload
  529. * @return void
  530. */
  531. public function receivedCallback(array $refund, int $type = 1, bool $isUpload = false)
  532. {
  533. try {
  534. /** @var StoreOrderServices $orderServices */
  535. $orderServices = app()->make(StoreOrderServices::class);
  536. /** @var StoreOrderRefundServices $orderRefundServices */
  537. $orderRefundServices = app()->make(StoreOrderRefundServices::class);
  538. $data = [
  539. 'refund_type' => 6, // 已退款
  540. 'refunded_time' => time()
  541. ];
  542. if ($type == 1) {
  543. $data['refund_status'] = 2;
  544. $data['refund_type'] = 6;
  545. } else if ($type == 2) {
  546. $data['refund_status'] = 0;
  547. $data['refund_type'] = 3;
  548. }
  549. $orderRefund = $orderRefundServices->getOne(['order_id' => $refund['outer_as_id']]);
  550. if (!$orderRefund) {
  551. throw new AdminException('Data does not exist!');
  552. }
  553. $id = (int)$orderRefund['id'];
  554. if ($type == 2) {
  555. $refundData = [
  556. 'refuse_reason' => 'ERP订单发货取消',
  557. 'refund_type' => 3,
  558. 'refunded_time' => time()
  559. ];
  560. $orderRefundServices->refuseRefund($id, $refundData, $orderRefund);
  561. } else {
  562. if ($orderRefund['is_cancel'] == 1) {
  563. throw new AdminException('用户已取消申请!');
  564. }
  565. $order = $orderServices->get((int)$orderRefund['store_order_id']);
  566. if (!$order) {
  567. throw new AdminException('Data does not exist!');
  568. }
  569. if (!in_array($orderRefund['refund_type'], [1, 2, 5])) {
  570. throw new AdminException('售后订单状态不支持该操作');
  571. }
  572. // 0元退款
  573. if ($orderRefund['refund_price'] == 0) {
  574. $refund_price = 0;
  575. } else {
  576. $refund_price = $orderRefund['refund_price'];
  577. if ($orderRefund['refund_price'] == $orderRefund['refunded_price']) {
  578. throw new AdminException('已退完支付金额!不能再退款了');
  579. }
  580. $data['refunded_price'] = bcadd($refund_price, $orderRefund['refunded_price'], 2);
  581. $bj = bccomp((string)$orderRefund['refund_price'], $data['refunded_price'], 2);
  582. if ($bj < 0) {
  583. throw new AdminException('退款金额大于支付金额,请修改退款金额');
  584. }
  585. }
  586. $refund_data['pay_price'] = $order['pay_price'];
  587. $refund_data['refund_price'] = $refund_price;
  588. if ($order['refund_price'] > 0) {
  589. mt_srand();
  590. $refund_data['refund_id'] = $order['order_id'] . rand(100, 999);
  591. }
  592. // 修改订单退款状态
  593. unset($data['refund_price']);
  594. if ($orderRefundServices->agreeRefund($id, $refund_data)) {
  595. $orderRefundServices->update($id, $data);
  596. } else {
  597. $orderRefundServices->storeProductOrderRefundYFasle($id, $refund_price);
  598. throw new AdminException('退货单退款失败');
  599. }
  600. // 重新上传订单
  601. if ($type && $isUpload) {
  602. $orders = explode('_', $refund['so_id']);
  603. $order = $orderServices->getOne(['order_id' => $orders[0] ?? ''], 'id, pid, status, refund_status, erp_order_id');
  604. if (!$order) {
  605. throw new AdminException('订单不存在');
  606. }
  607. $erpOrderId = [];
  608. $oid = (int)$order['id'];
  609. $status = (int)$order['status'];
  610. if ($order['pid'] < 0) {
  611. $childOrder = $orderServices->getOne([['pid', '=', $order['id']], ['status', '=', 0], ['refund_status', '=', 0]], 'id, pid, status, refund_status, erp_order_id');
  612. $refundChild = $orderServices->getColumn([['pid', '=', $order['id']], ['status', '=', 0], ['refund_status', '=', 2]], 'erp_order_id');
  613. if (!empty($refundChild)) {
  614. $erpOrderId = array_unique($refundChild);
  615. }
  616. if ($childOrder) {
  617. $oid = (int)$childOrder['id'];
  618. $erpOrderId[] = $childOrder['erp_order_id'];
  619. $status = (int)$childOrder['status'];
  620. }
  621. }
  622. if ($oid > 0) {
  623. !in_array($status, [2, 4]) && OrderSyncJob::dispatchDo('reorderOrder', [$order['erp_order_id'], $erpOrderId, $oid]);
  624. } else {
  625. Log::error(['msg' => '售后收货回调没有需要取消的订单', 'data' => $refund]);
  626. }
  627. }
  628. }
  629. } catch (\Exception $e) {
  630. Log::error(['msg' => '售后收货回调失败,原因:' . $e->getMessage(), 'data' => ['refund' => $refund, 'type' => $type, 'isUpload' => $isUpload]]);
  631. }
  632. }
  633. /**
  634. * 过滤ERP不可退订单
  635. * @param array $orders
  636. * @param int $oid
  637. * @return array
  638. */
  639. protected function filter(array $orders, int $oid): array
  640. {
  641. $cancelled = $waitConfirm = [];
  642. foreach ($orders as $cartOrder) {
  643. if (in_array($cartOrder['status'], ['WaitConfirm', 'Sent'])) {
  644. foreach ($cartOrder['items'] as $item) {
  645. if (!isset($waitConfirm[$item['sku_id']])) {
  646. $waitConfirm[$item['sku_id']] = $item['qty'];
  647. } else {
  648. $waitConfirm[$item['sku_id']] += $item['qty'];
  649. }
  650. }
  651. }
  652. }
  653. /** @var StoreOrderCartInfoServices $storeOrderCartInfoServices */
  654. $storeOrderCartInfoServices = app()->make(StoreOrderCartInfoServices::class);
  655. $cartInfo = $storeOrderCartInfoServices->getOrderCartInfo($oid);
  656. foreach ($cartInfo as $cart) {
  657. $attrInfo = $cart['cart_info']['productInfo']['attrInfo'];
  658. $cancelled[$attrInfo['code']] = $cart['cart_info']['cart_num'];
  659. if (isset($waitConfirm[$attrInfo['code']])) {
  660. $cancelled[$attrInfo['code']] -= $waitConfirm[$attrInfo['code']];
  661. if ($cancelled[$attrInfo['code']] < 1) {
  662. unset($cancelled[$attrInfo['code']]);
  663. }
  664. }
  665. }
  666. $refundData = $this->getOrderRefund($oid);
  667. foreach ($refundData as $key => $item) {
  668. $num = $cancelled[$key] ?? 0;
  669. if ($num > 0) {
  670. $cancelled[$key] -= $item;
  671. if ($cancelled[$key] < 1) {
  672. unset($cancelled[$key]);
  673. }
  674. }
  675. }
  676. return $cancelled;
  677. }
  678. /**
  679. * 累计订单已退数量
  680. * @param int $oid
  681. * @return array
  682. */
  683. public function getOrderRefund(int $oid): array
  684. {
  685. $attrData = [];
  686. $refunds = $this->orderRefundList($oid);
  687. foreach ($refunds as $refund) {
  688. foreach ($refund['cart_info'] as $item) {
  689. if (!isset($attrData[$item['productInfo']['attrInfo']['code']])) {
  690. $attrData[$item['productInfo']['attrInfo']['code']] = $item['cart_num'];
  691. } else {
  692. $attrData[$item['productInfo']['attrInfo']['code']] += $item['cart_num'];
  693. }
  694. }
  695. }
  696. return $attrData;
  697. }
  698. /**
  699. * 存在申请退货单则关闭
  700. * @param int $oid
  701. * @return bool
  702. */
  703. public function checkRefundApply(int $oid): bool
  704. {
  705. $refunds = $this->orderRefundList($oid, 1);
  706. if (!empty($refunds)) {
  707. $refundIds = [];
  708. foreach ($refunds as $refund) {
  709. $refundIds[] = $refund['id'];
  710. $this->receivedCallback(['outer_as_id' => $refund['order_id']], 2);
  711. }
  712. // 关闭 ERP 退货单
  713. $this->refundOrderUpload($refundIds, 'CLOSED');
  714. }
  715. return true;
  716. }
  717. /**
  718. * 订单退货单相关信息
  719. * @param int $id
  720. * @param int $refundType
  721. * @return array
  722. */
  723. public function orderRefundList(int $id, int $refundType = 4): array
  724. {
  725. /** @var StoreOrderRefundServices $storeOrderRefundServices */
  726. $storeOrderRefundServices = app()->make(StoreOrderRefundServices::class);
  727. $refunds = $storeOrderRefundServices->getRefundList(['store_order_id' => $id, 'refundTypes' => $refundType], 'id, store_order_id, store_id, order_id, uid, cart_info');
  728. /** @var StoreOrderServices $orderServices */
  729. $orderServices = app()->make(StoreOrderServices::class);
  730. $ids = $orderServices->Value(['pid' => $id], 'GROUP_CONCAT(id)');
  731. if ($ids) {
  732. $ids = array_filter(explode(',', $ids));
  733. $list = $storeOrderRefundServices->getRefundList(['store_order_id' => $ids, 'refundTypes' => $refundType], 'id, store_order_id, store_id, order_id, uid, cart_info');
  734. $refunds = array_merge($refunds, $list);
  735. }
  736. return $refunds;
  737. }
  738. /**
  739. * ERP订单号
  740. * @return string $orderId
  741. * @return string $erpOrderId
  742. */
  743. public function getErpOrderId(string $orderId, string $erpOrderId = ''): string
  744. {
  745. if (!empty($erpOrderId)) {
  746. $arr = explode('_', $erpOrderId);
  747. if (count($arr) == 1) {
  748. return $erpOrderId . '_1';
  749. }
  750. $num = end($arr);
  751. return $arr[0] . '_' . ++$num;
  752. }
  753. return $orderId;
  754. }
  755. }