Răsfoiți Sursa

先结申请售后的订单

shish 7 ore în urmă
părinte
comite
5a4144692b

+ 17 - 7
biz-ghs/custom/classes/CustomClass.php

@@ -699,7 +699,7 @@ class CustomClass extends BaseClass
 
 
     /**
     /**
      * 隔天售后/无原单返充余额:成对账户加余额,不改订单 actPrice;流水挂售后单。
      * 隔天售后/无原单返充余额:成对账户加余额,不改订单 actPrice;流水挂售后单。
-     * 入账后按本次金额 FIFO 销挂账(与客户详情充值消欠一致)
+     * 入账后按本次金额销挂账:有原单则先结本单(可部分结),剩余再 FIFO。
      * 调用方须已 lockAccountPair。
      * 调用方须已 lockAccountPair。
      */
      */
     public static function nextDayRefundReturnBalance($custom, $ghs, $refund, $order)
     public static function nextDayRefundReturnBalance($custom, $ghs, $refund, $order)
@@ -747,8 +747,8 @@ class CustomClass extends BaseClass
             'remark' => '',
             'remark' => '',
         ], true);
         ], true);
         self::addGhsRefundBalanceChange($custom, $ghs, $refund, $order, $refundPrice, $newBalance);
         self::addGhsRefundBalanceChange($custom, $ghs, $refund, $order, $refundPrice, $newBalance);
-        // 有挂账欠款时:本次返充金额去消欠(与充值 merchantRechargeWithAutoClear 一致
-        self::fifoClearAfterRefundCredit($custom, $ghs, $refund, $refundPrice, $balanceBefore);
+        // 有挂账欠款时:本次返充金额去消欠(优先当前售后原单,剩余再 FIFO
+        self::fifoClearAfterRefundCredit($custom, $ghs, $refund, $refundPrice, $balanceBefore, $order);
         $custom = CustomClass::getById(intval($custom->id ?? 0), true);
         $custom = CustomClass::getById(intval($custom->id ?? 0), true);
         return bcadd((string)($custom->balance ?? $newBalance), '0', 2);
         return bcadd((string)($custom->balance ?? $newBalance), '0', 2);
     }
     }
@@ -801,14 +801,22 @@ class CustomClass extends BaseClass
         ];
         ];
         CustomBalanceChangeClass::add($change, true);
         CustomBalanceChangeClass::add($change, true);
         self::addGhsRefundBalanceChange($custom, $ghs, $refund, $order, $refundPrice, $newBalance);
         self::addGhsRefundBalanceChange($custom, $ghs, $refund, $order, $refundPrice, $newBalance);
-        // 余额付原路退回 = 加余额,有挂账时一并消欠
-        self::fifoClearAfterRefundCredit($custom, $ghs, $refund, $refundPrice, $balanceBefore);
+        // 余额付原路退回 = 加余额,有挂账时一并消欠(优先当前售后原单)
+        self::fifoClearAfterRefundCredit($custom, $ghs, $refund, $refundPrice, $balanceBefore, $order);
     }
     }
 
 
     /**
     /**
-     * 售后加余额后的 FIFO 销挂账封装:组装门店/操作人后交给 GhsRechargeSettleService。
+     * 售后加余额后的销挂账封装:优先结当前售后原单(可部分结),剩余按 id FIFO。
+     * 无原单退款时 $order 为空,整笔走原 FIFO。
+     *
+     * @param object $custom
+     * @param object|null $ghs
+     * @param object $refund
+     * @param string|float $amount
+     * @param string $balanceBefore 入账前净余额
+     * @param object|null $order 售后关联销售单
      */
      */
-    protected static function fifoClearAfterRefundCredit($custom, $ghs, $refund, $amount, $balanceBefore)
+    protected static function fifoClearAfterRefundCredit($custom, $ghs, $refund, $amount, $balanceBefore, $order = null)
     {
     {
         $shopId = intval($refund->shopId ?? 0);
         $shopId = intval($refund->shopId ?? 0);
         $shop = $shopId > 0 ? ShopClass::getById($shopId, true) : null;
         $shop = $shopId > 0 ? ShopClass::getById($shopId, true) : null;
@@ -816,9 +824,11 @@ class CustomClass extends BaseClass
             'id' => intval($refund->shopAdminId ?? 0),
             'id' => intval($refund->shopAdminId ?? 0),
             'name' => (string)($refund->shopAdminName ?? ''),
             'name' => (string)($refund->shopAdminName ?? ''),
         ];
         ];
