Преглед изворни кода

Merge branch 'master' into smslogin

shish пре 1 месец
родитељ
комит
6439963e80

+ 60 - 366
biz-ghs/custom/classes/AccountMoneyClass.php

@@ -9,131 +9,80 @@ use common\components\dict;
 use Yii;
 
 /**
- * 客户/供货商账户金额(表 xhGhsCustom、xhGhs)
- *
- * 背景:原 debtAmount=挂账累计、balance=充值余额,现统一为净 balance(正=有余额,负=待结)。
- * 合并(客户/批发关系行):balance = 原 balance - 原 debtAmount,debtAmount 置 0,balanceMerged=1。
- * 合并(ghsApp 采购供货商行,ownPtStyle=ghs/kmGhs):按待结采购单 actPrice 合计重算净 balance,debtAmount 置 0。
- * 合并(hd 花店侧供货商行,ownPtStyle=hd):balance -= debtAmount 旧逻辑(mergeGhsDebtIntoBalanceIfNeeded);二者不可混用。
- * 写业务前调 ensureCustomMoneyReady / ensureGhsMoneyReady(仅行锁,不触发合并)。
- * 批量合并仅由控制台脚本调用 mergeCustomMoneyForScript / mergeGhsMoneyForScript 等。
- * 列表/详情返回前调 formatMoneyForClient。
- * 旧 App(appVersion<3)由 formatMoneyForClient 拆回待结+余额双字段;新 App 只读净 balance。
- *
- * 充值销账(合并后来款 FIFO 销单):bizGhs\custom\services\GhsRechargeSettleService
+ * 【用途】客户/供货商账户金额核心处理类(表 xhGhsCustom、xhGhs)
+ * 【为什么】在全部升级到新版、全部账户已合并的前提下,彻底移除旧版 appVersion < 3 兼容逻辑与 balanceMerged 复杂校验,极大地简化计算路径,提升系统性能与稳定性。
+ * 【说明】
+ *   - 数据库中的 balance 已经是唯一的净余额(正数代表有充值余额,负数代表待结欠款)。
+ *   - 数据库中的 debtAmount 字段已废弃,其值始终为 0.00。
+ *   - 写业务前调用 ensureCustomMoneyReady / ensureGhsMoneyReady(仅行锁,不触发合并)。
  */
 class AccountMoneyClass
 {
     /**
-     * 新端协议版本号:请求头 appVersion >= 此值时,接口只返回净 balance,不再拆分待结/余额两列。
-     * 与前端 ghsApp/ghs/hdApp/ghsPad 的 request.js 中 appVersion 保持一致。
+     * 新端协议版本号:只返回净 balance,不再拆分待结/余额两列。
      */
     const APP_VERSION_NET_BALANCE = 3;
 
     /**
-     * 【用途】获取当前 HTTP 请求里的客户端版本号,供 formatMoneyForClient 决定返回字段形态。
-     * 【调用时机】一般无需直接调;formatMoneyForClient 内部会自动读取。
-     * 【返回值】整数,默认 2(旧端);3 表示新端净余额协议。
+     * 【用途】获取当前 HTTP 请求里的客户端版本号。
+     * 【说明】由于全部已升级到新版,此方法固定返回 3。
      */
     public static function getClientAppVersion()
     {
-        if (isset(Yii::$app->params['clientAppVersion'])) {
-            return (int)Yii::$app->params['clientAppVersion'];
-        }
-        $headers = Yii::$app->request->headers ?? null;
-        $ver = $headers ? (int)$headers->get('appVersion', 2) : 2;
-        Yii::$app->params['clientAppVersion'] = $ver;
-        return $ver;
+        return self::APP_VERSION_NET_BALANCE;
     }
 
     /**
-     * 【用途】判断数据行是否带有 balanceMerged 字段(是否已执行 SQL 迁移)。
-     * 【调用时机】内部判断幂等标记;未迁移库时靠 debtAmount 是否为 0 代替。
-     * 【参数】$row 数组或 ActiveRecord
-     * 【返回值】true=表结构含 balanceMerged 列
+     * 【用途】判断数据行是否带有 balanceMerged 字段。
+     * 【说明】已全部合并,此方法固定返回 true。
      */
     public static function hasMergeFlagField($row)
     {
-        if (is_array($row)) {
-            return array_key_exists('balanceMerged', $row);
-        }
-        return is_object($row) && method_exists($row, 'hasAttribute') && $row->hasAttribute('balanceMerged');
+        return true;
     }
 
     /**
      * 【用途】判断该客户/供货商账户是否已经完成「挂账并入余额」。
-     * 【调用时机】合并前检查,避免重复合并、重复写说明流水。
-     * 【参数】$row 含 balanceMerged、debtAmount 的数组或模型
-     * 【返回值】true=已合并(或本无挂账)
+     * 【说明】已全部合并,此方法固定返回 true。
      */
     public static function isMerged($row)
     {
-        if (!self::hasMergeFlagField($row)) {
-            return bccomp(self::rawDebtAmount($row), '0', 2) == 0;
-        }
-        if (is_array($row)) {
-            return !empty($row['balanceMerged']);
-        }
-        return !empty($row->balanceMerged);
+        return true;
     }
 
     /**
-     * 【用途】内部取 debtAmount 原始值,供 isMerged / calcNetBalance 使用
-     * 【说明】外部业务请用 getNetBalanceFromRow,不要直接读 debtAmount
+     * 【用途】内部取 debtAmount 原始值。
+     * 【说明】已全部合并,此方法固定返回 '0.00'。
      */
     protected static function rawDebtAmount($row)
     {
-        if (is_array($row)) {
-            return $row['debtAmount'] ?? '0.00';
-        }
-        return $row->debtAmount ?? '0.00';
+        return '0.00';
     }
 
     /**
-     * 【用途】根据库里的 balance、debtAmount 计算「净账户余额」(合并前后均可用)。
-     * 【规则】未合并:净额 = balance - debtAmount(等价于旧逻辑下「余额减待结」后的真实资金位置);
-     *        已合并:净额 = balance(debtAmount 应为 0)。
-     * 【调用时机】统计总余额、采购列表算实欠、需要与旧 remainDebtAmount 对齐时。
-     * 【参数】$balance 充值余额字段;$debtAmount 挂账累计;$merged 是否已执行过合并
-     * 【返回值】字符串金额,bc 精度 2 位
+     * 【用途】根据库里的 balance 计算「净账户余额」。
+     * 【说明】已全部合并,净额就是 balance。
      */
-    public static function calcNetBalance($balance, $debtAmount, $merged = false)
+    public static function calcNetBalance($balance, $debtAmount, $merged = true)
     {
-        $balance = $balance ?? '0.00';
-        $debtAmount = $debtAmount ?? '0.00';
-        if ($merged || bccomp($debtAmount, '0', 2) == 0) {
-            return $balance;
-        }
-        return bcsub($balance, $debtAmount, 2);
+        return bcadd((string)($balance ?? '0.00'), '0', 2);
     }
 
     /**
-     * 【用途】从一条客户或供货商记录中取出净账户余额(自动识别是否已合并)。
-     * 【调用时机】导出汇总、showTotalBalance、任何仍持有双字段模型的读逻辑。
-     * 【参数】$row 客户/供货商数组或 AR
-     * 【返回值】净 balance 字符串
+     * 【用途】从记录中取出净账户余额。
+     * 【说明】已全部合并,直接返回 balance。
      */
     public static function getNetBalanceFromRow($row)
     {
         if (is_array($row)) {
-            return self::calcNetBalance(
-                $row['balance'] ?? 0,
-                $row['debtAmount'] ?? 0,
-                self::isMerged($row)
-            );
+            return bcadd((string)($row['balance'] ?? '0.00'), '0', 2);
         }
-        return self::calcNetBalance(
-            $row->balance ?? 0,
-            $row->debtAmount ?? 0,
-            self::isMerged($row)
-        );
+        return bcadd((string)($row->balance ?? '0.00'), '0', 2);
     }
 
     /**
-     * 【用途】得到当前账户「待结欠款」数额(正数),用于挂账额度校验、下单前是否超限。
-     * 【规则】净余额 >= 0 时返回 0;净余额 < 0 时返回 |净余额|(替代原 custom.debtAmount 与额度比较)。
-     * 【调用时机】客户自助下单校验 debtLimit;OrderService 欠款超限判断。
-     * 【返回值】待结金额字符串,无欠款为 '0.00'
+     * 【用途】得到当前账户「待结欠款」数额(正数)。
+     * 【规则】净余额 >= 0 时返回 0.00;净余额 < 0 时返回 |净余额|。
      */
     public static function getOutstandingDebt($row)
     {
@@ -145,182 +94,45 @@ class AccountMoneyClass
     }
 
     /**
-     * 【用途】把接口返回给前端的金额字段,按 appVersion 转成旧版或新版形态(多端平滑过渡的核心)。
-     * 【调用时机】CustomClass::groupBaseInfo 组装列表/详情后,对每条记录调用一次。
-     * 【行为】
-     *   - appVersion >= 3:balance=净额,debtAmount=0,remainDebtAmount 辅助展示;
-     *   - appVersion < 3:拆成旧「待结 debtAmount + 余额 balance + remainDebtAmount」,未升级 App 无需改 UI。
-     * 【参数】$row 须含 balance/debtAmount/balanceMerged;$appVersion 可空则自动读请求头
-     * 【返回值】追加 displayBalance 后的同一数组
+     * 【用途】把接口返回给前端的金额字段,按新版形态格式化。
+     * 【说明】由于已全部升级至新版,直接返回净 balance。为了前端各页面和打印机安全兼容,
+     *        我们将真实的待结欠款(正数)同时赋予 debtAmount 和 remainDebtAmount。
      */
     public static function formatMoneyForClient(array $row, $appVersion = null)
     {
-        if ($appVersion === null) {
-            $appVersion = self::getClientAppVersion();
-        }
         $net = self::getNetBalanceFromRow($row);
         $row['displayBalance'] = $net;
-
-        if ($appVersion >= self::APP_VERSION_NET_BALANCE) {
-            $row['balance'] = $net;
-            $row['debtAmount'] = '0.00';
-            $remain = bccomp($net, '0', 2) < 0 ? bcmul($net, '-1', 2) : '0.00';
-            if (bccomp($net, '0', 2) > 0) {
-                $remain = bcsub('0', $net, 2);
-            }
-            $row['remainDebtAmount'] = $remain;
-            return $row;
-        }
-
-        if (bccomp($net, '0', 2) < 0) {
-            $row['debtAmount'] = bcmul($net, '-1', 2);
-            $row['balance'] = '0.00';
-            $row['remainDebtAmount'] = $row['debtAmount'];
-        } else {
-            $row['debtAmount'] = '0.00';
-            $row['balance'] = $net;
-            $row['remainDebtAmount'] = bccomp($net, '0', 2) > 0 ? bcsub('0', $net, 2) : '0.00';
-        }
+        $row['balance'] = $net;
+        
+        // 计算出真实的待结欠款(正数)
+        $outstanding = bccomp($net, '0', 2) < 0 ? bcmul($net, '-1', 2) : '0.00';
+        
+        $row['debtAmount'] = $outstanding;
+        $row['remainDebtAmount'] = $outstanding;
         return $row;
     }
 
     /**
-     * 【用途】对 xhGhsCustom(客户)执行一次性「挂账并入余额」:balance -= debtAmount,debtAmount 置 0,打标 balanceMerged。
-     * 【调用时机】一般由 ensureCustomMoneyReady 调用;不要绕过 ensure 直接改 balance。
-     * 【参数】$custom 客户 AR;$writeBalanceChange true 时写一条「账户合并」余额变动(仅首次)
-     * 【返回值】合并后的客户对象
+     * 【用途】对 xhGhsCustom(客户)执行一次性「挂账并入余额」。
+     * 【说明】已全部合并,此方法直接返回。
      */
     public static function mergeCustomDebtIntoBalanceIfNeeded($custom, $writeBalanceChange = true)
     {
-        return self::mergeRowDebtIntoBalance($custom, 'custom', $writeBalanceChange);
+        return $custom;
     }
 
     /**
-     * 【用途】对 xhGhs(花店↔供货商关系行)执行与上相同的挂账并入余额。
-     * 【调用时机】mergeCustomMoneyForScript 会顺带处理 custom.ghsId;脚本侧 mergeGhsMoneyForScript 处理仅 ghs 行。
-     * 【说明】充值等场景要求 custom 与 ghs 两边净额一致,故成对合并。
+     * 【用途】对 xhGhs 执行一次性「挂账并入余额」。
+     * 【说明】已全部合并,此方法直接返回。
      */
     public static function mergeGhsDebtIntoBalanceIfNeeded($ghs, $writeBalanceChange = true)
     {
-        return self::mergeRowDebtIntoBalance($ghs, 'ghs', $writeBalanceChange);
-    }
-
-    /**
-     * 【用途】真正写库的单行合并实现(客户或供货商一行)。
-     * 【说明】已合并则只清理残留 debtAmount;无挂账则只打标;有挂账则改 balance 并可选记流水。
-     * 【注意】须在 getLockById 之后、业务改金额之前调用。
-     */
-    protected static function mergeRowDebtIntoBalance($row, $type, $writeBalanceChange)
-    {
-        if (empty($row)) {
-            return $row;
-        }
-        if (self::isMerged($row)) {
-            if (bccomp($row->debtAmount ?? '0', '0', 2) != 0) {
-                $row->debtAmount = '0.00';
-                $row->save(false, ['debtAmount']);
-            }
-            return $row;
-        }
-
-        $debtAmount = $row->debtAmount ?? '0.00';
-        if (bccomp($debtAmount, '0', 2) == 0) {
-            if (self::hasMergeFlagField($row)) {
-                $row->balanceMerged = 1;
-                $row->save(false, ['balanceMerged']);
-            }
-            return $row;
-        }
-
-        $oldBalance = $row->balance ?? '0.00';
-        $newBalance = bcsub($oldBalance, $debtAmount, 2);
-        $row->balance = $newBalance;
-        $row->debtAmount = '0.00';
-        $saveAttrs = ['balance', 'debtAmount'];
-        if (self::hasMergeFlagField($row)) {
-            $row->balanceMerged = 1;
-            $saveAttrs[] = 'balanceMerged';
-        }
-
-        if ($type === 'custom') {
-            $row->isDebt = bccomp($newBalance, '0', 2) < 0 ? CustomClass::IS_DEBT_YES : CustomClass::IS_DEBT_NO;
-            $saveAttrs[] = 'isDebt';
-            $row->save(false, $saveAttrs);
-        } else {
-            $row->debt = bccomp($newBalance, '0', 2) < 0 ? 2 : 1;
-            $saveAttrs[] = 'debt';
-            $row->save(false, $saveAttrs);
-        }
-
-        if ($writeBalanceChange) {
-            self::addMergeBalanceChange($row, $type, $debtAmount, $newBalance);
-        }
-
-        return $row;
-    }
-
-    /**
-     * 【用途】首次合并时往「余额变动明细」插一条说明,方便财务对账;不写挂账变动表。
-     * 【流水】capitalType=balanceMerge(81),事项如「账户合并:原挂账 X 元并入余额」。
-     */
-    protected static function addMergeBalanceChange($row, $type, $mergedDebt, $newBalance)
-    {
-        $capitalType = dict::getDict('capitalType', 'balanceMerge', 'id');
-        $event = '账户合并:原挂账 ' . floatval($mergedDebt) . ' 元并入余额';
-        if ($type === 'custom') {
-            $cbData = [
-                'customId' => $row->id ?? 0,
-                'customName' => $row->name ?? '',
-                'relateId' => 0,
-                'onlinePay' => 0,
-                'ptStyle' => dict::getDict('ptStyle', 'ghs'),
-                'capitalType' => $capitalType,
-                'amount' => $mergedDebt,
-                'balance' => $newBalance,
-                'staffId' => 0,
-                'staffName' => '',
-                'io' => 0,
-                'side' => 0,
-                'payWay' => 0,
-                'fromType' => dict::getDict('fromType', 'shop'),
-                'event' => $event,
-                'mainId' => $row->ownMainId ?? ($row->mainId ?? 0),
-                'shopId' => $row->ownShopId ?? ($row->shopId ?? 0),
-                'sjId' => $row->sjId ?? 0,
-                'remark' => '系统自动合并挂账与余额',
-            ];
-            CustomBalanceChangeClass::add($cbData, true);
-            return;
-        }
-
-        $gbData = [
-            'ghsId' => $row->id ?? 0,
-            'relateId' => 0,
-            'ptStyle' => dict::getDict('ptStyle', 'ghs'),
-            'capitalType' => $capitalType,
-            'amount' => $mergedDebt,
-            'balance' => $newBalance,
-            'io' => 0,
-            'side' => 0,
-            'onlinePay' => 0,
-            'payWay' => 0,
-            'fromType' => dict::getDict('fromType', 'shop'),
-            'event' => $event,
-            'mainId' => $row->ownMainId ?? ($row->mainId ?? 0),
-            'shopId' => $row->ownShopId ?? ($row->shopId ?? 0),
-            'sjId' => $row->sjId ?? 0,
-            'remark' => '系统自动合并挂账与余额',
-        ];
-        if (class_exists('\bizHd\ghs\classes\GhsBalanceChangeClass')) {
-            \bizHd\ghs\classes\GhsBalanceChangeClass::add($gbData, true);
-        } else {
-            GhsBalanceChangeClass::add($gbData, true);
-        }
+        return $ghs;
     }
 
     /**
      * 【用途】客户侧金额写操作前置:仅对客户行加锁,不执行挂账并入余额。
-     * 【调用时机】开单、结账、退款、充值等写 balance 前
+     * 【为什么】在全部升级并合并后,写操作只需要锁行即可。
      */
     public static function ensureCustomMoneyReady($custom, $writeMergeChange = true)
     {
@@ -328,43 +140,20 @@ class AccountMoneyClass
             return $custom;
         }
         $customId = is_array($custom) ? ($custom['id'] ?? 0) : ($custom->id ?? 0);
-        if (!is_object($custom) || !self::isMerged($custom)) {
-            $custom = CustomClass::getLockById($customId);
-        }
-        return $custom;
+        return CustomClass::getLockById($customId);
     }
 
     /**
-     * 【用途】控制台批量合并:客户行 + 其 ghsId 关系行一次性并入净 balance
-     * 【调用时机】仅 php yii balance-merge/run 等脚本,禁止在 HTTP 业务里调用
+     * 【用途】控制台批量合并。
+     * 【说明】已全部合并,此方法直接返回
      */
     public static function mergeCustomMoneyForScript($custom, $writeMergeChange = true)
     {
-        if (empty($custom)) {
-            return $custom;
-        }
-        $customId = is_array($custom) ? ($custom['id'] ?? 0) : ($custom->id ?? 0);
-        if (!is_object($custom) || !self::isMerged($custom)) {
-            $custom = CustomClass::getLockById($customId);
-        }
-        if (empty($custom)) {
-            return $custom;
-        }
-        self::mergeCustomDebtIntoBalanceIfNeeded($custom, $writeMergeChange);
-
-        $ghsId = $custom->ghsId ?? 0;
-        if ($ghsId > 0) {
-            $ghs = BizGhsClass::getLockById($ghsId);
-            if (!empty($ghs)) {
-                self::mergeGhsDebtIntoBalanceIfNeeded($ghs, $writeMergeChange);
-            }
-        }
         return $custom;
     }
 
     /**
-     * 【用途】判断 xhGhs 是否为 ghsApp「采购供货商」行(批发商向上游采购),而非 hd 花店侧供货商行。
-     * 【规则】同一表 xhGhs:ownPtStyle=hd(1) 为花店买方;ownPtStyle=ghs(2)/kmGhs(4) 为 ghsApp 采购买方。
+     * 【用途】判断 xhGhs 是否为 ghsApp「采购供货商」行。
      */
     public static function isGhsAppPurchaseSupplierRow($ghs)
     {
@@ -390,7 +179,6 @@ class AccountMoneyClass
 
     /**
      * 【用途】供货商关系行写操作前置:仅加行锁,不执行挂账并入余额。
-     * 【调用时机】hd 采购挂账/结账/退款、ghs 采购改金额等写 balance 前。
      */
     public static function ensureGhsMoneyReady($ghs, $writeMergeChange = true)
     {
@@ -398,37 +186,20 @@ class AccountMoneyClass
             return $ghs;
         }
         $ghsId = is_array($ghs) ? ($ghs['id'] ?? 0) : ($ghs->id ?? 0);
-        if (!is_object($ghs) || !self::isMerged($ghs)) {
-            $ghs = BizGhsClass::getLockById($ghsId);
-        }
-        return $ghs;
+        return BizGhsClass::getLockById($ghsId);
     }
 
     /**
-     * 【用途】控制台批量合并:单条 xhGhs(hd 走 debtAmount 合并,ghsApp 采购行走采购单重算)
-     * 【调用时机】仅 php yii balance-merge/run;ghsApp 采购行请用 ghs-purchase-balance-merge/run
+     * 【用途】控制台批量合并。
+     * 【说明】已全部合并,此方法直接返回
      */
     public static function mergeGhsMoneyForScript($ghs, $writeMergeChange = true)
     {
-        if (empty($ghs)) {
-            return $ghs;
-        }
-        $ghsId = is_array($ghs) ? ($ghs['id'] ?? 0) : ($ghs->id ?? 0);
-        if (!is_object($ghs) || !self::isMerged($ghs)) {
-            $ghs = BizGhsClass::getLockById($ghsId);
-        }
-        if (empty($ghs)) {
-            return $ghs;
-        }
-        if (!self::isMerged($ghs) && self::isGhsAppPurchaseSupplierRow($ghs)) {
-            return self::mergeOwnShopGhsBalanceFromPurchaseOrdersIfNeeded($ghs, $writeMergeChange);
-        }
-        return self::mergeGhsDebtIntoBalanceIfNeeded($ghs, $writeMergeChange);
+        return $ghs;
     }
 
     /**
-     * 【用途】汇总某采购供货商(xhGhs.id)下仍待结的 ghs 采购单金额。
-     * 【规则】debt=DEBT_YES 的订单 actPrice 之和,与供货商列表/小票统计一致。
+     * 【用途】汇总某采购供货商下仍待结的 ghs 采购单金额。
      */
     public static function sumPurchaseOrderDebtForGhs($ghsId)
     {
@@ -459,7 +230,7 @@ class AccountMoneyClass
     }
 
     /**
-     * 【用途】统计应清理的幽灵待结单数量(actPrice<=0 或已取消仍标待结,去重)
+     * 【用途】统计应清理的幽灵待结单数量。
      */
     public static function countInvalidPurchaseDebtOrders($ghsId)
     {
@@ -468,7 +239,7 @@ class AccountMoneyClass
     }
 
     /**
-     * 【用途】有效待结采购单统计(排除幽灵单后的笔数与金额,重算脚本与预览共用)
+     * 【用途】有效待结采购单统计。
      * @return array{num:int,amount:string}
      */
     public static function getValidPurchaseDebtStats($ghsId)
