PurchaseRequestMergeService.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. using Yitter.IdGenerator;
  2. namespace Admin.NET.Plugin.AiDOP.Supply;
  3. /// <summary>
  4. /// 采购申请合并服务。支持本次生成 PR 合并与租户待处理历史 PR 合并(对齐旧 PrAutoMerge2)。
  5. /// </summary>
  6. public class PurchaseRequestMergeService : ITransient
  7. {
  8. private readonly ISqlSugarClient _db;
  9. private readonly NumberRuleService _numberRuleService;
  10. public PurchaseRequestMergeService(ISqlSugarClient db, NumberRuleService numberRuleService)
  11. {
  12. _db = db;
  13. _numberRuleService = numberRuleService;
  14. }
  15. public PurchaseRequestMergeResult MergeGeneratedRequests(List<PurchaseRequestMain> requests) =>
  16. MergeInMemory(requests);
  17. /// <summary>合并租户内待处理历史 PR(state=1,5 周窗口)。</summary>
  18. public async Task<PurchaseRequestHistoricalMergeResult> MergeTenantPendingAsync(long tenantId, string account)
  19. {
  20. var tomorrow = DateTime.Today.AddDays(1);
  21. var windowEnd = GetWeekStart(DateTime.Today)?.AddDays(35) ?? DateTime.Today.AddDays(35);
  22. var pending = await _db.Ado.SqlQueryAsync<PurchaseRequestMain>(
  23. """
  24. SELECT
  25. Id, pr_billno AS PrBillNo, pr_purchaseid AS PrPurchaseId,
  26. pr_purchasenumber AS PrPurchaseNumber, pr_purchasename AS PrPurchaseName,
  27. pr_purchaser AS PrPurchaser, pr_purchaser_num AS PrPurchaserNum,
  28. pr_rqty AS PrRqty, pr_aqty AS PrAqty, pr_sqty AS PrSqty,
  29. icitem_id AS IcitemId, icitem_name AS IcitemName,
  30. pr_ssend_date AS PrSsendDate, pr_sarrive_date AS PrSarriveDate,
  31. pr_unit AS PrUnit, state AS State, pr_type AS PrType,
  32. currencytype AS CurrencyType, tenant_id AS TenantId,
  33. factory_id AS FactoryId, org_id AS OrgId, company_id AS CompanyId,
  34. IsRequireGoods, supplier_type AS SupplierType, IsDeleted
  35. FROM srm_pr_main
  36. WHERE tenant_id = @TenantId
  37. AND IFNULL(IsDeleted, 0) = 0
  38. AND IFNULL(state, 0) = 1
  39. AND IFNULL(analogcalcversion, '') = ''
  40. AND IFNULL(refer_pr_billno, '') = ''
  41. AND pr_ssend_date IS NOT NULL
  42. AND pr_ssend_date <= @WindowEnd
  43. ORDER BY pr_ssend_date, Id
  44. """,
  45. new SugarParameter("@TenantId", tenantId),
  46. new SugarParameter("@WindowEnd", windowEnd));
  47. if (pending.Count <= 1)
  48. {
  49. return new PurchaseRequestHistoricalMergeResult
  50. {
  51. PendingCount = pending.Count,
  52. MergedGroupCount = 0
  53. };
  54. }
  55. foreach (var pr in pending)
  56. {
  57. if (pr.PrSsendDate.HasValue && pr.PrSsendDate.Value.Date < tomorrow)
  58. {
  59. var shift = (tomorrow - pr.PrSsendDate.Value.Date).Days;
  60. pr.PrSsendDate = tomorrow;
  61. if (pr.PrSarriveDate.HasValue)
  62. pr.PrSarriveDate = pr.PrSarriveDate.Value.AddDays(shift);
  63. }
  64. }
  65. var groups = pending
  66. .GroupBy(x => new
  67. {
  68. x.TenantId,
  69. x.CompanyId,
  70. x.FactoryId,
  71. x.IcitemId,
  72. x.PrPurchaseId,
  73. x.IsRequireGoods,
  74. SupplierType = x.SupplierType ?? string.Empty,
  75. WeekStart = GetWeekStart(x.PrSsendDate)
  76. })
  77. .Where(g => g.Count() > 1)
  78. .ToList();
  79. if (groups.Count == 0)
  80. {
  81. return new PurchaseRequestHistoricalMergeResult
  82. {
  83. PendingCount = pending.Count,
  84. MergedGroupCount = 0
  85. };
  86. }
  87. var now = DateTime.Now;
  88. var createdCount = 0;
  89. var closedCount = 0;
  90. var reducedCount = 0;
  91. foreach (var group in groups)
  92. {
  93. var rows = group.OrderBy(x => x.PrSsendDate).ThenBy(x => x.PrSarriveDate).ThenBy(x => x.Id).ToList();
  94. var merged = BuildMergedRow(rows, account, now);
  95. var numbers = await _numberRuleService.NextBatchInCurrentTransactionAsync(
  96. "PR", merged.TenantId.ToString(), 1, account);
  97. merged.PrBillNo = numbers.FirstOrDefault()?.Trim()
  98. ?? throw Oops.Oh("历史 PR 合并编号生成失败");
  99. merged.Id = YitIdHelper.NextId();
  100. await InsertMergedPurchaseRequestAsync(merged);
  101. foreach (var old in rows)
  102. {
  103. await _db.Ado.ExecuteCommandAsync(
  104. """
  105. UPDATE srm_pr_main
  106. SET state = 0,
  107. refer_pr_billno = @NewBillNo,
  108. update_by_name = @User,
  109. update_time = @Now
  110. WHERE Id = @Id AND tenant_id = @TenantId
  111. """,
  112. new SugarParameter("@NewBillNo", merged.PrBillNo),
  113. new SugarParameter("@User", account),
  114. new SugarParameter("@Now", now),
  115. new SugarParameter("@Id", old.Id),
  116. new SugarParameter("@TenantId", tenantId));
  117. await _db.Ado.ExecuteCommandAsync(
  118. """
  119. UPDATE srm_po_occupy
  120. SET polist_id = @NewPrId,
  121. update_by_name = @User,
  122. update_time = @Now
  123. WHERE tenant_id = @TenantId AND polist_id = @OldPrId
  124. """,
  125. new SugarParameter("@NewPrId", merged.Id),
  126. new SugarParameter("@User", account),
  127. new SugarParameter("@Now", now),
  128. new SugarParameter("@TenantId", tenantId),
  129. new SugarParameter("@OldPrId", old.Id));
  130. }
  131. createdCount++;
  132. closedCount += rows.Count;
  133. reducedCount += rows.Count - 1;
  134. }
  135. return new PurchaseRequestHistoricalMergeResult
  136. {
  137. PendingCount = pending.Count,
  138. MergedGroupCount = createdCount,
  139. ClosedPrCount = closedCount,
  140. CreatedPrCount = createdCount,
  141. ReducedCount = reducedCount
  142. };
  143. }
  144. private static PurchaseRequestMergeResult MergeInMemory(List<PurchaseRequestMain> requests)
  145. {
  146. if (requests.Count <= 1)
  147. {
  148. return new PurchaseRequestMergeResult
  149. {
  150. Requests = requests,
  151. OriginalCount = requests.Count,
  152. MergedCount = requests.Count
  153. };
  154. }
  155. var merged = requests
  156. .GroupBy(x => new
  157. {
  158. x.TenantId,
  159. x.CompanyId,
  160. x.FactoryId,
  161. x.IcitemId,
  162. x.PrPurchaseId,
  163. x.IsRequireGoods,
  164. SupplierType = x.SupplierType ?? string.Empty,
  165. WeekStart = GetWeekStart(x.PrSsendDate)
  166. })
  167. .Select(group =>
  168. {
  169. var rows = group.OrderBy(x => x.PrSsendDate).ThenBy(x => x.PrSarriveDate).ThenBy(x => x.Id).ToList();
  170. return BuildMergedRow(rows, rows.First().CreateByName ?? "system", DateTime.Now);
  171. })
  172. .ToList();
  173. return new PurchaseRequestMergeResult
  174. {
  175. Requests = merged,
  176. OriginalCount = requests.Count,
  177. MergedCount = merged.Count
  178. };
  179. }
  180. private static PurchaseRequestMain BuildMergedRow(List<PurchaseRequestMain> rows, string account, DateTime now)
  181. {
  182. var first = rows.First();
  183. return new PurchaseRequestMain
  184. {
  185. Id = first.Id,
  186. PrPurchaseId = first.PrPurchaseId,
  187. PrPurchaseNumber = first.PrPurchaseNumber,
  188. PrPurchaseName = first.PrPurchaseName,
  189. PrPurchaser = first.PrPurchaser,
  190. PrPurchaserNum = first.PrPurchaserNum,
  191. PrRqty = rows.Sum(x => x.PrRqty ?? 0),
  192. PrAqty = rows.Sum(x => x.PrAqty ?? 0),
  193. PrSqty = rows.Sum(x => x.PrSqty ?? 0),
  194. IcitemId = first.IcitemId,
  195. IcitemName = first.IcitemName,
  196. PrSsendDate = rows.Min(x => x.PrSsendDate),
  197. PrSarriveDate = rows.Min(x => x.PrSarriveDate),
  198. PrUnit = first.PrUnit,
  199. State = 1,
  200. PrType = first.PrType ?? 3,
  201. CurrencyType = first.CurrencyType,
  202. CreateByName = account,
  203. CreateTime = now,
  204. UpdateByName = account,
  205. UpdateTime = now,
  206. TenantId = first.TenantId,
  207. FactoryId = first.FactoryId,
  208. OrgId = first.OrgId,
  209. CompanyId = first.CompanyId,
  210. IsDeleted = false,
  211. IsRequireGoods = first.IsRequireGoods,
  212. SupplierType = first.SupplierType
  213. };
  214. }
  215. private async Task InsertMergedPurchaseRequestAsync(PurchaseRequestMain pr)
  216. {
  217. await _db.Ado.ExecuteCommandAsync(
  218. """
  219. INSERT INTO srm_pr_main
  220. (Id,pr_billno,pr_purchaseid,pr_purchasenumber,pr_purchasename,pr_purchaser,pr_purchaser_num,
  221. pr_rqty,pr_aqty,pr_sqty,icitem_id,icitem_name,pr_ssend_date,pr_sarrive_date,pr_unit,state,pr_type,currencytype,
  222. create_by_name,create_time,update_by_name,update_time,tenant_id,factory_id,org_id,IsDeleted,company_id,IsRequireGoods,supplier_type)
  223. VALUES
  224. (@Id,@PrBillNo,@PrPurchaseId,@PrPurchaseNumber,@PrPurchaseName,@PrPurchaser,@PrPurchaserNum,
  225. @PrRqty,@PrAqty,@PrSqty,@IcitemId,@IcitemName,@PrSsendDate,@PrSarriveDate,@PrUnit,@State,@PrType,@CurrencyType,
  226. @CreateByName,@CreateTime,@UpdateByName,@UpdateTime,@TenantId,@FactoryId,@OrgId,0,@CompanyId,@IsRequireGoods,@SupplierType)
  227. """,
  228. new SugarParameter("@Id", pr.Id),
  229. new SugarParameter("@PrBillNo", pr.PrBillNo),
  230. new SugarParameter("@PrPurchaseId", pr.PrPurchaseId),
  231. new SugarParameter("@PrPurchaseNumber", pr.PrPurchaseNumber),
  232. new SugarParameter("@PrPurchaseName", pr.PrPurchaseName),
  233. new SugarParameter("@PrPurchaser", pr.PrPurchaser),
  234. new SugarParameter("@PrPurchaserNum", pr.PrPurchaserNum),
  235. new SugarParameter("@PrRqty", pr.PrRqty),
  236. new SugarParameter("@PrAqty", pr.PrAqty),
  237. new SugarParameter("@PrSqty", pr.PrSqty),
  238. new SugarParameter("@IcitemId", pr.IcitemId),
  239. new SugarParameter("@IcitemName", pr.IcitemName),
  240. new SugarParameter("@PrSsendDate", pr.PrSsendDate),
  241. new SugarParameter("@PrSarriveDate", pr.PrSarriveDate),
  242. new SugarParameter("@PrUnit", pr.PrUnit),
  243. new SugarParameter("@State", pr.State ?? 1),
  244. new SugarParameter("@PrType", pr.PrType ?? 3),
  245. new SugarParameter("@CurrencyType", pr.CurrencyType),
  246. new SugarParameter("@CreateByName", pr.CreateByName),
  247. new SugarParameter("@CreateTime", pr.CreateTime),
  248. new SugarParameter("@UpdateByName", pr.UpdateByName),
  249. new SugarParameter("@UpdateTime", pr.UpdateTime),
  250. new SugarParameter("@TenantId", pr.TenantId),
  251. new SugarParameter("@FactoryId", pr.FactoryId),
  252. new SugarParameter("@OrgId", pr.OrgId),
  253. new SugarParameter("@CompanyId", pr.CompanyId ?? 1000),
  254. new SugarParameter("@IsRequireGoods", pr.IsRequireGoods),
  255. new SugarParameter("@SupplierType", pr.SupplierType));
  256. }
  257. private static DateTime? GetWeekStart(DateTime? value)
  258. {
  259. if (!value.HasValue) return null;
  260. var date = value.Value.Date;
  261. var diff = ((int)date.DayOfWeek + 6) % 7;
  262. return date.AddDays(-diff);
  263. }
  264. }
  265. public sealed class PurchaseRequestMergeResult
  266. {
  267. public List<PurchaseRequestMain> Requests { get; set; } = new();
  268. public int OriginalCount { get; set; }
  269. public int MergedCount { get; set; }
  270. public int ReducedCount => Math.Max(OriginalCount - MergedCount, 0);
  271. }
  272. public sealed class PurchaseRequestHistoricalMergeResult
  273. {
  274. public int PendingCount { get; set; }
  275. public int MergedGroupCount { get; set; }
  276. public int ClosedPrCount { get; set; }
  277. public int CreatedPrCount { get; set; }
  278. public int ReducedCount { get; set; }
  279. }