+        $preferOrderId = !empty($order) ? intval($order->id ?? 0) : 0;
         GhsRechargeSettleService::afterCreditFifoClear($custom, $ghs, $amount, $shop, $staff, [
         GhsRechargeSettleService::afterCreditFifoClear($custom, $ghs, $amount, $shop, $staff, [
             'balanceBeforeRecharge' => $balanceBefore,
             'balanceBeforeRecharge' => $balanceBefore,
             'payWay' => dict::getDict('payWay', 'balancePay'),
             'payWay' => dict::getDict('payWay', 'balancePay'),
+            'preferOrderId' => $preferOrderId,
         ]);
         ]);
     }
     }
 
 

+ 39 - 6
biz-ghs/custom/services/GhsRechargeSettleService.php

@@ -20,7 +20,8 @@ use common\components\util;
  * 规则(仅此一套,无旧三件套):
  * 规则(仅此一套,无旧三件套):
  * 1. 净 balance:正=有余额,负=待结;历史挂账已并入 balance(ensure 幂等)。
  * 1. 净 balance:正=有余额,负=待结;历史挂账已并入 balance(ensure 幂等)。
  * 2. 来款:balance += 金额,记一条充值流水(amount=来款)。
  * 2. 来款:balance += 金额,记一条充值流水(amount=来款)。
- * 3. 销单:仅用「本次资金池」按订单 id FIFO 减少 remainDebtPrice,不再扣 balance。
+ * 3. 销单:仅用「本次资金池」减少 remainDebtPrice,不再扣 balance。
+ *    默认按订单 id FIFO;售后返充可传 preferOrderId,先结当前售后原单(可部分结),剩余再 FIFO。
  * 4. 用账面正余额销账:无来款,FIFO 后 balance -= 实销额,可记「结账」流水。
  * 4. 用账面正余额销账:无来款,FIFO 后 balance -= 实销额,可记「结账」流水。
  *
  *
  * 入口:merchantRechargeWithAutoClear / onlinePayFifoClear / confirmClearBillWithIncoming /
  * 入口:merchantRechargeWithAutoClear / onlinePayFifoClear / confirmClearBillWithIncoming /
@@ -189,7 +190,7 @@ class GhsRechargeSettleService
      * @param string|float $amount 本次入账金额
      * @param string|float $amount 本次入账金额
      * @param object|null $shop 供货商门店;空则取 ghs.shopId
      * @param object|null $shop 供货商门店;空则取 ghs.shopId
      * @param object|null $staff 操作人(结账单记录用)
      * @param object|null $staff 操作人(结账单记录用)
-     * @param array $options balanceBeforeRecharge|payWay
+     * @param array $options balanceBeforeRecharge|payWay|preferOrderId(售后原单优先消欠)
      * @return array 与 fifoSettleWithPool 相同结构
      * @return array 与 fifoSettleWithPool 相同结构
      */
      */
     public static function afterCreditFifoClear($custom, $ghs, $amount, $shop = null, $staff = null, $options = [])
     public static function afterCreditFifoClear($custom, $ghs, $amount, $shop = null, $staff = null, $options = [])
@@ -231,11 +232,15 @@ class GhsRechargeSettleService
     }
     }
 
 
     /**
     /**
-     * FIFO 分配计划:按订单 id 升序,资金池用尽即停止。
+     * 销挂账分配计划:可选优先本单,剩余按订单 id 升序 FIFO。
+     * 售后返充:preferOrderId=当前售后原单,先冲本单 remain(可部分结),再消其它挂账单。
      *
      *
+     * @param int $customId 客户 id
+     * @param string|float $poolAmount 本次可用金额
+     * @param int $preferOrderId 优先消欠的销售单 id,0=不优先
      * @return array<int, array{orderId:int, clearAmount:string, orderSn:string}>
      * @return array<int, array{orderId:int, clearAmount:string, orderSn:string}>
      */
      */
