OrderRefundLogic.php 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. <?php
  2. namespace common\logic\order;
  3. use addons\PerformanceDividend\common\logic\PerformanceDividendRewardLogic;
  4. use addons\Store\common\enums\StoreEnum;
  5. use addons\Store\common\models\StoreGoodsReserve;
  6. use addons\Store\common\models\StoreOrder;
  7. use addons\Store\common\models\StoreOrderDetail;
  8. use addons\Store\common\models\StoreOrderMarketing;
  9. use addons\ValetOrder\common\service\OrderService;
  10. use addons\WangDianTong\common\service\OrderRefundRejectService;
  11. use addons\CardCoupons\common\models\CardCouponsScoreGive;
  12. use common\enums\AddonsEnum;
  13. use common\enums\EventNameEnum;
  14. use common\enums\OrderSourceEnum;
  15. use common\enums\OrderStatusEnum;
  16. use common\enums\PaymentTradeOrderEnum;
  17. use common\enums\RefundStatusEnum;
  18. use common\enums\RefundTypeEnum;
  19. use common\enums\StatusEnum;
  20. use common\enums\UserWalletEnum;
  21. use common\events\order\OrderRefundChangeEvent;
  22. use common\events\order\OrderRefundFinishEvent;
  23. use common\events\order\OrderRefundUpdateEvent;
  24. use common\logic\statistics\StatisticsHandle;
  25. use common\logic\wechat\WechatMessageLogic;
  26. use common\models\common\PaymentTrade;
  27. use common\models\common\PaymentTradeOrder;
  28. use common\models\goods\GoodsReserve;
  29. use common\models\mall\MallAddons;
  30. use common\models\order\Order;
  31. use common\models\order\OrderDetail;
  32. use common\models\order\OrderMarketing;
  33. use common\models\order\OrderRefund;
  34. use common\models\order\OrderRefundStep;
  35. use common\models\common\Addons;
  36. use common\models\user\DigitalLog;
  37. use common\models\user\UserDigital;
  38. use common\traits\ErrorTrait;
  39. use Yii;
  40. use Exception;
  41. use services\common\UserWalletService;
  42. use yii\helpers\Json;
  43. use common\enums\PayTypeEnum;
  44. use common\globals\GlobalInvoke;
  45. use services\common\ThirdPartyRefundService;
  46. use common\models\common\CapitalLog;
  47. use addons\Boss\common\models\AddonsBossRewardLog;
  48. use addons\WuzhouChain\common\services\OrderService as ServicesOrderService;
  49. use common\enums\RabbitMqEnum;
  50. use common\models\common\PaymentTradeItemRefund;
  51. /**
  52. * 售后订单处理
  53. * Class OrderRefundLogic
  54. * @Author: lun
  55. * @DateTime: 2021/10/11 0011 14:11
  56. * @Copyright: copyright (c) 2021 广东七件事集团
  57. * @package common\logic\order
  58. */
  59. class OrderRefundLogic
  60. {
  61. use ErrorTrait;
  62. /**
  63. * 售后处理
  64. * @Author: lun
  65. * @DateTime: 2021/10/11 0011 15:55
  66. * @Copyright: copyright (c) 2021 广东七件事集团
  67. * @param $mallId
  68. * @param $refundId
  69. * @param $stepStatus
  70. */
  71. public function execute($mallId, $params)
  72. {
  73. $trans = Yii::$app->db->beginTransaction();
  74. try {
  75. $refund = OrderRefund::findOne(['id' => $params['id'], 'mall_id' => $mallId]);
  76. if(empty($refund)) throw new Exception("售后订单不存在或已删除");
  77. if ($refund->isRefundFinish()) throw new Exception('当前订单已售后');
  78. if(!empty($params['refund_price'])){
  79. $old_reality_refund_price = $refund->refund_price;
  80. $refund->refund_price = $params['refund_price'];
  81. $refund->old_reality_refund_price = $old_reality_refund_price;
  82. }
  83. if(!empty($params['refund_remark'])){
  84. $refund->refund_remark = (string)$params['refund_remark'];
  85. }
  86. switch ($params['step_status']) {
  87. case RefundStatusEnum::STEP_2:// 退货退款:已同意退货申请,等待买家处理
  88. $refund->saler_address = Json::encode($params['address']);
  89. $refund->refund_status = RefundStatusEnum::HANDEL;
  90. break;
  91. case RefundStatusEnum::STEP_4:// 退货退款:退货完成
  92. $refund->refund_status = RefundStatusEnum::FINISH;
  93. break;
  94. case RefundStatusEnum::STEP_6:// 仅退款:退款完成
  95. $refund->refund_status = RefundStatusEnum::FINISH;
  96. $refund_result = '售后通过';
  97. break;
  98. case RefundStatusEnum::STEP_8:// 换货:已同意换货申请,等待买家处理
  99. $refund->saler_address = Json::encode($params['address']);
  100. $refund->refund_status = RefundStatusEnum::HANDEL;
  101. $refund_result = '售后通过';
  102. break;
  103. case RefundStatusEnum::STEP_10:// 换货:已重新发货
  104. $refund->saler_express = $params['express']['saler_express'];
  105. $refund->saler_express_no = $params['express']['saler_express_no'];
  106. $refund->customer_name = $params['express']['customer_name'] ?? '';
  107. $refund->refund_status = RefundStatusEnum::EXCHANGE_GOODS;
  108. $refund->saler_express_at = time();// 重新发货时间
  109. break;
  110. case RefundStatusEnum::STEP_NEGATIVE_1:// 拒绝售后
  111. $refund->refund_status = RefundStatusEnum::FINISH;
  112. $refund_result = '售后驳回';
  113. break;
  114. case RefundStatusEnum::STEP_NEGATIVE_2:// 拒绝收货
  115. $refund->refund_status = RefundStatusEnum::REFUSE;
  116. break;
  117. }
  118. $refund_price = $refund->refund_price;
  119. // 自动维护主订单
  120. OrderDetail::updateAll(['is_feedback' => OrderStatusEnum::getIsFeedbackStatus($params['step_status']), 'updated_at' => time()], ['id' => $refund->order_detail_id]);
  121. $order = Order::getOne([['id' => $refund->order_id]]);
  122. $order_status = $order->order_status;
  123. $is_install = MallAddons::isInstall($mallId, AddonsEnum::ADDONS_NAME_STORE);
  124. Order::autoUpdateStatus($order);
  125. // 支付流水
  126. $out_trade_no = $order['out_trade_no'];
  127. $payment_trade = PaymentTrade::findOne(['out_trade_no' => $out_trade_no]);
  128. if($is_install && $order->order_source == AddonsEnum::ADDONS_NAME_STORE){
  129. $store_order = StoreOrder::findOne(['order_no'=>$order['order_no']]);
  130. if(!empty($store_order)){
  131. $order['out_trade_no'] = $store_order['out_trade_no'];
  132. StoreOrderDetail::updateAll(['is_feedback' => OrderStatusEnum::getIsFeedbackStatus($params['step_status']), 'updated_at' => time()], ['main_order_detail_id' => $refund->order_detail_id]);
  133. StoreOrder::autoUpdateStatus($store_order);
  134. }
  135. } elseif ($order->marketing_type == AddonsEnum::ADDONS_NAME_LAO_DA_FU && MallAddons::isInstall($mallId, AddonsEnum::ADDONS_NAME_LAO_DA_FU)) {
  136. $store_order = \addons\LaoDaFu\common\models\StoreOrder::findOne(['order_no'=>$order['order_no']]);
  137. if($store_order !== null){
  138. $order['out_trade_no'] = $store_order['out_trade_no'];
  139. \addons\LaoDaFu\common\models\StoreOrderDetail::updateAll(['is_feedback' => OrderStatusEnum::getIsFeedbackStatus($params['step_status']), 'updated_at' => time()], ['main_order_detail_id' => $refund->order_detail_id]);
  140. \addons\LaoDaFu\common\models\StoreOrder::autoUpdateStatus($store_order);
  141. }
  142. }
  143. // 操作退款-退货退款,仅退款
  144. $insert_wallet = []; //重置写入钱包数据
  145. if (in_array($params['step_status'], [RefundStatusEnum::STEP_4, RefundStatusEnum::STEP_6]) &&
  146. $order->marketing_type != AddonsEnum::ADDONS_NAME_WECHAT_VIDEO_SHOP) {
  147. // 判断是否为预约订单
  148. if (in_array($order->order_source, [OrderSourceEnum::SOURCE_RESERVE, OrderSourceEnum::SOURCE_STORE_RESERVE]) && empty($params['not_dec_fee'])) {
  149. // 如果是预约订单那么就需要判断是否需要扣点
  150. $orderDetail = OrderDetail::findOne(['id' => $refund->order_detail_id]);
  151. if ($order->order_source == OrderSourceEnum::SOURCE_STORE_RESERVE) {
  152. $goodsReserve = StoreGoodsReserve::findOne(['goods_id' => $orderDetail->goods_id]);
  153. } else {
  154. $goodsReserve = GoodsReserve::findOne(['goods_id' => $orderDetail->goods_id]);
  155. }
  156. $cancel_rule = !empty($goodsReserve->cancel_rule) ? Json::decode($goodsReserve->cancel_rule) : [];
  157. if (!empty($cancel_rule['cancel_fee_percent']) && in_array($order_status, [OrderStatusEnum::SHIPMENTS, OrderStatusEnum::SING])) {
  158. // 判断是否需要扣点 - 待服务/服务中
  159. if(empty($params['refund_price'])) {//后台有传,就用后台的
  160. $refund->refund_price -= bcmul(bcmul($refund->refund_price, $cancel_rule['cancel_fee_percent'], 2), 0.01, 2);
  161. }
  162. }
  163. }
  164. $wallet_type = UserWalletService::WALLET_TYPE_BALANCE;
  165. if ($order->payment_type == PayTypeEnum::SCORE) $wallet_type = UserWalletService::WALLET_TYPE_SCORE;
  166. $orderUserId = $order->user_id;
  167. $refundUserId = $refund->user_id;
  168. if ($refund->refund_price > 0) {// 只有退款金额大于0的情况下才可以进入
  169. $all_refund_price = $refund->refund_price;
  170. // 类型为后台主动退款时,处理佣金、积分数据
  171. if ($params['admin_handler_refund'] == UserWalletEnum::ADMIN_HANDLER_REFUND) {
  172. //检查该订单是否已经存在过售后完成的售后单
  173. // $check_order_detail_refund = OrderDetail::findOne(['order_id' => $refund->order_id, 'is_feedback' => 2]);
  174. $check_order_detail_refund = OrderRefund::findOne(['order_id' => $refund->order_id,'mall_id' => $mallId,'refund_status' => 1]);
  175. // $check_order_detail_refund = OrderDetail::find()->where(['order_id' => $refund->order_id])->asArray()->all();
  176. // print_r($check_order_detail_refund);exit;
  177. //如果不存在同订单的其他商品已经完成售后的售后单
  178. if(empty($check_order_detail_refund)){
  179. //如果为首次发起售后的,获取source_table为order的佣金,积分,余额数据
  180. $condition = [
  181. 'where'=>[
  182. ['source_table' => [Order::tableName()]],
  183. ['source_table_id' => $refund->order_id],
  184. ['mall_id' => $refund->mall_id],
  185. ['from_type' => ['Rebate']]
  186. ],
  187. ];
  188. $data = [
  189. 'condition' => $condition,
  190. 'params' => $params
  191. ];
  192. $insert_wallet = self::getOrderCommissionData($insert_wallet, $data, 'balance', $refund);
  193. $insert_wallet = self::getOrderCommissionData($insert_wallet, $data, 'commission', $refund);
  194. $insert_wallet = self::getOrderCommissionData($insert_wallet, $data, 'score', $refund);
  195. // print_r($insert_wallet);exit;
  196. }
  197. $condition = [
  198. 'where' => [
  199. ['source_table' => ['order_detail', OrderDetail::tableName()]],
  200. ['source_table_id' => $refund->order_detail_id],
  201. ['mall_id' => $refund->mall_id],
  202. ['from_type' => ['Agent', 'Area', 'CloudStock', 'Achievement', 'Distribution', 'EqualSubsidy', 'PerformanceDividend', 'consume']],
  203. ],
  204. ];
  205. // $wallet_type = $params['wallet_type'];
  206. // CapitalLog::$capitalType = "commission";
  207. // $commission_list = CapitalLog::getList($condition,0);
  208. //处理售后商品对应的佣金数据
  209. $data = [
  210. 'condition' => $condition,
  211. 'params' => $params
  212. ];
  213. //处理申请售后订单的商品对应的佣金数据
  214. $insert_wallet = self::getOrderCommissionData($insert_wallet, $data, 'commission', $refund);
  215. ////处理申请售后订单的商品对应的积分数据
  216. $insert_wallet = self::getOrderCommissionData($insert_wallet, $data, 'score', $refund);
  217. //获取已结算的积分数据
  218. //处理申请售后订单的商品对应的数字币数据
  219. $deduction_digital = self::getOrderDigital($data, $refund);
  220. // if (!empty($score_list)) {
  221. // foreach ($score_list as $k => $val) {
  222. // $score_refund_price = $val['change_num']*($params['refund_pre']/100) * -1;//处理为负数
  223. // $score_refund_price = number_format($score_refund_price, 2);
  224. // //admin_hander_refund 为后台主动退款时
  225. // $insert_wallet[] = [
  226. // 'mall_id' => $refund->mall_id,
  227. // 'user_id' => $val['user_id'],
  228. // 'is_frozen' => false,
  229. // 'wallet_type' => 'score',// 暂定退回积分
  230. // 'money' => $score_refund_price,
  231. // 'desc' => '后台主动退款',
  232. // 'source_table' => OrderRefund::tableName(),
  233. // 'source_table_id' => $refund->order_detail_id,
  234. // 'from_type' => UserWalletEnum::REFUND,
  235. // 'capital_type' => UserWalletEnum::REFUND_ORDER,
  236. // 'order_no' => $order->order_no,
  237. // 'not_add_total' => true,
  238. // 'admin_handler_refund' => $params['admin_handler_refund'] ? $params['admin_handler_refund'] : '',
  239. // ];//写入积分操作
  240. // }
  241. // }
  242. //处理该订单是否有boss分红
  243. $boss_reward_log_list = AddonsBossRewardLog::find()->where(['order_id' => $refund->order_id, 'is_settlement' => 0])->one();
  244. // print_r($boss_reward_log_list);exit;
  245. if (!empty($boss_reward_log_list)) {
  246. $boss_update_res = AddonsBossRewardLog::updateAll(['status' => StatusEnum::DELETE] ,['order_id' => $refund->order_id]);
  247. if (!$boss_update_res) throw new Exception("保存失败,msg= 处理订单boss分红数据失败");
  248. }
  249. }
  250. // 如果是代客下单,获取该订单是谁支付的
  251. if (MallAddons::isInstall($refund->mall_id, AddonsEnum::ADDONS_NAME_VALET_ORDER)) {
  252. $valetOrderOriginBuyUserId = (new OrderService())->getValetOrderByOrderId($order->id)['buy_user_id'] ?? null;
  253. if ($valetOrderOriginBuyUserId) {
  254. $refundUserId = $valetOrderOriginBuyUserId;
  255. $orderUserId = $valetOrderOriginBuyUserId;
  256. }
  257. }
  258. // 判断是否开启超级支付
  259. $tactics_collection_detail = null;
  260. if (MallAddons::isInstall($mallId, AddonsEnum::ADDONS_NAME_TACTICS_PAY)) {
  261. $tactics_setting = Yii::$app->services->setting->addons->get($mallId, AddonsEnum::ADDONS_NAME_TACTICS_PAY, 'basic');
  262. if (!empty($tactics_setting['is_enable'])) {
  263. $tactics_collection_detail = \addons\TacticsPay\common\models\TacticsPayCollectionDetail::findOne(['mall_id' => $mallId, 'trade_no' => $out_trade_no]);
  264. }
  265. }
  266. $list = $payment_trade->getRefundFeeList($refund->id);
  267. foreach ($list as $item) {
  268. if (in_array($item->pay_type, [PayTypeEnum::BALANCE, PayTypeEnum::SCORE])) {
  269. // 退款金额
  270. $refund_price = $item->getRefundFee($params, $refund);
  271. // 本地支付
  272. $wallet_type = $item->pay_type == PayTypeEnum::SCORE ? UserWalletService::WALLET_TYPE_SCORE : UserWalletService::WALLET_TYPE_BALANCE;
  273. // 如果是代付,那么就需要原路退回
  274. $extra_refund_price = 0;//额外抵扣金额
  275. if($wallet_type == UserWalletService::WALLET_TYPE_BALANCE){
  276. //判断是否有卡券插件
  277. if (MallAddons::isInstall($refund->mall_id, AddonsEnum::ADDONS_NAME_CARD_COUPONS)) {
  278. $card_refund_price = $refund->refund_price - $item->getRefundFee($params, $refund);
  279. $extra_refund_price = CardCouponsScoreGive::setOrderRefund([
  280. 'refund_price' => $card_refund_price,
  281. 'trade_id' => $item->getTradeId(),
  282. 'refund_id' => $refund->id
  283. ]);
  284. }
  285. }
  286. $insert_wallet[] = [
  287. 'mall_id' => $refund->mall_id,
  288. 'user_id' => $refundUserId,
  289. 'is_frozen' => false,
  290. 'wallet_type' => $wallet_type,// 暂定退回余额或积分
  291. 'money' => $refund_price,
  292. 'desc' => '订单售后退款-订单('.$refund->order_id.')',
  293. 'source_table' => OrderRefund::tableName(),
  294. 'source_table_id' => $refund->id,
  295. 'from_type' => UserWalletEnum::REFUND,
  296. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  297. 'order_no' => $order->order_no,
  298. 'not_add_total' => false
  299. ];
  300. $refund_price += $extra_refund_price;
  301. } elseif(isset($tactics_collection_detail) && in_array($item->pay_type, [PayTypeEnum::HUIFUPAY, PayTypeEnum::JOINPAY])){
  302. // 策略支付退款,暂时先处理汇聚、汇付
  303. $refund_price = $item->getRefundFee($params, $refund);
  304. // 退款金额需要大于0 组合支付金额过小的情况下 分配子订单退款金额可能为0
  305. if ($refund_price < 0) continue;
  306. $res = \addons\TacticsPay\common\global_func\Payment::refund([
  307. 'id' => $refund->id,
  308. 'mall_id' => $refund->mall_id,
  309. 'refund' => $refund,
  310. 'params' => $params,
  311. 'order_no' => $order['order_no'],
  312. 'refund_order_no' => $refund['order_no'],
  313. 'payment_trade_id' => $item->getTradeId(),
  314. 'refund_price' => $refund_price,
  315. 'out_trade_no' => $out_trade_no,
  316. 'refund_reason' => '订单售后退款',
  317. 'pay_type' => $item->pay_type,
  318. 'order_user_id' => $orderUserId,
  319. ], $tactics_collection_detail);
  320. if ($res === false) throw new \Exception('退款失败');
  321. } elseif(in_array($item->pay_type,[PayTypeEnum::JOINPAY,PayTypeEnum::ALI,PayTypeEnum::JOINPAY_FAST_PAY,PayTypeEnum::WECHAT,PayTypeEnum::SANDPAY_FAST_PAY])){
  322. $refund_price = $item->getRefundFee($params, $refund);
  323. // 退款金额需要大于0 组合支付金额过小的情况下 分配子订单退款金额可能为0
  324. if ($refund_price > 0) {
  325. //原路返回
  326. $originUserId = $order->user_id;
  327. $order->user_id = $orderUserId;
  328. $res = Yii::$app->services->pay->refund(
  329. $refund->refund_price,
  330. $refund_price,
  331. $payment_trade->out_trade_no,
  332. $order,
  333. '订单售后退款',
  334. '',
  335. '',
  336. $item->pay_type
  337. );
  338. $order->user_id = $originUserId;
  339. if ($res === false) throw new \Exception('退款失败');
  340. }
  341. } elseif ($order->payment_type == PayTypeEnum::OFFLINE) {
  342. // 此支付方式为后台管理员直接支付不做任何处理
  343. } elseif (
  344. // 当res返回为false 说明没有相对于的退款方式
  345. $res = GlobalInvoke::exec(GlobalInvoke::INVOKE_ORDER_REFUND, $mallId, [
  346. 'id' => $refund->id,
  347. 'mall_id' => $refund->mall_id,
  348. 'refund' => $refund,
  349. 'params' => $params,
  350. 'order_no' => $order['order_no'],
  351. 'refund_order_no' => $refund['order_no'],
  352. 'payment_trade_id' => $item->getTradeId(),
  353. 'refund_price' => $item->getRefundFee($params, $refund),
  354. 'pay_type' => $item->pay_type,
  355. ])
  356. ) {
  357. // 当res不为空 进此判断
  358. list($boolean, $msg) = $res;
  359. if ($boolean === false) {
  360. $this->setError($msg);
  361. return false;
  362. }
  363. } else {
  364. }
  365. }
  366. if (MallAddons::isInstall($refund->mall_id, AddonsEnum::ADDONS_NAME_PERFORMANCE_DIVIDEND)) {
  367. //业绩分红奖励扣除
  368. $logic = new PerformanceDividendRewardLogic();
  369. $logic->getRefundReward($refund->order_detail_id, $refund, $data['params']['refund_pre']);
  370. }
  371. // 设置为已退款
  372. $payment_trade->setRefund($refund->id);
  373. }
  374. $refund->is_refund = StatusEnum::ENABLED;
  375. $refund->refund_at = time();
  376. !$payment_trade->isMixedPay() && $refund->refund_price = $payment_trade->getRefundFee($params, $refund);
  377. $refund->reality_refund_price = $refund->getRealityRefundPrice($payment_trade, $params);
  378. $order_detail_ids = array_column($order->detail,'id');
  379. // 订单营销扣减返还
  380. if(empty($order['store_id'])){
  381. $res = OrderMarketing::deductBack($order['id'],$order_detail_ids,[$refund['order_detail_id']],$refund->mall_id, $refundUserId,'订单退款',UserWalletEnum::REFUND,UserWalletEnum::REFUND_ORDER);
  382. if ($res == false) throw new Exception(OrderMarketing::getStaticError());
  383. }else{
  384. if(MallAddons::isInstall($order['mall_id'],AddonsEnum::ADDONS_NAME_STORE) && $order->order_source == AddonsEnum::ADDONS_NAME_STORE){
  385. $store_order = StoreOrder::findOne(['order_no'=>$order['order_no']]);
  386. if (!empty($store_order)) {
  387. $store_order_detail_ids = array_column($store_order->detail,'id');
  388. $store_order_detail_arr = array_column($store_order->detail,'id','main_order_detail_id');
  389. $store_order_detail_id = $store_order_detail_arr[$refund['order_detail_id']]??0;
  390. $res = StoreOrderMarketing::deductBack($store_order,$store_order_detail_ids,[$store_order_detail_id],$refund->mall_id, $refundUserId,'订单退款',UserWalletEnum::REFUND,UserWalletEnum::REFUND_ORDER);
  391. if ($res == false) throw new Exception(StoreOrderMarketing::getStaticError());
  392. }
  393. }
  394. }
  395. }
  396. // 保存步骤
  397. $params['payment_type'] = $order->payment_type;
  398. $step_status = self::saveStep($refund, $params);
  399. // 修改售后信息
  400. $refund->step_status = $step_status;
  401. if (!$refund->save()) throw new Exception("保存失败,msg=" . $refund->getErrorMessage());
  402. $trans->commit();
  403. //操作钱包的数据不为空时,发起操作钱包数据的异步
  404. if (!empty($insert_wallet)) {
  405. $res = UserWalletService::batchHandleByQueue($insert_wallet);
  406. if (empty($res)) throw new \Exception(UserWalletService::getStaticError());
  407. }
  408. // 触发售后统计
  409. if (in_array($params['step_status'], [RefundStatusEnum::STEP_4, RefundStatusEnum::STEP_6]) && empty($params['is_async'])) {
  410. $refund_event = ['id' =>$refund->id, 'type' => RefundTypeEnum::EVENT_FINISH];
  411. StatisticsHandle::execute(EventNameEnum::ORDER_REFUND, $refund_event);
  412. //触发订单售后完成
  413. $event = new OrderRefundFinishEvent($refund->mall_id);
  414. $data = [];
  415. $data['id'] = $refund->id;
  416. $data['source_table'] = 'order_detail';
  417. $data['source_table_id'] = $refund['order_detail_id'];
  418. $data['step_status'] = $params['step_status'];
  419. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  420. }
  421. if($params['step_status']==RefundStatusEnum::STEP_2){
  422. Yii::error('退货申请审核通过开始');
  423. try {
  424. $send_data = [
  425. 'mall_id' => $order['mall_id'],
  426. 'user_id' => $order['user_id'],
  427. 'order_id' => $order['id'],
  428. 'refund_id' => $refund->id,
  429. 'created_at' => $order['created_at'],
  430. 'order_no' => $order['order_no'],
  431. 'mobile' => $order['mobile'],
  432. 'address' => $order['region_name'].$order['address'],
  433. 'receiver_name' => $order['receiver_name'],
  434. ];
  435. $obj = new WechatMessageLogic();
  436. $obj->orderRefundAgree($send_data);
  437. }catch (\Exception $e){
  438. Yii::error('退货申请审核通过Exception:'.$e->getMessage());
  439. }
  440. Yii::error('退货申请审核通过结束');
  441. }
  442. // 拒绝 - 退款
  443. if ($params['step_status'] == RefundStatusEnum::REFUSE) {
  444. // 设置为已拒绝
  445. $payment_trade->setRefuse($refund->id);
  446. }
  447. // 拒绝 - 需要处理ERP
  448. if ($params['step_status'] == RefundStatusEnum::REFUSE && MallAddons::isInstall($order['mall_id'],AddonsEnum::ADDONS_NAME_WANG_DIAN_TONG)) {
  449. (new OrderRefundRejectService())->sync($order['mall_id'], $refund->id);
  450. }
  451. // 是否需要触发更新事件
  452. if (!empty($params['is_trigger_update_event'])) {
  453. // 退款ID & 退款状态
  454. $event = new OrderRefundUpdateEvent($refund->mall_id);
  455. $data = [];
  456. $data['id'] = $refund->id;
  457. $data['step_status'] = $params['step_status'];
  458. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  459. }
  460. $event = new OrderRefundChangeEvent($refund->mall_id);
  461. $data = [];
  462. $data['id'] = $refund->id;
  463. $data['refund_reason'] = $params['refund_reason'] ?? '';
  464. $data['step_status'] = $params['step_status'];
  465. $ifExistArr = ['is_expired', 'operator_id', 'is_admin', 'is_store', 'is_mch', 'is_aiyunuo', 'is_manual', 'admin_handler_refund'];
  466. foreach ($ifExistArr as $field) {
  467. if (!empty($params[$field])) {
  468. $data[$field] = $params[$field];
  469. }
  470. }
  471. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  472. // 五洲供应链拒绝退款处理
  473. if (MallAddons::isInstall($refund->mall_id, AddonsEnum::ADDONS_NAME_WUZHOU_CHAIN)) {
  474. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_ORDER, RabbitMqEnum::ORDER_QUEUE, ['order_id' => $refund->order_id], ServicesOrderService::class, 'orderExpressStatusHandle');
  475. }
  476. if ($refund_result) {
  477. //订单售后结果消息模板发送
  478. $service = new \common\logic\wechat\WechatMessageLogic();
  479. $refund_send_data = [
  480. 'user_id' => $refund->user_id,
  481. 'mall_id' => $refund->mall_id,
  482. 'order_id' => $refund->order_id,
  483. 'order_no' => $order->order_no,
  484. 'handle_time' => date('Y-m-d H:i:s', time()),
  485. 'result' => $refund_result ?? ''
  486. ];
  487. if ($order['store_id']) {
  488. //获取门店消息提醒配置
  489. $notice_wechat_config = \Yii::$app->services->setting->addons->get($store_order['mall_id'], AddonsEnum::ADDONS_NAME_STORE, 'notice');
  490. $notice_wechat = empty($notice_wechat_config['notice_wechat']) ? [] : json_decode($notice_wechat_config['notice_wechat'],true);
  491. $notice_wechat_template_message_value = $notice_wechat['template_message'] ?? [];
  492. //判断门店是否开启了用户消息通知
  493. if (!$notice_wechat['is_logging'] || empty($notice_wechat_template_message_value[StoreEnum::WECHAT_TEMPLATE_ORDER_REFUND_RESULT]['tempid'])) {
  494. //门店没配置 不发送
  495. return $refund;
  496. }
  497. $refund_send_data['template_id'] = $notice_wechat_template_message_value[StoreEnum::WECHAT_TEMPLATE_ORDER_REFUND_RESULT]['tempid'];
  498. }
  499. //发送售后结果消息模板
  500. $service->orderRefund($refund_send_data);
  501. }
  502. return $refund;
  503. } catch (Exception $e) {
  504. $trans->rollBack();
  505. $this->setError($e->getMessage() . $e->getFile() . $e->getLine());
  506. return false;
  507. }
  508. }
  509. /**
  510. * 保存步骤
  511. * @Author: lun
  512. * @DateTime: 2021/10/11 0011 15:55
  513. * @Copyright: copyright (c) 2021 广东七件事集团
  514. * @param $refund
  515. * @param $stepStatus
  516. * @param array $params
  517. */
  518. public static function saveStep($refund, $params = [])
  519. {
  520. $return_address = '';
  521. if (!empty($params['address'])) {
  522. $return_address = $params['address']['address'] . ' ' . $params['address']['consignee'] . ' ' . $params['address']['mobile'];
  523. }
  524. $step_text = RefundStatusEnum::getStepStatusText($params['step_status']);
  525. $payment_map = PayTypeEnum::getMap();
  526. switch ($params['step_status']) {
  527. case RefundStatusEnum::STEP_2:
  528. $content = [
  529. $step_text['desc'],
  530. '退货地址:' . $return_address
  531. ];
  532. break;
  533. case RefundStatusEnum::STEP_4:
  534. $content = [
  535. $step_text['desc'],
  536. '申请退款金额:¥' . $refund->refund_price,
  537. '实际退款金额:¥' . $refund->reality_refund_price,
  538. '退款方式:' . $payment_map[$params['payment_type']],
  539. ];
  540. break;
  541. case RefundStatusEnum::STEP_6:
  542. $content = [
  543. $step_text['desc'],
  544. '申请退款金额:¥' . $refund->refund_price,
  545. '实际退款金额:¥' . $refund->reality_refund_price,
  546. '退款方式:' . $payment_map[$params['payment_type']],
  547. ];
  548. break;
  549. case RefundStatusEnum::STEP_8:
  550. $content = [
  551. $step_text['desc'],
  552. '退货地址:' . $return_address
  553. ];
  554. break;
  555. case RefundStatusEnum::STEP_10:
  556. $content = [
  557. $step_text['desc'],
  558. '物流公司:' . $refund->saler_express,
  559. '物流编号:' . $refund->saler_express_no,
  560. ];
  561. break;
  562. case RefundStatusEnum::STEP_NEGATIVE_1:
  563. case RefundStatusEnum::STEP_NEGATIVE_2:
  564. $content = [
  565. $step_text['desc'],
  566. '拒绝理由:' . $params['refund_reason'] ?? '',
  567. ];
  568. break;
  569. }
  570. $step = new OrderRefundStep();
  571. $step->order_refund_id = $refund->id;
  572. $step->role = $step_text['role'];
  573. $step->content = Json::encode($content);
  574. $step->step_num = RefundStatusEnum::getStepNumByStatus($params['step_status']);
  575. $step->step_status = $params['step_status'];
  576. if (!$step->save()) throw new Exception("退款步骤保存失败,msg=" . $refund->getErrorMessage());
  577. return $params['step_status'];
  578. }
  579. /**
  580. * 商家拒绝售后(暂供应链订单使用)
  581. *
  582. * @param integer $mall_id
  583. * @param integer $id
  584. * @param string $reason
  585. * @return void
  586. */
  587. public static function refuseRefundForChain(int $mall_id, int $id, string $reason = '拒绝售后')
  588. {
  589. // 售后订单
  590. $refund = OrderRefund::find()->where(['mall_id' => $mall_id, 'id' => $id])->one();
  591. if ($refund->isRefundFinish()) throw new Exception('当前订单已售后');
  592. $refund->refund_status = RefundStatusEnum::FINISH;
  593. // 自动维护主订单
  594. OrderDetail::updateAll(
  595. [
  596. 'is_feedback' => OrderStatusEnum::getIsFeedbackStatus(RefundStatusEnum::STEP_NEGATIVE_1),
  597. 'updated_at' => time()
  598. ],
  599. ['id' => $refund->order_detail_id]
  600. );
  601. $order = Order::getOne([['id' => $refund->order_id]]);
  602. $order->is_feedback = OrderStatusEnum::getIsFeedbackStatus(RefundStatusEnum::STEP_NEGATIVE_1);
  603. $order->save();
  604. // 更新组合支付中,订单退款状态
  605. $payment_trade_order_model = PaymentTradeOrder::findOne(['order_no' => $order->order_no, 'is_pay' => 1]);
  606. $payment_trade_model = PaymentTrade::findOne(['id' => $payment_trade_order_model['trade_id']]);
  607. if ($payment_trade_model && $payment_trade_model->isMixedPay()) {
  608. PaymentTradeItemRefund::updateAll(['refund_status' => PaymentTradeItemRefund::REFUSE, 'updated_at' => time()], ['refund_id' => $id]);
  609. }
  610. // 保存步骤
  611. $step_status = self::saveStep($refund, [
  612. 'payment_type' => $order->payment_type,
  613. 'address' => [
  614. 'address' => '',
  615. 'consignee' => '',
  616. 'mobile' => '',
  617. ],
  618. 'step_status' => RefundStatusEnum::STEP_NEGATIVE_1,
  619. 'refund_reason' => $reason,
  620. ]);
  621. // 修改售后信息
  622. $refund->step_status = $step_status;
  623. if (!$refund->save()) throw new Exception("保存失败,msg=" . $refund->getErrorMessage());
  624. // 拒绝 - 需要处理ERP
  625. if (MallAddons::isInstall($order['mall_id'],AddonsEnum::ADDONS_NAME_WANG_DIAN_TONG)) {
  626. (new OrderRefundRejectService())->sync($order['mall_id'], $refund->id);
  627. }
  628. $event = new OrderRefundChangeEvent($refund->mall_id);
  629. $data = [];
  630. $data['id'] = $refund->id;
  631. $data['step_status'] = RefundStatusEnum::STEP_NEGATIVE_1;
  632. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  633. }
  634. /**
  635. * 商家拒绝售后(暂供应链订单使用)
  636. *
  637. * @param integer $mall_id
  638. * @param integer $id
  639. * @param string $reason
  640. * @return void
  641. */
  642. public static function refuseRefundForChainQijuhe(int $mall_id, int $id, string $reason = '拒绝售后')
  643. {
  644. // 售后订单
  645. $refund = OrderRefund::find()->where(['mall_id' => $mall_id, 'order_detail_id' => $id])->one();
  646. if ($refund->isRefundFinish()) throw new Exception('当前订单已售后');
  647. $refund->refund_status = RefundStatusEnum::FINISH;
  648. // 自动维护主订单
  649. OrderDetail::updateAll(
  650. [
  651. 'is_feedback' => OrderStatusEnum::getIsFeedbackStatus(RefundStatusEnum::STEP_NEGATIVE_1),
  652. 'updated_at' => time()
  653. ],
  654. ['id' => $refund->order_detail_id]
  655. );
  656. $order = Order::getOne([['id' => $refund->order_id]]);
  657. $order->is_feedback = OrderStatusEnum::getIsFeedbackStatus(RefundStatusEnum::STEP_NEGATIVE_1);
  658. $order->save();
  659. // 更新组合支付中,订单退款状态
  660. $payment_trade_order_model = PaymentTradeOrder::findOne(['order_no' => $order->order_no, 'is_pay' => 1]);
  661. $payment_trade_model = PaymentTrade::findOne(['id' => $payment_trade_order_model['trade_id']]);
  662. if ($payment_trade_model && $payment_trade_model->isMixedPay()) {
  663. PaymentTradeItemRefund::updateAll(['refund_status' => PaymentTradeItemRefund::REFUSE, 'updated_at' => time()], ['refund_id' => $id]);
  664. }
  665. // 保存步骤
  666. $step_status = self::saveStep($refund, [
  667. 'payment_type' => $order->payment_type,
  668. 'address' => [
  669. 'address' => '',
  670. 'consignee' => '',
  671. 'mobile' => '',
  672. ],
  673. 'step_status' => RefundStatusEnum::STEP_NEGATIVE_1,
  674. 'refund_reason' => $reason,
  675. ]);
  676. // 修改售后信息
  677. $refund->step_status = $step_status;
  678. if (!$refund->save()) throw new Exception("保存失败,msg=" . $refund->getErrorMessage());
  679. // 拒绝 - 需要处理ERP
  680. if (MallAddons::isInstall($order['mall_id'],AddonsEnum::ADDONS_NAME_WANG_DIAN_TONG)) {
  681. (new OrderRefundRejectService())->sync($order['mall_id'], $refund->id);
  682. }
  683. $event = new OrderRefundChangeEvent($refund->mall_id);
  684. $data = [];
  685. $data['id'] = $refund->id;
  686. $data['step_status'] = RefundStatusEnum::STEP_NEGATIVE_1;
  687. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  688. }
  689. public static function getOrderCommissionData($insert_wallet = [], $data = [], $capitalType = 'commission', $refund = []){
  690. // $wallet_type = $params['wallet_type'];
  691. CapitalLog::$capitalType = "{$capitalType}";
  692. $list = CapitalLog::getList($data['condition'],0);
  693. $refund_pre = $data['params']['refund_pre'];
  694. if ($capitalType === 'score') {
  695. $refund_pre = $data['params']['score_pre'];
  696. }
  697. // print_r($list);exit;
  698. if (!empty($list)) {
  699. foreach ($list as $k => $val) {
  700. $score_refund_price = $val['change_num']*($refund_pre/100) * -1;//处理为负数
  701. $score_refund_price = bcmul($score_refund_price, 1, 2);
  702. //admin_hander_refund 为后台主动退款时
  703. $insert_wallet[] = [
  704. 'mall_id' => $refund->mall_id,
  705. 'user_id' => $val['user_id'],
  706. 'is_frozen' => false,
  707. 'wallet_type' => $capitalType,// 暂定退回积分
  708. 'money' => $score_refund_price,
  709. 'desc' => '后台主动退款-订单('.$refund->order_id.')',
  710. 'source_table' => OrderRefund::tableName(),
  711. 'source_table_id' => $refund->id,
  712. 'from_type' => UserWalletEnum::REFUND,
  713. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  714. 'order_no' => $refund->order_no,
  715. 'not_add_total' => true,
  716. 'admin_handler_refund' => $data['params']['admin_handler_refund'] ? $data['params']['admin_handler_refund'] : '',
  717. ];//写入积分操作
  718. }
  719. }
  720. return $insert_wallet;
  721. }
  722. public static function getOrderDigital($data, $refund)
  723. {
  724. //数字币
  725. $condition = [
  726. 'where' => [
  727. ['source_table' => ['order_detail', OrderDetail::tableName()]],
  728. ['source_table_id' => $refund->order_detail_id],
  729. ['mall_id' => $refund->mall_id],
  730. ['from_type' => ['Agent', 'Area', 'CloudStock', 'Achievement', 'Distribution', 'EqualSubsidy', 'PerformanceDividend', 'consume']],
  731. ['send_status' => DigitalLog::SEND_STATUS_NORMAL]
  732. ],
  733. ];
  734. $list = DigitalLog::lists($condition);
  735. if ($list) {
  736. foreach ($list as $item) {
  737. $price = $item['change_num'] * ($data['params']['digital_pre'] / 100) * -1;//处理为负数
  738. $price = bcmul($price, 1, 2);
  739. UserDigital::handleUserDigital($item['mall_id'], $item['user_id'], $price, false, [
  740. 'from_type' => UserWalletEnum::REFUND,
  741. 'capital_type' => UserWalletEnum::REFUND_ORDER,
  742. 'desc' => '后台主动退款-订单(' . $refund->order_id . ')',
  743. ]);
  744. }
  745. }
  746. return true;
  747. }
  748. }