@@ -498,7 +269,7 @@ class AccountMoneyClass
     }
 
     /**
-     * 【用途】收集幽灵待结采购单 id(actPrice<=0、已取消仍标待结)
+     * 【用途】收集幽灵待结采购单 id。
      */
     protected static function collectInvalidPurchaseDebtOrderIds($ghsId)
     {
@@ -531,8 +302,7 @@ class AccountMoneyClass
     }
 
     /**
-     * 【用途】清理不应再计待结的采购单(已退尽、已取消仍标待结),避免「N 笔 0 元」。
-     * 【返回】被改为已结清的订单数。
+     * 【用途】清理不应再计待结的采购单。
      */
     public static function syncInvalidPurchaseDebtOrders($ghsId)
     {
@@ -561,87 +331,11 @@ class AccountMoneyClass
     }
 
     /**
-     * 【用途】按采购待结订单重算 ownShop 侧 xhGhs 净 balance(ghsApp 采购供货商列表专用)。
-     * 【公式】净余额 = max(当前 balance 正数部分, 0) - 采购待结合计;debtNum 与有效待结单数对齐。
-     * 【参数】$forceRecalc true 时已合并行也重算(控制台脚本重复执行);false 时仅首次合并。
+     * 【用途】按采购待结订单重算 ownShop 侧 xhGhs 净 balance。
+     * @return object
      */
     public static function mergeOwnShopGhsBalanceFromPurchaseOrdersIfNeeded($ghs, $writeBalanceChange = true, $forceRecalc = false)
     {
-        if (empty($ghs)) {
-            return $ghs;
-        }
-        if (!self::isGhsAppPurchaseSupplierRow($ghs)) {
-            return self::mergeGhsDebtIntoBalanceIfNeeded($ghs, $writeBalanceChange);
-        }
-        $wasMerged = self::isMerged($ghs);
-        if ($wasMerged && !$forceRecalc) {
-            if (bccomp($ghs->debtAmount ?? '0', '0', 2) != 0) {
-                $ghs->debtAmount = '0.00';
-                $ghs->save(false, ['debtAmount']);
-            }
-            return $ghs;
-        }
-
-        $ghsId = intval($ghs->id ?? 0);
-        self::syncInvalidPurchaseDebtOrders($ghsId);
-
-        $debtStats = self::getValidPurchaseDebtStats($ghsId);
-        $orderDebt = $debtStats['amount'];
-        $orderDebtNum = $debtStats['num'];
-
-        $rawBalance = bcadd((string)($ghs->balance ?? '0'), '0', 2);
-        $positiveCredit = bccomp($rawBalance, '0', 2) > 0 ? $rawBalance : '0.00';
-        $newBalance = bcsub($positiveCredit, $orderDebt, 2);
-
-        $ghs->balance = $newBalance;
-        $ghs->debtAmount = '0.00';
-        $ghs->debtNum = $orderDebtNum;
-        $ghs->debt = bccomp($newBalance, '0', 2) < 0 ? BizGhsClass::DEBT_YES : BizGhsClass::DEBT_NO;
-
-        $saveAttrs = ['balance', 'debtAmount', 'debtNum', 'debt'];
-        if (self::hasMergeFlagField($ghs)) {
-            $ghs->balanceMerged = 1;
-            $saveAttrs[] = 'balanceMerged';
-        }
-        $ghs->save(false, $saveAttrs);
-
-        // 仅首次合并写说明流水,强制重跑不重复记
-        if ($writeBalanceChange && !$wasMerged && bccomp($orderDebt, '0', 2) > 0) {
-            self::addPurchaseOrderMergeBalanceChange($ghs, $orderDebt, $newBalance);
-        }
-
         return $ghs;
     }
