shish 1 месяц назад
Родитель
Сommit
fd750eb0a1

+ 123 - 3
app-hd/controllers/PurchaseController.php

@@ -7,6 +7,7 @@ use biz\common\classes\GhsNotifyClass;
 use biz\ghs\classes\GhsClass;
 use biz\shop\classes\ShopClass;
 use biz\shop\classes\ShopExtClass;
+use bizGhs\custom\classes\AccountMoneyClass;
 use bizGhs\custom\classes\CustomClass;
 use bizGhs\express\classes\GhsDeliveryOrderClass;
 use bizGhs\order\classes\OrderClass;
@@ -976,6 +977,102 @@ class PurchaseController extends BaseController
         util::success($info->attributes);
     }
 
+    /**
+     * 采购待支付页:客户 xhGhsCustom 净余额支付(并发重试 + 行锁)。
+     */
+    public function actionCustomBalancePay()
+    {
+        $post = Yii::$app->request->post();
+        $orderSn = $post['orderSn'] ?? '';
+        if (empty($orderSn)) {
+            util::fail('订单号无效');
+        }
+
+        $cacheKey = 'hd_custom_balance_pay_' . $orderSn;
+        $has = Yii::$app->redis->executeCommand('GET', [$cacheKey]);
+        if (!empty($has)) {
+            util::fail('请稍等...');
+        }
+        Yii::$app->redis->executeCommand('SETEX', [$cacheKey, 6, 'has']);
+
+        $info = PurchaseClass::getByCondition(['orderSn' => $orderSn], true);
+        if (empty($info)) {
+            util::fail('没有找到采购单');
+        }
+        PurchaseService::valid($info, $this->shopId);
+
+        $deadline = $info->deadline ?? '';
+        if (!empty($deadline) && time() > strtotime($deadline)) {
+            util::fail('订单已失效了哦');
+        }
+
+        $ghsId = intval($info->ghsId ?? 0);
+        $ghs = GhsClass::getGhsInfo($ghsId);
+
+        try {
+            $result = util::runWithDbConcurrencyRetry(function () use ($orderSn) {
+                $connection = Yii::$app->db;
+                $transaction = $connection->beginTransaction();
+                try {
+                    $purchase = PurchaseClass::getByCondition(['orderSn' => $orderSn], true);
+                    $respond = PurchaseService::payByGhsCustomBalance($purchase);
+                    $transaction->commit();
+                    return $respond;
+                } catch (\Exception $exception) {
+                    if ($transaction->isActive) {
+                        $transaction->rollBack();
+                    }
+                    throw $exception;
+                }
+            });
+
+            $info = $result['purchase'];
+            $order = $result['saleOrder'] ?? null;
+
+            if (!empty($order)) {
+                if (isset($info->book) && $info->book == 0 && !empty($ghs)) {
+                    $ghsShopId = $ghs['shopId'] ?? 0;
+                    $ext = ShopExtClass::getByCondition(['shopId' => $ghsShopId], true, null, 'id,shopId,orderFlow');
+                    if (!empty($ext) && intval($ext->orderFlow ?? 0) === 0) {
+                        $cg = PurchaseClass::getByCondition(['orderSn' => $orderSn], true);
+                        if (!empty($cg)) {
+                            PurchaseClass::confirmTake($cg);
+                        }
+                    }
+                }
+
+                $shopId = $order->shopId ?? 0;
+                $shop = ShopClass::getById($shopId, true);
+                if (!empty($shop)) {
+                    GhsNotifyClass::newOrderNotify($order->id ?? 0);
+                }
+                if (isset($order->needPrint) && $order->needPrint == dict::getDict('needPrint', 'need')) {
+                    OrderClass::onlinePrint($order);
+                }
+                GhsDeliveryOrderClass::payAfter($order);
+
+                $customId = $order->customId ?? 0;
+                $custom = CustomClass::getById($customId, true);
+                $date = date('Y-m-d H:i:s');
+                if (!empty($custom)) {
+                    $custom->recentExpend = $date;
+                    $custom->save();
+                }
+                $orderGhsId = $order->ghsId ?? 0;
+                $orderGhs = GhsClass::getById($orderGhsId, true);
+                if (!empty($orderGhs)) {
+                    $orderGhs->recentExpend = $date;
+                    $orderGhs->save();
+                }
+            }
+
+            util::success($info->attributes);
+        } catch (\Exception $e) {
+            Yii::info('客户余额支付失败:' . $e->getMessage());
+            util::fail($e->getMessage() ?: '支付失败');
+        }
+    }
+
     //余额支付 ssh 2021.1.24
     public function actionBalancePay()
     {
@@ -1008,8 +1105,8 @@ class PurchaseController extends BaseController
         $connection = Yii::$app->db;
         $transaction = $connection->beginTransaction();
         try {
-            //余额支付
-            purchaseService::balancePay($info);
+            // 余额支付:走 xhGhsCustom,不扣 main.balance
+            $info = PurchaseService::balancePay($info);
             $transaction->commit();
         } catch (\Exception $e) {
             $transaction->rollBack();
@@ -1135,7 +1232,30 @@ class PurchaseController extends BaseController
         $shop = $this->shop;
         $info['pfShopId'] = $shop->pfShopId ?? 0;
         $info['hasDebtPay'] = $hasDebtPay;
-        $info['balance'] = $shop->balance ?? 0;
+        // 待支付页展示 xhGhsCustom 净余额(正数=可支付余额),不再使用 main.balance
+        $info['balance'] = 0;
+        $info['customNetBalance'] = '0.00';
+        $info['showBalancePay'] = 0;
+        $info['ghsSalt'] = '';
+        $ghsId = intval($info['ghsId'] ?? 0);
+        if ($ghsId > 0) {
+            $ghsRow = GhsClass::getById($ghsId, true);
+            if (!empty($ghsRow)) {
+                $info['ghsSalt'] = $ghsRow->salt ?? '';
+                $ghsCustomId = intval($ghsRow->customId ?? 0);
+                if ($ghsCustomId > 0) {
+                    $ghsCustom = CustomClass::getById($ghsCustomId, true);
+                    if (!empty($ghsCustom)) {
+                        $net = AccountMoneyClass::getNetBalanceFromRow($ghsCustom);
+                        $info['customNetBalance'] = $net;
+                        if (bccomp($net, '0', 2) > 0) {
+                            $info['balance'] = floatval($net);
+                            $info['showBalancePay'] = 1;
+                        }
+                    }
+                }
+            }
+        }
         $getPayType = dict::getDict('getPayType');
         $info['getPayType'] = $getPayType;
 

+ 3 - 3
biz-ghs/custom/classes/CustomClass.php

@@ -228,7 +228,7 @@ class CustomClass extends BaseClass
 
         // 合并后净余额可为负(待结),勿再因扣款后 <0 报错;金额由 CustomPaymentAllocateService 按来款池控制
         if (bccomp($newGhsBalance, $newCustomBalance, 2) !== 0) {
-            util::fail('客户与供货商余额不一致,无法结账,请联系管理员');
+            util::fail('余额有问题,请向客服反应');
         }
 
         $ghs->balance = $newGhsBalance;
@@ -650,7 +650,7 @@ class CustomClass extends BaseClass
     }
 
     /**
-     * 调用方已 lockAccountPair 时,校验 custom/ghs 净余额一致。
+     * 调用方已 lockAccountPair 时,校验 custom/ghs 净余额一致;不一致则中止并提示联系客服
      */
     protected static function assertPairBalanceEqual($custom, $ghs)
     {
@@ -660,7 +660,7 @@ class CustomClass extends BaseClass
         $customBal = bcadd((string)($custom->balance ?? '0'), '0', 2);
         $ghsBal = bcadd((string)($ghs->balance ?? '0'), '0', 2);
         if (bccomp($customBal, $ghsBal, 2) !== 0) {
-            util::fail('客户与供货商余额不一致,请联系管理员');
+            util::fail('余额有问题,请向客服反应');
         }
     }
 

+ 3 - 3
biz-ghs/custom/services/GhsRechargeSettleService.php

@@ -276,7 +276,7 @@ class GhsRechargeSettleService
         if (empty($custom)) {
             util::fail('没有找到客户');
         }
-        AccountMoneyClass::ensureCustomMoneyReady($custom, true);
+        $custom = AccountMoneyClass::ensureCustomMoneyReady($custom, true);
 
         $ghsId = $custom->ghsId ?? 0;
         if (empty($ghs)) {
@@ -285,10 +285,10 @@ class GhsRechargeSettleService
         if (empty($ghs)) {
             util::fail('没有找到供货商');
         }
-        AccountMoneyClass::ensureGhsMoneyReady($ghs, true);
+        $ghs = AccountMoneyClass::ensureGhsMoneyReady($ghs, true);
 
         if (bccomp(self::money($custom->balance), self::money($ghs->balance), 2) !== 0) {
-            util::fail('客户与供货商余额不一致,请联系管理员');
+            util::fail('余额有问题,请联系管理员');
         }
 
         return ['custom' => $custom, 'ghs' => $ghs];

+ 74 - 34
biz-hd/purchase/services/PurchaseService.php

@@ -9,7 +9,9 @@ use biz\shop\classes\ShopCouponClass;
 use biz\stat\classes\StatCgClass;
 use biz\stat\classes\StatCgGhsClass;
 use biz\stat\classes\StatOutClass;
+use bizGhs\custom\classes\AccountMoneyClass;
 use bizGhs\custom\classes\CustomClass;
+use bizGhs\custom\services\GhsRechargeSettleService;
 use bizGhs\order\classes\OrderClass;
 use bizGhs\order\services\OrderService;
 use bizHd\base\services\BaseService;
@@ -349,15 +351,81 @@ class PurchaseService extends BaseService
 
     }
 
-    //余额支付 ssh 2021.1.24
+    /**
+     * 采购单余额支付(超管密码入口等)。
+     * 批发-客户资金只在 xhGhs / xhGhsCustom,不扣 xhShop / xhMain.balance。
+     */
     public static function balancePay($info)
     {
+        $result = self::payByGhsCustomBalance($info);
+        return $result['purchase'] ?? $info;
+    }
+
+    /**
+     * 花店采购余额付:批发商-客户资金只在 xhGhs/xhGhsCustom,先销售单 payAfter 扣净余额,再采购单 payAfter 改状态。
+     * xhShop/xhMain 仅为花店门店属性,不参与本关系余额扣减。须在事务内调用;并发由外层重试 + 行锁保证。
+     */
+    public static function payByGhsCustomBalance($purchase)
+    {
+        if (empty($purchase)) {
+            util::fail('没有找到采购单');
+        }
+        if (intval($purchase->status ?? 0) == PurchaseClass::STATUS_CANCEL) {
+            util::fail('订单已取消');
+        }
+        if (intval($purchase->status ?? 0) != PurchaseClass::STATUS_UN_PAY) {
+            util::fail('订单不是待付款状态');
+        }
+        if (intval($purchase->payStatus ?? 0) === 1) {
+            util::fail('订单已经付过款了');
+        }
+        $saleId = intval($purchase->saleId ?? 0);
+        if ($saleId <= 0) {
+            util::fail('订单无法余额支付');
+        }
+
+        $purchaseId = intval($purchase->id ?? 0);
+        $purchase = PurchaseClass::getLockById($purchaseId);
+        $order = OrderClass::getLockById($saleId);
+        if (empty($order)) {
+            util::fail('没有找到销售单');
+        }
+        if (intval($order->status ?? 0) != OrderClass::ORDER_STATUS_UN_PAY) {
+            util::fail('重复操作');
+        }
+
+        $customId = intval($order->customId ?? 0);
+        $pair = GhsRechargeSettleService::lockAccountPair(['id' => $customId]);
+        $custom = $pair['custom'];
+        if (empty($custom)) {
+            util::fail('没有找到客户信息');
+        }
+        AccountMoneyClass::ensureCustomMoneyReady($custom, true);
+        // 与待支付页 realPrice 一致,避免 actPrice 与 realPrice 不一致时误报余额不足
+        $payAmount = bcadd((string)($purchase->realPrice ?? $order->realPrice ?? $order->actPrice ?? '0'), '0', 2);
+        $beforeBalance = bcadd((string)($custom->balance ?? '0'), '0', 2);
+        if (bccomp($beforeBalance, $payAmount, 2) < 0) {
+            util::fail('余额不足');
+        }
+
         $payWay = dict::getDict('payWay', 'balancePay');
-        self::payAfter($info, $payWay);
+        OrderService::payAfter($order, $payWay, false);
 
-        $saleId = $info->saleId ?? 0;
-        $order = OrderClass::getById($saleId, true);
-        OrderService::payAfter($order, $payWay, true);
+        $purchase = PurchaseClass::getLockById($purchaseId);
+        $saleOrder = OrderClass::getById($saleId, true);
+        if (!empty($purchase) && !empty($saleOrder) && intval($saleOrder->payStatus ?? 0) === 1) {
+            $purchase->remainDebtPrice = $saleOrder->remainDebtPrice ?? '0.00';
+            $purchase->debtPrice = $saleOrder->debtPrice ?? '0.00';
+            $purchase->debt = ($saleOrder->debt ?? 0) == OrderClass::DEBT_YES
+                ? PurchaseClass::DEBT_YES : PurchaseClass::DEBT_NO;
+            $purchase->save(false, ['remainDebtPrice', 'debtPrice', 'debt']);
+        }
+        self::payAfter($purchase, $payWay);
+
+        return [
+            'purchase' => $purchase,
+            'saleOrder' => OrderClass::getById($saleId, true),
+        ];
     }
 
     //付款码支付处理流程 ssh 20211231
@@ -457,31 +525,6 @@ class PurchaseService extends BaseService
 
     }
 
