shish 17 часов назад
Родитель
Сommit
8ecbd15bb4

+ 46 - 14
app-ghs/controllers/CgRefundController.php

@@ -3,6 +3,7 @@
 namespace ghs\controllers;
 
 
+use bizGhs\cg\classes\CgPurchaseNextDayClass;
 use bizGhs\cg\classes\CgRefundClass;
 use bizGhs\cg\classes\CgRefundItemClass;
 use bizGhs\cg\services\CgRefundService;
@@ -15,6 +16,33 @@ use Yii;
 class CgRefundController extends BaseController
 {
 
+    /**
+     * 采购原单是否可走隔天售后(售后页预检)
+     * GET: orderId
+     */
+    public function actionCheckNextDayEligible()
+    {
+        $orderId = Yii::$app->request->get('orderId', 0);
+        $order = PurchaseOrderClass::getById($orderId, true);
+        if (empty($order) || intval($order->mainId) !== intval($this->mainId)) {
+            util::fail('没有找到采购单');
+        }
+        $check = CgPurchaseNextDayClass::checkEligible($order);
+        $check['hasEntryTime'] = CgPurchaseNextDayClass::hasValidEntryTime($order) ? 1 : 0;
+        $check['hasNextDay'] = bccomp((string)($order->nextDayTkPrice ?? '0'), '0', 2) > 0 ? 1 : 0;
+        $check['nextDayTkPrice'] = $order->nextDayTkPrice ?? '0.00';
+        $check['tkPrice'] = $order->tkPrice ?? '0.00';
+        $check['actPrice'] = $order->actPrice ?? '0.00';
+        $check['couldRefundPrice'] = CgPurchaseNextDayClass::mustUseNextDay($order)
+            ? CgPurchaseNextDayClass::getCouldRefundPrice($order)
+            : bcadd((string)($order->realPrice ?? '0'), '0', 2);
+        $check['payWay'] = $order->payWay ?? 0;
+        $check['debt'] = intval($order->debt ?? 0);
+        $check['clearId'] = intval($order->clearId ?? 0);
+        $check['entryTime'] = $order->entryTime ?? '';
+        util::success($check);
+    }
+
     //详情 ssh 20220910
     public function actionGetRefundFullInfo()
     {
@@ -124,7 +152,7 @@ class CgRefundController extends BaseController
         util::success($respond);
     }
 
-    //退款 ssh 20220909
+    //退款 ssh 20220909;支持隔天售后(sameDay=0,不减 actPrice)
     public function actionCreateOrder()
     {
 
@@ -143,12 +171,8 @@ class CgRefundController extends BaseController
             if ($order->mainId != $this->mainId) {
                 util::fail('没有权限');
             }
-            if ($order->status != PurchaseOrderClass::PURCHASE_ORDER_STATUS_COMPLETE) {
-                util::fail('已完成订单才能申请退货退款');
-            }
-            if ($order->debt != 1) {
-                util::fail('订单没有被结清才能申请退货退款');
-            }
+            // 资格、当天/隔天金额上限在 CgRefundService::addRefund 内校验
+            CgPurchaseNextDayClass::assertCanRefund($order);
 
             if (getenv('YII_ENV') == 'production') {
                 if ($this->mainId == 23390) {
@@ -184,9 +208,6 @@ class CgRefundController extends BaseController
             if ($post['price'] <= 0) {
                 util::fail("退款金额不能小于0");
             }
-            if (bccomp($post['price'], $order->realPrice, 2) == 1) {
-                util::fail("退款金额超过订单金额");
-            }
 
             $post['shopId'] = $this->shopId;
             $post['shop'] = $this->shop;
@@ -196,14 +217,25 @@ class CgRefundController extends BaseController
             $shopAdmin = $this->shopAdmin;
             $adminName = $shopAdmin['name'] ?? '';
             $post['shopAdminName'] = $adminName;
-            CgRefundService::addRefund($order, $post);
+            $refund = CgRefundService::addRefund($order, $post);
             $transaction->commit();
+            util::success([
+                'id' => intval($refund->id ?? 0),
+                'refundSn' => $refund->refundSn ?? '',
+                'sameDay' => intval($refund->sameDay ?? 1),
+                'hasReturn' => intval($refund->hasReturn ?? 0),
+                'couldReturn' => intval($refund->couldReturn ?? 0),
+                'returnBalance' => intval($refund->returnBalance ?? 0),
+                'refundPrice' => $refund->refundPrice ?? 0,
+            ]);
         } catch (\Exception $exception) {
-            $transaction->rollBack();
+            if ($transaction->isActive) {
+                $transaction->rollBack();
+            }
             Yii::info("退款申请出错了,报错信息:" . $exception->getMessage());
-            util::fail('操作失败');
+            // 透出业务失败原因,便于隔天售后排错
+            util::fail($exception->getMessage() ?: '操作失败');
         }
-        util::complete();
     }
 
 

+ 169 - 0
biz-ghs/cg/classes/CgPurchaseNextDayClass.php

@@ -0,0 +1,169 @@
+<?php
+/**
+ * 上游采购隔天售后业务类(biz-ghs/cg/classes)
+ * 用途:按入库日/结清状态判定当天 vs 隔天;写支付快照;隔天累加 nextDayTkPrice 并买方加余额。
+ * 调用方:CgRefundService、CgRefundController;与销售 NextDayRefundClass 语义对齐,时间基准用 entryTime。
+ */
+namespace bizGhs\cg\classes;
+
+use bizGhs\order\classes\PurchaseOrderClass;
+use common\components\dict;
+use common\components\util;
+
+class CgPurchaseNextDayClass
+{
+    const SAME_DAY_YES = 1;
+    const SAME_DAY_NO = 0;
+
+    /** 三态开关:否 */
+    const FLAG_NO = 0;
+    /** 三态开关:是 */
+    const FLAG_YES = 1;
+
+    /**
+     * 是否有有效入库时间(采购售后唯一时间依据)
+     */
+    public static function hasValidEntryTime($order)
+    {
+        if (empty($order)) {
+            return false;
+        }
+        $entryTime = $order->entryTime ?? '';
+        return !empty($entryTime) && $entryTime !== '0000-00-00 00:00:00';
+    }
+
+    /**
+     * 售后入口校验:须已入库且有有效入库时间
+     */
+    public static function assertCanRefund($order)
+    {
+        if (empty($order)) {
+            util::fail('没有原采购单');
+        }
+        if (intval($order->status ?? 0) !== PurchaseOrderClass::PURCHASE_ORDER_STATUS_COMPLETE) {
+            util::fail('已入库订单才能申请退货退款');
+        }
+        if (!self::hasValidEntryTime($order)) {
+            util::fail('订单无入库时间,不能售后');
+        }
+    }
+
+    /**
+     * 原单是否必须走隔天售后:
+     * - 入库日非当天;或
+     * - 已结清 debt=2;或
+     * - 已进结账单 clearId>0
+     * @return array{ok:bool,reason:string,sameDay:int,orderCleared:int,hasEntryTime:int}
+     */
+    public static function checkEligible($order)
+    {
+        if (empty($order)) {
+            return [
+                'ok' => false,
+                'reason' => '没有原采购单',
+                'sameDay' => self::SAME_DAY_YES,
+                'orderCleared' => 0,
+                'hasEntryTime' => 0,
+            ];
+        }
+        if (!self::hasValidEntryTime($order)) {
+            return [
+                'ok' => false,
+                'reason' => '订单无入库时间,不能售后',
+                'sameDay' => self::SAME_DAY_YES,
+                'orderCleared' => 0,
+                'hasEntryTime' => 0,
+            ];
+        }
+        $entryTime = $order->entryTime;
+        $isToday = date('Y-m-d', strtotime($entryTime)) === date('Y-m-d');
+        $debtCleared = intval($order->debt ?? 0) === PurchaseOrderClass::DEBT_NO;
+        $orderCleared = !empty($order->clearId);
+
+        // 当天入库且仍待结、未进结账单 → 走普通当天售后
+        if ($isToday && !$debtCleared && !$orderCleared) {
+            return [
+                'ok' => false,
+                'reason' => '当天待结采购单请走普通售后;已结清/已结账/跨入库日可隔天售后',
+                'sameDay' => self::SAME_DAY_YES,
+                'orderCleared' => 0,
+                'hasEntryTime' => 1,
+            ];
+        }
+        return [
+            'ok' => true,
+            'reason' => '',
+            'sameDay' => self::SAME_DAY_NO,
+            'orderCleared' => $orderCleared ? 1 : 0,
+            'hasEntryTime' => 1,
+        ];
+    }
+
+    /**
+     * 创建时是否必须按隔天售后(sameDay=0)
+     */
+    public static function mustUseNextDay($order)
+    {
+        if (!self::hasValidEntryTime($order)) {
+            return false;
+        }
+        $check = self::checkEligible($order);
+        return !empty($check['ok']);
+    }
+
+    /**
+     * 隔天可退上限:actPrice - nextDayTkPrice(不吃掉历史实付)
+     */
+    public static function getCouldRefundPrice($order)
+    {
+        $actPrice = bcadd((string)($order->actPrice ?? '0'), '0', 2);
+        $nextDayTk = bcadd((string)($order->nextDayTkPrice ?? '0'), '0', 2);
+        $could = bcsub($actPrice, $nextDayTk, 2);
+        if (bccomp($could, '0', 2) < 0) {
+            return '0.00';
+        }
+        return $could;
+    }
+
+    /**
+     * 生成采购售后资金快照(落 xhGhsCgRefund)
+     * 上游采购统一买方加余额,couldReturn=0;onlinePay 无订单字段时按非线上。
+     *
+     * @param object $order 原采购单
+     * @return array{onlinePay:int,payWay:int,hasReturn:int,couldReturn:int,returnBalance:int}
+     */
+    public static function buildRefundPaySnapshot($order)
+    {
+        $onlineNot = intval(dict::getDict('onlinePay', 'not'));
+        $payWay = intval($order->payWay ?? 0);
+        return [
+            'onlinePay' => $onlineNot,
+            'payWay' => $payWay,
+            'hasReturn' => self::FLAG_NO,
+            'couldReturn' => self::FLAG_NO,
+            'returnBalance' => self::FLAG_NO,
+        ];
+    }
+
+    /**
+     * 隔天通过:累计 nextDayTkPrice(不改 actPrice);买方加余额后 hasReturn=1
+     *
+     * @param object $refund 采购售后单
+     * @param object $order 采购单(已加锁)
+     * @param object $ghs 买方供货商(已加锁)
+     * @param int $sjId
+     * @param int $shopId
+     */
+    public static function applyAmountAndFund($refund, $order, $ghs, $sjId, $shopId)
+    {
+        $refundPrice = bcadd((string)($refund->refundPrice ?? '0'), '0', 2);
+        $order->nextDayTkPrice = bcadd((string)($order->nextDayTkPrice ?? '0'), $refundPrice, 2);
+        $order->refund = 2;
+        $order->save(false, ['nextDayTkPrice', 'refund']);
+
+        PurchaseOrderClass::applyPurchaseRefundOnAccounts($ghs, $refundPrice, $refund, $order, $sjId, $shopId);
+
+        $refund->hasReturn = self::FLAG_YES;
+        $refund->save(false, ['hasReturn']);
+    }
+}

+ 4 - 0
biz-ghs/cg/models/CgRefund.php

@@ -2,6 +2,10 @@
 namespace bizGhs\cg\models;
 use bizGhs\base\models\Base;
 
+/**
+ * 供应商采购退款单 xhGhsCgRefund
+ * sameDay/hasReturn/couldReturn/returnBalance:对齐销售售后资金三态;采购调账默认 hasReturn=1
+ */
 class CgRefund extends Base
 {
 

+ 81 - 20
biz-ghs/cg/services/CgRefundService.php

@@ -1,4 +1,8 @@
 <?php
+/**
+ * 上游采购售后服务(biz-ghs/cg/services)
+ * 用途:创建采购售后单;按 sameDay 分支当天减实付 / 隔天累加 nextDayTkPrice,并买方加余额。
+ */
 
 namespace bizGhs\cg\services;
 
@@ -8,13 +12,13 @@ use bizGhs\book\classes\BookItemClass;
 use bizGhs\book\classes\BookItemCustomClass;
 use bizGhs\book\classes\BookItemGhsClass;
 use bizGhs\cg\classes\CgOrderItemSendClass;
+use bizGhs\cg\classes\CgPurchaseNextDayClass;
 use bizGhs\cg\classes\CgRefundClass;
 use bizGhs\cg\classes\CgRefundItemClass;
 use bizGhs\ghs\classes\GhsClass;
 use bizGhs\order\classes\PurchaseOrderClass;
 use bizGhs\order\classes\PurchaseOrderItemClass;
 use bizGhs\product\classes\ProductClass;
-use common\components\dict;
 use common\components\orderSn;
 use common\components\util;
 use Yii;
@@ -24,30 +28,68 @@ class CgRefundService extends BaseService
 
     public static $baseFile = '\bizGhs\cg\classes\CgRefundClass';
 
-    //退款
+    /**
+     * 创建采购售后:当天减 actPrice/加 tkPrice;隔天只加 nextDayTkPrice;均买方加余额。
+     * @param object $order 已加锁采购单
+     * @param array $post price/refundType/product/sameDay/...
+     */
     public static function addRefund($order, $post)
     {
         $refundPrice = $post['price'] ?? 0;
         if ($refundPrice <= 0) {
             util::fail('请填写退款金额');
         }
-        $order->refundPrice = $refundPrice;
-        $currentTkPrice = bcadd($order->tkPrice, $refundPrice, 2);
-        $order->tkPrice = $currentTkPrice;
-        $actPrice = $order->actPrice ?? 0;
-        $realPrice = $order->realPrice ?? 0;
+
+        CgPurchaseNextDayClass::assertCanRefund($order);
+
+        // 非当天入库 / 已结清 / 已结账 → 强制隔天
+        $sameDay = intval($post['sameDay'] ?? CgPurchaseNextDayClass::SAME_DAY_YES);
+        if (CgPurchaseNextDayClass::mustUseNextDay($order)) {
+            $sameDay = CgPurchaseNextDayClass::SAME_DAY_NO;
+        }
+        if ($sameDay === CgPurchaseNextDayClass::SAME_DAY_NO) {
+            $check = CgPurchaseNextDayClass::checkEligible($order);
+            if (empty($check['ok'])) {
+                util::fail($check['reason'] ?? '当前订单不可隔天售后');
+            }
+            $couldRefund = CgPurchaseNextDayClass::getCouldRefundPrice($order);
+            if (bccomp((string)$refundPrice, $couldRefund, 2) === 1) {
+                util::fail('退款金额超过可退金额');
+            }
+        } else {
+            // 当天售后须仍待结、未进结账单
+            if (intval($order->debt ?? 0) !== PurchaseOrderClass::DEBT_YES) {
+                util::fail('订单没有被结清才能申请当天退货退款');
+            }
+            if (!empty($order->clearId)) {
+                util::fail('已结账订单请走隔天售后');
+            }
+            if (bccomp((string)$refundPrice, (string)($order->realPrice ?? '0'), 2) === 1) {
+                util::fail('退款金额超过订单金额');
+            }
+        }
+
         $bookSn = $order->bookSn ?? 0;
-        $currentActPrice = bcsub($actPrice, $refundPrice, 2);
-        $currentRealPrice = bcsub($realPrice, $refundPrice, 2);
-        $order->actPrice = $currentActPrice;
-        $order->realPrice = $currentRealPrice;
-        $order->refund = 2;
-        // 待结采购单售后退尽:actPrice 已为 0 须同步改为已结清,否则会留下 debt=待结 的幽灵单
-        if (intval($order->debt ?? 0) === PurchaseOrderClass::DEBT_YES
-            && bccomp($currentActPrice, '0', 2) <= 0) {
-            $order->debt = PurchaseOrderClass::DEBT_NO;
+        $isNextDay = $sameDay === CgPurchaseNextDayClass::SAME_DAY_NO;
+
+        // 当天:减实付、累加 tkPrice;隔天:实付不动,nextDayTkPrice 在 applyAmountAndFund 写入
+        if (!$isNextDay) {
+            $currentTkPrice = bcadd($order->tkPrice, $refundPrice, 2);
+            $order->tkPrice = $currentTkPrice;
+            $actPrice = $order->actPrice ?? 0;
+            $realPrice = $order->realPrice ?? 0;
+            $currentActPrice = bcsub($actPrice, $refundPrice, 2);
+            $currentRealPrice = bcsub($realPrice, $refundPrice, 2);
+            $order->actPrice = $currentActPrice;
+            $order->realPrice = $currentRealPrice;
+            $order->refund = 2;
+            // 待结采购单售后退尽:actPrice 已为 0 须同步改为已结清,否则会留下 debt=待结 的幽灵单
+            if (intval($order->debt ?? 0) === PurchaseOrderClass::DEBT_YES
+                && bccomp($currentActPrice, '0', 2) <= 0) {
+                $order->debt = PurchaseOrderClass::DEBT_NO;
+            }
+            $order->save();
         }
-        $order->save();
 
         $data = [];
         $data['status'] = 1;
@@ -98,9 +140,12 @@ class CgRefundService extends BaseService
                 $unitName = $currentCgItem->bigUnit ?? '';
                 $unitCost = $currentCgItem->cost ?? 0;
 
-                //已退数量增加
+                // refundNum=总已退;隔天部分另记 nextRefundNum
                 $currentRefundNum = bcadd($currentCgItem->refundNum, $num);
                 $currentCgItem->refundNum = $currentRefundNum;
+                if ($isNextDay) {
+                    $currentCgItem->nextRefundNum = bcadd($currentCgItem->nextRefundNum ?? 0, $num);
+                }
                 $currentCgItem->save();
 
                 $totalPrice = bcmul($unitPrice, $num, 2);
@@ -234,6 +279,15 @@ class CgRefundService extends BaseService
         $data['orderTime'] = $order->entryTime;
         $data['ghsId'] = $order->ghsId ?? 0;
         $data['ghsName'] = $order->ghsName ?? '';
+
+        $snap = CgPurchaseNextDayClass::buildRefundPaySnapshot($order);
+        $data['sameDay'] = $sameDay;
+        $data['onlinePay'] = $snap['onlinePay'];
+        $data['payWay'] = $snap['payWay'];
+        $data['hasReturn'] = $snap['hasReturn'];
+        $data['couldReturn'] = $snap['couldReturn'];
+        $data['returnBalance'] = $snap['returnBalance'];
+
         $refund = CgRefundClass::add($data, true);
 
         //锁定当前增加资产的运作,有多处,请搜索关键词 ghsAddDebtAction
@@ -242,9 +296,16 @@ class CgRefundService extends BaseService
         if (empty($ghs)) {
             util::fail('没有找到供货商');
         }
-        PurchaseOrderClass::applyPurchaseRefundOnAccounts($ghs, $refundPrice, $refund, $order, $sjId, $shopId);
+
+        if ($isNextDay) {
+            CgPurchaseNextDayClass::applyAmountAndFund($refund, $order, $ghs, $sjId, $shopId);
+        } else {
+            PurchaseOrderClass::applyPurchaseRefundOnAccounts($ghs, $refundPrice, $refund, $order, $sjId, $shopId);
+            $refund->hasReturn = CgPurchaseNextDayClass::FLAG_YES;
+            $refund->save(false, ['hasReturn']);
+        }
 
         return $refund;
     }
 
-}
+}

+ 4 - 1
biz-ghs/order/models/PurchaseOrder.php

@@ -1,7 +1,10 @@
 <?php
 namespace bizGhs\order\models;
 use bizGhs\base\models\Base;
-//采购订单
+/**
+ * 采购订单 xhGhsCgOrder
+ * nextDayTkPrice:隔天售后累计退款,不改 actPrice;可退上限=actPrice-nextDayTkPrice
+ */
 class PurchaseOrder extends Base
 {
 

+ 4 - 1
biz-ghs/order/models/PurchaseOrderItem.php

@@ -1,7 +1,10 @@
 <?php
 namespace bizGhs\order\models;
 use bizGhs\base\models\Base;
-//采购订单详情
+/**
+ * 采购订单明细 xhGhsCgOrderItem
+ * nextRefundNum:隔天售后已退数量;当天已退 = refundNum - nextRefundNum
+ */
 class PurchaseOrderItem extends Base
 {