AfterSaleService.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <?php
  2. namespace addons\TinyShop\services\order;
  3. use Yii;
  4. use yii\helpers\Json;
  5. use yii\web\UnprocessableEntityHttpException;
  6. use common\enums\StatusEnum;
  7. use common\helpers\ArrayHelper;
  8. use common\components\Service;
  9. use common\helpers\StringHelper;
  10. use addons\TinyShop\common\models\order\Order;
  11. use addons\TinyShop\common\enums\RefundTypeEnum;
  12. use addons\TinyShop\common\forms\OrderAfterSaleForm;
  13. use addons\TinyShop\common\enums\RefundStatusEnum;
  14. use addons\TinyShop\common\models\order\AfterSale;
  15. use addons\TinyShop\common\enums\OrderStatusEnum;
  16. use addons\TinyShop\common\enums\SubscriptionActionEnum;
  17. use addons\TinyShop\common\enums\OrderAfterSaleTypeEnum;
  18. use addons\TinyShop\common\models\order\OrderProduct;
  19. /**
  20. * 售后
  21. *
  22. * Class AfterSaleService
  23. * @package addons\TinyShop\services\order
  24. * @author jianyan74 <751393839@qq.com>
  25. */
  26. class AfterSaleService extends Service
  27. {
  28. /**
  29. * 退货/退款申请
  30. *
  31. * @param OrderAfterSaleForm $refundForm
  32. * @param $member_id
  33. * @return AfterSale|void
  34. * @throws UnprocessableEntityHttpException
  35. */
  36. public function apply(OrderAfterSaleForm $refundForm, $member_id)
  37. {
  38. $orderProduct = Yii::$app->tinyShopService->orderProduct->findById($refundForm->id);
  39. if (!$orderProduct) {
  40. throw new UnprocessableEntityHttpException('订单产品不存在');
  41. }
  42. if (in_array($orderProduct->refund_status, RefundStatusEnum::refund())) {
  43. throw new UnprocessableEntityHttpException('售后已经在处理中');
  44. }
  45. if ($orderProduct->refund_status == RefundStatusEnum::NO_PASS_ALWAYS) {
  46. throw new UnprocessableEntityHttpException('退款已拒绝,不能再次申请');
  47. }
  48. if ($orderProduct->refund_status == RefundStatusEnum::CANCEL) {
  49. throw new UnprocessableEntityHttpException('申请已关闭不可再次申请');
  50. }
  51. if ($member_id && $member_id != $orderProduct->buyer_id) {
  52. throw new UnprocessableEntityHttpException('权限不足');
  53. }
  54. if (
  55. in_array($orderProduct->order_status, [OrderStatusEnum::NOT_PAY, OrderStatusEnum::PAY]) &&
  56. $refundForm->refund_type == RefundTypeEnum::EXCHANGE_PRODUCT
  57. ) {
  58. throw new UnprocessableEntityHttpException('未发货不允许申请换货');
  59. }
  60. /** @var Order $order */
  61. $order = $orderProduct->order;
  62. /** @var AfterSale $model */
  63. $model = new AfterSale();
  64. $model->attributes = ArrayHelper::toArray($orderProduct);
  65. $model->merchant_id = $orderProduct['merchant_id'];
  66. $model->order_product_id = $orderProduct['id'];
  67. $model->type = $orderProduct->order_status != OrderStatusEnum::ACCOMPLISH ? OrderAfterSaleTypeEnum::IN_SALE : OrderAfterSaleTypeEnum::AFTER_SALE ;
  68. $model->number = $orderProduct->num;
  69. $model->refund_type = $refundForm->refund_type;
  70. $model->refund_reason = $refundForm->refund_reason;
  71. $model->refund_evidence = $refundForm->refund_evidence;
  72. $model->refund_apply_money = $refundForm->refund_apply_money;
  73. $model->refund_evidence = is_array($refundForm->refund_evidence) ? $refundForm->refund_evidence : Json::decode($refundForm->refund_evidence);
  74. $model->order_sn = $order->order_sn;
  75. $model->store_id = $order->store_id;
  76. $model->buyer_id = $order->buyer_id;
  77. $model->buyer_nickname = $order->buyer_nickname;
  78. $model->refund_status = RefundStatusEnum::APPLY;
  79. if (!$model->save()) {
  80. throw new UnprocessableEntityHttpException($this->getError($model));
  81. }
  82. // 售后时间判断
  83. if ($model->type == OrderAfterSaleTypeEnum::AFTER_SALE) {
  84. $config = Yii::$app->tinyShopService->config->setting();
  85. if (!empty($config->order_after_sale_date) && ($order->finish_time + $config->order_after_sale_date * 60 * 60 * 24) < time()) {
  86. throw new UnprocessableEntityHttpException('可售后时间已过,不可申请');
  87. }
  88. }
  89. // 记录操作
  90. Yii::$app->services->actionLog->create('orderAfterSale', '申请退款', $model->id);
  91. // 售后状态
  92. Yii::$app->tinyShopService->order->updateAfterSale($order->id, StatusEnum::ENABLED);
  93. // 订单退款申请提醒
  94. Yii::$app->tinyShopService->notify->createRemind(
  95. $model->order_id,
  96. SubscriptionActionEnum::ORDER_AFTER_SALE_APPLY,
  97. $model->merchant_id,
  98. ['order' => $order]
  99. );
  100. return $model;
  101. }
  102. /**
  103. * 同意退款申请
  104. *
  105. * @param $id
  106. * @return Order|array|\yii\db\ActiveRecord|null
  107. * @throws UnprocessableEntityHttpException
  108. */
  109. public function pass($id)
  110. {
  111. $model = $this->findById($id);
  112. // 仅退款
  113. if ($model->refund_type == RefundTypeEnum::MONEY) {
  114. $model->refund_status = RefundStatusEnum::AFFIRM_RETURN_MONEY;
  115. } else {
  116. // 退货、退款 / 换货
  117. $model->refund_status = RefundStatusEnum::SALES_RETURN;
  118. // 退款 / 换货通知
  119. /** @var OrderProduct $orderProduct */
  120. $orderProduct = $model->orderProduct;
  121. $orderProduct->product_name = StringHelper::textNewLine($orderProduct->product_name, 15, 1)[0]; // 内容过长无法通知
  122. Yii::$app->tinyShopService->notify->createRemindByReceiver(
  123. $model->order_id,
  124. SubscriptionActionEnum::ORDER_RETURN_MEMBER_DELIVER,
  125. $model->buyer_id,
  126. [
  127. 'afterSale' => $model,
  128. 'orderProduct' => $orderProduct,
  129. ]
  130. );
  131. }
  132. if (!$model->save()) {
  133. throw new UnprocessableEntityHttpException($this->getError($model));
  134. }
  135. // 记录行为
  136. Yii::$app->services->actionLog->create('orderAfterSale', '同意退款申请', $model->id);
  137. return $model;
  138. }
  139. /**
  140. * 拒绝退款申请
  141. *
  142. * @param $id
  143. * @param $always
  144. * @return Order|array|\yii\db\ActiveRecord|null
  145. * @throws UnprocessableEntityHttpException
  146. */
  147. public function refuse($id, $always)
  148. {
  149. $model = $this->findById($id);
  150. if ($model->refund_status != RefundStatusEnum::APPLY) {
  151. throw new UnprocessableEntityHttpException('操作失败,未申请退款或已被处理');
  152. }
  153. $model->refund_status = $always == true ? RefundStatusEnum::NO_PASS_ALWAYS : RefundStatusEnum::NO_PASS;
  154. if (!$model->save()) {
  155. throw new UnprocessableEntityHttpException($this->getError($model));
  156. }
  157. // 记录行为
  158. Yii::$app->services->actionLog->create('orderAfterSale', '拒绝退款申请', $model->id);
  159. // 自动处理订单状态
  160. Yii::$app->tinyShopService->order->autoUpdateAfterSale($model->order_id);
  161. return $model;
  162. }
  163. /**
  164. * 退货提交
  165. *
  166. * @param OrderAfterSaleForm $refundForm
  167. * @param $member_id
  168. * @throws UnprocessableEntityHttpException
  169. */
  170. public function salesReturn($refundForm, $member_id)
  171. {
  172. $model = $this->findByIdAndVerify($refundForm->id, $member_id);
  173. $model->member_express_company = $refundForm->member_express_company;
  174. $model->member_express_no = $refundForm->member_express_no;
  175. $model->member_express_mobile = $refundForm->member_express_mobile;
  176. $model->member_express_time = time();
  177. // 未填写发货手机号码
  178. if (empty($model->member_express_mobile)) {
  179. $model->member_express_mobile = $model->order->receiver_mobile ?? '';
  180. }
  181. if (!in_array($model->refund_type, [RefundTypeEnum::MONEY_AND_PRODUCT, RefundTypeEnum::EXCHANGE_PRODUCT])) {
  182. throw new UnprocessableEntityHttpException('未申请退货退款/换货');
  183. }
  184. if ($model->refund_status == RefundStatusEnum::AFFIRM_SALES_RETURN) {
  185. throw new UnprocessableEntityHttpException('已经提交退货申请');
  186. }
  187. if ($model->refund_status != RefundStatusEnum::SALES_RETURN) {
  188. throw new UnprocessableEntityHttpException('操作失败,已经已被处理');
  189. }
  190. $model->refund_status = RefundStatusEnum::AFFIRM_SALES_RETURN;
  191. if (!$model->save()) {
  192. throw new UnprocessableEntityHttpException($this->getError($model));
  193. }
  194. // 记录行为
  195. Yii::$app->services->actionLog->create('orderAfterSale', '退货提交', $model->id);
  196. return $model;
  197. }
  198. /**
  199. * 关闭退款/退货申请
  200. *
  201. * @param $id
  202. * @param $member_id
  203. * @throws UnprocessableEntityHttpException
  204. */
  205. public function close($id, $member_id)
  206. {
  207. $model = $this->findByIdAndVerify($id, $member_id);
  208. if ($model->refund_status == 0) {
  209. throw new UnprocessableEntityHttpException('未申请退款');
  210. }
  211. if ($model->refund_status == RefundStatusEnum::CANCEL) {
  212. throw new UnprocessableEntityHttpException('申请已关闭');
  213. }
  214. if ($model->refund_status == RefundStatusEnum::SHIPMENTS) {
  215. throw new UnprocessableEntityHttpException('卖家已发货');
  216. }
  217. if ($model->refund_status == RefundStatusEnum::CONSENT) {
  218. throw new UnprocessableEntityHttpException('卖家已同意退款');
  219. }
  220. $model->refund_status = RefundStatusEnum::CANCEL;
  221. if (!$model->save()) {
  222. throw new UnprocessableEntityHttpException($this->getError($model));
  223. }
  224. // 记录行为
  225. Yii::$app->services->actionLog->create('orderAfterSale', '关闭退款/退货申请', $model->id);
  226. // 自动处理订单状态
  227. Yii::$app->tinyShopService->order->autoUpdateAfterSale($model->order_id);
  228. return $model;
  229. }
  230. /**
  231. * 确认收货
  232. *
  233. * @param $id
  234. * @throws UnprocessableEntityHttpException
  235. */
  236. public function merchantTakeDelivery($id)
  237. {
  238. $model = $this->findById($id);
  239. if (!$model) {
  240. throw new UnprocessableEntityHttpException('申请售后记录不存在');
  241. }
  242. if ($model->refund_status != RefundStatusEnum::AFFIRM_SALES_RETURN) {
  243. throw new UnprocessableEntityHttpException('确认收货失败,已经被处理');
  244. }
  245. $model->refund_status = RefundStatusEnum::AFFIRM_RETURN_MONEY;
  246. // 换货
  247. if ($model->refund_type == RefundTypeEnum::EXCHANGE_PRODUCT) {
  248. $model->refund_status = RefundStatusEnum::AFFIRM_SHIPMENTS;
  249. }
  250. if (!$model->save()) {
  251. throw new UnprocessableEntityHttpException($this->getError($model));
  252. }
  253. Yii::$app->services->actionLog->create('orderAfterSale', '确认收货', $model->id);
  254. return $model;
  255. }
  256. /**
  257. * 换货, 商家发货
  258. *
  259. * @param AfterSale $afterSale
  260. * @return AfterSale
  261. * @throws UnprocessableEntityHttpException
  262. * @throws \yii\base\InvalidConfigException
  263. */
  264. public function merchantDelivery(AfterSale $afterSale)
  265. {
  266. if ($afterSale->refund_status != RefundStatusEnum::AFFIRM_SHIPMENTS) {
  267. throw new UnprocessableEntityHttpException('操作失败, 已被处理');
  268. }
  269. $afterSale->merchant_express_time = time();
  270. $afterSale->refund_status = RefundStatusEnum::SHIPMENTS;
  271. if (!$afterSale->save()) {
  272. throw new UnprocessableEntityHttpException($this->getError($afterSale));
  273. }
  274. // 记录行为
  275. Yii::$app->services->actionLog->create('orderAfterSale', '换货, 商家发货', $afterSale->id);
  276. return $afterSale;
  277. }
  278. /**
  279. * 用户确认收货
  280. *
  281. * @param $id
  282. * @throws UnprocessableEntityHttpException
  283. */
  284. public function memberTakeDelivery($id, $member_id)
  285. {
  286. $model = $this->findByIdAndVerify($id, $member_id);
  287. if ($model->refund_status != RefundStatusEnum::SHIPMENTS) {
  288. throw new UnprocessableEntityHttpException('操作失败, 已被处理');
  289. }
  290. $model->refund_status = RefundStatusEnum::MEMBER_AFFIRM;
  291. if (!$model->save()) {
  292. throw new UnprocessableEntityHttpException($this->getError($model));
  293. }
  294. Yii::$app->services->actionLog->create('orderAfterSale', '确认收货', $model->id);
  295. // 售后状态
  296. Yii::$app->tinyShopService->order->updateAfterSale($model->order_id, StatusEnum::ENABLED);
  297. return $model;
  298. }
  299. /**
  300. * 确认退款
  301. *
  302. * @param $id
  303. * @return AfterSale
  304. * @throws UnprocessableEntityHttpException
  305. * @throws \yii\web\NotFoundHttpException
  306. */
  307. public function returnMoney($id, $refund_money)
  308. {
  309. /** @var AfterSale $model */
  310. $model = $this->findById($id);
  311. /** @var Order $order */
  312. $order = $model->order;
  313. // 实际退款金额
  314. $model->refund_money = $refund_money;
  315. $model->refund_time = time();
  316. // 退款确认
  317. if ($model->refund_status != RefundStatusEnum::AFFIRM_RETURN_MONEY) {
  318. throw new UnprocessableEntityHttpException('操作失败,用户已关闭或已处理');
  319. }
  320. $model->refund_status = RefundStatusEnum::CONSENT;
  321. if (!$model->save()) {
  322. throw new UnprocessableEntityHttpException($this->getError($model));
  323. }
  324. // 退款为 0
  325. if ($model->refund_money > 0) {
  326. // 增加本身订单退款金额
  327. Order::updateAllCounters(['refund_money' => $model->refund_money], ['id' => $order->id]);
  328. // 订单退款提醒
  329. Yii::$app->tinyShopService->notify->createRemindByReceiver(
  330. $order->id,
  331. SubscriptionActionEnum::ORDER_RETURN_MONEY,
  332. $order->buyer_id,
  333. ['order' => $order]
  334. );
  335. // 订单退款提醒
  336. Yii::$app->tinyShopService->notify->createRemind(
  337. $order->id,
  338. SubscriptionActionEnum::ORDER_RETURN_MONEY,
  339. $order->merchant_id,
  340. ['order' => $order]
  341. );
  342. }
  343. // 记录行为
  344. Yii::$app->services->actionLog->create('orderAfterSale', '确认退款', $model->id);
  345. return $model;
  346. }
  347. /**
  348. * @param $order_product_id
  349. * @param string $member_id
  350. * @return array|\yii\db\ActiveRecord|null
  351. * @throws UnprocessableEntityHttpException
  352. */
  353. public function findByIdAndVerify($order_product_id, $member_id = '')
  354. {
  355. $model = $this->findByOrderProductId($order_product_id, $member_id);
  356. if (!$model) {
  357. throw new UnprocessableEntityHttpException('申请售后记录不存在');
  358. }
  359. return $model;
  360. }
  361. /**
  362. * @param $order_product_id
  363. * @param $member_id
  364. * @return array|\yii\db\ActiveRecord|null
  365. */
  366. public function findByOrderProductId($order_product_id, $member_id)
  367. {
  368. return AfterSale::find()
  369. ->where(['order_product_id' => $order_product_id, 'status' => StatusEnum::ENABLED])
  370. ->andFilterWhere(['buyer_id' => $member_id])
  371. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  372. ->orderBy('id desc')
  373. ->one();
  374. }
  375. /**
  376. * 获取记录
  377. *
  378. * @param $order_product_id
  379. * @return array|\yii\db\ActiveRecord|null|AfterSale
  380. */
  381. public function findById($id)
  382. {
  383. return AfterSale::find()
  384. ->where(['id' => $id, 'status' => StatusEnum::ENABLED])
  385. ->andFilterWhere(['merchant_id' => $this->getMerchantId()])
  386. ->one();
  387. }
  388. }
粤ICP备19079148号