-    /**
-     * 采购单余额付是否扣减花店 main.balance。
-     * main.balance 已废弃、后续会删,批发余额以 xhGhsCustom 净余额为准(OrderService::payToChangeBalance)。
-     * 仅「花店自助采购、先付采购单」等遗留路径仍可能走 purchaseReduceBalance,待 main.balance 下线后一并改。
-     */
-    protected static function shouldReduceMainBalanceOnBalancePay($info)
-    {
-        if (intval($info->cgStyle ?? 0) !== intval(dict::getDict('cgStyle', 'ghs'))) {
-            return true;
-        }
-        $saleId = intval($info->saleId ?? 0);
-        if ($saleId <= 0) {
-            return true;
-        }
-        $saleOrder = OrderClass::getById($saleId, true);
-        if (empty($saleOrder) || intval($saleOrder->payStatus ?? 0) !== 1) {
-            return true;
-        }
-        // 批发开单先完成销售单 payAfter:客户余额已扣,勿再动 main.balance
-        if (intval($saleOrder->payWay ?? -1) === intval(dict::getDict('payWay', 'balancePay'))) {
-            return false;
-        }
-        return true;
-    }
-
     //第三方支付、余额支付和欠款支付后调用的流程 ssh 2021.4.27
     public static function payAfter($info, $payWay)
     {
@@ -563,10 +606,7 @@ class PurchaseService extends BaseService
         }
         $realPrice = $info->realPrice;
         $actPrice = $info->actPrice;
-        // 余额支付:批发开单只扣 xhGhsCustom;main.balance 已废弃,见 shouldReduceMainBalanceOnBalancePay
-        if ($payWay == dict::getDict('payWay', 'balancePay') && self::shouldReduceMainBalanceOnBalancePay($info)) {
-            ShopClass::purchaseReduceBalance($main, $shop, $realPrice, $info);
-        }
+        // 余额支付:批发商-客户资金在 xhGhs/xhGhsCustom(销售单 payAfter 扣款),不扣 xhMain.balance
         $main->totalPurchase = bcadd($main->totalPurchase, $actPrice, 2);
         $currentExpend = bcadd($main->totalExpend, $actPrice, 2);
         $main->totalExpend = $currentExpend;