RefundApply.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * Name: 芸众商城系统
  5. * Author: 广州市芸众信息科技有限公司
  6. * Profile: 广州市芸众信息科技有限公司位于国际商贸中心的广州,专注于移动电子商务生态系统打造,拥有芸众社交电商系统、区块链数字资产管理系统、供应链管理系统、电子合同等产品/服务。官网 :www.yunzmall.com www.yunzshop.com
  7. * Date: 2021/12/23
  8. * Time: 15:39
  9. */
  10. namespace app\frontend\modules\refund\services\operation;
  11. use app\backend\modules\refund\services\operation\RefundOperation;
  12. use app\common\exceptions\AppException;
  13. use app\common\models\OrderGoods;
  14. use app\common\models\refund\RefundGoodsLog;
  15. use app\common\models\refund\RefundProcessLog;
  16. use app\common\modules\refund\RefundOrderFactory;
  17. use app\frontend\modules\member\listeners\Order;
  18. use app\frontend\modules\refund\services\RefundMessageService;
  19. use app\common\events\order\OrderRefundApplyEvent;
  20. use app\common\services\SystemMsgService;
  21. use app\frontend\modules\refund\services\RefundService;
  22. class RefundApply extends RefundOperation
  23. {
  24. protected $statusAfterChanged = self::WAIT_CHECK;
  25. protected $name = '申请退款';
  26. protected $timeField = 'create_time'; //操作时间
  27. protected $refundGoods;
  28. public function __construct(array $attributes = [])
  29. {
  30. parent::__construct($attributes);
  31. if (!isset($this->uid) && \YunShop::app()->getMemberId()) {
  32. $this->uid = \YunShop::app()->getMemberId();
  33. }
  34. }
  35. protected function updateBefore()
  36. {
  37. //售后记录必须是这笔订单所属会员的
  38. if (!isset($this->uid) || $this->uid != $this->order->uid) {
  39. $this->uid = $this->order->uid;
  40. }
  41. $refundApplyData = $this->getRequest()->only([
  42. 'reason', 'content', 'refund_type', 'order_id','receive_status','refund_way_type',
  43. ]);
  44. if (is_array($this->getRequest()->input('images'))) {
  45. $refundApplyData['images'] = $this->getRequest()->input('images');
  46. } else {
  47. $refundApplyData['images'] = $this->getRequest()->input('images') ? json_decode($this->getRequest()->input('images'), true):[];
  48. }
  49. //退款总金额,换货售后退款金额为0
  50. if ($refundApplyData['refund_type'] == static::REFUND_TYPE_EXCHANGE_GOODS) {
  51. $refundApplyData['price'] = 0;
  52. } else {
  53. //申请退款金额
  54. $refundApplyData['apply_price'] = $this->getRefundApplyPrice();
  55. //退款运费金额
  56. $refundApplyData['freight_price'] = $this->getFreightPrice();
  57. //退款其他金额
  58. $refundApplyData['other_price'] = $this->getOtherPrice();
  59. //售后金额不能大于订单实付金额
  60. $price = bcadd($refundApplyData['apply_price'], ($refundApplyData['freight_price'] + $refundApplyData['other_price']),2);
  61. $refundApplyData['price'] = $this->finalAmount($price);
  62. }
  63. $this->fill($this->finalFillAttribute($refundApplyData));
  64. $this->relatedPluginOrder()->handleAfterSales($this);//交给对应的订单做最后的业务处理
  65. $this->backWay()->init($this); //走退货方式验证
  66. }
  67. protected function updateAfter()
  68. {
  69. $this->order->refund_id = $this->id;
  70. if (!$this->order->save()) {
  71. throw new AppException('订单退款状态更变失败');
  72. }
  73. if ($this->getRefundGoods()) {
  74. $this->createOrderGoodsRefundLog($this->getRefundGoods());
  75. }
  76. $this->setBackWay(); //保存退货方式内容
  77. }
  78. //订单商品申请退款金额和
  79. protected function getRefundApplyPrice()
  80. {
  81. $refund_price = collect($this->getRefundGoods())->sum('refund_price');
  82. return min($this->order->price, $refund_price);
  83. }
  84. protected function getFreightPrice()
  85. {
  86. return $this->getRequest()->input('freight_price',0);
  87. }
  88. protected function getOtherPrice()
  89. {
  90. return $this->getRequest()->input('other_price',0);
  91. }
  92. //最终退款金额,插件订单可以修改最终退款金额
  93. protected function finalAmount($price)
  94. {
  95. return min($this->order->price, $price);
  96. }
  97. protected function finalFillAttribute($data)
  98. {
  99. if (!isset($data['part_refund'])) {
  100. $data['part_refund'] = $this->getPartRefund();
  101. }
  102. if (!isset($data['refund_sn'])) {
  103. $data['refund_sn'] = RefundService::createOrderRN();
  104. }
  105. return $data;
  106. }
  107. public function getPartRefund()
  108. {
  109. return $this->relatedPluginOrder()->applyPratRefundStatus($this->getRefundGoods(), $this->getRequest());
  110. // if ($this->getRequest()->input('refund_type') == RefundApply::REFUND_TYPE_EXCHANGE_GOODS) {
  111. // return 0;
  112. // }
  113. //
  114. // //订单商品总数量
  115. // $orderGoodsNum = $this->order->orderGoods->sum('total');
  116. //
  117. // $currentApplyNum = array_sum(array_column($this->getRefundGoods(), 'total'));
  118. //
  119. //
  120. // //一次性申请全部商品退款
  121. // if ($orderGoodsNum == $currentApplyNum) {
  122. // return 3; //申请全额退款
  123. // }
  124. //
  125. // //todo 问题:已经换过一次货的商品,是否需要过滤掉换货售后记录的退款数量?
  126. // //订单已售后总数量,这里要不要过滤换货售后
  127. // $refundedNum = RefundGoodsLog::where('order_id',$this->order->id)
  128. // //->where('refund_type', '!=', self::REFUND_TYPE_EXCHANGE_GOODS)
  129. // ->sum('refund_total');
  130. //
  131. // if ($orderGoodsNum == ($currentApplyNum + $refundedNum)) {
  132. // return 2; //最后一次退款
  133. // }
  134. //
  135. // return 1; //部分退款
  136. }
  137. public function requestRefundGoods()
  138. {
  139. if (is_array($this->getRequest()->input('order_goods'))) {
  140. $refundGoods = $this->getRequest()->input('order_goods');
  141. } else {
  142. $refundGoods = json_decode($this->getRequest()->input('order_goods'), true);
  143. }
  144. return $refundGoods;
  145. }
  146. /**
  147. * @return array
  148. * @throws AppException
  149. */
  150. protected function getRefundGoods()
  151. {
  152. if (isset($refundGoods)) {
  153. return $this->refundGoods;
  154. }
  155. $refundGoods = $this->requestRefundGoods();
  156. if (!$refundGoods) {
  157. $refundGoods = $this->order->orderGoods->map(function (OrderGoods $orderGoods) {
  158. //已退款金额
  159. $refundedAmount = $orderGoods->manyRefundedGoodsLog->sum('refund_price');
  160. //已退款数量
  161. $refundedTotal = $orderGoods->getRefundTotal();
  162. return [
  163. 'id'=> $orderGoods->id,
  164. 'total'=> max($orderGoods->total - $refundedTotal,0),
  165. 'refund_price'=> max($orderGoods->payment_amount - $refundedAmount,0),
  166. ];
  167. })->values()->all();
  168. } else {
  169. $totalArrays = array_column($refundGoods,'total','id');
  170. $refundGoods = $this->order->orderGoods->whereIn('id',array_keys($totalArrays))->map(function ($orderGoods) use ($totalArrays) {
  171. $refund_price = ($orderGoods->payment_amount / $orderGoods->total) * $totalArrays[$orderGoods->id];
  172. return ['id'=> $orderGoods->id, 'total'=>$totalArrays[$orderGoods->id],'refund_price'=> $refund_price];
  173. })->values()->all();
  174. }
  175. if (!$refundGoods) {
  176. throw new AppException('无商品可售后');
  177. }
  178. return $this->refundGoods = $refundGoods;
  179. }
  180. //售后申请监听
  181. protected function afterEventClass()
  182. {
  183. return new OrderRefundApplyEvent($this);
  184. }
  185. protected function writeLog()
  186. {
  187. $detail = [
  188. '售后类型:'. $this->getRefundTypeName()[$this->refund_type],
  189. $this->refund_type == static::REFUND_TYPE_EXCHANGE_GOODS ? '': '退款金额:'.$this->price,
  190. $this->freight_price?'运费:'. $this->freight_price :'',
  191. $this->other_price?'其他费用:'. $this->other_price :'',
  192. '售后原因:'.$this->reason,
  193. '说明:'.$this->content,
  194. ];
  195. $processLog = RefundProcessLog::logInstance($this, RefundProcessLog::OPERATOR_MEMBER);
  196. $processLog->setAttribute('operate_type', RefundProcessLog::OPERATE_APPLY);
  197. $processLog->saveLog($detail,request()->input());
  198. }
  199. protected function sendMessage()
  200. {
  201. //通知买家
  202. RefundMessageService::applyRefundNotice($this);
  203. RefundMessageService::applyRefundNoticeBuyer($this);
  204. //【系統消息通知】
  205. (new SystemMsgService())->applyRefundNotice($this);
  206. if (app('plugins')->isEnabled('instation-message')) {
  207. //开启了站内消息插件
  208. event(new \Yunshop\InstationMessage\event\OrderRefundApplyEvent($this));
  209. }
  210. }
  211. }