OrderGoodsCancel.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. <?php
  2. namespace app\models;
  3. use app\constants\OptionSetting;
  4. use app\modules\admin\models\MerchantForm;
  5. use app\modules\client\models\v1\order\OrderForm;
  6. use app\modules\client\models\v1\ShareMoneyForm;
  7. use app\modules\common\models\NotifyForm;
  8. use app\utils\BookOrderNum;
  9. use app\utils\Notice\NoticeSend;
  10. use app\utils\OrderNo;
  11. use app\utils\OrderRevoke;
  12. use app\utils\PrintOrder;
  13. use app\utils\Refund;
  14. use yii\behaviors\TimestampBehavior;
  15. /**
  16. * @property integer $id
  17. * @property integer $order_id
  18. * @property integer $order_detail_id
  19. * @property integer $num
  20. * @property integer $user_id
  21. * @property integer $store_id
  22. * @property string $refund_order_no
  23. * @property float $refund_price
  24. * @property integer $status
  25. * @property string $created_at
  26. * @property string $updated_at
  27. */
  28. class OrderGoodsCancel extends \yii\db\ActiveRecord
  29. {
  30. /**
  31. * 申请状态:申请中
  32. */
  33. const STATUS_APPLY = 0;
  34. /**
  35. * 申请状态:已通过
  36. */
  37. const STATUS_PASS = 1;
  38. /**
  39. * 申请状态:已拒绝
  40. */
  41. const STATUS_REFUSE = 2;
  42. /**
  43. * 申请状态:打款完成
  44. */
  45. const STATUS_PAY = 3;
  46. /**
  47. * 申请状态:打款失败
  48. */
  49. const STATUS_PAY_FAIL = 4;
  50. /**
  51. * 申请状态:用户取消
  52. */
  53. const STATUS_CANCEL = 5;
  54. public static $status_text = [
  55. self::STATUS_APPLY => '申请中',
  56. self::STATUS_PASS => '已通过',
  57. self::STATUS_REFUSE => '已拒绝',
  58. self::STATUS_PAY => '已打款',
  59. self::STATUS_PAY_FAIL => '打款失败',
  60. self::STATUS_CANCEL => '用户取消',
  61. ];
  62. public static $status_desc = [
  63. self::STATUS_APPLY => '退款申请中',
  64. self::STATUS_PASS => '已通过',
  65. self::STATUS_REFUSE => '已拒绝',
  66. self::STATUS_PAY => '退款已退回至原申请方',
  67. self::STATUS_PAY_FAIL => '退款失败 等待商户重试',
  68. self::STATUS_CANCEL => '用户取消',
  69. ];
  70. public static function tableName()
  71. {
  72. return '{{%order_goods_cancel}}';
  73. }
  74. public function beforeSave($insert)
  75. {
  76. $order_detail = OrderDetail::findOne($this->order_detail_id);
  77. if (!self::is_activity_goods($this->order_id, $order_detail->goods_id)) {
  78. $this->addError('goods', '营销活动产品不可单独退款');
  79. return false;
  80. }
  81. return parent::beforeSave($insert); // TODO: Change the autogenerated stub
  82. }
  83. public function rules()
  84. {
  85. return [
  86. [['id', 'order_id', 'order_detail_id', 'num', 'user_id', 'store_id', 'status'], 'integer'],
  87. [['refund_price', 'created_at', 'updated_at'], 'number'],
  88. [['refund_order_no'], 'string']
  89. ];
  90. }
  91. public function behaviors()
  92. {
  93. return [
  94. [
  95. 'class' => TimestampBehavior::class
  96. ]
  97. ];
  98. }
  99. public function attributeLabels()
  100. {
  101. return [
  102. 'id' => '',
  103. 'order_id' => '订单ID',
  104. 'order_detail_id' => '订单详情ID',
  105. 'num' => '退款订单数量',
  106. 'user_id' => '用户ID',
  107. 'store_id' => '商城ID',
  108. 'refund_order_no' => '取消订单商户号',
  109. 'refund_price' => '取消订单金额(单种商品退款总金额)',
  110. 'status' => '状态:0申请中 1已通过 2已拒绝 3打款完成 4打款失败 5用户取消',
  111. 'created_at' => '',
  112. 'updated_at' => '',
  113. ];
  114. }
  115. public static function getOrderNo()
  116. {
  117. $refund_order_no = null;
  118. while (true) {
  119. $refund_order_no = date('YmdHis') . mt_rand(100000, 999999);
  120. $exist_order_refund_no = self::find()->where(['refund_order_no' => $refund_order_no])->exists();
  121. if (!$exist_order_refund_no) {
  122. break;
  123. }
  124. }
  125. return 'OGC' . $refund_order_no;
  126. }
  127. public static function getCancelGoodsOrderQuery($order_id, $field = '', $group = '', $order_detail_id = 0) {
  128. $query = self::find()->where([
  129. 'order_id' => $order_id,
  130. ]);
  131. if ($order_detail_id) {
  132. $query->andWhere(['order_detail_id' => $order_detail_id]);
  133. }
  134. $query->andWhere(['status' => [
  135. self::STATUS_APPLY,
  136. self::STATUS_PASS,
  137. self::STATUS_PAY,
  138. self::STATUS_PAY_FAIL
  139. ]]);
  140. if ($group) {
  141. $query->groupBy($group);
  142. }
  143. if ($field) {
  144. $query->select($field);
  145. }
  146. return $query;
  147. }
  148. //判断是否是活动产品
  149. public static function is_activity_goods($order_id, $goods_id) {
  150. //推N返1
  151. $activityRebateOrderN = ActivityRebateOrderNLog::findOne(['is_delete' => 0, 'goods_id' => $goods_id]);
  152. if ($activityRebateOrderN) {
  153. return false;
  154. }
  155. //排队免单
  156. $activityQueueLog = QueueLog::findOne(['order_id' => $order_id]);
  157. if ($activityQueueLog) {
  158. return false;
  159. }
  160. //消费全返
  161. $activityRebateOrderSelf = ActivityOrderRebateSelfLog::findOne(['order_id' => $order_id, 'goods_id' => $goods_id]);
  162. if ($activityRebateOrderSelf) {
  163. return false;
  164. }
  165. //团队级差分红
  166. $activityTeamBonus = TeamBonusOrderExt::findOne(['order_id' => $order_id]);
  167. if ($activityTeamBonus) {
  168. return false;
  169. }
  170. //团队业绩分红
  171. $activityTeamGrades = TeamGradesPoolDetailExt::findOne(['order_id' => $order_id]);
  172. if ($activityTeamGrades) {
  173. return false;
  174. }
  175. //37拼购
  176. $activityShareGroup = ShareGroupPurchaseParentLog::findOne(['order_id' => $order_id]);
  177. if ($activityShareGroup) {
  178. return false;
  179. }
  180. //超级卖货
  181. $activitySuperSales = SuperSalesSub::findOne(['order_id' => $order_id]);
  182. if ($activitySuperSales) {
  183. return false;
  184. }
  185. //消费积分增值
  186. $activityIntegralAppreciation = IntegralAppreciationPoolSub::findOne(['order_id' => $order_id]);
  187. if ($activityIntegralAppreciation) {
  188. return false;
  189. }
  190. //视频号
  191. $activityVideoShopOrder = VideoShopOrderExt::findOne(['order_id' => $order_id]);
  192. if ($activityVideoShopOrder) {
  193. return false;
  194. }
  195. //股东/链动
  196. $activityShareHolder = ShareDetail::find()->where(['type_id' => $order_id])->andWhere(['>', 'money', 0])->asArray()->one();
  197. if ($activityShareHolder) {
  198. return false;
  199. }
  200. $activityShareHolderPool = BonusPoolDetail::find()->where(['order_id' => $order_id])->asArray()->one();
  201. if ($activityShareHolderPool) {
  202. return false;
  203. }
  204. $order = Order::findOne($order_id);
  205. if ($order) {
  206. if ($order->activity_cut_price_order_id > 0 ||
  207. $order->activity_wechat_room_id > 0 ||
  208. $order->pt_order_id > 0 ||
  209. $order->seckill_order_id > 0 ||
  210. $order->mch_id > 0 ||
  211. intval($order->order_type) === Order::ORDER_TYPE_GIVING_GIFTS
  212. ) {
  213. return false;
  214. }
  215. }
  216. $share_holder_level = ShareHolderLevel::find()->where(['is_delete' => 0, 'status' => 1, 'store_id' => $order->store_id])->orderBy('level desc')->all();
  217. foreach ($share_holder_level as $value) {
  218. $condition = json_decode($value->condition, true);
  219. $goods = $condition['goods'];
  220. if ((bool)$goods['is_open'] && !empty($goods['value']['id'])) {
  221. $order_model = Order::find()->alias('o')->leftJoin(['od' => OrderDetail::tableName()], 'od.order_id=o.id')->where(['o.id' => $order->id, 'o.is_delete' => 0, 'o.is_pay' => 1])
  222. ->andWhere(['od.goods_id' => $goods['value']['id']])->select('o.id order_id')->asArray()->one();
  223. if ($order_model) {
  224. return false;
  225. }
  226. }
  227. }
  228. $goods = Goods::findOne($goods_id);
  229. if (intval($goods->cloud_goods_id) > 0) {
  230. $cloud_url = "/goods/getGoodsInfo";
  231. $cloud_data = [];
  232. $cloud_data['goods_id'] = $goods->cloud_goods_id;
  233. $domain = (new OptionSetting)->getCloudDomainName();
  234. $cloud_info = cloud_post($domain . $cloud_url, $cloud_data);
  235. $cloud_info = json_decode($cloud_info, true);
  236. if ($cloud_info['code'] != 0) {
  237. return false;
  238. } else {
  239. $cloud_goods = $cloud_info['data']['goods'];
  240. //获取云仓产品
  241. if (intval($cloud_goods['stbz_goods_id'])) {
  242. return false;
  243. }
  244. }
  245. }
  246. return true;
  247. }
  248. public static function orderGoodsCancelHandle($id, $status) {
  249. try {
  250. $order_goods_cancel = self::findOne($id);
  251. if (!$order_goods_cancel) {
  252. throw new \Exception('订单取消记录不存在');
  253. }
  254. $status = intval($status);
  255. //如果当前状态不是未审核或者打款失败的状态 那么就不能操作
  256. // if (!in_array($order_goods_cancel->status, [
  257. // self::STATUS_APPLY,
  258. // self::STATUS_PAY_FAIL
  259. // ])) {
  260. // throw new \Exception('当前申请记录已打款/已取消');
  261. // }
  262. if (!in_array($status, [
  263. self::STATUS_PASS,
  264. self::STATUS_REFUSE,
  265. ])) {
  266. throw new \Exception('状态错误');
  267. }
  268. $order_id = $order_goods_cancel->order_id;
  269. $order = \app\models\Order::findOne($order_id);
  270. // if (intval($order->trade_status) !== Order::ORDER_FLOW_NO_SEND) {
  271. // throw new \Exception('订单状态错误');
  272. // }
  273. if (intval($order->is_sale) || ($order->future_sales_time > 0 && $order->future_sales_time <= time())) {
  274. throw new \Exception('订单已过售后期');
  275. }
  276. $refund_price = $order_goods_cancel->refund_price;
  277. $order_detail_array = \app\models\OrderDetail::find()->where(['order_id' => $order_id])
  278. ->select('id, num')->asArray()->all();
  279. //判断如果订单详情的所有商品已经都取消 那就认为这就是最后一笔订单 那么就可以开始取消积分优惠券卡券等信息
  280. $open = true;
  281. foreach ($order_detail_array as $order_detail_item) {
  282. $cancel_order_detail_num = self::find()
  283. ->where([
  284. 'order_id' => $order_id,
  285. 'order_detail_id' => $order_detail_item['id'],
  286. 'status' => self::STATUS_PAY
  287. ])->sum('num') ?: 0;
  288. //再给当前的也计算上
  289. if ($order_detail_item['id'] == $order_goods_cancel->order_detail_id) {
  290. $cancel_order_detail_num += $order_goods_cancel->num;
  291. }
  292. if ($cancel_order_detail_num < $order_detail_item['num']) {
  293. $open = false;
  294. break;
  295. }
  296. }
  297. //如果是全部订单都退款 那么退款金额还需要包含运费信息
  298. if ($open) {
  299. $refund_price = bcadd($refund_price, $order->express_price, 2);
  300. }
  301. if ($status === self::STATUS_REFUSE) {
  302. $order_goods_cancel->status = $status;
  303. $order_goods_cancel->save();
  304. return [
  305. 'code' => 0,
  306. 'msg' => '处理完成'
  307. ];
  308. }
  309. debug_log([ '开始退款'], 'orderDetail.log');
  310. $orderRevoke = new OrderRevoke();
  311. $orderRevoke->order = $order;
  312. $orderRevoke->type = 0;
  313. if (in_array($order_goods_cancel->status, [
  314. self::STATUS_APPLY,
  315. self::STATUS_PAY_FAIL
  316. ])) {
  317. if ($order->pay_type == Order::PAY_TYPE_BALANCE_PAID) {
  318. if (!$orderRevoke->OrderRefund($refund_price)) {
  319. $order_goods_cancel->status = self::STATUS_PAY_FAIL;
  320. if (!$order_goods_cancel->save()) {
  321. //保存失败
  322. throw new \Exception(implode(';', array_values($order_goods_cancel->firstErrors)));
  323. }
  324. throw new \Exception('退款失败,取消失败');
  325. }
  326. $order_goods_cancel->status = self::STATUS_PAY;
  327. if (!$order_goods_cancel->save()) {
  328. //保存失败
  329. throw new \Exception(implode(';', array_values($order_goods_cancel->firstErrors)));
  330. }
  331. }
  332. if (in_array($order->pay_type, [
  333. Order::ORDER_TYPE_GIVING_GIFTS,
  334. Order::PAY_TYPE_YUNST_WECHAT_PAY,
  335. Order::PAY_TYPE_WECHAT,
  336. Order::PAY_TYPE_WX_B2B,
  337. Order::PAY_TYPE_ADAPAY_WX,
  338. Order::PAY_TYPE_ADAPAY_ALIPAY,
  339. Order::PAY_TYPE_ADAPAY_QUICKPAY_FRONTPAY,
  340. Order::PAY_TYPE_HUIFU_V2_JSPAY_WX,
  341. Order::PAY_TYPE_ALLINPAY_WX,
  342. Order::PAY_TYPE_ALI,
  343. Order::PAY_TYPE_MONTH
  344. ])) {
  345. $res = Refund::refund(
  346. $order,
  347. $order->order_union_id ? OrderNo::ORDER_UNION : '',
  348. $order_goods_cancel->refund_order_no,
  349. $refund_price
  350. );
  351. debug_log(['Refund' => $res], 'orderDetail.log');
  352. if ($res !== true) {
  353. $order_goods_cancel->status = self::STATUS_PAY_FAIL;
  354. if (!$order_goods_cancel->save()) {
  355. //保存失败
  356. throw new \Exception(implode(';', array_values($order_goods_cancel->firstErrors)));
  357. }
  358. throw new \Exception($res['msg']);
  359. }
  360. $order_goods_cancel->status = self::STATUS_PAY;
  361. if (!$order_goods_cancel->save()) {
  362. //保存失败
  363. throw new \Exception(implode(';', array_values($order_goods_cancel->firstErrors)));
  364. }
  365. }
  366. }
  367. // 恢复商品库存
  368. $orderRevoke->backGoodsNum($order_goods_cancel->id);
  369. //查询当前订单中是否还存在未取消的订单 如果没有 那么就开始退款积分优惠券卡券等信息
  370. if ($open) {
  371. $order->trade_status = Order::ORDER_FLOW_CANCEL;
  372. //开始取消积分优惠券卡券等信息
  373. // 预约商品增加库存
  374. BookOrderNum::bookNumAdd($order);
  375. // 取消下单时送的核销卡
  376. VerifyCardSale::cancelCard($order);
  377. $order->save();
  378. $goods = Goods::findOne(OrderDetail::findOne(['order_id' => $order->id])->goods_id);
  379. if ($order->order_type == Order::ORDER_TYPE_Adopt) {
  380. NoticeSend::OrderCancel($order->user_id, $order->mobile, $order->order_no, $order->pay_price, $goods->name, 1);
  381. }else{
  382. NoticeSend::OrderCancel($order->user_id, $order->mobile, $order->order_no, $order->pay_price, $goods->name, 0);
  383. }
  384. // 订单打印
  385. if ((int)$order->md_id === -1 || (int)$order->md_id === 0 || !isset($order->md_id)) {
  386. $order->md_id = 0;
  387. }
  388. $printer_order = new PrintOrder($order->store_id, $order->id, 'confirm', 0, $order->md_id, 0, $order['mch_id']);
  389. $printer_order->is_refund = true;
  390. $printer_order->print_order();
  391. $check = SaaSLeaguePriceLog::findOne(['order_id' => $order->id, 'type' => 4]);
  392. if($order->take_price > 0){
  393. $user = User::findOne($order->user_id);
  394. $saas_user = SaasUser::findOne(['mobile' => $user->binding]);
  395. if($saas_user){
  396. $take_price = $order->take_price;
  397. $before = $saas_user->league_price;
  398. $saas_user->updateCounters(['league_price' => floatval($take_price)]);
  399. SaaSLeaguePriceLog::setLeaguePriceLog(
  400. $order->store_id,
  401. $saas_user->id,
  402. $take_price,
  403. $before,
  404. SaaSLeaguePriceLog::TYPE_CANCEL,
  405. SaaSLeaguePriceLog::SEND_TYPE,
  406. SaaSLeaguePriceLog::ROLE_USER,
  407. $order->id
  408. );
  409. }
  410. }
  411. $balance = isset($order->balance) ? $order->balance : 0;
  412. if ($balance > 0) {
  413. AccountLog::saveLog(
  414. $order->user_id,
  415. $balance,
  416. AccountLog::TYPE_BALANCE,
  417. AccountLog::LOG_TYPE_INCOME,
  418. 0,
  419. $order->id,
  420. "商城订单取消,商品余额抵扣"
  421. );
  422. }
  423. $integral = isset($order->integral) ? json_decode($order->integral)->forehead_integral : 0;
  424. if ($integral > 0) {
  425. AccountLog::saveLog(
  426. $order->user_id,
  427. $integral,
  428. AccountLog::TYPE_INTEGRAL,
  429. AccountLog::LOG_TYPE_INCOME,
  430. 0,
  431. $order->id,
  432. "商城订单取消,商品积分抵扣恢复"
  433. );
  434. }
  435. //如果已经转单 就调用取消接口
  436. $orderTransit = OrderTransit::findOne(['order_id' => $order->id, 'is_delete' => 0]);
  437. if ($orderTransit) {
  438. $merchant = new MerchantForm();
  439. $merchant->order_id = $orderTransit->cloud_order_id;
  440. $result = $merchant->purchaseOrderCancel();
  441. debug_log('云仓订单取消');
  442. debug_log($result);
  443. }
  444. }
  445. //商品取消后通知云仓代码
  446. $order_detail = OrderDetail::findOne($order_goods_cancel->order_detail_id);
  447. $order_transit = OrderTransit::find()->where(['order_id' => $order->id, 'is_delete' => 0])
  448. ->asArray()->all();
  449. if ($order_transit) {
  450. $goods = Goods::findOne($order_detail->goods_id);
  451. $attr = '';
  452. $cloud_order_id = 0;
  453. $order_detail_attr = json_decode($order_detail->attr, true);
  454. $order_detail_attr_id = array_column($order_detail_attr, 'attr_id');
  455. sort($order_detail_attr_id);
  456. $order_detail_goods_info = json_decode($order_detail->goods_info, true);
  457. if (is_string($order_detail_goods_info['attr'])) {
  458. $order_detail_goods_info['attr'] = json_decode($order_detail_goods_info['attr'], true);
  459. }
  460. $goods_attr_no = '';
  461. foreach ($order_detail_goods_info['attr'] as $order_detail_goods_attr_item) {
  462. $goods_attr_id = array_column($order_detail_goods_attr_item['attr_list'], 'attr_id');
  463. sort($goods_attr_id);
  464. if (!array_diff($goods_attr_id, $order_detail_attr_id)) {
  465. $goods_attr_no = $order_detail_goods_attr_item['no'];
  466. }
  467. }
  468. $cloud_order_id_array = array_column($order_transit, 'cloud_order_id');
  469. $domain = (new OptionSetting)->getCloudDomainName();
  470. $order_data_url = "/user/getPurchaseOrder";
  471. $order_data['order_ids'] = implode(',', $cloud_order_id_array);
  472. $order_data_info = cloud_post($domain . $order_data_url, $order_data);
  473. $order_data_info = json_decode($order_data_info, true);
  474. if ($order_data_info['code'] || !isset($order_data_info['data']['list'])) {
  475. return;
  476. }
  477. foreach ($order_data_info['data']['list'] as $cloud_order_item) {//订单
  478. foreach ($cloud_order_item['goods_list'] as $cloud_order_goods_item) {//商品
  479. if ($goods_attr_no == $cloud_order_goods_item['attr']['no']) {
  480. $attr = $cloud_order_goods_item['attr'];
  481. $cloud_order_id = $cloud_order_item['id'];
  482. }
  483. }
  484. }
  485. $attr = json_encode($attr, JSON_UNESCAPED_UNICODE);
  486. $form = new MerchantForm();
  487. $form->store_id = $order->store_id;
  488. $form->order_id = $cloud_order_id;
  489. $form->goods_id = $goods->cloud_goods_id;
  490. $form->attr = $attr;
  491. $form->mch_cancel_id = $order_goods_cancel->id;
  492. $form->goods_num = $order_goods_cancel->num;
  493. $form->md_id = $order->md_id;
  494. $result = $form->purchaseOrderCancelGoods();
  495. if ($result['code']) {
  496. return $result;
  497. }
  498. }
  499. //如果只是部分退款 那么就走下面的计算逻辑 如果是全退 那么就直接会改变订单状态 重新计算没意义
  500. if (!$open) {
  501. //重新计算分销门店佣金/分账金额
  502. $form = new ShareMoneyForm();
  503. $form->order = $order;
  504. $form->order_type = 0;
  505. $form->setData();
  506. if ($order->md_id && !in_array($order->order_type, [1, 2])) {
  507. $result = OrderForm::findPrice($order->id);
  508. MdProfit::handleProfit($order->id, $result['original_price'], $result['md_price'], $order->md_id);
  509. }else{
  510. if ($order->is_offline || in_array($order->order_type, [1, 2])) {
  511. MdProfit::handleProfit($order->id, $order->total_price, $order->total_price, $order->md_id);
  512. }
  513. }
  514. }
  515. if ($order->pay_type === Order::PAY_TYPE_WECHAT) {
  516. $notifyForm = new NotifyForm();
  517. $refund_price = OrderGoodsCancel::find()->where(['order_id' => $order->id, 'status' => [
  518. OrderGoodsCancel::STATUS_PASS,
  519. OrderGoodsCancel::STATUS_PAY,
  520. OrderGoodsCancel::STATUS_PAY_FAIL
  521. ]])->sum('refund_price');
  522. $order->pay_price = bcsub($order->pay_price, $refund_price, 2);
  523. $notifyForm->addReceiver($order);
  524. }
  525. return [
  526. 'code' => 0,
  527. 'msg' => '处理完成'
  528. ];
  529. } catch (\Exception $e) {
  530. return [
  531. 'code' => 1,
  532. 'msg' => $e->getMessage()
  533. ];
  534. }
  535. }
  536. }