AdoS0SrmPurchasesController.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. using Admin.NET.Plugin.AiDOP.Dto.S0.Supply;
  2. using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
  3. using Admin.NET.Plugin.AiDOP.Entity.S0.Supply;
  4. using Admin.NET.Plugin.AiDOP.Infrastructure;
  5. namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Supply;
  6. /// <summary>
  7. /// S0 货源清单(srm_purchase 语义,左联物料主数据展示)
  8. /// </summary>
  9. [ApiController]
  10. [Route("api/s0/supply/srm-purchases")]
  11. [AllowAnonymous]
  12. [NonUnify]
  13. public class AdoS0SrmPurchasesController : ControllerBase
  14. {
  15. private readonly SqlSugarRepository<AdoS0SrmPurchase> _rep;
  16. private readonly SqlSugarRepository<AdoS0ItemMaster> _itemRep;
  17. private readonly SqlSugarRepository<AdoS0SuppMaster> _suppRep;
  18. public AdoS0SrmPurchasesController(
  19. SqlSugarRepository<AdoS0SrmPurchase> rep,
  20. SqlSugarRepository<AdoS0ItemMaster> itemRep,
  21. SqlSugarRepository<AdoS0SuppMaster> suppRep)
  22. {
  23. _rep = rep;
  24. _itemRep = itemRep;
  25. _suppRep = suppRep;
  26. }
  27. [HttpGet]
  28. public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0SrmPurchaseQueryDto q)
  29. {
  30. (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize);
  31. var baseQuery = _rep.AsQueryable()
  32. .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId.Value)
  33. .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId.Value)
  34. .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode)
  35. .WhereIF(!string.IsNullOrWhiteSpace(q.SupplierType), x => x.SupplierType != null && x.SupplierType.Contains(q.SupplierType!))
  36. // 启用筛选按启用/停用编码集合等值匹配(IN),废弃旧 Contains("true"/"false") 无法命中 DB“是”的错误口径。
  37. .WhereIF(q.IsActive == true, AdoS0SrmPurchaseIsActive.EnabledSql("is_active"))
  38. .WhereIF(q.IsActive == false, AdoS0SrmPurchaseIsActive.DisabledSql("is_active"))
  39. .WhereIF(!string.IsNullOrWhiteSpace(q.SupplierName), x => x.SupplierName != null && x.SupplierName.Contains(q.SupplierName!))
  40. .WhereIF(!string.IsNullOrWhiteSpace(q.SupplierNumber), x => x.SupplierNumber != null && x.SupplierNumber.Contains(q.SupplierNumber!))
  41. .WhereIF(q.CurrencyType.HasValue, x => x.CurrencyType == q.CurrencyType.Value);
  42. if (!string.IsNullOrWhiteSpace(q.Keyword))
  43. {
  44. var kw = q.Keyword!;
  45. var itemIds = await _itemRep.AsQueryable()
  46. .Where(it =>
  47. it.ItemNum.Contains(kw) ||
  48. it.Descr.Contains(kw) ||
  49. (it.Descr1 != null && it.Descr1.Contains(kw)))
  50. .Select(it => it.Id)
  51. .ToListAsync();
  52. baseQuery = baseQuery.Where(x =>
  53. (x.IcitemName != null && x.IcitemName.Contains(kw)) ||
  54. (x.SupplierName != null && x.SupplierName.Contains(kw)) ||
  55. (x.SupplierNumber != null && x.SupplierNumber.Contains(kw)) ||
  56. (itemIds.Count > 0 && itemIds.Contains(x.IcitemId)));
  57. }
  58. var total = await baseQuery.CountAsync();
  59. var list = await baseQuery
  60. .OrderByDescending(x => x.CreateTime)
  61. .Skip((q.Page - 1) * q.PageSize)
  62. .Take(q.PageSize)
  63. .ToListAsync();
  64. await ApplyItemAndSupplierDisplayAsync(list);
  65. var dtoList = list.Select(AdoS0SrmPurchaseDto.FromEntity).ToList();
  66. return Ok(new { total, page = q.Page, pageSize = q.PageSize, list = dtoList });
  67. }
  68. [HttpGet("{id:long}")]
  69. public async Task<IActionResult> GetAsync(long id)
  70. {
  71. var item = await _rep.GetByIdAsync(id);
  72. if (item == null) return NotFound();
  73. await ApplyItemAndSupplierDisplayAsync(new List<AdoS0SrmPurchase> { item });
  74. return Ok(AdoS0SrmPurchaseDto.FromEntity(item));
  75. }
  76. [HttpPost]
  77. public async Task<IActionResult> CreateAsync([FromBody] AdoS0SrmPurchaseUpsertDto dto)
  78. {
  79. var dateErr = ValidateDateRange(dto.EffectiveDate, dto.ExpiringDate);
  80. if (dateErr != null) return AdoS0ApiErrors.InvalidRequest(dateErr);
  81. var refErr = await ValidateReferencesAsync(dto.IcitemId, dto.SupplierId);
  82. if (refErr != null) return refErr;
  83. var now = DateTime.Now;
  84. var entity = MapDtoToEntity(dto, new AdoS0SrmPurchase(), now, isNew: true);
  85. await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
  86. await ApplyItemAndSupplierDisplayAsync(new List<AdoS0SrmPurchase> { entity });
  87. return Ok(AdoS0SrmPurchaseDto.FromEntity(entity));
  88. }
  89. [HttpPut("{id:long}")]
  90. public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0SrmPurchaseUpsertDto dto)
  91. {
  92. var entity = await _rep.GetByIdAsync(id);
  93. if (entity == null) return NotFound();
  94. var dateErr = ValidateDateRange(dto.EffectiveDate, dto.ExpiringDate);
  95. if (dateErr != null) return AdoS0ApiErrors.InvalidRequest(dateErr);
  96. var refErr = await ValidateReferencesAsync(dto.IcitemId, dto.SupplierId);
  97. if (refErr != null) return refErr;
  98. MapDtoToEntity(dto, entity, DateTime.Now, isNew: false);
  99. await _rep.AsUpdateable(entity).ExecuteCommandAsync();
  100. await ApplyItemAndSupplierDisplayAsync(new List<AdoS0SrmPurchase> { entity });
  101. return Ok(AdoS0SrmPurchaseDto.FromEntity(entity));
  102. }
  103. [HttpDelete("{id:long}")]
  104. public async Task<IActionResult> DeleteAsync(long id)
  105. {
  106. var item = await _rep.GetByIdAsync(id);
  107. if (item == null) return NotFound();
  108. await _rep.DeleteAsync(item);
  109. return Ok(new { message = "删除成功" });
  110. }
  111. private static string? ValidateDateRange(DateTime? effective, DateTime? expiring)
  112. {
  113. if (effective.HasValue && expiring.HasValue && effective.Value.Date > expiring.Value.Date)
  114. return "生效日期不能晚于失效日期";
  115. return null;
  116. }
  117. private async Task<IActionResult?> ValidateReferencesAsync(long icitemId, long supplierId)
  118. {
  119. if (!await _itemRep.IsAnyAsync(x => x.Id == icitemId))
  120. return AdoS0ApiErrors.InvalidReference(AdoS0ErrorCodes.MaterialReferenceInvalid, "物料主数据引用无效");
  121. if (!await _suppRep.IsAnyAsync(x => x.Id == supplierId))
  122. return AdoS0ApiErrors.InvalidReference(AdoS0ErrorCodes.InvalidReference, "供应商主数据引用无效");
  123. return null;
  124. }
  125. private static AdoS0SrmPurchase MapDtoToEntity(AdoS0SrmPurchaseUpsertDto dto, AdoS0SrmPurchase entity, DateTime now, bool isNew)
  126. {
  127. entity.CompanyRefId = dto.CompanyRefId;
  128. entity.FactoryRefId = dto.FactoryRefId;
  129. entity.DomainCode = dto.DomainCode;
  130. entity.IcitemId = dto.IcitemId;
  131. entity.IcitemName = dto.IcitemName;
  132. entity.SupplierType = dto.SupplierType?.Trim();
  133. // bool? → “是”/“否”/null 显式编码,不依赖 JSON bool 自动转 string。
  134. entity.IsActive = AdoS0SrmPurchaseIsActive.Encode(dto.IsActive);
  135. entity.SupplierId = dto.SupplierId;
  136. entity.SupplierName = dto.SupplierName;
  137. entity.SupplierNumber = dto.SupplierNumber;
  138. entity.OrderPrice = dto.OrderPrice;
  139. entity.CurrencyType = dto.CurrencyType;
  140. entity.Taxrate = dto.Taxrate;
  141. entity.Tariff = dto.Tariff;
  142. entity.Freight = dto.Freight;
  143. entity.PriceTerms = dto.PriceTerms;
  144. entity.EffectiveDate = dto.EffectiveDate;
  145. entity.ExpiringDate = dto.ExpiringDate;
  146. entity.QuotaRate = dto.QuotaRate;
  147. entity.LeadTime = dto.LeadTime;
  148. entity.QtyMin = dto.QtyMin;
  149. entity.PackagingQty = dto.PackagingQty;
  150. entity.OrderRectorName = dto.OrderRectorName;
  151. entity.OrderRectorNum = dto.OrderRectorNum;
  152. entity.IsRequireGoods = dto.IsRequireGoods;
  153. if (isNew)
  154. {
  155. entity.CreateUser = dto.CreateUser;
  156. entity.CreateTime = now;
  157. entity.UpdateUser = dto.UpdateUser;
  158. entity.UpdateTime = null;
  159. }
  160. else
  161. {
  162. entity.UpdateUser = dto.UpdateUser;
  163. entity.UpdateTime = now;
  164. }
  165. return entity;
  166. }
  167. private async Task ApplyItemAndSupplierDisplayAsync(List<AdoS0SrmPurchase> rows)
  168. {
  169. if (rows.Count == 0) return;
  170. // 物料编码直接取 srm_purchase.number(历史迁移已回填,100% 有值)。
  171. // 旧平台 icitem_id 为雪花 ID,与本地 ItemMaster.RecID 不同域、匹配为 0,故辅助展示字段
  172. // (规格/单位/物料类型)改按 number == ItemMaster.ItemNum 关联补齐;未匹配行仅缺辅助字段。
  173. var numbers = rows.Select(x => x.Number)
  174. .Where(n => !string.IsNullOrWhiteSpace(n))
  175. .Distinct()
  176. .ToList();
  177. var items = numbers.Count == 0
  178. ? new List<AdoS0ItemMaster>()
  179. : await _itemRep.AsQueryable().Where(it => numbers.Contains(it.ItemNum)).ToListAsync();
  180. // MySQL 默认 ci 排序,用大小写不敏感字典对齐旧 SQL 的 number=ItemNum 关联语义。
  181. var itemByNum = new Dictionary<string, AdoS0ItemMaster>(StringComparer.OrdinalIgnoreCase);
  182. foreach (var it in items)
  183. {
  184. if (!string.IsNullOrWhiteSpace(it.ItemNum) && !itemByNum.ContainsKey(it.ItemNum))
  185. itemByNum[it.ItemNum] = it;
  186. }
  187. foreach (var sp in rows)
  188. {
  189. AdoS0ItemMaster? it = null;
  190. if (!string.IsNullOrWhiteSpace(sp.Number))
  191. itemByNum.TryGetValue(sp.Number, out it);
  192. sp.MaterialCode = sp.Number;
  193. sp.Model = it?.Descr1;
  194. sp.Unit = it?.UM;
  195. sp.ItemTypeLabel = string.IsNullOrWhiteSpace(it?.ItemType) ? "原材料" : it!.ItemType;
  196. var num = sp.MaterialCode ?? "";
  197. var name = sp.IcitemName ?? "";
  198. sp.Icitem = $"{num}{name}";
  199. var sname = sp.SupplierName ?? "";
  200. var snum = sp.SupplierNumber ?? "";
  201. sp.Supplier = $"{sname}{snum}";
  202. }
  203. }
  204. }