AccountMoneyClass.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <?php
  2. namespace bizGhs\custom\classes;
  3. use biz\ghs\classes\GhsClass as BizGhsClass;
  4. use bizGhs\ghs\classes\GhsBalanceChangeClass;
  5. use bizGhs\order\classes\PurchaseOrderClass;
  6. use common\components\dict;
  7. use Yii;
  8. /**
  9. * 【用途】客户/供货商账户金额核心处理类(表 xhGhsCustom、xhGhs)
  10. * 【为什么】在全部升级到新版、全部账户已合并的前提下,彻底移除旧版 appVersion < 3 兼容逻辑与 balanceMerged 复杂校验,极大地简化计算路径,提升系统性能与稳定性。
  11. * 【说明】
  12. * - 数据库中的 balance 已经是唯一的净余额(正数代表有充值余额,负数代表待结欠款)。
  13. * - 数据库中的 debtAmount 字段已废弃,其值始终为 0.00。
  14. * - 写业务前调用 ensureCustomMoneyReady / ensureGhsMoneyReady(仅行锁,不触发合并)。
  15. */
  16. class AccountMoneyClass
  17. {
  18. /**
  19. * 新端协议版本号:只返回净 balance,不再拆分待结/余额两列。
  20. */
  21. const APP_VERSION_NET_BALANCE = 3;
  22. /**
  23. * 【用途】获取当前 HTTP 请求里的客户端版本号。
  24. * 【说明】由于全部已升级到新版,此方法固定返回 3。
  25. */
  26. public static function getClientAppVersion()
  27. {
  28. return self::APP_VERSION_NET_BALANCE;
  29. }
  30. /**
  31. * 【用途】判断数据行是否带有 balanceMerged 字段。
  32. * 【说明】已全部合并,此方法固定返回 true。
  33. */
  34. public static function hasMergeFlagField($row)
  35. {
  36. return true;
  37. }
  38. /**
  39. * 【用途】判断该客户/供货商账户是否已经完成「挂账并入余额」。
  40. * 【说明】已全部合并,此方法固定返回 true。
  41. */
  42. public static function isMerged($row)
  43. {
  44. return true;
  45. }
  46. /**
  47. * 【用途】内部取 debtAmount 原始值。
  48. * 【说明】已全部合并,此方法固定返回 '0.00'。
  49. */
  50. protected static function rawDebtAmount($row)
  51. {
  52. return '0.00';
  53. }
  54. /**
  55. * 【用途】根据库里的 balance 计算「净账户余额」。
  56. * 【说明】已全部合并,净额就是 balance。
  57. */
  58. public static function calcNetBalance($balance, $debtAmount, $merged = true)
  59. {
  60. return bcadd((string)($balance ?? '0.00'), '0', 2);
  61. }
  62. /**
  63. * 【用途】从记录中取出净账户余额。
  64. * 【说明】已全部合并,直接返回 balance。
  65. */
  66. public static function getNetBalanceFromRow($row)
  67. {
  68. if (is_array($row)) {
  69. return bcadd((string)($row['balance'] ?? '0.00'), '0', 2);
  70. }
  71. return bcadd((string)($row->balance ?? '0.00'), '0', 2);
  72. }
  73. /**
  74. * 【用途】得到当前账户「待结欠款」数额(正数)。
  75. * 【规则】净余额 >= 0 时返回 0.00;净余额 < 0 时返回 |净余额|。
  76. */
  77. public static function getOutstandingDebt($row)
  78. {
  79. $net = self::getNetBalanceFromRow($row);
  80. if (bccomp($net, '0', 2) >= 0) {
  81. return '0.00';
  82. }
  83. return bcmul($net, '-1', 2);
  84. }
  85. /**
  86. * 【用途】把接口返回给前端的金额字段,按新版形态格式化。
  87. * 【说明】由于已全部升级至新版,直接返回净 balance。为了前端各页面和打印机安全兼容,
  88. * 我们将真实的待结欠款(正数)同时赋予 debtAmount 和 remainDebtAmount。
  89. */
  90. public static function formatMoneyForClient(array $row, $appVersion = null)
  91. {
  92. $net = self::getNetBalanceFromRow($row);
  93. $row['displayBalance'] = $net;
  94. $row['balance'] = $net;
  95. // 计算出真实的待结欠款(正数)
  96. $outstanding = bccomp($net, '0', 2) < 0 ? bcmul($net, '-1', 2) : '0.00';
  97. $row['debtAmount'] = $outstanding;
  98. $row['remainDebtAmount'] = $outstanding;
  99. return $row;
  100. }
  101. /**
  102. * 【用途】对 xhGhsCustom(客户)执行一次性「挂账并入余额」。
  103. * 【说明】已全部合并,此方法直接返回。
  104. */
  105. public static function mergeCustomDebtIntoBalanceIfNeeded($custom, $writeBalanceChange = true)
  106. {
  107. return $custom;
  108. }
  109. /**
  110. * 【用途】对 xhGhs 执行一次性「挂账并入余额」。
  111. * 【说明】已全部合并,此方法直接返回。
  112. */
  113. public static function mergeGhsDebtIntoBalanceIfNeeded($ghs, $writeBalanceChange = true)
  114. {
  115. return $ghs;
  116. }
  117. /**
  118. * 【用途】客户侧金额写操作前置:仅对客户行加锁,不执行挂账并入余额。
  119. * 【为什么】在全部升级并合并后,写操作只需要锁行即可。
  120. */
  121. public static function ensureCustomMoneyReady($custom, $writeMergeChange = true)
  122. {
  123. if (empty($custom)) {
  124. return $custom;
  125. }
  126. $customId = is_array($custom) ? ($custom['id'] ?? 0) : ($custom->id ?? 0);
  127. return CustomClass::getLockById($customId);
  128. }
  129. /**
  130. * 【用途】控制台批量合并。
  131. * 【说明】已全部合并,此方法直接返回。
  132. */
  133. public static function mergeCustomMoneyForScript($custom, $writeMergeChange = true)
  134. {
  135. return $custom;
  136. }
  137. /**
  138. * 【用途】判断 xhGhs 是否为 ghsApp「采购供货商」行。
  139. */
  140. public static function isGhsAppPurchaseSupplierRow($ghs)
  141. {
  142. $ownShopId = intval(is_array($ghs) ? ($ghs['ownShopId'] ?? 0) : ($ghs->ownShopId ?? 0));
  143. if ($ownShopId <= 0) {
  144. return false;
  145. }
  146. $ownPtStyle = intval(is_array($ghs) ? ($ghs['ownPtStyle'] ?? 0) : ($ghs->ownPtStyle ?? 0));
  147. if ($ownPtStyle <= 0) {
  148. $shop = \biz\shop\classes\ShopClass::getById($ownShopId);
  149. if (!empty($shop)) {
  150. $ownPtStyle = intval(is_array($shop) ? ($shop['ptStyle'] ?? 0) : ($shop->ptStyle ?? 0));
  151. }
  152. }
  153. $hdPt = (int)dict::getDict('ptStyle', 'hd');
  154. if ($ownPtStyle === $hdPt) {
  155. return false;
  156. }
  157. $ghsPt = (int)dict::getDict('ptStyle', 'ghs');
  158. $kmGhsPt = (int)dict::getDict('ptStyle', 'kmGhs');
  159. return in_array($ownPtStyle, [$ghsPt, $kmGhsPt], true);
  160. }
  161. /**
  162. * 【用途】供货商关系行写操作前置:仅加行锁,不执行挂账并入余额。
  163. */
  164. public static function ensureGhsMoneyReady($ghs, $writeMergeChange = true)
  165. {
  166. if (empty($ghs)) {
  167. return $ghs;
  168. }
  169. $ghsId = is_array($ghs) ? ($ghs['id'] ?? 0) : ($ghs->id ?? 0);
  170. return BizGhsClass::getLockById($ghsId);
  171. }
  172. /**
  173. * 【用途】控制台批量合并。
  174. * 【说明】已全部合并,此方法直接返回。
  175. */
  176. public static function mergeGhsMoneyForScript($ghs, $writeMergeChange = true)
  177. {
  178. return $ghs;
  179. }
  180. /**
  181. * 【用途】汇总某采购供货商下仍待结的 ghs 采购单金额。
  182. */
  183. public static function sumPurchaseOrderDebtForGhs($ghsId)
  184. {
  185. $ghsId = intval($ghsId);
  186. if ($ghsId <= 0) {
  187. return '0.00';
  188. }
  189. $sum = PurchaseOrderClass::sum(
  190. ['ghsId' => $ghsId, 'debt' => PurchaseOrderClass::DEBT_YES],
  191. 'actPrice'
  192. );
  193. return bcadd((string)($sum ?: '0'), '0', 2);
  194. }
  195. /**
  196. * 【用途】统计某采购供货商仍待结的 ghs 采购单笔数。
  197. */
  198. public static function countPurchaseOrderDebtForGhs($ghsId)
  199. {
  200. $ghsId = intval($ghsId);
  201. if ($ghsId <= 0) {
  202. return 0;
  203. }
  204. return (int)PurchaseOrderClass::getCount([
  205. 'ghsId' => $ghsId,
  206. 'debt' => PurchaseOrderClass::DEBT_YES,
  207. ]);
  208. }
  209. /**
  210. * 【用途】统计应清理的幽灵待结单数量。
  211. */
  212. public static function countInvalidPurchaseDebtOrders($ghsId)
  213. {
  214. $ids = self::collectInvalidPurchaseDebtOrderIds($ghsId);
  215. return count($ids);
  216. }
  217. /**
  218. * 【用途】有效待结采购单统计。
  219. * @return array{num:int,amount:string}
  220. */
  221. public static function getValidPurchaseDebtStats($ghsId)
  222. {
  223. $ghsId = intval($ghsId);
  224. if ($ghsId <= 0) {
  225. return ['num' => 0, 'amount' => '0.00'];
  226. }
  227. $invalidIds = self::collectInvalidPurchaseDebtOrderIds($ghsId);
  228. $list = PurchaseOrderClass::getAllByCondition([
  229. 'ghsId' => $ghsId,
  230. 'debt' => PurchaseOrderClass::DEBT_YES,
  231. ], null, 'id,actPrice', null, true);
  232. $num = 0;
  233. $amount = '0.00';
  234. if (!empty($list)) {
  235. foreach ($list as $order) {
  236. $orderId = intval($order->id ?? 0);
  237. if (isset($invalidIds[$orderId])) {
  238. continue;
  239. }
  240. $num++;
  241. $amount = bcadd($amount, bcadd((string)($order->actPrice ?? '0'), '0', 2), 2);
  242. }
  243. }
  244. return ['num' => $num, 'amount' => $amount];
  245. }
  246. /**
  247. * 【用途】收集幽灵待结采购单 id。
  248. */
  249. protected static function collectInvalidPurchaseDebtOrderIds($ghsId)
  250. {
  251. $ghsId = intval($ghsId);
  252. $ids = [];
  253. if ($ghsId <= 0) {
  254. return $ids;
  255. }
  256. $zeroList = PurchaseOrderClass::getAllByCondition([
  257. 'ghsId' => $ghsId,
  258. 'debt' => PurchaseOrderClass::DEBT_YES,
  259. 'actPrice<=' => 0,
  260. ], null, 'id', null, true);
  261. if (!empty($zeroList)) {
  262. foreach ($zeroList as $order) {
  263. $ids[intval($order->id ?? 0)] = 1;
  264. }
  265. }
  266. $cancelList = PurchaseOrderClass::getAllByCondition([
  267. 'ghsId' => $ghsId,
  268. 'debt' => PurchaseOrderClass::DEBT_YES,
  269. 'status' => PurchaseOrderClass::PURCHASE_ORDER_STATUS_CANCEL,
  270. ], null, 'id', null, true);
  271. if (!empty($cancelList)) {
  272. foreach ($cancelList as $order) {
  273. $ids[intval($order->id ?? 0)] = 1;
  274. }
  275. }
  276. return $ids;
  277. }
  278. /**
  279. * 【用途】清理不应再计待结的采购单。
  280. */
  281. public static function syncInvalidPurchaseDebtOrders($ghsId)
  282. {
  283. $ghsId = intval($ghsId);
  284. if ($ghsId <= 0) {
  285. return 0;
  286. }
  287. $invalidIds = self::collectInvalidPurchaseDebtOrderIds($ghsId);
  288. if (empty($invalidIds)) {
  289. return 0;
  290. }
  291. $fixed = 0;
  292. foreach (array_keys($invalidIds) as $orderId) {
  293. if ($orderId <= 0) {
  294. continue;
  295. }
  296. $order = PurchaseOrderClass::getById($orderId, true);
  297. if (empty($order) || intval($order->debt ?? 0) !== PurchaseOrderClass::DEBT_YES) {
  298. continue;
  299. }
  300. $order->debt = PurchaseOrderClass::DEBT_NO;
  301. $order->save(false, ['debt']);
  302. $fixed++;
  303. }
  304. return $fixed;
  305. }
  306. /**
  307. * 【用途】按采购待结订单重算 ownShop 侧 xhGhs 净 balance。
  308. * @return object
  309. */
  310. public static function mergeOwnShopGhsBalanceFromPurchaseOrdersIfNeeded($ghs, $writeBalanceChange = true, $forceRecalc = false)
  311. {
  312. return $ghs;
  313. }
  314. }