RefundService.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. <?php
  2. namespace addons\QiJuhe\common\service;
  3. use addons\QiJuhe\common\models\ExpressModel;
  4. use addons\QiJuhe\common\models\GoodsSkuModel;
  5. use addons\QiJuhe\common\models\MallOrderRefundModel;
  6. use addons\QiJuhe\common\enums\OrderRefundEnum;
  7. use addons\QiJuhe\common\enums\QiJuheEnum;
  8. use addons\QiJuhe\common\enums\RefundTypeEnum;
  9. use addons\QiJuhe\common\models\MallOrderModel;
  10. use addons\QiJuhe\common\models\OrderModel;
  11. use addons\QiJuhe\common\models\OrderRefundModel;
  12. use common\enums\EventNameEnum;
  13. use common\enums\OrderStatusEnum;
  14. use common\enums\RefundStatusEnum;
  15. use common\enums\RefundTypeEnum as EnumsRefundTypeEnum;
  16. use common\enums\AddonsEnum;
  17. use common\enums\StatusEnum;
  18. use common\enums\RabbitMqEnum;
  19. use common\globals\GlobalInvoke;
  20. use common\logic\order\OrderRefundLogic;
  21. use common\logic\statistics\StatisticsHandle;
  22. use common\models\common\PaymentTrade;
  23. use common\models\order\Order;
  24. use common\models\order\OrderDetail;
  25. use common\models\order\OrderRefund;
  26. use common\models\order\OrderRefundStep;
  27. use common\models\goods\Goods;
  28. use Yii;
  29. use Exception;
  30. use yii\data\Pagination;
  31. use yii\db\Expression;
  32. use yii\helpers\Json;
  33. /**
  34. * 售后服务类
  35. */
  36. class RefundService extends BaseService
  37. {
  38. public $mall_id;
  39. public $supply_id;
  40. public function getList($params)
  41. {
  42. $limit = 10;
  43. $model = OrderRefundModel::find();
  44. $model->where(['mall_id' => $this->mall_id]);
  45. $model->with([
  46. // 'detail' => function ($query) {
  47. // $query->select('id, order_id, goods_attr_name, price, goods_price, pic_url, goods_name');
  48. // },
  49. 'order' => function ($query) {
  50. $query->select('id, region_name, address, receiver_name, shipping_money, goods_price, mobile');
  51. },
  52. 'refund' => function ($query) {
  53. $query->select('id, order_no, reason, refund_price, pic_list, type, remark, refund_status');
  54. },
  55. ]);
  56. // 查询
  57. if (!empty($params['name'])) {
  58. $model->andWhere([
  59. 'order_id' => OrderDetail::find()->where(['like', 'goods_name', $params['name']])->select('order_id'),
  60. ]);
  61. }
  62. if (!empty($params['order_no'])) {
  63. $model->andWhere([
  64. 'order_refund_id' => OrderRefund::find()->where(['like', 'order_no', $params['order_no']])->select('id')->column(),
  65. ]);
  66. }
  67. // 订单编号
  68. if (!empty($params['shop_order_no'])) {
  69. $model->andWhere(['order_no' => trim($params['shop_order_no'])]);
  70. }
  71. if (isset($params['refund_status']) && $params['refund_status'] !== '') {
  72. $model->andWhere(['order_id' => OrderRefund::find()->where(['refund_status' => $params['refund_status']])
  73. ->andWhere(['mall_id' => $this->mall_id])->select('order_id')->column()]);
  74. }
  75. if (!empty($params['data'])) {
  76. list($start, $end) = $params['data'];
  77. $start = strtotime($start);
  78. $end = strtotime($end);
  79. $model->andFilterWhere(['between', 'created_at', $start, $end]);
  80. }
  81. $pagination = new Pagination(['totalCount' => $model->count(), 'pageSize' => $limit]);
  82. $model->limit($pagination->limit)->offset($pagination->offset);
  83. $list = $model->orderBy('id desc')
  84. ->asArray()
  85. ->all();
  86. foreach ($list as $k => $v) {
  87. $list[$k]['status_text'] = OrderRefundEnum::getValue($v['status']);
  88. $list[$k]['data'] = Json::decode($v['data']);
  89. $list[$k]['refund']['pic_list'] = Json::decode($v['refund']['pic_list']);
  90. $list[$k]['refund']['type_text'] = RefundTypeEnum::getValue($v['refund']['type']);
  91. if(!empty($v['order_detail_id'])){
  92. $field = ['id'=>$v['order_detail_id']];
  93. }else{
  94. $field = ['order_id'=>$v['order_id']];
  95. }
  96. $list[$k]['detail'] = OrderDetail::find()
  97. ->where($field)
  98. ->select('id, order_id, goods_attr_name, price, goods_price, pic_url, goods_name')
  99. ->asArray()
  100. ->one();
  101. }
  102. $data['list'] = $list;
  103. $data['page_count'] = $pagination->pageCount;
  104. $data['refund_status_list'] = RefundStatusEnum::getMap();
  105. return $data;
  106. }
  107. /**
  108. * 获取售后类型
  109. *
  110. * @return void
  111. */
  112. public function getFeedbackTypeReason($feedback_type)
  113. {
  114. if (empty($feedback_type)) {
  115. return [];
  116. }
  117. foreach ($feedback_type as $k => $v) {
  118. $param = RefundTypeEnum::getReasonParam($v['id']);
  119. $res = ApiService::getConnect($this->mall_id, $this->supply_id)->afterSalesReason(...array_values($param));
  120. if ($res === false) {
  121. return $this->error('获取获取售后原因失败');
  122. }
  123. $feedback_type[$k]['reason'] = $this->formatReasons($res['reasons']);
  124. }
  125. return $feedback_type;
  126. }
  127. public function getAfterSalesTypeNameMap($order_item_id)
  128. {
  129. // 获取订单ID
  130. return ApiService::getConnect($this->mall_id, $this->supply_id)->getAfterSalesTypeNameMap(0, $order_item_id);
  131. }
  132. /**
  133. * 获取售后原因
  134. *
  135. * @param integer $status
  136. * @return array|boolean
  137. */
  138. public function afterSalesReason($status)
  139. {
  140. if (empty($params = $this->status_arr[$status])) {
  141. return $this->error('');
  142. }
  143. $res = ApiService::getConnect($this->mall_id)->afterSalesReason(...array_values($params));
  144. if ($res === false) {
  145. return $this->error('获取获取售后原因失败');
  146. }
  147. return $res['reasons'];
  148. }
  149. /**
  150. * 格式化售后原因
  151. *
  152. * @param array $reasons
  153. * @return array
  154. */
  155. private function formatReasons($reasons)
  156. {
  157. $res = [];
  158. foreach ($reasons as $k => $v) {
  159. $res[] = [
  160. 'id' => $k,
  161. 'value' => $v,
  162. ];
  163. }
  164. return $res;
  165. }
  166. /**
  167. * 添加售后
  168. *
  169. * @param array $params
  170. * @return MallOrderRefund
  171. */
  172. public function doRefund($params)
  173. {
  174. $params2 = $params;
  175. $db = Yii::$app->db->beginTransaction();
  176. try {
  177. //订单详情表
  178. $order_detail = OrderDetail::findOne(['id' => $params['order_detail_id'], 'user_id' => $params['user_id']]);
  179. if (empty($order_detail)) throw new Exception('订单不存在');
  180. if ($order_detail->order_status == OrderStatusEnum::ACCOMPLISH) throw new Exception('订单已过售后时间,无法申请售后');
  181. if (!in_array($order_detail->order_status, [OrderStatusEnum::PAY, OrderStatusEnum::SHIPMENTS, OrderStatusEnum::SING])) {
  182. throw new Exception('该订单状态下,无法申请售后');
  183. }
  184. // 订单是否聚合
  185. $order = MallOrderModel::find()->where(['id' => $order_detail['order_id']])->select('id, order_no, mall_id, shipping_money, order_source, pay_money, order_status, out_trade_no')->one();
  186. if($order->order_source == AddonsEnum::ADDONS_NAME_CARD_COUPONS){
  187. $goods = Goods::findOne($order_detail['goods_id']);
  188. if(!$goods || $goods['goods_source'] != QiJuheEnum::PLUGIN_SIGN) throw new Exception('该订单不是聚合供应链订单');
  189. }else{
  190. if ($order->order_source != QiJuheEnum::PLUGIN_SIGN) throw new Exception('该订单不是聚合供应链订单');
  191. }
  192. // 公共调用
  193. // $order_detail = GlobalInvoke::exec(GlobalInvoke::ORDER_REFUND_SUBMIT, $this->mall_id, [
  194. // 'order_detail' => $order_detail,
  195. // 'mall_id' => $this->mall_id,
  196. // ]) ?: $order_detail;
  197. $params['refund_way'] = StatusEnum::ENABLED; // 整单退
  198. if ($params['type'] == RefundTypeEnum::ONLY_PART_MONEY) { // 部分退款
  199. $params['type'] = RefundTypeEnum::ONLY_MONEY; // 修改成仅退款
  200. $params['refund_way'] = StatusEnum::DISABLED; // 自定义金额
  201. } elseif ($params['type'] != RefundTypeEnum::EXCHANGE_GOODS) { // 按数量退款
  202. // 一共可以退款
  203. $refund_total_money = bcadd($order_detail->goods_price, $order_detail->adjust_money, 2);
  204. // 待发货添加邮费
  205. // if ($order_detail['order_status'] == OrderStatusEnum::PAY) {
  206. // $data['goods_info']['goods_price'] = bcadd($order_detail['goods_price'], $order->shipping_money, 2);
  207. // }
  208. // 计算单价
  209. $goods_single_price = bcdiv($refund_total_money, $order_detail->num, 2);
  210. $params['refund_price'] = bcmul($params['num'], $goods_single_price, 2);
  211. if ($params['refund_price'] > $refund_total_money) {
  212. throw new Exception('最多可退款金额¥' . $refund_total_money);
  213. }
  214. } else {
  215. $params['refund_price'] = 0; // 如果是换货,则退款金额为0
  216. }
  217. $order_detail->is_feedback = OrderStatusEnum::FEEDBACKING;
  218. $order_detail->num = intval($order_detail->num);
  219. if(!$order_detail->save()) throw new Exception($order_detail->getErrorMessage());
  220. // 处理维权步骤
  221. !empty($params['pic_list']) && $params['pic_list'] = Json::decode($params['pic_list']);
  222. MallOrderRefundModel::stepHandle($params);
  223. // 退款主表
  224. $params2['is_feedback'] = $params['is_feedback'] = OrderStatusEnum::FEEDBACKING;
  225. $params2['order_id'] = $params['order_id'] = $order_detail->order_id;
  226. $params2['order_no'] = $params['order_no'] = Order::generateOrderNo('r');
  227. $params['is_full'] = "{$order_detail->order_id}_{$order_detail->id}"; // 是否整单退
  228. $order_refund_model = MallOrderRefundModel::setData($params);
  229. if(!$order_refund_model) throw new Exception(MallOrderRefundModel::getStaticError());
  230. $params2['order_refund_id'] = $params['order_refund_id'] = $order_refund_model->id;
  231. $res = OrderRefundStep::setData($params);
  232. if(!$res) throw new Exception(OrderRefundStep::getStaticError());
  233. //自动维护订单主表
  234. $res = Order::autoUpdateStatus($order);
  235. $trade = PaymentTrade::findOne(['out_trade_no' => $order->out_trade_no]);
  236. $trade->addRefundItem($order_refund_model, $order);
  237. // 中台订单
  238. $supply_order = OrderModel::find()->where(['order_id' => $order->id,'order_detail_id'=>$order_detail['id']])->one();
  239. if (empty($supply_order)) {
  240. throw new Exception('供应链订单不存在');
  241. }
  242. // 添加聚合售后
  243. OrderRefundModel::add([
  244. 'user_id' => $params['user_id'],
  245. 'order_no' => $order->order_no,
  246. 'mall_id' => $order->mall_id,
  247. 'supply_id' => $supply_order->supply_id,
  248. 'order_id' => $order_detail['order_id'],
  249. 'order_detail_id' => $order_detail['id'],
  250. 'order_refund_id' => $order_refund_model->id,
  251. 'num' => intval($params['num'] ?: $order_detail->num),
  252. 'data' => $this->getSendData($params, $order, $supply_order, $order_detail),
  253. ]);
  254. $db->commit();
  255. // 触发售后统计
  256. $refund_event = ['id' =>$order_refund_model->id, 'type' => EnumsRefundTypeEnum::EVENT_APPLY];
  257. Yii::$app->services->rabbitMq->push(
  258. RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  259. 'type' => EventNameEnum::ORDER_REFUND,
  260. 'params' => $refund_event,
  261. ], StatisticsHandle::class, 'handleSync'
  262. );
  263. return $order_refund_model;
  264. } catch (Exception $e) {
  265. $db->rollback();
  266. return $this->error($e->getMessage());
  267. }
  268. }
  269. /**
  270. * 获取推送数据
  271. *
  272. * @param array $params
  273. * @return array
  274. */
  275. public function getSendData($params, $order, $supply_order, OrderDetail $order_detail)
  276. {
  277. $data = [
  278. 'third_order_sn' => $order->order_no, // 订单id与采购端订单号两个必传一个
  279. 'order_item_id' => (int)$supply_order->getOrderItemId(), // 子订单id
  280. 'reason_type' => (int)$params['reason_id'], // 售后原因(售后原因API返回)
  281. 'reason' => $params['remark'], // 售后原因是 其他的的时候填写
  282. 'description' => $params['remark'], // 描述
  283. 'is_received' => (int)RefundTypeEnum::getReasonParam($params['type'])['is_received'], // 是否收到货 1是0否
  284. 'detail_images' => $params['pic_list'] ?? [], // 图片凭证
  285. 'refund_type' => (int)RefundTypeEnum::getReasonParam($params['type'])['after_sale_type'], // 0退款 1退货退款
  286. 'refund_way' => $params['refund_way'], // 0自定义金额 1个数 部分退款的情况下 这里只能填写0
  287. // 'amount' => intval(bcmul($params['refund_price'], 100)),
  288. ];
  289. $attr = GoodsSkuModel::findOne(['attr_id' => $order_detail->goods_attr_id]);
  290. if ($data['refund_type'] == 2) { // 换货
  291. if (isset($data['num'])) unset($data['num']);
  292. $data['barter_sku_id'] = $attr->sku_id; // 换货中台规格id
  293. $data['barter_num'] = $params['num'] ?? intval($order_detail->num); // 换货数量 - 买了多少就是多少
  294. // $data['amount'] = 0; // 换货则退款金额为0
  295. }
  296. if ($params['refund_way'] == StatusEnum::DISABLED) { // 部分退款
  297. $data['amount'] = intval(bcmul($params['refund_price'], 100)); // 中台退款金额
  298. $data['apply_amount'] = $params['refund_price']; // 申请金额
  299. } else {
  300. $data['num'] = $params['num'] ?? intval($order_detail->num); // 退款商品个数 refund_way=1必填
  301. }
  302. return $data;
  303. }
  304. /**
  305. * 驳回申请
  306. *
  307. * @param array $ids
  308. * @return integer
  309. */
  310. public function reject($ids)
  311. {
  312. $list = OrderRefundModel::find()->where(['id' => $ids, 'status' => OrderRefundEnum::NOT_PUSHED])->all();
  313. $num = 0;
  314. foreach ($list as $v) {
  315. $db = Yii::$app->db->beginTransaction();
  316. try {
  317. self::refuse($v);
  318. $db->commit();
  319. $num++;
  320. } catch (Exception $e) {
  321. $db->rollback();
  322. $v->error_msg = $e->getMessage();
  323. $v->save();
  324. }
  325. }
  326. return $num;
  327. }
  328. /**
  329. * 同意申请
  330. *
  331. * @param array $ids [1,2,3]
  332. * @return void
  333. */
  334. public function agree(array $ids, $amount, $refund_way)
  335. {
  336. /**
  337. * @var $list OrderRefundModel[]
  338. */
  339. $list = OrderRefundModel::find()->where(['id' => $ids, 'status' => OrderRefundEnum::NOT_PUSHED])->all();
  340. $num = 0;
  341. foreach ($list as $v) {
  342. $db = Yii::$app->db->beginTransaction();
  343. try {
  344. $data = Json::decode($v->data);
  345. if ($data['refund_way'] === 0 && $refund_way === '0' && $amount > 0) {
  346. $data['amount'] = intval(bcmul($amount, 100));
  347. }
  348. $v->data = Json::encode($data);
  349. $res = $this->push($v);
  350. $v->pushOk($res);
  351. $num++;
  352. $db->commit();
  353. } catch (Exception $e) {
  354. $db->rollback();
  355. $v->error_msg = $e->getMessage();
  356. $v->save();
  357. }
  358. }
  359. return $num;
  360. }
  361. /**
  362. * 推送申请
  363. *
  364. * @param OrderRefundModel $model
  365. * @return void
  366. */
  367. public function push(OrderRefundModel $model)
  368. {
  369. $api = ApiService::getConnect($model->mall_id, $model->supply_id);
  370. if (false !== $api->afterSalesCreate(Json::decode($model->data))) {
  371. return [];
  372. }
  373. throw new Exception($api->error);
  374. }
  375. /**
  376. * 驳回
  377. *
  378. * @param OrderRefundModel $model
  379. * @return void
  380. */
  381. public static function refuse(OrderRefundModel $model)
  382. {
  383. $model->status = OrderRefundEnum::REFUSE;
  384. $res = $model->save();
  385. if ($res === false) throw new Exception($model->getError());
  386. $res = OrderRefundLogic::refuseRefundForChain($model->mall_id, $model->order_refund_id);
  387. if ($res === false) throw new Exception(OrderRefund::$static_error);
  388. }
  389. /**
  390. * 同意售后
  391. *
  392. * @param string $order_no
  393. * @return boolean
  394. */
  395. public function after_sales_pass($order_no, $after_sales_id, $order_item_id)
  396. {
  397. // $order_id = Order::find()->where(['order_no' => $order_no])->select('id')->scalar();
  398. // $order_reufnd = OrderRefund::find()
  399. // ->where(['mall_id' => $this->mall_id])
  400. // ->andWhere(['=', 'order_id', $order_id])
  401. // ->andWhere(['>', 'step_status', 0])
  402. // ->andWhere(['<>', 'refund_status', RefundStatusEnum::REVOKE])
  403. // ->one();
  404. //
  405. // // 插件售后订单
  406. // $order_refund = OrderRefundModel::findOne(['order_no' => $order_no, 'after_sales_id' => new Expression("after_sales_id IS NULL"), 'status' => OrderRefundEnum::PUSHED]);
  407. /**
  408. * @var $order_refund OrderRefundModel
  409. */
  410. $order_refund = OrderRefundModel::find()->where(['order_no' => $order_no])->andWhere(['status' => OrderRefundEnum::PUSHED])
  411. ->andWhere(['like', 'data', new Expression('\'%"order_item_id":'.$order_item_id.',%\'')])->orderBy('id desc')->one();
  412. if (empty($order_refund) || !empty($order_refund->after_sales_id)) return true;
  413. $order_reufnd = OrderRefund::findOne(['id' => $order_refund->order_refund_id]);
  414. // 更新插件订单供应链端售后ID
  415. $order_refund->after_sales_id = $after_sales_id;
  416. // 仅退款
  417. if ($order_reufnd->type == RefundTypeEnum::ONLY_MONEY) {
  418. $order_refund->save();
  419. return true;
  420. }
  421. // 退货退款
  422. if (in_array($order_reufnd->type, [RefundTypeEnum::MONEY_AND_PRODUCT, RefundTypeEnum::EXCHANGE_GOODS])) {
  423. // 获取退货地址
  424. $api = ApiService::getConnect($order_refund->mall_id, $order_refund->supply_id);
  425. if (false == ($address = $api->findShopAddressByOrderId($after_sales_id))) {
  426. throw new Exception($api->error);
  427. }
  428. // 修改退货地址ID
  429. $order_refund->shipping_address_id = $address['id'];
  430. $order_refund->save();
  431. // 退货地址
  432. $refund_address = [
  433. 'consignee' => $address['contacts'],
  434. 'mobile' => $address['tel'],
  435. 'address' => $address['province_name']['name'] . $address['city_name']['name'] . $address['district_name']['name'] . $address['address'],
  436. ];
  437. $logic = new OrderRefundLogic();
  438. if ($logic->execute($this->mall_id, [
  439. 'id' => $order_reufnd->id,
  440. 'address' => $refund_address,
  441. 'step_status' => $order_reufnd->type == RefundTypeEnum::MONEY_AND_PRODUCT ? RefundStatusEnum::STEP_2 : RefundStatusEnum::STEP_8,
  442. ])) {
  443. return true;
  444. } else {
  445. throw new Exception($logic->getError());
  446. }
  447. }
  448. throw new Exception('退货退款');
  449. }
  450. /**
  451. * 中台发货
  452. * @param $order_no
  453. * @param $after_sales_id
  454. * @return true|void
  455. * @throws Exception
  456. */
  457. public function after_sales_receiving($order_no, $after_sales_id)
  458. {
  459. /**
  460. * @var $order_refund OrderRefundModel
  461. */
  462. $refund = OrderRefundModel::find()->where(['after_sales_id' => $after_sales_id])->orderBy('id desc')->one();
  463. if (empty($refund)) return true;
  464. $order_refund = OrderRefund::findOne(['id' => $refund->order_refund_id]);
  465. if (empty($order_refund)) throw new Exception('退款订单不存在!');
  466. // 这里处理退款订单信息
  467. if (in_array($order_refund->type, [RefundTypeEnum::EXCHANGE_GOODS, RefundTypeEnum::MONEY_AND_PRODUCT])) {
  468. // 中台订单
  469. /**
  470. * @var $supply_order OrderModel
  471. */
  472. $supply_order = OrderModel::find()->where(['order_id' => $order_refund->order_id,'order_detail_id'=>$order_refund->order_detail_id])->one();
  473. if (empty($supply_order)) {
  474. throw new Exception('供应链订单不存在');
  475. }
  476. $api = ApiService::getConnect($order_refund->mall_id, $refund->supply_id);
  477. if (!$res = $api->getAfterSalesByOrderItemId($supply_order->getOrderItemId())) {
  478. throw new Exception($api->error);
  479. }
  480. $logic = new OrderRefundLogic();
  481. $rLogic = $logic->execute($order_refund->mall_id, [
  482. 'id' => $order_refund->id,
  483. 'step_status' => RefundStatusEnum::STEP_9,
  484. 'express' => [
  485. 'saler_express' => $res['after_sales']['return_order_express']['company_name'] ?? '',
  486. 'saler_express_no' => $res['after_sales']['return_order_express']['express_no'] ?? '',
  487. ],
  488. ]);
  489. if ($rLogic) {
  490. return true;
  491. }
  492. throw new Exception('退货退款' . $logic->getError());
  493. }
  494. }
  495. public function after_receipt($refund_id)
  496. {
  497. $refund = OrderRefundModel::findOne(['order_refund_id' => $refund_id]);
  498. if (empty($refund)) throw new Exception('退款订单不存在');
  499. $api = ApiService::getConnect($refund->mall_id, $refund->supply_id);
  500. if (!$api->refundUserReceive($refund->after_sales_id)) {
  501. throw new Exception($api->error);
  502. }
  503. }
  504. /**
  505. * 售后确认退款
  506. *
  507. * @param string $order_no
  508. * @return void
  509. */
  510. public function after_sales_complete($order_no, $after_sales_id)
  511. {
  512. // $order_id = Order::find()->where(['order_no' => $order_no])->select('id')->scalar();
  513. // $order_reufnd = OrderRefund::find()
  514. // ->where(['mall_id' => $this->mall_id])
  515. // ->andWhere(['=', 'order_id', $order_id])
  516. // ->andWhere(['>', 'step_status', 0])
  517. // ->andWhere(['<>', 'refund_status', RefundStatusEnum::REVOKE])
  518. // ->one();
  519. /**
  520. * @var $order_refund OrderRefundModel
  521. */
  522. $t_refund = OrderRefundModel::find()->where(['after_sales_id' => $after_sales_id])->orderBy('id desc')->one();
  523. if (empty($t_refund)) return true;
  524. $order_reufnd = OrderRefund::findOne(['id' => $t_refund->order_refund_id]);
  525. // 仅退款
  526. if ($order_reufnd->type == RefundTypeEnum::ONLY_MONEY) {
  527. $logic = new OrderRefundLogic();
  528. if ($logic->execute($this->mall_id, [
  529. 'id' => $order_reufnd->id,
  530. 'step_status' => RefundStatusEnum::STEP_6
  531. ])) {
  532. return true;
  533. }
  534. throw new Exception('仅退款' . $logic->getError());
  535. }
  536. // 退货退款
  537. if ($order_reufnd->type == RefundTypeEnum::MONEY_AND_PRODUCT) {
  538. $logic = new OrderRefundLogic();
  539. if ($logic->execute($this->mall_id, [
  540. 'id' => $order_reufnd->id,
  541. 'step_status' => RefundStatusEnum::STEP_4
  542. ])) {
  543. return true;
  544. }
  545. throw new Exception('退货退款' . $logic->getError());
  546. }
  547. }
  548. /**
  549. * 驳回售后并关闭售后
  550. *
  551. * @param string $order_no
  552. * @param int $after_sales_id
  553. * @return void
  554. */
  555. public function after_sales_reject($order_no, $after_sales_id, $order_item_id)
  556. {
  557. $order_id = Order::find()->where(['order_no' => $order_no])->select('id')->scalar();
  558. $order_reufnd = OrderRefund::find()
  559. ->where(['mall_id' => $this->mall_id])
  560. ->andWhere(['=', 'order_id', $order_id])
  561. ->andWhere(['>', 'step_status', 0])
  562. ->andWhere(['<>', 'refund_status', RefundStatusEnum::REVOKE])
  563. ->one();
  564. $orderDetail = OrderRefundModel::find()
  565. ->where("JSON_CONTAINS(data, CAST(:q AS JSON), '$.order_item_id')", [':q' => $order_item_id])
  566. ->select('order_detail_id')
  567. ->one();
  568. if (empty($orderDetail)) {
  569. throw new Exception('未找到匹配的记录');
  570. }
  571. if (empty($order_reufnd)) {
  572. throw new Exception('订单不存在或者已处理');
  573. }
  574. $order_refund_model = OrderRefundModel::findOne(['order_refund_id'=>$order_reufnd['id']]);
  575. if (empty($order_refund_model)) {
  576. throw new Exception('七件事供应链售后订单不存在或者已处理');
  577. }
  578. // 查询售后订单
  579. $api = ApiService::getConnect($order_reufnd->mall_id, $order_refund_model->supply_id);
  580. $detail = $api->getAfterSales($after_sales_id);
  581. $reason = isset($detail['after_sales']['logs'][1]['content']) ? $detail['after_sales']['logs'][1]['content'] : '上游拒绝售后';
  582. OrderRefundLogic::refuseRefundForChainQijuhe($order_reufnd->mall_id, $orderDetail->order_detail_id, $reason);
  583. }
  584. /**
  585. * 售后取消
  586. *
  587. * @param integer $id
  588. * @return void
  589. */
  590. public function cancel($id)
  591. {
  592. $order_refund = OrderRefundModel::find()->where(['order_refund_id' => $id])->one();
  593. if (empty($order_refund)) {
  594. return true;
  595. }
  596. // 推送
  597. if ($order_refund->status == OrderRefundEnum::PUSHED) {
  598. $api = ApiService::getConnect($order_refund->mall_id, $order_refund->supply_id);
  599. // 获取售后ID
  600. $sales_list = $api->afterSalesList($order_refund->order_no);
  601. if (empty($sales_list)) {
  602. return $this->error('供应链端无法查到售后信息');
  603. }
  604. $salse_info = current($sales_list['list']);
  605. if (empty($salse_info)) {
  606. return $this->error('供应链端无法查到售后信息2');
  607. }
  608. // 关闭售后订单
  609. $api->afterSalesClose($salse_info['id']);
  610. }
  611. $order_refund->status = OrderRefundEnum::REFUSE;
  612. $order_refund->save();
  613. return true;
  614. }
  615. public function thirdCancel($order_no, $after_sales_id)
  616. {
  617. /**
  618. * @var $order_refund OrderRefundModel
  619. */
  620. $order_refund = OrderRefundModel::find()->where(['order_no' => $order_no])
  621. ->andWhere(['after_sales_id' => $after_sales_id])->one();
  622. if (empty($order_refund)) {
  623. return true;
  624. }
  625. $api = ApiService::getConnect($order_refund->mall_id, $order_refund->supply_id);
  626. // 获取售后ID
  627. $sales = $api->getAfterSales($after_sales_id);
  628. if (empty($sales)) {
  629. return $this->error('供应链端无法查到售后信息');
  630. }
  631. $logic = new OrderRefundLogic();
  632. $res = $logic->execute($order_refund->mall_id, [
  633. 'step_status' => -1,
  634. 'id' => $order_refund->order_refund_id,
  635. 'refund_reason' => $sales['after_sales_audit']['cause'] ?? '未查询到理由',
  636. ]);
  637. if (!$res) return $this->error($logic->getError());
  638. return true;
  639. }
  640. public static function isSupportExchange($goods_id)
  641. {
  642. $attr = GoodsSkuModel::findOne(['goods_id' => $goods_id]);
  643. if (empty($attr)) return false;
  644. $goods = GoodsSkuModel::findOne(['goods_id' => $attr->supply_goods_id]);
  645. if (empty($goods)) return false;
  646. return $goods->source == QiJuheEnum::SUPPLY_YIYATONG;
  647. }
  648. public function buyerDelivery($id)
  649. {
  650. $refund = OrderRefundModel::findOne(['order_refund_id' => $id]);
  651. if (empty($refund)) throw new Exception("退款不存在");
  652. $order_refund = OrderRefund::findOne(['id' => $id]);
  653. if (empty($order_refund)) throw new Exception("退款不存在!");
  654. $api = ApiService::getConnect($refund->mall_id, $refund->supply_id);
  655. $express = ExpressModel::findOne(['name' => $order_refund->express]);
  656. if (empty($express)) throw new Exception("快递公司不存在 {$order_refund->express} {$order_refund->order_no}");
  657. $sales_list = $api->afterSalesSend([
  658. 'after_sales_id' => $refund->after_sales_id,
  659. 'company_name' => $express->name,
  660. 'company_code' => $express->code,
  661. 'express_no' => $order_refund->express_no,
  662. // 'shipping_address_id' => ''
  663. ]);
  664. var_dump($sales_list);
  665. }
  666. }