-    public static function planFifoByPool($customId, $poolAmount)
+    public static function planFifoByPool($customId, $poolAmount, $preferOrderId = 0)
     {
     {
         $pool = self::money($poolAmount);
         $pool = self::money($poolAmount);
         if (bccomp($pool, '0', 2) <= 0) {
         if (bccomp($pool, '0', 2) <= 0) {
@@ -249,12 +254,39 @@ class GhsRechargeSettleService
 
 
         $plan = [];
         $plan = [];
         $used = '0.00';
         $used = '0.00';
+        $preferOrderId = intval($preferOrderId);
+
+        // 1)有售后原单且仍挂账:先结本单(金额不够则部分结)
+        if ($preferOrderId > 0) {
+            foreach ($orderList as $order) {
+                if (intval($order->id ?? 0) !== $preferOrderId) {
+                    continue;
+                }
+                $remain = self::money($order->remainDebtPrice ?? 0);
+                if (bccomp($remain, '0', 2) <= 0) {
+                    break;
+                }
+                $clearAmount = bccomp($remain, $pool, 2) <= 0 ? $remain : $pool;
+                $plan[] = [
+                    'orderId' => $preferOrderId,
+                    'clearAmount' => $clearAmount,
+                    'orderSn' => $order->orderSn ?? '',
+                ];
+                $used = bcadd($used, $clearAmount, 2);
+                break;
+            }
+        }
 
 
+        // 2)剩余金额按 id 升序 FIFO(跳过已优先消过的本单)
         foreach ($orderList as $order) {
         foreach ($orderList as $order) {
             $left = bcsub($pool, $used, 2);
             $left = bcsub($pool, $used, 2);
             if (bccomp($left, '0', 2) <= 0) {
             if (bccomp($left, '0', 2) <= 0) {
                 break;
                 break;
             }
             }
+            $oid = intval($order->id ?? 0);
+            if ($preferOrderId > 0 && $oid === $preferOrderId) {
+                continue;
+            }
 
 
             $remain = self::money($order->remainDebtPrice ?? 0);
             $remain = self::money($order->remainDebtPrice ?? 0);
             if (bccomp($remain, '0', 2) <= 0) {
             if (bccomp($remain, '0', 2) <= 0) {
@@ -263,7 +295,7 @@ class GhsRechargeSettleService
 
 
             $clearAmount = bccomp($remain, $left, 2) <= 0 ? $remain : $left;
             $clearAmount = bccomp($remain, $left, 2) <= 0 ? $remain : $left;
             $plan[] = [
             $plan[] = [
-                'orderId' => (int)($order->id ?? 0),
+                'orderId' => $oid,
                 'clearAmount' => $clearAmount,
                 'clearAmount' => $clearAmount,
                 'orderSn' => $order->orderSn ?? '',
                 'orderSn' => $order->orderSn ?? '',
             ];
             ];
@@ -282,8 +314,9 @@ class GhsRechargeSettleService
         $ghs = $pair['ghs'];
         $ghs = $pair['ghs'];
         $customId = $custom->id ?? 0;
         $customId = $custom->id ?? 0;
         $pool = self::money($poolAmount);
         $pool = self::money($poolAmount);
+        $preferOrderId = intval($options['preferOrderId'] ?? 0);
 
 
-        $plan = self::planFifoByPool($customId, $pool);
+        $plan = self::planFifoByPool($customId, $pool, $preferOrderId);
         if (empty($plan)) {
         if (empty($plan)) {
             self::refreshDebtFlags($custom, $ghs);
             self::refreshDebtFlags($custom, $ghs);
             return self::emptyAllocateResult($custom, $ghs);
             return self::emptyAllocateResult($custom, $ghs);

+ 38 - 6
biz-ghs/order/classes/PurchaseOrderClass.php

@@ -205,17 +205,18 @@ class PurchaseOrderClass extends BaseClass
     }
     }
 
 
     /**
     /**
-     * 【用途】采购售后返充后 FIFO 消欠并生成结账单,回写余额流水 clear 字段。
-     * 【为什么】对齐销售侧 afterCreditFifoClear,便于余额变动展示「本单销」并可跳转结账单
+     * 【用途】采购售后返充后消欠并生成结账单,回写余额流水 clear 字段。
+     * 【为什么】对齐销售侧:先结当前售后采购单(可部分结),剩余再按 id FIFO
      *
      *
      * @param object $ghs 买方供货商(已锁、余额已加完)
      * @param object $ghs 买方供货商(已锁、余额已加完)
      * @param string|float $amount 本次返充金额
      * @param string|float $amount 本次返充金额
      * @param object $refund 采购售后单
      * @param object $refund 采购售后单
      * @param int $sjId
      * @param int $sjId
      * @param int $shopId
      * @param int $shopId
+     * @param object|null $preferOrder 当前售后关联采购单,空则整笔 FIFO
      * @return array{clearId:int,clearSn:string,clearAmount:string}
      * @return array{clearId:int,clearSn:string,clearAmount:string}
      */
      */
-    public static function fifoClearRemainAfterCreditWithBill($ghs, $amount, $refund, $sjId, $shopId)
+    public static function fifoClearRemainAfterCreditWithBill($ghs, $amount, $refund, $sjId, $shopId, $preferOrder = null)
     {
     {
         $empty = ['clearId' => 0, 'clearSn' => '', 'clearAmount' => '0.00'];
         $empty = ['clearId' => 0, 'clearSn' => '', 'clearAmount' => '0.00'];
         $pool = bcadd((string)$amount, '0', 2);
         $pool = bcadd((string)$amount, '0', 2);
@@ -235,11 +236,42 @@ class PurchaseOrderClass extends BaseClass
         }
         }
         $plan = [];
         $plan = [];
         $used = '0.00';
         $used = '0.00';
+        $preferOrderId = !empty($preferOrder) ? intval($preferOrder->id ?? 0) : 0;
+
+        // 1)优先结当前售后采购单(可部分结)
+        if ($preferOrderId > 0) {
+            foreach ($orderList as $order) {
+                if (intval($order->id ?? 0) !== $preferOrderId) {
+                    continue;
+                }
+                $remain = bcadd((string)($order->remainDebtPrice ?? '0'), '0', 2);
+                if (bccomp($remain, '0', 2) <= 0) {
+                    $remain = bcadd((string)($order->actPrice ?? '0'), '0', 2);
+                }
+                if (bccomp($remain, '0', 2) <= 0) {
+                    break;
+                }
+                $cut = bccomp($remain, $pool, 2) <= 0 ? $remain : $pool;
+                $plan[] = [
+                    'cgId' => $preferOrderId,
+                    'clearAmount' => $cut,
+                    'orderSn' => (string)($order->orderSn ?? ''),
+                ];
+                $used = bcadd($used, $cut, 2);
+                break;
+            }
+        }
+
+        // 2)剩余金额按 id 升序 FIFO(跳过已优先的本单)
         foreach ($orderList as $order) {
         foreach ($orderList as $order) {
             $left = bcsub($pool, $used, 2);
             $left = bcsub($pool, $used, 2);
             if (bccomp($left, '0', 2) <= 0) {
             if (bccomp($left, '0', 2) <= 0) {
                 break;
                 break;
             }
             }
+            $oid = intval($order->id ?? 0);
+            if ($preferOrderId > 0 && $oid === $preferOrderId) {
+                continue;
+            }
             $remain = bcadd((string)($order->remainDebtPrice ?? '0'), '0', 2);
             $remain = bcadd((string)($order->remainDebtPrice ?? '0'), '0', 2);
             if (bccomp($remain, '0', 2) <= 0) {
             if (bccomp($remain, '0', 2) <= 0) {
                 $remain = bcadd((string)($order->actPrice ?? '0'), '0', 2);
                 $remain = bcadd((string)($order->actPrice ?? '0'), '0', 2);
@@ -249,7 +281,7 @@ class PurchaseOrderClass extends BaseClass
             }
             }
             $cut = bccomp($remain, $left, 2) <= 0 ? $remain : $left;
             $cut = bccomp($remain, $left, 2) <= 0 ? $remain : $left;
             $plan[] = [
             $plan[] = [
-                'cgId' => intval($order->id ?? 0),
+                'cgId' => $oid,
                 'clearAmount' => $cut,
                 'clearAmount' => $cut,
                 'orderSn' => (string)($order->orderSn ?? ''),
                 'orderSn' => (string)($order->orderSn ?? ''),
             ];
             ];
@@ -420,9 +452,9 @@ class PurchaseOrderClass extends BaseClass
             }
             }
         }
         }
 
 
-        // 隔天等:入账后 FIFO 消挂账并生成结账单;当天已减本单 remain 则不再 FIFO,避免重复消欠
+        // 隔天等:入账后消挂账并生成结账单(优先当前售后采购单);当天已减本单 remain 则不再消,避免重复
         if ($doFifo) {
         if ($doFifo) {
-            self::fifoClearRemainAfterCreditWithBill($ghs, $amount, $refund, $sjId, $shopId);
+            self::fifoClearRemainAfterCreditWithBill($ghs, $amount, $refund, $sjId, $shopId, $order);
         }
         }
 
 
         return $ghs;
         return $ghs;