-
-    /**
-     * 【用途】采购供货商按订单重算合并时写一条余额说明流水。
-     */
-    protected static function addPurchaseOrderMergeBalanceChange($row, $orderDebt, $newBalance)
-    {
-        $capitalType = dict::getDict('capitalType', 'balanceMerge', 'id');
-        $event = '账户合并:按采购待结订单合计 ' . floatval($orderDebt) . ' 元重算净余额';
-        $gbData = [
-            'ghsId' => $row->id ?? 0,
-            'relateId' => 0,
-            'ptStyle' => dict::getDict('ptStyle', 'ghs'),
-            'capitalType' => $capitalType,
-            'amount' => $orderDebt,
-            'balance' => $newBalance,
-            'io' => 0,
-            'side' => 0,
-            'onlinePay' => 0,
-            'payWay' => 0,
-            'fromType' => dict::getDict('fromType', 'shop'),
-            'event' => $event,
-            'mainId' => $row->ownMainId ?? ($row->mainId ?? 0),
-            'shopId' => $row->ownShopId ?? ($row->shopId ?? 0),
-            'sjId' => $row->sjId ?? 0,
-            'remark' => '系统按采购待结订单重算余额',
-        ];
-        if (class_exists('\bizHd\ghs\classes\GhsBalanceChangeClass')) {
-            \bizHd\ghs\classes\GhsBalanceChangeClass::add($gbData, true);
-        } else {
-            GhsBalanceChangeClass::add($gbData, true);
-        }
-    }
 }

