Refund.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <?php
  2. namespace app\common\model;
  3. use app\common\service\OrderRefundService;
  4. use app\common\service\RefundService;
  5. use Carbon\Carbon;
  6. use think\db\Query;
  7. use think\Model;
  8. use Yansongda\Supports\Arr;
  9. /**
  10. * 短信验证码
  11. * @property Orders orders
  12. * @property User user
  13. * @property int refund_status
  14. * @property int refund_type
  15. * @property int create_time
  16. * @property int audit_time
  17. * @method static static|Query FilterRefund($status=null)
  18. * @method static static|Query FilterTs()
  19. */
  20. class Refund Extends Model
  21. {
  22. protected $name='order_info_refund';
  23. protected $append=[
  24. 'timeline',
  25. ];
  26. const REFUND_ING=100;
  27. const REFUND_PASS=200;
  28. const REFUND_JI=250;
  29. const REFUND_RUNNING=270;
  30. const REFUND_REFUNDING=280;
  31. const REFUND_REJECT=300;
  32. const REFUND_CANCEL=400;
  33. const REFUND_OVER=500;
  34. public static $refundStatus=[
  35. self::REFUND_ING=>'申请审核中',
  36. self::REFUND_PASS=>'申请通过',
  37. self::REFUND_JI=>'用户寄回',
  38. self::REFUND_RUNNING=>'售后中',
  39. self::REFUND_REFUNDING=>'退款中',
  40. self::REFUND_REJECT=>'申请驳回',
  41. self::REFUND_CANCEL=>'已取消',
  42. self::REFUND_OVER=>'处理完成',
  43. ];
  44. /**
  45. * @return string[]
  46. */
  47. public static function getRefundStatus(): array
  48. {
  49. return self::$refundStatus;
  50. }
  51. const REFUND_TYPE_MONEY=1;
  52. const REFUND_TYPE_ALL=2;
  53. const REFUND_TYPE_HHBX=5;
  54. public static $refundTypes=[
  55. self::REFUND_TYPE_MONEY=>'仅退款',
  56. self::REFUND_TYPE_ALL=>'退款退货',
  57. self::REFUND_TYPE_HHBX=>'换货/维修',
  58. ];
  59. public static $refundTypeGoods=[self::REFUND_TYPE_ALL,];
  60. public static $refundTypeMoney=[self::REFUND_TYPE_MONEY,self::REFUND_TYPE_ALL];
  61. const REASON_QU=1;
  62. public static $reasons=[
  63. self::REASON_QU=>'质量问题',
  64. 2=>'7天无理由退货(退回运费需客户承担)',
  65. 3=>'协商一致退款',
  66. 4=>'快递运输破损',
  67. 5=>'其他',
  68. ];
  69. const TH_TYPE_NONE=0;
  70. const TH_TYPE_SELF_SEND=1;
  71. const TH_TYPE_SELF_FEE=2;
  72. const TH_TYPE_FREE=3;
  73. public static $goodsTypes=[
  74. self::TH_TYPE_NONE=>'无需退货',
  75. self::TH_TYPE_SELF_SEND=>'自行邮寄',
  76. self::TH_TYPE_SELF_FEE=>'拍维修费',
  77. self::TH_TYPE_FREE=>'一年以内免费保修',
  78. ];
  79. protected $autoWriteTimestamp=true;
  80. public function orderInfo(){
  81. return $this->belongsTo(OrderInfo::class);
  82. }
  83. public function orders(){
  84. return $this->belongsTo(Orders::class,'order_id');
  85. }
  86. public function user(){
  87. return $this->belongsTo(User::class);
  88. }
  89. public function allowCancel(){
  90. return in_array($this['refund_status'],[
  91. self::REFUND_ING,
  92. ]);
  93. }
  94. public function allowApply(){
  95. return in_array($this->refund_status,[
  96. self::REFUND_REJECT,
  97. self::REFUND_CANCEL,
  98. ]);
  99. }
  100. public function allowEdit(){
  101. return in_array($this->refund_status,[
  102. self::REFUND_ING,
  103. ]);
  104. }
  105. public function makeCancel(){
  106. $this['refund_status']=self::REFUND_CANCEL;
  107. $this->save();
  108. }
  109. #根据订单状态选择售后
  110. public static function makeRefundConfig(OrderInfo $orderInfo,$inject=true){
  111. $order=$orderInfo->orders;
  112. $refundConfig=[];
  113. if($orderInfo->refund && $orderInfo->refund->allowApply()) {
  114. #未发货
  115. if (in_array($order['status'], [$order::S_WAIT_SEND, $order::S_WAIT_REC])) {
  116. $refundConfig['refund_type'] = Arr::only(self::getRefundTypes(), [
  117. self::REFUND_TYPE_MONEY,
  118. self::REFUND_TYPE_ALL,
  119. ]);
  120. $refundConfig['type'] = [
  121. self::REFUND_TYPE_MONEY => null,
  122. self::REFUND_TYPE_ALL => Arr::only(self::getGoodsTypes(), [
  123. self::TH_TYPE_SELF_SEND,
  124. ])
  125. ];
  126. $refundConfig['req_order'] = [
  127. self::REFUND_TYPE_MONEY=>false,
  128. self::REFUND_TYPE_ALL=>false,
  129. ];
  130. $refundConfig['reason'] = array_values(Refund::getReasons());
  131. list($amount_single, $amount_install_single) = RefundService::setOrderInfo($orderInfo, $orderInfo['num'], $orderInfo['num_install'])->amount();
  132. $refundConfig['amount_single'] = $amount_single;
  133. $refundConfig['amount_install_single'] = $amount_install_single;
  134. $refundConfig['num'] = $orderInfo['num'];
  135. $refundConfig['num_install'] = $orderInfo['num_install'];
  136. }
  137. #已发货
  138. /*elseif(in_array($order['status'],[$order::S_WAIT_REC,$order::S_OVER])){
  139. #七天内可以退货退款
  140. if(time()<$order['send_time']+7*86400){
  141. $refundConfig['refund_type']=[
  142. self::REFUND_TYPE_ALL=>'退款退货',
  143. ];
  144. $refundConfig['type']=[
  145. self::TH_TYPE_FREE=>'自行邮寄',
  146. ];
  147. $refundConfig['reason']=array_values(Refund::getReasons());
  148. $refundConfig['req_amount']=1;
  149. $refundConfig['amount']=RefundService::setOrderInfo($orderInfo,$orderInfo['num'],$orderInfo['num_install'])->amount(true);
  150. $refundConfig['req_order']=0;
  151. $refundConfig['num']=$orderInfo['num'];
  152. $refundConfig['num_install']=$orderInfo['num_install'];
  153. }
  154. #只能换货保修
  155. else{
  156. $refundConfig['refund_type']=[
  157. self::REFUND_TYPE_HHBX=>'换货保修',
  158. ];
  159. $refundConfig['req_order']=0;
  160. #一年内
  161. if(time()<strtotime('+1year',$order['send_time'])){
  162. $refundConfig['type']=[
  163. self::TH_TYPE_FREE=>'一年以内免费保修',
  164. ];
  165. }else{
  166. $refundConfig['type']=[
  167. self::TH_TYPE_SELF_FEE=>'拍维修费',
  168. ];
  169. $refundConfig['req_order']=1;
  170. $refundConfig['req_order_goods']=Goods::getFixGoods();
  171. }
  172. $refundConfig['reason']=array_values(Refund::getReasons());
  173. $refundConfig['req_amount']=0;
  174. }
  175. }*/
  176. elseif ($order['status'] == $order::S_OVER) {
  177. $gtYear=Carbon::now()->gt(Carbon::createFromTimestamp($order->send_time)->addYear());
  178. $refundConfig['refund_type'] = Arr::only(self::getRefundTypes(), [
  179. self::REFUND_TYPE_ALL,
  180. self::REFUND_TYPE_HHBX,
  181. ]);
  182. $refundConfig['type'] = [
  183. self::REFUND_TYPE_ALL => Arr::only(self::getGoodsTypes(), [
  184. self::TH_TYPE_SELF_SEND,
  185. ]),
  186. self::REFUND_TYPE_HHBX => Arr::only(self::getGoodsTypes(), [
  187. self::TH_TYPE_SELF_SEND,
  188. ]),
  189. ];
  190. $refundConfig['req_order'] = [
  191. self::REFUND_TYPE_ALL=>false,
  192. self::REFUND_TYPE_HHBX=>$gtYear,
  193. ];
  194. $refundConfig['reason'] = array_values(Refund::getReasons());
  195. list($amount_single, $amount_install_single) = RefundService::setOrderInfo($orderInfo, $orderInfo['num'], $orderInfo['num_install'])->amount();
  196. $refundConfig['amount_single'] = $amount_single;
  197. $refundConfig['amount_install_single'] = $amount_install_single;
  198. $refundConfig['num'] = $orderInfo['num'];
  199. $refundConfig['num_install'] = $orderInfo['num_install'];
  200. $refundConfig['req_order_goods']=Goods::getFixGoods();
  201. }
  202. }
  203. if(empty($refundConfig)){
  204. $refundConfig=null;
  205. }
  206. if($inject){
  207. $orderInfo['refund_config']=$refundConfig;
  208. }
  209. return $refundConfig;
  210. }
  211. public function allowAudit(){
  212. return $this['refund_status']==self::REFUND_ING;
  213. }
  214. /**
  215. * @return string[]
  216. */
  217. public static function getGoodsTypes(): array
  218. {
  219. return self::$goodsTypes;
  220. }
  221. #退款相关
  222. /**
  223. * 退款金额
  224. */
  225. public function getRefundAmount(){
  226. return $this['amount'];
  227. }
  228. public function refundResult($succ,$remark=''){
  229. if(is_bool($succ)){
  230. $this['pay_status']=$succ?1:2;
  231. }else{
  232. $this['pay_status']=$succ;
  233. }
  234. $this['pay_remark']=$remark;
  235. $this->save();
  236. }
  237. public function isRefundMoney(){
  238. return in_array($this['refund_type'],self::getRefundTypeMoney());
  239. }
  240. #end
  241. public function makeAudit($pass){
  242. $this['refund_status']=$pass?self::REFUND_PASS:self::REFUND_REJECT;
  243. $refundMoney=false;
  244. if($pass){
  245. #如果退货
  246. if(in_array($this['refund_type'],self::getRefundTypeGoods())){
  247. $this->orderInfo()->update(['is_return_goods'=>1]);
  248. }
  249. #如果退款,执行退款
  250. if(in_array($this['refund_type'],self::getRefundTypeMoney())){
  251. $this['order_no']=order_no('TK');
  252. $refundMoney=true;
  253. }
  254. }
  255. if(!$this->save()){
  256. throw_user('保存失败');
  257. }
  258. if($refundMoney){
  259. $payment=$this->orders->payment??null;
  260. if($payment){
  261. $refund=new OrderRefundService();
  262. $refund->setPayment($payment);
  263. $refund->setRefund($this);
  264. $refund->setBody("订单[{$this->orders->order_no}]退款");
  265. $refund->pay();
  266. }
  267. }
  268. SiteMsg::sendMsg(
  269. $pass?SiteMsg::TYPE_ORDER_REFUND_PASS:SiteMsg::TYPE_ORDER_REFUND_REJECT,
  270. $this->user
  271. );
  272. #总订单
  273. $order=$this->orders;
  274. if($order && $order->status<Orders::S_OVER){
  275. $has=OrderInfo::where('order_id',$this['order_id'])
  276. ->filterHasUnRefund()
  277. ->find();
  278. if(!$has){
  279. $order->makeCancel();
  280. }
  281. }
  282. }
  283. /**
  284. * @return string[]
  285. */
  286. public static function getReasons(): array
  287. {
  288. $reasons=self::$reasons;
  289. $arr=[];
  290. foreach ($reasons as $key=>$value){
  291. $arr[$key]=compact('key','value');
  292. }
  293. return $arr;
  294. }
  295. /**
  296. * @return string[]
  297. */
  298. public static function getRefundBys(): array
  299. {
  300. $obj=self::$goodsTypes;
  301. $arr=[];
  302. foreach ($obj as $key=>$value){
  303. $arr[$key]=compact('key','value');
  304. }
  305. return $arr;
  306. }
  307. /**
  308. * @return string[]
  309. */
  310. public static function getRefundTypes(): array
  311. {
  312. return self::$refundTypes;
  313. }
  314. /**
  315. * @return int[]
  316. */
  317. public static function getRefundTypeGoods(): array
  318. {
  319. return self::$refundTypeGoods;
  320. }
  321. /**
  322. * @return int[]
  323. */
  324. public static function getRefundTypeMoney(): array
  325. {
  326. return self::$refundTypeMoney;
  327. }
  328. /**
  329. * 售后中及已售后的
  330. * @param Query $query
  331. * @param null $status 1进行中2已完成
  332. */
  333. public function scopeFilterRefund(Query $query,$status=null){
  334. if(is_null($status)) {
  335. $query->whereBetween("{$this->getTable()}.refund_status", [self::REFUND_ING, self::REFUND_PASS]);
  336. }elseif ($status==1){
  337. $query->where("{$this->getTable()}.refund_status", self::REFUND_ING);
  338. }elseif ($status==2){
  339. $query->where("{$this->getTable()}.refund_status", self::REFUND_PASS);
  340. }
  341. }
  342. /**
  343. * 投诉类型的售后
  344. * @param Query $query
  345. */
  346. public function scopeFilterTs(Query $query){
  347. $query->where("{$this->getTable()}.reason1",self::REASON_QU);
  348. }
  349. public function getTimelineAttr(){
  350. $arr=[
  351. [
  352. 'title'=>'提交申请',
  353. 'time' =>Carbon::createFromTimestamp($this->create_time)->toDateTimeString(),
  354. ],
  355. ];
  356. if($this->refund_status==self::REFUND_REJECT){
  357. $arr[]=[
  358. 'title'=>'审核不通过',
  359. 'time' =>Carbon::createFromTimestamp($this->audit_time)->toDateTimeString(),
  360. ];
  361. $arr[]=[
  362. 'title'=>'完成',
  363. 'time' =>Carbon::createFromTimestamp($this->audit_time)->toDateTimeString(),
  364. ];
  365. }else{
  366. $arr[]=[
  367. 'title'=>'申请审核',
  368. 'time' =>Carbon::createFromTimestamp($this->audit_time)->toDateTimeString(),
  369. ];
  370. switch ($this->refund_type){
  371. case self::REFUND_TYPE_MONEY:
  372. $arr[]=[
  373. 'title'=>'提交申请',
  374. 'time' =>Carbon::createFromTimestamp($this->create_time)->toDateTimeString(),
  375. ];
  376. break;
  377. }
  378. }
  379. return $arr;
  380. }
  381. protected static function init()
  382. {
  383. self::beforeInsert(function (self $refund){
  384. $refund['refund_status']=self::REFUND_ING;
  385. });
  386. self::afterUpdate(function (self $refund){
  387. if($refund->refund_status==self::REFUND_PASS && $refund->isRefundMoney()){
  388. Transaction::addTransaction($refund);
  389. }
  390. });
  391. }
  392. }