RefundController.php 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. <?php
  2. //退款退货
  3. namespace ghs\controllers;
  4. use bizGhs\notify\classes\NotifyClass;
  5. use bizGhs\order\classes\CheckOrderClass;
  6. use bizGhs\order\classes\RefundOrderItemClass;
  7. use bizGhs\order\services\RefundOrderService;
  8. use bizGhs\order\classes\NextDayRefundClass;
  9. use bizGhs\shop\classes\ShopClass;
  10. use bizHd\cg\classes\CgRefundClass;
  11. use bizHd\cg\classes\CgRefundItemClass;
  12. use bizHd\notice\classes\NoticeClass;
  13. use bizHd\purchase\classes\PurchaseClass;
  14. use common\components\dateUtil;
  15. use common\components\dict;
  16. use common\components\imgUtil;
  17. use Yii;
  18. use bizGhs\order\classes\RefundOrderClass;
  19. use common\components\util;
  20. use bizGhs\product\classes\ProductClass;
  21. use bizGhs\order\classes\OrderClass;
  22. use bizGhs\order\services\OrderService;
  23. use bizGhs\order\classes\StockWastageOrderClass;
  24. class RefundController extends BaseController
  25. {
  26. /**
  27. * 原单是否可走跨天售后(售后页预检)
  28. * GET: orderId
  29. */
  30. public function actionCheckNextDayEligible()
  31. {
  32. $orderId = Yii::$app->request->get('orderId', 0);
  33. $order = OrderClass::getById($orderId, true);
  34. if (empty($order) || intval($order->mainId) !== intval($this->mainId)) {
  35. util::fail('没有找到订单');
  36. }
  37. $check = NextDayRefundClass::checkEligible($order);
  38. $check['hasPayTime'] = NextDayRefundClass::hasValidPayTime($order) ? 1 : 0;
  39. $check['hasNextDay'] = bccomp((string)($order->nextDayTkPrice ?? '0'), '0', 2) > 0 ? 1 : 0;
  40. $check['nextDayTkPrice'] = $order->nextDayTkPrice ?? '0.00';
  41. $check['tkPrice'] = $order->tkPrice ?? '0.00';
  42. $check['actPrice'] = $order->actPrice ?? '0.00';
  43. $check['payWay'] = $order->payWay ?? 0;
  44. $check['onlinePay'] = $order->onlinePay ?? 0;
  45. $check['debtPrice'] = $order->debtPrice ?? '0.00';
  46. $check['remainDebtPrice'] = $order->remainDebtPrice ?? '0.00';
  47. $check['clearId'] = intval($order->clearId ?? 0);
  48. $check['payTime'] = $order->payTime ?? '';
  49. util::success($check);
  50. }
  51. //取消的订单退钱,特殊情况的操作:商家取消订单时,客户同时付款成功了,造成的流程冲突,需要退钱 ssh 20250429
  52. public function actionRefundMoney()
  53. {
  54. $get = Yii::$app->request->get();
  55. $id = $get['id'] ?? '';
  56. $order = OrderClass::getById($id, true);
  57. if (empty($order)) {
  58. util::fail('没有找到订单');
  59. }
  60. if ($order->mainId != $this->mainId) {
  61. util::fail('不是你的订单');
  62. }
  63. $addTime = $order->addTime;
  64. $addTime = strtotime($addTime);
  65. $earTime = strtotime('2025-05-01 00:00:00');
  66. if ($addTime < $earTime) {
  67. util::fail('2025.5.1号前的订单,不能退款');
  68. }
  69. $shop = $this->shop;
  70. $purchaseId = $order->purchaseId ?? 0;
  71. $cg = PurchaseClass::getById($purchaseId, true);
  72. if (empty($cg)) {
  73. util::fail('没有找到订单');
  74. }
  75. $staff = $this->shopAdmin;
  76. if ($staff->mobile != '15280215347') {
  77. util::fail('请联系管理员操作');
  78. }
  79. $respond = CgRefundClass::onlyRefundMoney($shop, $cg);
  80. $status = $respond['status'];
  81. $msg = $respond['msg'];
  82. util::success(['status' => $status], $msg);
  83. }
  84. //修改金额 ssh 20240412
  85. public function actionModifyAmount()
  86. {
  87. $get = Yii::$app->request->get();
  88. $id = $get['id'] ?? 0;
  89. $amount = $get['amount'] ?? 0;
  90. $refund = RefundOrderClass::getById($id, true);
  91. RefundOrderClass::valid($refund, $this->mainId);
  92. if (is_numeric($amount) == false) {
  93. util::fail('请填写金额');
  94. }
  95. if ($refund->status != 0) {
  96. util::fail('待审核订单才能修改');
  97. }
  98. $connection = Yii::$app->db;
  99. $transaction = $connection->beginTransaction();
  100. try {
  101. $refund->refundPrice = $amount;
  102. $refund->save();
  103. $cgRefundId = $refund->cgRefundId ?? 0;
  104. $cgRefund = CgRefundClass::getById($cgRefundId, true);
  105. if (empty($cgRefund)) {
  106. util::fail('没有找到退款信息');
  107. }
  108. $cgRefund->refundPrice = $amount;
  109. $cgRefund->save();
  110. $transaction->commit();
  111. util::complete();
  112. } catch (\Exception $e) {
  113. $transaction->rollBack();
  114. Yii::info("操作失败原因:" . $e->getMessage());
  115. util::fail('操作失败');
  116. }
  117. }
  118. //修改申请原因 ssh 20240302
  119. public function actionModifyCause()
  120. {
  121. $staff = $this->shopAdmin;
  122. if (isset($staff->finance) == false || $staff->finance == 0) {
  123. util::fail('请有财务权限的人修改');
  124. }
  125. $get = Yii::$app->request->get();
  126. $id = $get['id'] ?? 0;
  127. $cause = $get['cause'] ?? 0;
  128. $refund = RefundOrderClass::getById($id, true);
  129. RefundOrderClass::valid($refund, $this->mainId);
  130. $refund->cause = $cause;
  131. $refund->save();
  132. $orderSn = $refund->orderSn ?? '';
  133. RefundOrderItemClass::updateByCondition(['orderSn' => $orderSn], ['cause' => $cause]);
  134. $cgRefundId = $refund->cgRefundId ?? 0;
  135. $cgRefund = CgRefundClass::getById($cgRefundId, true);
  136. if (empty($cgRefund)) {
  137. util::fail('没有找到退款信息');
  138. }
  139. $cgRefund->cause = $cause;
  140. $cgRefund->save();
  141. $cgOrderSn = $cgRefund->orderSn ?? '';
  142. CgRefundItemClass::updateByCondition(['orderSn' => $cgOrderSn], ['cause' => $cause]);
  143. util::complete('修改成功');
  144. }
  145. //审核通过 ssh 20230811
  146. public function actionPass()
  147. {
  148. $shopAdmin = $this->shopAdmin;
  149. if (isset($shopAdmin->super) == false || $shopAdmin->super != 1) {
  150. util::fail('无法操作');
  151. }
  152. //多处要同步修改ghs_refund ssh 20250331
  153. if (getenv('YII_ENV') == 'production') {
  154. //小向花卉售后权限
  155. if ($this->mainId == 23390) {
  156. if (!in_array($this->adminId, [23960,77951,4])) {
  157. util::fail('你不能售后哦');
  158. }
  159. //小向花卉售后必须备注
  160. $remark = $post['remark'] ?? '';
  161. if (empty($remark)) {
  162. util::fail('请填写备注007');
  163. }
  164. }
  165. //国恋只有指定人有退款权限
  166. if ($this->mainId == 7704) {
  167. if (in_array($this->shopAdminId, [133727, 133545]) == false) {
  168. util::fail('无法操作。。');
  169. }
  170. }
  171. //小齐出车指定人可以售后
  172. if ($this->mainId == 42680) {
  173. if (!in_array($this->adminId, [40144])) {
  174. util::fail('你不能售后哦。。');
  175. }
  176. }
  177. }
  178. $get = Yii::$app->request->get();
  179. $id = $get['id'] ?? 0;
  180. $version = $get['version'] ?? 0;
  181. $confirm = $get['confirm'] ?? 0;
  182. $refund = RefundOrderClass::getById($id, true);
  183. RefundOrderClass::valid($refund, $this->mainId);
  184. $relateOrderSn = $refund->relateOrderSn ?? '';
  185. if (empty($relateOrderSn)) {
  186. util::fail('没有订单信息');
  187. }
  188. $refundList = RefundOrderClass::getAllByCondition(['relateOrderSn' => $relateOrderSn], null, '*', null, true);
  189. $has = false;
  190. if (!empty($refundList)) {
  191. foreach ($refundList as $single) {
  192. if ($single->status == 1) {
  193. $has = true;
  194. }
  195. }
  196. }
  197. if ($has == true) {
  198. if ($version == 0) {
  199. util::fail('请先升级系统');
  200. }
  201. if ($confirm == 0) {
  202. util::success(['error' => 'notFirstApply']);
  203. }
  204. }
  205. $connection = Yii::$app->db;
  206. $transaction = $connection->beginTransaction();
  207. try {
  208. RefundOrderService::passRefund($refund);
  209. $transaction->commit();
  210. $staff = $this->shopAdmin;
  211. $staffId = $staff->id;
  212. $staffName = $staff->name ?? '';
  213. $refund->shopAdminId = $staffId;
  214. $refund->shopAdminName = $staffName;
  215. $refund->save();
  216. //审核结果通知
  217. $cgRefundId = $refund->cgRefundId ?? 0;
  218. $cgRefund = CgRefundClass::getById($cgRefundId, true);
  219. $cgShopId = $cgRefund->shopId ?? 0;
  220. $cgShop = ShopClass::getById($cgShopId, true);
  221. if (!empty($cgRefund) && !empty($cgShop)) {
  222. NoticeClass::afterSaleNotice($cgShop, $cgRefund);
  223. }
  224. util::complete('操作成功');
  225. } catch (\Exception $e) {
  226. $transaction->rollBack();
  227. Yii::info("操作失败原因:" . $e->getMessage());
  228. util::fail('操作失败');
  229. }
  230. }
  231. //驳回 ssh 20230813
  232. public function actionReject()
  233. {
  234. $shopAdmin = $this->shopAdmin;
  235. if (isset($shopAdmin->super) == false || $shopAdmin->super != 1) {
  236. util::fail('无法操作');
  237. }
  238. //多处要同步修改ghs_refund ssh 20250331
  239. if (getenv('YII_ENV') == 'production') {
  240. //小向花卉售后权限
  241. if ($this->mainId == 23390) {
  242. if (!in_array($this->adminId, [23960,77951,4])) {
  243. util::fail('你不能售后哦。。');
  244. }
  245. //小向花卉售后必须备注
  246. $remark = $post['remark'] ?? '';
  247. if (empty($remark)) {
  248. util::fail('请填写备注007');
  249. }
  250. }
  251. //国恋只有指定人有退款权限
  252. if ($this->mainId == 7704) {
  253. if (in_array($this->shopAdminId, [133727, 133545]) == false) {
  254. util::fail('无法操作。。');
  255. }
  256. }
  257. //小齐出车指定人可以售后
  258. if ($this->mainId == 42680) {
  259. if (!in_array($this->adminId, [40144])) {
  260. util::fail('你不能售后哦。。。');
  261. }
  262. }
  263. }
  264. $post = Yii::$app->request->post();
  265. $id = $post['id'] ?? 0;
  266. $rejectReason = $post['rejectReason'] ?? '';
  267. $refund = RefundOrderClass::getById($id, true);
  268. RefundOrderClass::valid($refund, $this->mainId);
  269. $connection = Yii::$app->db;
  270. $transaction = $connection->beginTransaction();
  271. try {
  272. RefundOrderClass::rejectRefund($refund, $rejectReason);
  273. $transaction->commit();
  274. $staff = $this->shopAdmin;
  275. $staffId = $staff->id;
  276. $staffName = $staff->name ?? '';
  277. $refund->shopAdminId = $staffId;
  278. $refund->shopAdminName = $staffName;
  279. $refund->save();
  280. //审核结果通知
  281. $cgRefundId = $refund->cgRefundId ?? 0;
  282. $cgRefund = CgRefundClass::getById($cgRefundId, true);
  283. $cgShopId = $cgRefund->shopId ?? 0;
  284. $cgShop = ShopClass::getById($cgShopId, true);
  285. if (!empty($cgRefund) && !empty($cgShop)) {
  286. NoticeClass::afterSaleNotice($cgShop, $cgRefund);
  287. }
  288. util::complete('操作成功');
  289. } catch (\Exception $e) {
  290. $transaction->rollBack();
  291. Yii::info("操作失败原因:" . $e->getMessage());
  292. util::fail('操作失败');
  293. }
  294. }
  295. //售后之后减少库存 ssh 20250912
  296. public function actionReduceStock()
  297. {
  298. $shopAdmin = $this->shopAdmin;
  299. if (!isset($shopAdmin->super) || $shopAdmin->super != 1) {
  300. util::fail('管理员才能报损');
  301. }
  302. $post = Yii::$app->request->post();
  303. $refundItemId = $post['refundItemId'] ?? 0;
  304. $refundItem = RefundOrderItemClass::getById($refundItemId, true);
  305. if (empty($refundItem)) {
  306. util::fail('没有找到退货信息');
  307. }
  308. if ($refundItem->mainId != $this->mainId) {
  309. util::fail('不是您的退款信息');
  310. }
  311. $reduceProductId = $refundItem->productId ?? 0;
  312. $reduceNum = $post['reduceNum'] ?? 0;
  313. $productInfo = ProductClass::getById($reduceProductId, true);
  314. if (empty($productInfo)) {
  315. util::fail('没有找到花材');
  316. }
  317. if ($productInfo->mainId != $this->mainId) {
  318. util::fail('不是你的花材');
  319. }
  320. $unitType = $refundItem->xhUnitType ?? 0;
  321. if ($unitType == 1) {
  322. util::fail('小单位,无法减库存');
  323. }
  324. $refundNum = $refundItem->xhNum ?? 0;
  325. $hasReduceNum = $refundItem->xhReduceNum ?? 0;
  326. $remainNum = bcsub($refundNum, $hasReduceNum);
  327. if ($reduceNum > $remainNum) {
  328. util::fail('剩余可减数量 ' . $remainNum);
  329. }
  330. $stock = $productInfo->stock ?? 0;
  331. if ($reduceNum > $stock) {
  332. util::fail('库存不足了');
  333. }
  334. $orderSn = $refundItem->orderSn ?? '';
  335. $bigNum = bcsub($stock, $reduceNum);
  336. $refundItem->xhReduceNum = bcadd($refundItem->xhReduceNum, $reduceNum);
  337. $refundItem->save();
  338. $shop = $this->shop;
  339. $sjId = $shop->sjId ?? 0;
  340. $shopId = $shop->id ?? 0;
  341. $mainId = $shop->mainId ?? 0;
  342. $adminId = $this->adminId;
  343. $staff = $this->shopAdmin;
  344. $staffName = $staff->name ?? '';
  345. $params = [
  346. 'pdType' => '',
  347. 'itemInfo' => [['productId' => $reduceProductId, 'bigNum' => $bigNum, 'smallNum' => '']],
  348. 'remark' => '售后' . $orderSn . '直接减少库存',
  349. 'sjId' => $sjId,
  350. 'shopId' => $shopId,
  351. 'adminId' => $adminId,
  352. 'mainId' => $mainId,
  353. 'staffName' => $staffName,
  354. ];
  355. CheckOrderClass::opOrder($params);
  356. util::complete('操作成功');
  357. }
  358. //退款之后报损 ssh 20230409
  359. public function actionRefundWaste()
  360. {
  361. $shopAdmin = $this->shopAdmin;
  362. if (!isset($shopAdmin->super) || $shopAdmin->super != 1) {
  363. util::fail('管理员才能报损');
  364. }
  365. $post = Yii::$app->request->post();
  366. $refundItemId = $post['refundItemId'] ?? 0;
  367. $refundItem = RefundOrderItemClass::getById($refundItemId, true);
  368. if (empty($refundItem)) {
  369. util::fail('没有找到退货信息');
  370. }
  371. if ($refundItem->mainId != $this->mainId) {
  372. util::fail('不是您的退款信息');
  373. }
  374. $unitType = $refundItem->xhUnitType ?? 0;
  375. if ($unitType == 1) {
  376. util::fail('小单位,无法报损');
  377. }
  378. $wasteProductId = $refundItem->productId ?? 0;
  379. $wasteNum = $post['wasteNum'] ?? 0;
  380. unset($post['refundItemId']);
  381. unset($post['wasteNum']);
  382. unset($post['wasteProductId']);
  383. $productInfo = ProductClass::getById($wasteProductId, true);
  384. if (empty($productInfo)) {
  385. util::fail('没有找到花材');
  386. }
  387. $ptItemId = $productInfo->itemId ?? 0;
  388. $ratio = $productInfo->ratio ?? 20;
  389. $refundNum = $refundItem->xhNum ?? 0;
  390. $hasWasteNum = $refundItem->xhWasteNum ?? 0;
  391. $remainNum = bcsub($refundNum, $hasWasteNum);
  392. if ($wasteNum > $remainNum) {
  393. util::fail('剩余可报损数量 ' . $remainNum);
  394. }
  395. $productList = [['productId' => $wasteProductId, 'bigNum' => $wasteNum, 'smallNum' => 0, 'classId' => 0, 'itemId' => $ptItemId, 'ratio' => $ratio]];
  396. $post['shopId'] = $this->shopId;
  397. $post['sjId'] = $this->sjId;
  398. $post['mainId'] = $this->mainId;
  399. $post['shopAdminId'] = $this->shopAdminId;
  400. $adminName = $shopAdmin['name'] ?? '';
  401. $post['shopAdminName'] = $adminName;
  402. //属于售后报损
  403. $post['saleType'] = 1;
  404. $connection = Yii::$app->db;
  405. $transaction = $connection->beginTransaction();
  406. try {
  407. $refundItem->xhWasteNum = bcadd($refundItem->xhWasteNum, $wasteNum);
  408. $refundItem->save();
  409. $post['product'] = $productList;
  410. $return = StockWastageOrderClass::addOrder($post);
  411. $transaction->commit();
  412. util::success($return);
  413. } catch (\Exception $e) {
  414. $transaction->rollBack();
  415. Yii::info("报损失败原因:" . $e->getMessage());
  416. util::fail('报损失败');
  417. }
  418. }
  419. /**
  420. * 销售售后记录列表:ghs /order/refund-list、小程序售后记录共用。
  421. * matchPayTime/fromAffect=1 时按原单 payTime + 售后 addTime,与跨天影响统计同口径。
  422. */
  423. public function actionList()
  424. {
  425. $get = Yii::$app->request->get();
  426. $where = [];
  427. $where['mainId'] = $this->mainId;
  428. $orderSn = $get['orderSn'] ?? '';
  429. if (!empty($orderSn)) {
  430. $where['relateOrderSn'] = $orderSn;
  431. }
  432. // 可选:只查无关联销售单
  433. $freeOnly = intval($get['freeOnly'] ?? 0);
  434. if ($freeOnly === 1) {
  435. $where['relateOrderSn'] = '';
  436. }
  437. // sameDay:-1全部 1当天售后 0跨天售后
  438. $sameDay = isset($get['sameDay']) ? intval($get['sameDay']) : -1;
  439. if ($sameDay === 0 || $sameDay === 1) {
  440. $where['sameDay'] = $sameDay;
  441. }
  442. $searchTime = $get['searchTime'] ?? '';
  443. if (!empty($searchTime)) {
  444. $startTime = $get['startTime'] ?? '';
  445. $endTime = $get['endTime'] ?? '';
  446. $period = dateUtil::formatTime($searchTime, $startTime, $endTime);
  447. $where['orderTime'] = ['between', [$period['startTime'], $period['endTime']]];
  448. }
  449. $refundSearchTime = $get['refundSearchTime'] ?? '';
  450. // 工作台退款记录:未传时间时默认近30天
  451. $useDefaultLast30 = intval($get['useDefaultLast30'] ?? 0);
  452. if ($useDefaultLast30 === 1 && empty($refundSearchTime) && empty($searchTime)) {
  453. $refundSearchTime = 'last30Days';
  454. }
  455. // 申请日只用 refundStart/End,禁止回落到订单日 startTime/endTime;
  456. // 否则从跨天影响点进来会把「原单日」当成「售后创建日」,列表金额偏大
  457. if (!empty($refundSearchTime)) {
  458. $refundStartTime = $get['refundStartTime'] ?? '';
  459. $refundEndTime = $get['refundEndTime'] ?? '';
  460. $period = dateUtil::formatTime($refundSearchTime, $refundStartTime, $refundEndTime);
  461. $where['addTime'] = ['between', [$period['startTime'], $period['endTime']]];
  462. }
  463. $customId = $get['customId'] ?? '';
  464. if (!empty($customId)) {
  465. $where['customId'] = $customId;
  466. }
  467. $shopAdminId = $get['shopAdminId'] ?? 0;
  468. if (!empty($shopAdminId)) {
  469. $where['shopAdminId'] = $shopAdminId;
  470. }
  471. $status = $get['status'] ?? -1;
  472. if ($status > -1) {
  473. $where['status'] = $status;
  474. }
  475. $export = $get['export'] ?? 0;
  476. if ($export == 1) {
  477. if (isset($where['orderTime']) == false && isset($where['addTime']) == false) {
  478. util::fail('请选时间');
  479. }
  480. ini_set('memory_limit', '2045M');
  481. set_time_limit(0);
  482. if (isset($where['orderTime'])) {
  483. $start = $where['orderTime'][1][0];
  484. $end = $where['orderTime'][1][1];
  485. $monthTime = bcmul(31, 86400);
  486. $startArea = strtotime($start);
  487. $endArea = strtotime($end);
  488. $diff = bcsub($endArea, $startArea);
  489. if ($diff > $monthTime) {
  490. util::fail('最长可导出一个月');
  491. }
  492. }
  493. if (isset($where['addTime'])) {
  494. $start = $where['addTime'][1][0];
  495. $end = $where['addTime'][1][1];
  496. $monthTime = bcmul(31, 86400);
  497. $startArea = strtotime($start);
  498. $endArea = strtotime($end);
  499. $diff = bcsub($endArea, $startArea);
  500. if ($diff > $monthTime) {
  501. util::fail('最长可导出一个月');
  502. }
  503. }
  504. }
  505. // 从跨天售后影响点进来:按原单 payTime + 售后 addTime,且必须挂上批发单,与统计同口径
  506. $matchPayTime = intval($get['matchPayTime'] ?? 0);
  507. if ($matchPayTime !== 1) {
  508. $matchPayTime = intval($get['fromAffect'] ?? 0);
  509. }
  510. $useAffectList = $matchPayTime === 1
  511. && !empty($searchTime)
  512. && isset($where['addTime'])
  513. && isset($where['orderTime']);
  514. if ($useAffectList) {
  515. $respond = NextDayRefundClass::getListByOrderPayTime(
  516. $this->mainId,
  517. ['startTime' => $where['addTime'][1][0], 'endTime' => $where['addTime'][1][1]],
  518. ['startTime' => $where['orderTime'][1][0], 'endTime' => $where['orderTime'][1][1]],
  519. [
  520. 'status' => $status,
  521. 'sameDay' => $sameDay,
  522. 'customId' => $customId,
  523. 'shopAdminId' => $shopAdminId,
  524. ]
  525. );
  526. } else {
  527. $respond = RefundOrderClass::getOrderList($where);
  528. }
  529. if ($export == 1) {
  530. RefundOrderClass::exportRefundData($respond, $this->mainId);
  531. }
  532. util::success($respond);
  533. }
  534. //主要根据花材找退款记录 ssh 20240229
  535. public function actionGetListByItem()
  536. {
  537. $get = Yii::$app->request->get();
  538. $where = [];
  539. $where['mainId'] = $this->mainId;
  540. $status = $get['status'] ?? -1;
  541. $cause = $get['cause'] ?? -1;
  542. if ($status > -1) {
  543. $where['status'] = $status;
  544. }
  545. $string = $get['ids'];
  546. $ids = json_decode($string, true);
  547. if (!empty($ids)) {
  548. $productIds = [];
  549. $itemList = ProductClass::getAllByCondition(['mainId' => $this->mainId, 'classId' => ['in', $ids]], null, 'id', 'id');
  550. if (!empty($itemList)) {
  551. $productIds = array_keys($itemList);
  552. }
  553. if (empty($productIds)) {
  554. util::success(['list' => [], 'moreData' => 0, 'totalAmount' => 0, 'totalNum' => 0, 'totalPage' => 1]);
  555. }
  556. $where['productId'] = ['in', $productIds];
  557. }
  558. if ($cause > -1) {
  559. $where['cause'] = $cause;
  560. }
  561. $searchTime = isset($get['searchTime']) && !empty($get['searchTime']) ? $get['searchTime'] : 'today';
  562. $startTime = $get['startTime'] ?? '';
  563. $endTime = $get['endTime'] ?? '';
  564. $period = dateUtil::formatTime($searchTime, $startTime, $endTime, true);
  565. $start = $period['startTime'];
  566. $end = $period['endTime'];
  567. $currentStartDate = date('Y-m-d', strtotime($start));
  568. $currentEndDate = date('Y-m-d', strtotime($end));
  569. $currentStartTime = $currentStartDate . ' 00:00:00';
  570. $currentEndTime = $currentEndDate . ' 23:59:59';
  571. $where['addTime'] = ['between', [$currentStartTime, $currentEndTime]];
  572. $list = RefundOrderItemClass::getRefundItemList($where);
  573. util::success($list);
  574. }
  575. public function actionDetail()
  576. {
  577. $id = Yii::$app->request->get('id', 0);
  578. $refund = RefundOrderClass::getById($id);
  579. if (empty($refund)) {
  580. util::fail('没有找到退款信息');
  581. }
  582. if (isset($refund['mainId']) == false || $refund['mainId'] != $this->mainId) {
  583. util::fail('没有权限');
  584. }
  585. $imgString = $refund['imgList'] ?? '';
  586. $relateOrderSn = $refund['relateOrderSn'] ?? '';
  587. // 无原单退款:不强制关联销售单
  588. $order = null;
  589. if ($relateOrderSn !== '' && $relateOrderSn !== null) {
  590. $order = OrderClass::getByCondition(['orderSn' => $relateOrderSn], true);
  591. }
  592. if (!empty($order)) {
  593. $refund['relateOrderId'] = $order->id;
  594. //补充原单信息,供前端判断该售后是否要走"转冲销单"(非当天单/欠款已结清),
  595. //以及转冲销单弹框里只读展示原单支付渠道。ssh 冲销单功能
  596. $refund['orderInfo'] = [
  597. 'id' => $order->id,
  598. 'payTime' => $order->payTime,
  599. 'debtPrice' => $order->debtPrice,
  600. 'remainDebtPrice' => $order->remainDebtPrice,
  601. 'actPrice' => $order->actPrice,
  602. 'forwardPrice' => $order->forwardPrice ?? 0,
  603. 'payWay' => $order->payWay,
  604. ];
  605. $refund['isFreeRefund'] = 0;
  606. } else {
  607. $refund['relateOrderId'] = 0;
  608. $refund['orderInfo'] = null;
  609. $refund['isFreeRefund'] = 1;
  610. }
  611. if (!empty($imgString)) {
  612. $imgArr = json_decode($imgString, true);
  613. $smallImgList = [];
  614. $bigImgList = [];
  615. if (!empty($imgArr)) {
  616. foreach ($imgArr as $image) {
  617. $smallImg = imgUtil::groupImg($image) . "?x-oss-process=image/resize,m_fill,h_130,w_130";
  618. $bigImg = imgUtil::groupImg($image) . "?x-oss-process=image/resize,m_fill,h_700,w_700";
  619. $smallImgList[] = $smallImg;
  620. $bigImgList[] = $bigImg;
  621. }
  622. }
  623. $refund['imgList'] = $imgArr;
  624. $refund['smallImgList'] = $smallImgList;
  625. $refund['bigImgList'] = $bigImgList;
  626. }
  627. $orderSn = $refund['orderSn'] ?? '';
  628. $itemList = RefundOrderItemClass::getAllByCondition(['orderSn' => $orderSn], null, '*');
  629. $staff = $this->shopAdmin;
  630. $staffId = $this->shopAdminId;
  631. $notify = NotifyClass::getByCondition(['staffId' => $staffId, 'type' => 0, 'targetId' => $id], true);
  632. if (!empty($notify)) {
  633. if ($notify->read == 0) {
  634. $notify->read = 1;
  635. $notify->save();
  636. $notifyNum = $staff->notifyNum;
  637. if ($notifyNum > 0) {
  638. $notifyNum--;
  639. $staff->notifyNum = $notifyNum;
  640. $staff->save();
  641. }
  642. }
  643. }
  644. $itemRefundOption = dict::getDict('itemRefundOption');
  645. util::success(['info' => $refund, 'itemList' => $itemList, 'itemRefundOption' => $itemRefundOption]);
  646. }
  647. //退款 lqh 2021.6.25
  648. public function actionCreateOrder()
  649. {
  650. $post = Yii::$app->request->post();
  651. $shopAdmin = $this->shopAdmin;
  652. if (isset($shopAdmin['super']) == false || $shopAdmin['super'] != 1) {
  653. util::fail('超管才能操作退款');
  654. }
  655. //多处要同步修改ghs_refund ssh 20250331
  656. if (getenv('YII_ENV') == 'production') {
  657. //小向花卉售后权限
  658. if ($this->mainId == 23390) {
  659. if (!in_array($this->adminId, [23960,77951,4])) {
  660. util::fail('你不能售后哈。。');
  661. }
  662. //小向花卉售后必须备注
  663. $remark = $post['remark'] ?? '';
  664. if (empty($remark)) {
  665. util::fail('请填写备注007');
  666. }
  667. }
  668. //国恋只有指定人有退款权限
  669. if ($this->mainId == 7704) {
  670. if (in_array($this->shopAdminId, [133727, 133545]) == false) {
  671. util::fail('无法操作。。');
  672. }
  673. }
  674. //小齐出车指定人可以售后
  675. if ($this->mainId == 42680) {
  676. if (!in_array($this->adminId, [40144])) {
  677. util::fail('你不能售后哦。。');
  678. }
  679. }
  680. }
  681. $id = isset($post['id']) ? $post['id'] : 0;
  682. $refundType = $post['refundType'] ?? 1;
  683. //避免网络延迟,暂时重复申请
  684. $cacheKey = 'ghs_order_refund_' . $id;
  685. $has = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
  686. if (!empty($has)) {
  687. util::fail('请5秒之后再提交');
  688. }
  689. Yii::$app->redis->executeCommand('SETEX', [$cacheKey, 5, 'has']);
  690. try {
  691. $respond = util::runWithDbConcurrencyRetry(function () use ($post, $id, $refundType) {
  692. $connection = Yii::$app->db;
  693. $transaction = $connection->beginTransaction();
  694. try {
  695. if ($refundType == RefundOrderClass::REFUND_TYPE_MONEY_GOOD) {
  696. //花材列表结构
  697. // [{productId:0,num:1,unitType:0,unitPrice:12,unitName:'扎'}]
  698. $productJson = $post['product'] ?? '';
  699. if (empty($productJson)) {
  700. util::fail('请选择花材');
  701. }
  702. $productList = json_decode($productJson, true);
  703. if (!is_array($productList)) {
  704. util::fail('请选择花材哦');
  705. }
  706. $post['product'] = $productList;
  707. } else {
  708. //仅退款花材直接设置为空
  709. $post['product'] = [];
  710. }
  711. $post['price'] = $post['price'] ?? 0;
  712. if ($post['price'] <= 0) {
  713. util::fail("退款金额不能小于0");
  714. }
  715. $post['shopId'] = $this->shopId;
  716. $post['sjId'] = $this->sjId;
  717. $post['shopAdminId'] = $this->shopAdminId;
  718. $post['mainId'] = $this->mainId;
  719. $shopAdmin = $this->shopAdmin;
  720. $adminName = $shopAdmin['name'] ?? '';
  721. $post['shopAdminName'] = $adminName;
  722. $respond = OrderService::refund($id, $post);
  723. $refundId = $respond->id;
  724. $refundInfo = RefundOrderClass::getById($refundId, true);
  725. RefundOrderService::passRefund($refundInfo);
  726. $transaction->commit();
  727. // 跨天售后:返回海报/结果页所需字段
  728. $refundInfo = RefundOrderClass::getById($refundId, true);
  729. $order = OrderClass::getById($id, true);
  730. $custom = null;
  731. if (!empty($order)) {
  732. $custom = \bizGhs\custom\classes\CustomClass::getById(intval($order->customId ?? 0), true);
  733. }
  734. return [
  735. 'id' => $refundId,
  736. 'orderSn' => $refundInfo->orderSn ?? '',
  737. 'sameDay' => intval($refundInfo->sameDay ?? 1),
  738. 'onlinePay' => intval($refundInfo->onlinePay ?? 0),
  739. 'payWay' => intval($refundInfo->payWay ?? 0),
  740. 'hasReturn' => intval($refundInfo->hasReturn ?? 0),
  741. 'couldReturn' => intval($refundInfo->couldReturn ?? 0),
  742. 'returnBalance' => intval($refundInfo->returnBalance ?? 0),
  743. 'refundPrice' => $refundInfo->refundPrice ?? 0,
  744. 'customId' => intval($order->customId ?? 0),
  745. 'customName' => $custom->name ?? ($order->customName ?? ''),
  746. 'customBalance' => bcadd((string)($custom->balance ?? '0'), '0', 2),
  747. 'orderId' => intval($id),
  748. 'nextDayTkPrice' => $order->nextDayTkPrice ?? '0.00',
  749. 'needFundAction' => NextDayRefundClass::needFundAction($refundInfo),
  750. ];
  751. } catch (\Exception $exception) {
  752. if ($transaction->isActive) {
  753. $transaction->rollBack();
  754. }
  755. throw $exception;
  756. }
  757. });
  758. util::success($respond);
  759. } catch (\Exception $exception) {
  760. Yii::info("退款出错了,报错信息:" . $exception->getMessage());
  761. if (util::isDbConcurrencyException($exception)) {
  762. util::fail('系统繁忙中,请稍后再试');
  763. } else {
  764. // 透出业务失败原因,便于跨天售后排错
  765. util::fail($exception->getMessage() ?: '操作失败');
  766. }
  767. }
  768. }
  769. /**
  770. * 无原单退款(工作台「退款」)
  771. * POST: customId, price, product(json), payWay, refundType/returnWay, remark
  772. * 立即写售后单 sameDay=0 并通过;资金落地留给成功页确认。
  773. */
  774. public function actionCreateFreeRefund()
  775. {
  776. $post = Yii::$app->request->post();
  777. $shopAdmin = $this->shopAdmin;
  778. if (isset($shopAdmin['super']) == false || $shopAdmin['super'] != 1) {
  779. util::fail('超管才能操作退款');
  780. }
  781. // 与 create-order 生产环境售后权限保持一致
  782. if (getenv('YII_ENV') == 'production') {
  783. if ($this->mainId == 23390) {
  784. if (!in_array($this->adminId, [23960, 77951, 4])) {
  785. util::fail('你不能售后哈。。');
  786. }
  787. $remark = $post['remark'] ?? '';
  788. if (empty($remark)) {
  789. util::fail('请填写备注007');
  790. }
  791. }
  792. if ($this->mainId == 7704) {
  793. if (in_array($this->shopAdminId, [133727, 133545]) == false) {
  794. util::fail('无法操作。。');
  795. }
  796. }
  797. if ($this->mainId == 42680) {
  798. if (!in_array($this->adminId, [40144])) {
  799. util::fail('你不能售后哦。。');
  800. }
  801. }
  802. }
  803. $customId = intval($post['customId'] ?? 0);
  804. $cacheKey = 'ghs_free_refund_' . $this->mainId . '_' . $customId;
  805. $has = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
  806. if (!empty($has)) {
  807. util::fail('请5秒之后再提交');
  808. }
  809. Yii::$app->redis->executeCommand('SETEX', [$cacheKey, 5, 'has']);
  810. try {
  811. $respond = util::runWithDbConcurrencyRetry(function () use ($post) {
  812. $connection = Yii::$app->db;
  813. $transaction = $connection->beginTransaction();
  814. try {
  815. // returnWay 优先:1库存退回要花材;0库存不退可只记金额
  816. $returnWay = intval($post['returnWay'] ?? -1);
  817. if ($returnWay === 1) {
  818. $refundType = RefundOrderClass::REFUND_TYPE_MONEY_GOOD;
  819. } elseif ($returnWay === 0) {
  820. $refundType = RefundOrderClass::REFUND_TYPE_MONEY;
  821. } else {
  822. $refundType = intval($post['refundType'] ?? RefundOrderClass::REFUND_TYPE_MONEY_GOOD);
  823. }
  824. $post['refundType'] = $refundType;
  825. if ($refundType == RefundOrderClass::REFUND_TYPE_MONEY_GOOD) {
  826. $productJson = $post['product'] ?? '';
  827. if (empty($productJson)) {
  828. util::fail('请选择花材');
  829. }
  830. $productList = is_array($productJson) ? $productJson : json_decode($productJson, true);
  831. if (!is_array($productList) || empty($productList)) {
  832. util::fail('请选择花材哦');
  833. }
  834. $post['product'] = $productList;
  835. } else {
  836. // 库存不退:金额已够,明细可选保留供对账
  837. $productJson = $post['product'] ?? '';
  838. if (!empty($productJson)) {
  839. $productList = is_array($productJson) ? $productJson : json_decode($productJson, true);
  840. $post['product'] = is_array($productList) ? $productList : [];
  841. } else {
  842. $post['product'] = [];
  843. }
  844. }
  845. $post['price'] = $post['price'] ?? 0;
  846. if ($post['price'] <= 0) {
  847. util::fail('退款金额不能小于0');
  848. }
  849. $post['shopId'] = $this->shopId;
  850. $post['sjId'] = $this->sjId;
  851. $post['shopAdminId'] = $this->shopAdminId;
  852. $post['mainId'] = $this->mainId;
  853. $shopAdmin = $this->shopAdmin;
  854. $post['shopAdminName'] = $shopAdmin['name'] ?? '';
  855. $result = RefundOrderService::createFreeRefund($post);
  856. $transaction->commit();
  857. return $result;
  858. } catch (\Exception $exception) {
  859. if ($transaction->isActive) {
  860. $transaction->rollBack();
  861. }
  862. throw $exception;
  863. }
  864. });
  865. util::success($respond);
  866. } catch (\Exception $exception) {
  867. Yii::info('无原单退款出错:' . $exception->getMessage());
  868. if (util::isDbConcurrencyException($exception)) {
  869. util::fail('系统繁忙中,请稍后再试');
  870. }
  871. util::fail($exception->getMessage() ?: '操作失败');
  872. }
  873. }
  874. /**
  875. * 确认已线下原路退回(现金/银行卡等人工确认)
  876. * POST: id=售后单 id
  877. */
  878. public function actionMarkHasReturn()
  879. {
  880. $post = Yii::$app->request->post();
  881. $id = intval($post['id'] ?? 0);
  882. $refund = RefundOrderClass::getById($id, true);
  883. if (empty($refund)) {
  884. util::fail('没有找到退款信息');
  885. }
  886. if (intval($refund->mainId) !== intval($this->mainId)) {
  887. util::fail('没有权限');
  888. }
  889. try {
  890. $respond = util::runWithDbConcurrencyRetry(function () use ($refund) {
  891. $connection = Yii::$app->db;
  892. $transaction = $connection->beginTransaction();
  893. try {
  894. $refund = NextDayRefundClass::markHasReturn($refund);
  895. $transaction->commit();
  896. return [
  897. 'id' => intval($refund->id ?? 0),
  898. 'hasReturn' => intval($refund->hasReturn ?? 0),
  899. 'couldReturn' => intval($refund->couldReturn ?? 0),
  900. 'returnBalance' => intval($refund->returnBalance ?? 0),
  901. 'payWay' => intval($refund->payWay ?? 0),
  902. 'onlinePay' => intval($refund->onlinePay ?? 0),
  903. 'refundPrice' => $refund->refundPrice ?? 0,
  904. 'customId' => intval($refund->customId ?? 0),
  905. 'customName' => $refund->customName ?? '',
  906. 'orderSn' => $refund->orderSn ?? '',
  907. ];
  908. } catch (\Exception $exception) {
  909. if ($transaction->isActive) {
  910. $transaction->rollBack();
  911. }
  912. throw $exception;
  913. }
  914. });
  915. util::success($respond);
  916. } catch (\Exception $exception) {
  917. util::fail($exception->getMessage() ?: '操作失败');
  918. }
  919. }
  920. /**
  921. * 不想线下转账:返充到客户余额
  922. * POST: id=售后单 id
  923. */
  924. public function actionReturnBalance()
  925. {
  926. $post = Yii::$app->request->post();
  927. $id = intval($post['id'] ?? 0);
  928. $refund = RefundOrderClass::getById($id, true);
  929. if (empty($refund)) {
  930. util::fail('没有找到退款信息');
  931. }
  932. if (intval($refund->mainId) !== intval($this->mainId)) {
  933. util::fail('没有权限');
  934. }
  935. try {
  936. $respond = util::runWithDbConcurrencyRetry(function () use ($refund) {
  937. $connection = Yii::$app->db;
  938. $transaction = $connection->beginTransaction();
  939. try {
  940. $order = null;
  941. $relateOrderSn = trim((string)($refund->relateOrderSn ?? ''));
  942. if ($relateOrderSn !== '') {
  943. $order = OrderClass::getByCondition(['orderSn' => $relateOrderSn], true);
  944. }
  945. $result = NextDayRefundClass::applyReturnBalance($refund, $order);
  946. $refund = $result['refund'];
  947. $transaction->commit();
  948. return [
  949. 'id' => intval($refund->id ?? 0),
  950. 'hasReturn' => intval($refund->hasReturn ?? 0),
  951. 'couldReturn' => intval($refund->couldReturn ?? 0),
  952. 'returnBalance' => intval($refund->returnBalance ?? 0),
  953. 'payWay' => intval($refund->payWay ?? 0),
  954. 'onlinePay' => intval($refund->onlinePay ?? 0),
  955. 'refundPrice' => $refund->refundPrice ?? 0,
  956. 'customId' => intval($refund->customId ?? 0),
  957. 'customName' => $refund->customName ?? '',
  958. 'customBalance' => $result['customBalance'],
  959. 'orderSn' => $refund->orderSn ?? '',
  960. ];
  961. } catch (\Exception $exception) {
  962. if ($transaction->isActive) {
  963. $transaction->rollBack();
  964. }
  965. throw $exception;
  966. }
  967. });
  968. util::success($respond);
  969. } catch (\Exception $exception) {
  970. util::fail($exception->getMessage() ?: '操作失败');
  971. }
  972. }
  973. //修改售后原因 ssh 20250623
  974. public function actionChangeRefundOption()
  975. {
  976. $post = Yii::$app->request->post();
  977. $id = $post['id'] ?? 0;
  978. $sonId = $post['sonId'] ?? 0;
  979. $optionId = $post['optionId'] ?? 0;
  980. $refund = RefundOrderClass::getById($id, true);
  981. if (empty($refund)) {
  982. util::fail('没有找到退款信息');
  983. }
  984. if ($refund->mainId != $this->mainId) {
  985. util::fail('没有权限');
  986. }
  987. $orderSn = $refund->orderSn;
  988. $son = RefundOrderItemClass::getById($sonId, true);
  989. if (empty($son)) {
  990. util::fail('没有找到花材信息');
  991. }
  992. if ($son->orderSn != $orderSn) {
  993. util::fail('退款信息有问题');
  994. }
  995. $map = dict::getDict('itemRefundOption');
  996. $info = $map[$optionId] ?? [];
  997. $name = $info['name'] ?? '未选';
  998. $son->refundOptionId = $optionId;
  999. $son->refundOptionName = $name;
  1000. $son->save();
  1001. util::complete('修改成功');
  1002. }
  1003. }