+ 11 - 9
biz-ghs/order/classes/OrderClass.php

@@ -824,8 +824,8 @@ class OrderClass extends BaseClass
         $mainId = $data['mainId'] ?? 0;
         $ghsId = $data['ghsId'] ?? 0;
         $customId = $data['customId'] ?? 0;
-		$distId = $custom['distId'] ?? 0;
-		$data['customDistId'] = $distId;
+        $distId = $custom['distId'] ?? 0;
+        $data['customDistId'] = $distId;
         $snData = ['shopId' => $shopId, 'mainId' => $mainId, 'ghsId' => $ghsId, 'customId' => $customId];
         $orderSn = orderSn::getGhsOrderSn($snData);
 
@@ -1557,7 +1557,8 @@ class OrderClass extends BaseClass
             $list[$key]['customName'] = !empty($val['customId']) ? $val['customName'] : '散客';
 
             $customId = $val['customId'] ?? '';
-            $debtAmount = $customInfoList[$customId]['debtAmount'] ?? 0.00;
+            $balance = $customInfoList[$customId]['balance'] ?? 0.00;
+            $debtAmount = $balance<0 ? abs($balance) : 0;
 
             //打印数据组合
             $printData = self::getPrintData($val, $debtAmount, $showItemData);
@@ -1976,7 +1977,8 @@ class OrderClass extends BaseClass
 
         $customId = $orderInfo['customId'] ?? 0;
         $c = CustomClass::getById($customId);
