Refund.php 16 KB

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