OrderCgClearClass.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. namespace bizGhs\clear\classes;
  3. use bizGhs\base\classes\BaseClass;
  4. use bizGhs\order\classes\OrderClass;
  5. use bizGhs\order\classes\PurchaseOrderClass;
  6. use bizHd\purchase\classes\PurchaseClass;
  7. use bizHd\purchase\classes\PurchaseClearClass;
  8. class OrderCgClearClass extends BaseClass
  9. {
  10. public static $baseFile = '\bizGhs\clear\models\OrderCgClear';
  11. const STATUS_AWAIT_PAY = 1;
  12. const STATUS_HAS_PAY = 2;
  13. const STATUS_EXPIRE = 3;
  14. public static function buildPairsFromOrders($orderList)
  15. {
  16. if (empty($orderList)) {
  17. return [];
  18. }
  19. $hdPurchaseIds = [];
  20. foreach ($orderList as $order) {
  21. $purchaseId = is_array($order) ? ($order['purchaseId'] ?? 0) : ($order->purchaseId ?? 0);
  22. if (!empty($purchaseId)) {
  23. $hdPurchaseIds[] = $purchaseId;
  24. }
  25. }
  26. $cgMap = [];
  27. if (!empty($hdPurchaseIds)) {
  28. $cgList = PurchaseClass::getByIds(array_unique($hdPurchaseIds));
  29. if (!empty($cgList)) {
  30. $cgMap = array_column($cgList, null, 'id');
  31. }
  32. }
  33. $pairs = [];
  34. foreach ($orderList as $order) {
  35. $orderId = is_array($order) ? ($order['id'] ?? 0) : ($order->id ?? 0);
  36. $orderSn = is_array($order) ? ($order['orderSn'] ?? '') : ($order->orderSn ?? '');
  37. $amount = self::resolveDebtAmount($order);
  38. $cgId = intval(is_array($order) ? ($order['purchaseId'] ?? 0) : ($order->purchaseId ?? 0));
  39. $cgSn = '';
  40. if ($cgId > 0 && isset($cgMap[$cgId])) {
  41. $cgSn = $cgMap[$cgId]['orderSn'] ?? '';
  42. }
  43. $pairs[] = [
  44. 'orderId' => intval($orderId),
  45. 'orderSn' => $orderSn,
  46. 'cgId' => $cgId,
  47. 'cgSn' => $cgSn,
  48. 'amount' => $amount,
  49. ];
  50. }
  51. return $pairs;
  52. }
  53. public static function buildPairsFromPurchases($purchaseList)
  54. {
  55. if (empty($purchaseList)) {
  56. return [];
  57. }
  58. $ghsOrderIds = [];
  59. foreach ($purchaseList as $purchase) {
  60. $saleId = is_array($purchase) ? ($purchase['saleId'] ?? 0) : ($purchase->saleId ?? 0);
  61. if (!empty($saleId)) {
  62. $ghsOrderIds[] = $saleId;
  63. }
  64. }
  65. $orderMap = [];
  66. if (!empty($ghsOrderIds)) {
  67. $orderList = OrderClass::getByIds(array_unique($ghsOrderIds));
  68. if (!empty($orderList)) {
  69. $orderMap = array_column($orderList, null, 'id');
  70. }
  71. }
  72. $pairs = [];
  73. foreach ($purchaseList as $purchase) {
  74. $cgId = is_array($purchase) ? ($purchase['id'] ?? 0) : ($purchase->id ?? 0);
  75. $cgSn = is_array($purchase) ? ($purchase['orderSn'] ?? '') : ($purchase->orderSn ?? '');
  76. $amount = self::resolveDebtAmount($purchase);
  77. $orderId = intval(is_array($purchase) ? ($purchase['saleId'] ?? 0) : ($purchase->saleId ?? 0));
  78. $orderSn = '';
  79. if ($orderId > 0 && isset($orderMap[$orderId])) {
  80. $orderSn = $orderMap[$orderId]['orderSn'] ?? '';
  81. }
  82. $pairs[] = [
  83. 'orderId' => $orderId,
  84. 'orderSn' => $orderSn,
  85. 'cgId' => intval($cgId),
  86. 'cgSn' => $cgSn,
  87. 'amount' => $amount,
  88. ];
  89. }
  90. return $pairs;
  91. }
  92. /**
  93. * gys2KmGys:cgId 存 xhGhsCgOrder.id,orderId=0
  94. */
  95. public static function buildPairsFromGhsCgOrders($cgList)
  96. {
  97. if (empty($cgList)) {
  98. return [];
  99. }
  100. $pairs = [];
  101. foreach ($cgList as $cg) {
  102. $cgId = intval(is_array($cg) ? ($cg['id'] ?? 0) : ($cg->id ?? 0));
  103. if ($cgId <= 0) {
  104. continue;
  105. }
  106. $cgSn = is_array($cg) ? ($cg['orderSn'] ?? '') : ($cg->orderSn ?? '');
  107. $pairs[] = [
  108. 'orderId' => 0,
  109. 'orderSn' => '',
  110. 'cgId' => $cgId,
  111. 'cgSn' => $cgSn,
  112. 'amount' => self::resolveDebtAmount($cg),
  113. ];
  114. }
  115. return $pairs;
  116. }
  117. public static function bindRelations($clear, $pairs, $status = self::STATUS_AWAIT_PAY)
  118. {
  119. $clearId = is_object($clear) ? ($clear->id ?? 0) : ($clear['id'] ?? 0);
  120. $clearSn = is_object($clear) ? ($clear->orderSn ?? '') : ($clear['orderSn'] ?? '');
  121. if (empty($clearId) || empty($pairs)) {
  122. return;
  123. }
  124. foreach ($pairs as $pair) {
  125. $orderId = intval($pair['orderId'] ?? 0);
  126. $cgId = intval($pair['cgId'] ?? 0);
  127. if ($orderId <= 0 && $cgId <= 0) {
  128. continue;
  129. }
  130. $exists = self::getByCondition([
  131. 'clearId' => $clearId,
  132. 'orderId' => $orderId,
  133. 'cgId' => $cgId,
  134. ], true);
  135. $newAmount = self::normalizeAmount($pair['amount'] ?? 0);
  136. if (!empty($exists)) {
  137. self::patchAmountIfEmpty($exists, $newAmount);
  138. continue;
  139. }
  140. self::add([
  141. 'clearId' => $clearId,
  142. 'clearSn' => $clearSn,
  143. 'orderId' => $orderId,
  144. 'orderSn' => $pair['orderSn'] ?? '',
  145. 'cgId' => $cgId,
  146. 'cgSn' => $pair['cgSn'] ?? '',
  147. 'amount' => $newAmount,
  148. 'status' => $status,
  149. ]);
  150. }
  151. }
  152. public static function markDoneByClearId($clearId)
  153. {
  154. self::markStatusByClearId($clearId, self::STATUS_HAS_PAY);
  155. }
  156. public static function markExpireByClearId($clearId)
  157. {
  158. self::markStatusByClearId($clearId, self::STATUS_EXPIRE);
  159. }
  160. public static function markStatusByClearId($clearId, $status)
  161. {
  162. $clearId = intval($clearId);
  163. $status = intval($status);
  164. if ($clearId <= 0) {
  165. return;
  166. }
  167. $list = self::getAllByCondition(['clearId' => $clearId], null, '*', null, true);
  168. if (empty($list)) {
  169. return;
  170. }
  171. foreach ($list as $item) {
  172. $item->status = $status;
  173. $item->save();
  174. }
  175. }
  176. /**
  177. * 销售单已销账的结账单列表(部分销、多次销均含,对齐 hd orderSettleList)
  178. *
  179. * @return array<int, array{clearId:int, clearSn:string, amount:string}>
  180. */
  181. public static function listPaidClearRecordsForOrder($orderId)
  182. {
  183. $orderId = intval($orderId);
  184. if ($orderId <= 0) {
  185. return [];
  186. }
  187. $list = self::getAllByCondition([
  188. 'orderId' => $orderId,
  189. 'status' => self::STATUS_HAS_PAY,
  190. ], 'id asc', '*', null, true);
  191. return self::mapPaidClearRecordRows($list);
  192. }
  193. /**
  194. * 采购单关联的已销结账单(只认本采购单 cgId,绝不回退到 saleId)
  195. * 说明1:若按 saleId 查,会把该关联销售单的客户结账一并带出,采购详情「结账信息」会错混销售结账。
  196. * 说明2:xhOrderCgClear.cgId 在不同结账场景下可能指向不同表的主键(如供货商自身采购
  197. * bizGhs\order\classes\PurchaseOrderClass 与花店采购 bizHd\purchase\classes\PurchaseClass),
  198. * 两边自增 id 存在撞号可能。仅按 cgId 过滤仍可能混入其它业务线的结账记录,
  199. * 必须再按 xhClear.clearStyle 精确限定属于哪种结账方式,才能确认是「同一个」采购单。
  200. *
  201. * @param int $purchaseId 采购单/cgId
  202. * @param int|null $clearStyle 限定的结账方式(dict clearStyle),传 null 则不按结账方式过滤
  203. * @return array<int, array{clearId:int, clearSn:string, amount:string}>
  204. */
  205. public static function listPaidClearRecordsForPurchase($purchaseId, $clearStyle = null)
  206. {
  207. $purchaseId = intval($purchaseId);
  208. if ($purchaseId <= 0) {
  209. return [];
  210. }
  211. $cgList = self::getAllByCondition([
  212. 'cgId' => $purchaseId,
  213. 'status' => self::STATUS_HAS_PAY,
  214. ], 'id asc', '*', null, true);
  215. if (empty($cgList)) {
  216. return [];
  217. }
  218. if ($clearStyle !== null) {
  219. $clearIds = array_values(array_unique(array_filter(array_map(function ($item) {
  220. return intval(is_object($item) ? ($item->clearId ?? 0) : ($item['clearId'] ?? 0));
  221. }, $cgList))));
  222. $allowedClearIds = empty($clearIds) ? [] : ClearClass::getAllByCondition([
  223. 'id' => ['in', $clearIds],
  224. 'clearStyle' => intval($clearStyle),
  225. ], null, 'id', null);
  226. $allowedClearIds = array_flip(array_map('intval', array_column($allowedClearIds, 'id')));
  227. $cgList = array_values(array_filter($cgList, function ($item) use ($allowedClearIds) {
  228. $clearId = intval(is_object($item) ? ($item->clearId ?? 0) : ($item['clearId'] ?? 0));
  229. return isset($allowedClearIds[$clearId]);
  230. }));
  231. }
  232. return self::mapPaidClearRecordRows($cgList);
  233. }
  234. protected static function mapPaidClearRecordRows($list)
  235. {
  236. if (empty($list)) {
  237. return [];
  238. }
  239. $rows = [];
  240. foreach ($list as $item) {
  241. $clearId = intval(is_object($item) ? ($item->clearId ?? 0) : ($item['clearId'] ?? 0));
  242. if ($clearId <= 0) {
  243. continue;
  244. }
  245. $rows[] = [
  246. 'clearId' => $clearId,
  247. 'clearSn' => (string)(is_object($item) ? ($item->clearSn ?? '') : ($item['clearSn'] ?? '')),
  248. 'amount' => self::normalizeAmount(is_object($item) ? ($item->amount ?? 0) : ($item['amount'] ?? 0)),
  249. ];
  250. }
  251. return $rows;
  252. }
  253. public static function getOrderIdsByClearId($clearId)
  254. {
  255. $clearId = intval($clearId);
  256. if ($clearId <= 0) {
  257. return [];
  258. }
  259. $list = self::getAllByCondition(['clearId' => $clearId, 'orderId>' => 0], null, 'orderId', null);
  260. return array_values(array_unique(array_filter(array_map('intval', array_column($list, 'orderId')))));
  261. }
  262. public static function getCgIdsByClearId($clearId)
  263. {
  264. $clearId = intval($clearId);
  265. if ($clearId <= 0) {
  266. return [];
  267. }
  268. $list = self::getAllByCondition(['clearId' => $clearId, 'cgId>' => 0], null, 'cgId', null);
  269. return array_values(array_unique(array_filter(array_map('intval', array_column($list, 'cgId')))));
  270. }
  271. public static function getPendingClearIdByOrderId($orderId, $customId)
  272. {
  273. $orderId = intval($orderId);
  274. $customId = intval($customId);
  275. if ($orderId <= 0) {
  276. return 0;
  277. }
  278. $list = self::getAllByCondition([
  279. 'orderId' => $orderId,
  280. 'status' => self::STATUS_AWAIT_PAY,
  281. ], null, 'clearId', null);
  282. if (empty($list)) {
  283. return 0;
  284. }
  285. $clearIds = array_unique(array_map('intval', array_column($list, 'clearId')));
  286. $now = date("Y-m-d H:i:s");
  287. foreach ($clearIds as $clearId) {
  288. $clear = ClearClass::getById($clearId, true);
  289. if (empty($clear)) {
  290. continue;
  291. }
  292. if (($clear->status ?? 0) != PurchaseClearClass::STATUS_AWAIT_PAY) {
  293. continue;
  294. }
  295. if ($customId > 0 && ($clear->customId ?? 0) != $customId) {
  296. continue;
  297. }
  298. $deadline = $clear->deadline ?? '';
  299. if (!empty($deadline) && $deadline < $now) {
  300. continue;
  301. }
  302. return intval($clearId);
  303. }
  304. return 0;
  305. }
  306. /**
  307. * 待结金额:销售单/采购单用 remainDebtPrice(与 hd 充值销账 FIFO 一致),勿用 actPrice(实付会小于待结)
  308. */
  309. protected static function resolveDebtAmount($entity)
  310. {
  311. $remain = self::readField($entity, 'remainDebtPrice');
  312. if ($remain !== '' && $remain !== null && bccomp(self::normalizeAmount($remain), '0', 2) > 0) {
  313. return self::normalizeAmount($remain);
  314. }
  315. return self::normalizeAmount(self::readField($entity, 'actPrice'));
  316. }
  317. protected static function readField($entity, $key)
  318. {
  319. return is_array($entity) ? ($entity[$key] ?? 0) : ($entity->$key ?? 0);
  320. }
  321. protected static function normalizeAmount($amount)
  322. {
  323. if ($amount === '' || $amount === null) {
  324. return '0.00';
  325. }
  326. return bcadd((string)$amount, '0', 2);
  327. }
  328. protected static function patchAmountIfEmpty($row, $amount)
  329. {
  330. $amount = self::normalizeAmount($amount);
  331. if (bccomp($amount, '0', 2) <= 0) {
  332. return false;
  333. }
  334. $current = self::normalizeAmount(is_object($row) ? ($row->amount ?? 0) : ($row['amount'] ?? 0));
  335. if (bccomp($current, '0', 2) > 0) {
  336. return false;
  337. }
  338. if (is_object($row)) {
  339. $row->amount = $amount;
  340. return $row->save(false);
  341. }
  342. return false;
  343. }
  344. }