-        $debtAmount = $c['debtAmount'] ?? 0.00;
+        $balance = $c['balance'] ?? 0.00;
+        $debtAmount = $balance<0 ? abs($balance) : 0;
         $orderSn = $orderInfo['orderSn'] ?? '';
         $shopId = $orderInfo['shopId'] ?? 0;
         $shop = ShopClass::getById($shopId, true);
@@ -2171,7 +2173,7 @@ class OrderClass extends BaseClass
                 $content .= '实付金额:' . floatval($respond['realPrice']) . '<BR>';
             }
             if ($debtAmount > 0) {
-                $content .= '累计赊账:' . floatval($debtAmount) . '<BR>';
+                $content .= '累计待结:' . floatval($debtAmount) . '<BR>';
             }
             $content .= '--------------------------------<BR>';
         }
@@ -3059,7 +3061,7 @@ XL;
         /**************************如果是好多花的云仓还要再打一下标签纸,多处要同步修改,关键词 hdh_yc *****************************/
         $orderMainId = $order->mainId;
         if (getenv('YII_ENV') == 'production') {
-            $map = [65726, 58, 25119, 28500, 1294, 12925, 2644,10652,2084,89473];
+            $map = [65726, 58, 25119, 28500, 1294, 12925, 2644, 10652, 2084, 89473];
         } else {
             $map = [828];
         }
@@ -3084,7 +3086,7 @@ XL;
             }
         }
         //东莞我要花 手机后四号变星号
-        if (in_array((int)$orderMainId, [2644,10652,2084,89473], true)) {
+        if (in_array((int)$orderMainId, [2644, 10652, 2084, 89473], true)) {
             $customMobile = preg_replace('/(.{4})$/u', '****', trim($customMobile));
         }
         $customName = $order->customName ?? '';
@@ -3156,12 +3158,12 @@ XL;
             $payTime = $order->payTime ?? '';
             $payDate = date('m-d H:i', strtotime($payTime));
             $content = '<TEXT x="400" y="30" font="12" w="3" h="3" r="90">' . $customName . '</TEXT>';
-            
+
             // 地址超过15个字,字体大小(w和h)变小一级(由 w="2" h="2" 变为 w="1" h="1")
             $addressLen = mb_strlen($fullAddress, 'UTF-8');
             $fontScale = $addressLen > 16 ? '1' : '2';
             $content .= '<TEXT x="280" y="30" font="12" w="' . $fontScale . '" h="' . $fontScale . '" r="90">' . $fullAddress . '</TEXT>';
-            
+
             $content .= '<TEXT x="180" y="30" font="12" w="2" h="2" r="90">' . $customMobile . '  ' . $itemNum . '扎</TEXT>';
             $content .= '<TEXT x="86" y="30" font="12" w="2" h="2" r="90">' . $distName . ' ' . $payDate . '</TEXT>';
             /**************************如果是好多花的云仓还要再打一下标签纸,多处要同步修改,关键词 hdh_yc *****************************/

+ 1 - 1
biz-ghs/order/classes/PurchaseOrderClass.php

@@ -628,7 +628,7 @@ class PurchaseOrderClass extends BaseClass
             $content .= '实付金额:' . floatval($orderInfo['realPrice']) . '元<BR>';
         }
 //        if ($debtAmount > 0) {
-//            $content .= '累计赊账:' . $debtCount . '笔 ' . floatval($debtAmount) . '元<BR>';
+//            $content .= '累计待结:' . $debtCount . '笔 ' . floatval($debtAmount) . '元<BR>';
 //        }
         $content .= '--------------------------------<BR>';
         $content .= '订单编号:' . $orderInfo['orderSn'] . '<BR>';

+ 14 - 0
biz-hd/product/classes/ProductClass.php

@@ -644,6 +644,20 @@ class ProductClass extends BaseClass
         return true;
     }
 
+    // 校验清空限购消息是否是当前有效版本
+    public static function checkLimitBuyClearMessage($productId, $clearAt)
+    {
+        $productId = intval($productId);
+        $clearAt = intval($clearAt);
+        if ($productId <= 0 || $clearAt <= 0) {
+            return false;
+        }
+
+        $clearMarkKey = self::CLEAR_MARK_KEY . $productId;
+        $currentClearAt = intval(Yii::$app->redis->executeCommand('GET', [$clearMarkKey]));
+        return $currentClearAt > 0 && $currentClearAt == $clearAt;
+    }
+
     // 清理限购清空标记
     public static function clearLimitBuyClearMark($productId)
     {

+ 1 - 0
common/config/params.php

@@ -44,6 +44,7 @@ $config = [
     'accessKeyId' => 'LTAI4GCXNRgt87qnCB8JrGFt',
     'accessKeySecret' => 'm5q7aU3wGcoid9wr0yFJ4JqzHMKfks',
     'endpoint' => 'oss-cn-hangzhou.aliyuncs.com',
+    'captchaSceneId' => 'qp3zripj', // 阿里云验证码场景ID
 
     //达达相关配置
     'dadaAppKey' => 'dadae6c9d6064e69cf4',

+ 0 - 95
console/controllers/BalanceMergeController.php

@@ -1,95 +0,0 @@
-<?php
-
-namespace console\controllers;
-
-use biz\ghs\classes\GhsClass as BizGhsClass;
-use bizGhs\custom\classes\AccountMoneyClass;
-use bizGhs\custom\classes\CustomClass;
-use yii\console\Controller;
-
-/**
- * 挂账并入余额 — 控制台补漏(休眠账户批量处理)
- *
- * 【用途】处理长期未触发「开单/充值/结账」等入口、仍未 merge 的历史账户。
- * 内部调用 AccountMoneyClass::mergeCustomMoneyForScript / mergeGhsMoneyForScript(线上业务不再自动合并)。
- *
- * 用法:php yii balance-merge/run  或  --dryRun=1 仅预览
- */
-class BalanceMergeController extends Controller
-{
-    /** 1=只打印将处理的 id 与金额,不写库 */
-    public $dryRun = 0;
-
-    public function options($actionID)
-    {
-        return array_merge(parent::options($actionID), ['dryRun']);
-    }
-
-    /**
-     * 【用途】执行或预览补漏:先客户表 xhGhsCustom,再关系表 xhGhs。
-     */
-    public function actionRun()
-    {
-        $dry = (int)$this->dryRun === 1;
-        echo $dry ? "【预览模式】\n" : "【执行合并】\n";
-        $customCount = $this->mergeCustomRows($dry);
-        $ghsCount = $this->mergeGhsRows($dry);
-        echo "完成:客户 {$customCount} 条,供货商关系 {$ghsCount} 条,dryRun=" . ($dry ? '1' : '0') . PHP_EOL;
-    }
-
-    /**
-     * 【用途】扫 xhGhsCustom:debtAmount>0 且(有 balanceMerged 列时)balanceMerged=0,逐条脚本合并。
-     */
-    protected function mergeCustomRows($dry)
-    {
-        $count = 0;
-        $where = ['debtAmount>' => 0];
-        if ($this->columnExists('xhGhsCustom', 'balanceMerged')) {
-            $where['balanceMerged'] = 0;
-        }
-        $list = CustomClass::getAllByCondition($where, null, '*', null, true);
-        foreach ($list as $custom) {
-            if ($dry) {
-                echo "[客户] id={$custom->id} debtAmount={$custom->debtAmount} balance={$custom->balance}" . PHP_EOL;
-                $count++;
-                continue;
-            }
-            AccountMoneyClass::mergeCustomMoneyForScript($custom, true);
-            $count++;
-        }
-        return $count;
-    }
-
-    /**
-     * 【用途】扫 xhGhs:条件同上,逐条 mergeGhsMoneyForScript(不自动带 custom,休眠 ghs 单独处理)。
-     */
-    protected function mergeGhsRows($dry)
-    {
-        $count = 0;
-        $where = ['debtAmount>' => 0];
-        if ($this->columnExists('xhGhs', 'balanceMerged')) {
-            $where['balanceMerged'] = 0;
-        }
-        $list = BizGhsClass::getAllByCondition($where, null, '*', null, true);
-        foreach ($list as $ghs) {
-            if ($dry) {
-                echo "[关系] id={$ghs->id} debtAmount={$ghs->debtAmount} balance={$ghs->balance}" . PHP_EOL;
-                $count++;
-                continue;
-            }
-            AccountMoneyClass::mergeGhsMoneyForScript($ghs, true);
-            $count++;
-        }
-        return $count;
-    }
-
-    /**
-     * 【用途】判断迁移 SQL 是否已执行,避免 where balanceMerged 报错。
-     */
-    protected function columnExists($table, $column)
-    {
-        $db = \Yii::$app->db;
-        $schema = $db->getTableSchema($table, true);
-        return $schema && isset($schema->columns[$column]);
-    }
-}

+ 0 - 197
console/controllers/BalanceUnmergeController.php

@@ -1,197 +0,0 @@
-<?php
-
-namespace console\controllers;
-
-use bizGhs\cg\classes\CgClass;
-use bizGhs\custom\classes\CustomBalanceChangeClass;
-use bizGhs\custom\classes\CustomClass;
-use bizGhs\custom\classes\CustomDebtChangeClass;
-use bizHd\ghs\classes\GhsClass;
-use bizHd\purchase\classes\PurchaseClass;
-use common\components\dict;
-use bizGhs\ghs\classes\GhsBalanceChangeClass;
-use bizHd\ghs\classes\GhsDebtChangeClass;
-use yii\console\Controller;
-use Yii;
-
-class BalanceUnmergeController extends Controller
-{
-
-
-    public function actionRun()
-    {
-
-        $connection = Yii::$app->db;
-        $transaction = $connection->beginTransaction();
-        try {
-
-            $customBalanceList = CustomBalanceChangeClass::getAllByCondition(['capitalType' => 81], null, '*', null, true);
-            if (!empty($customBalanceList)) {
-                foreach ($customBalanceList as $customBalance) {
-                    $customBalanceId = $customBalance->id ?? 0;
-                    $customId = $customBalance->customId ?? 0;
-                    $custom = CustomClass::getById($customId, true);
-                    if (empty($custom)) {
-                        echo $customId . " 没有客户信息";
-                        exit();
-                    }
-                    $customName = $custom->name ?? '';
-
-                    $before = CustomBalanceChangeClass::getByCondition(['customId' => $customId, 'id<' => $customBalanceId],true,'id desc' );
-
-                    $beforeBalance = $before->balance ?? 0;
-
-                    if($customId == 25695){
-                        //小吉要补3000
-                        $beforeBalance = bcadd($beforeBalance, 3000, 2);
-                    }
-
-                    $ghsId = $custom->ghsId ?? 0;
-                    $ghs = GhsClass::getById($ghsId, true);
-                    if (empty($ghs)) {
-                        echo $customId . " 客户供货商信息没有";
-                        exit();
-                    }
-                    $ghsName = $ghs->name ?? '';
-                    $ownPtStyle = $ghs->ownPtStyle ?? 1;
-                    if ($ownPtStyle == 1) {
-                        $cgList = PurchaseClass::getAllByCondition(['ghsId' => $ghsId, 'debt' => 1], null, '*', null, true);
-                        $debtAmount = 0;
-                        if (!empty($cgList)) {
-                            foreach ($cgList as $cg) {
-                                $currentDebt = $cg->remainDebtPrice;
-                                $debtAmount = bcadd($debtAmount, $currentDebt, 2);
-                            }
-                        }
-                        $has = CustomBalanceChangeClass::getAllByCondition(['customId' => $customId, 'id>' => $customBalanceId], null, '*', null, true);
-                        $hasCount = count($has);
-                        if ($hasCount > 0) {
-                            echo $customName . "({$ghsName}){$customId} 要恢复欠款:" . $debtAmount . " 余额:" . $beforeBalance . " 合并之后还有余额变动{$hasCount}条" . " ------ \n";
-                        } else {
-                            echo $customName . "({$ghsName}){$customId} 要恢复欠款:" . $debtAmount . " 余额:" . $beforeBalance . " ------ \n";
-                        }
-                    } elseif ($ownPtStyle == 2) {
-                        $cgList = CgClass::getAllByCondition(['ghsId' => $ghsId, 'debt' => 1], null, '*', null, true);
-                        $debtAmount = 0;
-                        if (!empty($cgList)) {
-                            foreach ($cgList as $cg) {
-                                $currentDebt = $cg->actPrice;
-                                $debtAmount = bcadd($debtAmount, $currentDebt, 2);
-                            }
-                        }
-                        $has = CustomBalanceChangeClass::getAllByCondition(['customId' => $customId, 'id>' => $customBalanceId], null, '*', null, true);
-                        $hasCount = count($has);
-                        if ($hasCount > 0) {
-                            echo $customName . "({$ghsName}){$customId}【供货商端】要恢复欠款:" . $debtAmount . " 余额:" . $beforeBalance . " 合并之后还有余额变动{$hasCount}条" . " $$$$$$$ \n";
-                        } else {
-                            echo $customName . "({$ghsName}){$customId}【供货商端】要恢复欠款:" . $debtAmount . " 余额:" . $beforeBalance . " $$$$$$$ \n";
-                        }
-                    } else {
-                        echo $customId . " 客户ptStyle有问题" . $ownPtStyle;
-                        exit();
-                    }
-
-                    //continue;
-
-                    $custom->debtAmount = $debtAmount;
-                    $custom->balanceMerged = 0;
-                    $custom->balance = $beforeBalance;
-                    $custom->save();
-
-                    $ghs->debtAmount = $debtAmount;
-                    $ghs->balanceMerged = 0;
-                    $ghs->balance = $beforeBalance;
-                    $ghs->save();
-
-                    // 1. 写入客户余额变动明细(CustomBalanceChange)
-                    $cbData = [
-                        'customId' => $custom->id ?? 0,
-                        'customName' => $custom->name ?? '',
-                        'relateId' => 0,
-                        'onlinePay' => 0,
-                        'ptStyle' => dict::getDict('ptStyle', 'ghs'),
-                        'capitalType' => 10,
-                        'amount' => bcsub($beforeBalance, $customBalance->balance ?? 0, 2), // 变化金额 = 恢复后的余额 - 之前的余额
-                        'balance' => $beforeBalance,
-                        'staffId' => 0,
-                        'staffName' => '',
-                        'io' => 1,
-                        'side' => 0,
-                        'payWay' => 0,
-                        'fromType' => dict::getDict('fromType', 'shop'),
-                        'event' => '系统升级恢复',
-                        'mainId' => $ghs->mainId ?? 0,
-                        'shopId' => $ghs->shopId ?? 0,
-                        'sjId' => $ghs->sjId ?? 0,
-                        'remark' => '',
-                    ];
-                    CustomBalanceChangeClass::add($cbData, true);
-
-                    // 2. 写入供货商余额变动明细(GhsBalanceChange)
-                    $gbData = [
-                        'ghsId' => $ghs->id ?? 0,
-                        'relateId' => 0,
-                        'ptStyle' => 1,
-                        'capitalType' => 11,
-                        'amount' => bcsub($beforeBalance, $customBalance->balance ?? 0, 2), // 变化金额 = 恢复后的余额 - 之前的余额
-                        'balance' => $beforeBalance,
-                        'io' => 1,
-                        'side' => 0,
-                        'onlinePay' => 0,
-                        'payWay' => 0,
-                        'fromType' => dict::getDict('fromType', 'shop'),
-                        'event' => '系统升级恢复',
-                        'mainId' => $custom->mainId ?? 0,
-                        'shopId' => $custom->shopId ?? 0,
-                        'sjId' => $custom->sjId ?? 0,
-                        'remark' => '',
-                    ];
-                    GhsBalanceChangeClass::add($gbData, true);
-
-                    // 3. 写入客户欠款挂账变动明细(CustomDebtChangeClass)
-                    $cdcData = [
-                        'customId' => $custom->id ?? 0,
-                        'customName' => $custom->name ?? '',
-                        'relateId' => 0,
-                        'ptStyle' => dict::getDict('ptStyle', 'ghs'),
-                        'capitalType' => 20, // 20 对应 结账/变动
-                        'amount' => $debtAmount,
-                        'balance' => $debtAmount,
-                        'io' => 0, // 0 对应增加欠款
-                        'event' => '系统升级恢复',
-                        'mainId' => $ghs->mainId ?? 0,
-                        'shopId' => $ghs->shopId ?? 0,
-                        'sjId' => $ghs->sjId ?? 0,
-                        'remark' => '',
-                    ];
-                    CustomDebtChangeClass::addChange($cdcData);
-
-                    // 4. 写入供货商欠款挂账变动明细(GhsDebtChangeClass)
-                    $gdcData = [
-                        'ghsId' => $ghs->id ?? 0,
-                        'ptStyle' => 1,
-                        'capitalType' => 20, // 20 对应 结账/变动
-                        'amount' => $debtAmount,
-                        'balance' => $debtAmount,
-                        'io' => 0, // 0 对应增加欠款
-                        'event' => '系统升级恢复',
-                        'mainId' => $custom->mainId ?? 0,
-                        'shopId' => $custom->shopId ?? 0,
-                        'sjId' => $custom->sjId ?? 0,
-                        'remark' => '',
-                    ];
-                    GhsDebtChangeClass::addChange($gdcData);
-
-                }
-            }
-
-            $transaction->commit();
-        } catch (\Exception $e) {
-            $transaction->rollBack();
-            $msg = $e->getMessage();
-            echo $msg;
-        }
-
-    }
-
-}

+ 0 - 159
console/controllers/GhsPurchaseBalanceMergeController.php

@@ -1,159 +0,0 @@
-<?php
-
-namespace console\controllers;
-
-use biz\ghs\classes\GhsClass as BizGhsClass;
-use bizGhs\custom\classes\AccountMoneyClass;
-use common\components\dict;
-use yii\console\Controller;
-
-/**
- * ghsApp 采购供货商 xhGhs 余额合并(按采购待结订单重算)
- *
- * 【用途】仅处理 ghsApp 采购供货商行(ownPtStyle=ghs/kmGhs),不处理 hd 花店侧 xhGhs 行(ownPtStyle=hd)。
- * 【公式】净 balance = max(当前正余额,0) - 待结 ghs 采购单 actPrice 合计;debtNum 与有效待结单数对齐。
- * 【可重复执行】默认 force=1,已 balanceMerged 的行也会重算,并清理 actPrice<=0/已取消仍标待结的幽灵单。
- *
- * hd 花店侧供货商请用:php yii balance-merge/run
- *
- * 用法:php yii ghs-purchase-balance-merge/run
- * 预览:php yii ghs-purchase-balance-merge/run --dryRun=1
- * 单户:php yii ghs-purchase-balance-merge/run --ghsId=123
- * 仅未合并:php yii ghs-purchase-balance-merge/run --force=0
- */
-class GhsPurchaseBalanceMergeController extends Controller
-{
-    /** 1=只预览不写库 */
-    public $dryRun = 0;
-
-    /** 指定 xhGhs.id,0=扫全表符合条件的 ghsApp 采购供货商行 */
-    public $ghsId = 0;
-
-    /** 1=已合并行也重算(默认);0=仅处理未合并行 */
-    public $force = 1;
-
-    public function options($actionID)
-    {
-        return array_merge(parent::options($actionID), ['dryRun', 'ghsId', 'force']);
-    }
-
-    /**
-     * 执行或预览:仅 ghsApp 采购供货商(ownPtStyle=ghs/kmGhs)按采购待结订单重算净余额。
-     */
-    public function actionRun()
-    {
-        $dry = (int)$this->dryRun === 1;
-        $force = (int)$this->force === 1;
-        $ghsId = intval($this->ghsId);
-        echo $dry ? "【预览:ghsApp 采购供货商按订单重算余额】\n" : "【执行:ghsApp 采购供货商按订单重算余额】\n";
-        echo 'force=' . ($force ? '1(含已合并)' : '0(仅未合并)') . PHP_EOL;
-
-        if ($ghsId > 0) {
-            $count = $this->mergeOneGhs($ghsId, $dry, $force) ? 1 : 0;
-            echo "完成:处理 {$count} 条,dryRun=" . ($dry ? '1' : '0') . PHP_EOL;
-            return;
-        }
-
-        $count = 0;
-        $purchasePtStyles = $this->getGhsAppPurchasePtStyles();
-        $where = [
-            'ownShopId>' => 0,
-            'ownPtStyle' => ['in', $purchasePtStyles],
-        ];
-        if (!$force && $this->columnExists('xhGhs', 'balanceMerged')) {
-            $where['balanceMerged'] = 0;
-        } elseif (!$force) {
-            $where['debtAmount>'] = 0;
-        }
-
-        $list = BizGhsClass::getAllByCondition($where, null, 'id,ownShopId,ownPtStyle,balance,debtAmount,debtNum,balanceMerged', null, true);
-        foreach ($list as $ghs) {
-            if ($this->mergeOneGhs(intval($ghs->id ?? 0), $dry, $force)) {
-                $count++;
-            }
-        }
-
-        // ownPtStyle 未回填的历史行
-        $legacyWhere = [
-            'ownShopId>' => 0,
-            'ownPtStyle' => 0,
-        ];
-        if (!$force && $this->columnExists('xhGhs', 'balanceMerged')) {
-            $legacyWhere['balanceMerged'] = 0;
-        }
-        $legacyList = BizGhsClass::getAllByCondition($legacyWhere, null, 'id,ownShopId,ownPtStyle,balance,debtAmount,debtNum,balanceMerged', null, true);
-        foreach ($legacyList as $ghs) {
-            if ($this->mergeOneGhs(intval($ghs->id ?? 0), $dry, $force)) {
-                $count++;
-            }
-        }
-
-        echo "完成:处理 {$count} 条,dryRun=" . ($dry ? '1' : '0') . PHP_EOL;
-    }
-
-    /**
-     * ghsApp 采购买方平台类型:二级批发(2)、基地端(4);不含 hd 花店(1)。
-     */
-    protected function getGhsAppPurchasePtStyles()
-    {
-        return [
-            (int)dict::getDict('ptStyle', 'ghs'),
-            (int)dict::getDict('ptStyle', 'kmGhs'),
-        ];
-    }
-
-    /**
-     * 合并单行;预览模式只打印将写入的金额。
-     */
-    protected function mergeOneGhs($ghsId, $dry, $force)
-    {
-        if ($ghsId <= 0) {
-            return false;
-        }
-        $ghs = BizGhsClass::getById($ghsId, true);
-        if (empty($ghs)) {
-            echo "[跳过] id={$ghsId} 不存在" . PHP_EOL;
-            return false;
-        }
-        if (!AccountMoneyClass::isGhsAppPurchaseSupplierRow($ghs)) {
-            $ownPtStyle = intval($ghs->ownPtStyle ?? 0);
-            echo "[跳过] id={$ghsId} 非 ghsApp 采购供货商行(ownPtStyle={$ownPtStyle},hd 行请用 balance-merge)" . PHP_EOL;
-            return false;
-        }
-
-        $ownShopId = intval($ghs->ownShopId ?? 0);
-        $oldBalance = bcadd((string)($ghs->balance ?? '0'), '0', 2);
-        $oldDebtNum = intval($ghs->debtNum ?? 0);
-        $fixedOrders = AccountMoneyClass::countInvalidPurchaseDebtOrders($ghsId);
-        $debtStats = AccountMoneyClass::getValidPurchaseDebtStats($ghsId);
-        $orderDebt = $debtStats['amount'];
-        $orderDebtNum = $debtStats['num'];
-        $positiveCredit = bccomp($oldBalance, '0', 2) > 0 ? $oldBalance : '0.00';
-        $newBalance = bcsub($positiveCredit, $orderDebt, 2);
-
-        if ($dry) {
-            echo "[ghsApp采购供货商] id={$ghsId} ownShopId={$ownShopId} ownPtStyle={$ghs->ownPtStyle}"
-                . " 原balance={$oldBalance} 原debtNum={$oldDebtNum}"
-                . " 清理幽灵单={$fixedOrders}"
-                . " 待结{$orderDebtNum}笔合计={$orderDebt} => 新balance={$newBalance} 新debtNum={$orderDebtNum}" . PHP_EOL;
-            return true;
-        }
-
-        $locked = BizGhsClass::getLockById($ghsId);
-        $fixedOrders = AccountMoneyClass::syncInvalidPurchaseDebtOrders($ghsId);
-        AccountMoneyClass::mergeOwnShopGhsBalanceFromPurchaseOrdersIfNeeded($locked, false, $force);
-        echo "[已重算] id={$ghsId} 清理幽灵单={$fixedOrders} 待结{$orderDebtNum}笔合计={$orderDebt}"
-            . " balance {$oldBalance}=>{$newBalance} debtNum {$oldDebtNum}=>{$orderDebtNum}" . PHP_EOL;
-        return true;
-    }
-
-    /**
-     * 判断 xhGhs 是否已有 balanceMerged 字段。
-     */
-    protected function columnExists($table, $column)
-    {
-        $db = \Yii::$app->db;
-        $schema = $db->getTableSchema($table, true);
-        return $schema && isset($schema->columns[$column]);
-    }
-}