AdoS0MaterialsController.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. using Admin.NET.Plugin.AiDOP.Dto.S0.Sales;
  2. using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
  3. using Admin.NET.Plugin.AiDOP.Infrastructure;
  4. namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Sales;
  5. /// <summary>
  6. /// S0 物料主数据(ItemMaster 语义)
  7. /// </summary>
  8. [ApiController]
  9. [Route("api/s0/sales/materials")]
  10. [NonUnify]
  11. public class AdoS0MaterialsController : ControllerBase
  12. {
  13. private readonly SqlSugarRepository<AdoS0ItemMaster> _rep;
  14. private readonly AdoS0ReferenceChecker _refChecker;
  15. public AdoS0MaterialsController(SqlSugarRepository<AdoS0ItemMaster> rep, AdoS0ReferenceChecker refChecker)
  16. {
  17. _rep = rep;
  18. _refChecker = refChecker;
  19. }
  20. [HttpGet]
  21. public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0ItemMasterQueryDto q)
  22. {
  23. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  24. (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize);
  25. var query = _rep.ScopedTo(tenantId)
  26. .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId.Value)
  27. .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId.Value)
  28. .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode)
  29. .WhereIF(!string.IsNullOrWhiteSpace(q.ItemNum), x => x.ItemNum.Contains(q.ItemNum!))
  30. .WhereIF(!string.IsNullOrWhiteSpace(q.Descr), x => x.Descr.Contains(q.Descr!))
  31. .WhereIF(!string.IsNullOrWhiteSpace(q.Descr1), x => x.Descr1 != null && x.Descr1.Contains(q.Descr1!))
  32. .WhereIF(!string.IsNullOrWhiteSpace(q.ItemType), x => x.ItemType == q.ItemType)
  33. .WhereIF(!string.IsNullOrWhiteSpace(q.PurMfg), x => x.PurMfg == q.PurMfg)
  34. .WhereIF(!string.IsNullOrWhiteSpace(q.Location), x => x.Location != null && x.Location.Contains(q.Location!))
  35. .WhereIF(!string.IsNullOrWhiteSpace(q.Status), x => x.Status == q.Status)
  36. .WhereIF(q.IsActive.HasValue, x => x.IsActive == q.IsActive.Value)
  37. .WhereIF(q.IsConfirm.HasValue, x => x.IsConfirm == q.IsConfirm.Value)
  38. .WhereIF(
  39. !string.IsNullOrWhiteSpace(q.Keyword),
  40. x => x.ItemNum.Contains(q.Keyword!) || x.Descr.Contains(q.Keyword!));
  41. var total = await query.CountAsync();
  42. var list = await query
  43. .OrderByDescending(x => x.CreateTime)
  44. .Skip((q.Page - 1) * q.PageSize)
  45. .Take(q.PageSize)
  46. .ToListAsync();
  47. foreach (var x in list)
  48. x.LocationDescr = FormatLocationDescr(x.Location, null);
  49. return Ok(new { total, page = q.Page, pageSize = q.PageSize, list });
  50. }
  51. [HttpGet("{id:long}")]
  52. public async Task<IActionResult> GetAsync(long id)
  53. {
  54. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  55. var item = await _rep.ByIdScopedAsync(id, tenantId);
  56. if (item == null) return NotFound();
  57. item.LocationDescr = FormatLocationDescr(item.Location, null);
  58. return Ok(item);
  59. }
  60. [HttpPost]
  61. public async Task<IActionResult> CreateAsync([FromBody] AdoS0ItemMasterUpsertDto dto)
  62. {
  63. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  64. if (await _rep.IsAnyAsync(x =>
  65. x.TenantId == tenantId &&
  66. x.CompanyRefId == dto.CompanyRefId &&
  67. x.FactoryRefId == dto.FactoryRefId &&
  68. x.ItemNum == dto.ItemNum))
  69. {
  70. return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.MaterialCodeExists, "物料编码已存在");
  71. }
  72. var now = DateTime.Now;
  73. var entity = MapDtoToNewEntity(dto, now);
  74. // 租户只由后端可信上下文赋值,忽略 DTO 中任何 tenant 相关入参。
  75. entity.TenantId = tenantId;
  76. entity.Status = ResolveStatus(dto);
  77. await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
  78. return Ok(entity);
  79. }
  80. [HttpPut("{id:long}")]
  81. public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0ItemMasterUpsertDto dto)
  82. {
  83. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  84. var entity = await _rep.ByIdScopedAsync(id, tenantId);
  85. if (entity == null) return NotFound();
  86. if (await _rep.IsAnyAsync(x =>
  87. x.TenantId == tenantId &&
  88. x.Id != id &&
  89. x.CompanyRefId == dto.CompanyRefId &&
  90. x.FactoryRefId == dto.FactoryRefId &&
  91. x.ItemNum == dto.ItemNum))
  92. {
  93. return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.MaterialCodeExists, "物料编码已存在");
  94. }
  95. ApplyDtoToEntity(entity, dto);
  96. entity.Status = ResolveStatus(dto);
  97. entity.UpdateTime = DateTime.Now;
  98. await _rep.AsUpdateable(entity).ExecuteCommandAsync();
  99. return Ok(entity);
  100. }
  101. [HttpPatch("{id:long}/toggle-enabled")]
  102. public async Task<IActionResult> ToggleActiveAsync(long id, [FromBody] AdoS0ItemMasterToggleActiveDto dto)
  103. {
  104. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  105. var entity = await _rep.ByIdScopedAsync(id, tenantId);
  106. if (entity == null) return NotFound();
  107. entity.IsActive = dto.IsActive;
  108. entity.Status = AdoS0SalesRules.ForbidStatusFromIsEnabled(dto.IsActive);
  109. entity.UpdateTime = DateTime.Now;
  110. await _rep.AsUpdateable(entity).ExecuteCommandAsync();
  111. return Ok(entity);
  112. }
  113. [HttpDelete("{id:long}")]
  114. public async Task<IActionResult> DeleteAsync(long id)
  115. {
  116. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  117. var item = await _rep.ByIdScopedAsync(id, tenantId);
  118. if (item == null) return NotFound();
  119. var refInfo = await _refChecker.MaterialReferencesAsync(tenantId, item.ItemNum);
  120. if (refInfo is { } r)
  121. return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DeleteBlocked,
  122. $"存在 {r.Count} 条 {r.Table} 引用该物料,无法删除");
  123. await _rep.DeleteAsync(item);
  124. return Ok(new { message = "删除成功" });
  125. }
  126. private static string ResolveStatus(AdoS0ItemMasterUpsertDto dto) =>
  127. string.IsNullOrWhiteSpace(dto.Status)
  128. ? AdoS0SalesRules.ForbidStatusFromIsEnabled(dto.IsActive)
  129. : dto.Status!;
  130. private static string FormatLocationDescr(string? location, string? locationMasterDescr)
  131. {
  132. var a = location?.Trim();
  133. var b = locationMasterDescr?.Trim();
  134. if (string.IsNullOrEmpty(a)) return b ?? string.Empty;
  135. if (string.IsNullOrEmpty(b)) return a;
  136. return $"{a} {b}".Trim();
  137. }
  138. private static AdoS0ItemMaster MapDtoToNewEntity(AdoS0ItemMasterUpsertDto dto, DateTime now) => new()
  139. {
  140. CompanyRefId = dto.CompanyRefId,
  141. FactoryRefId = dto.FactoryRefId,
  142. DomainCode = dto.DomainCode,
  143. ItemNum = dto.ItemNum,
  144. Descr = dto.Descr,
  145. Descr1 = dto.Descr1,
  146. PkgCode = dto.PkgCode,
  147. UM = dto.UM,
  148. PurMfg = dto.PurMfg,
  149. Drawing = dto.Drawing,
  150. ItemType = dto.ItemType,
  151. Location = dto.Location,
  152. DefaultShelf = dto.DefaultShelf,
  153. KeyItem = dto.KeyItem,
  154. NetWeight = dto.NetWeight,
  155. NetWeightUM = dto.NetWeightUM,
  156. Inspect = dto.Inspect,
  157. PurLT = dto.PurLT.GetValueOrDefault(),
  158. InsLT = dto.InsLT.GetValueOrDefault(),
  159. MfgLT = dto.MfgLT,
  160. Length = dto.Length,
  161. Size = dto.Size,
  162. SizeUM = dto.SizeUM,
  163. IssuePolicy = dto.IssuePolicy,
  164. MfgMttr = dto.MfgMttr,
  165. LocationType = dto.LocationType,
  166. CommodityCode = dto.CommodityCode,
  167. Rev = dto.Rev,
  168. MaxOrd = dto.MaxOrd.GetValueOrDefault(),
  169. MinOrd = dto.MinOrd.GetValueOrDefault(),
  170. OrdMult = dto.OrdMult.GetValueOrDefault(),
  171. MinOrdSales = dto.MinOrdSales.GetValueOrDefault(),
  172. AutoLotNums = dto.AutoLotNums,
  173. Install = dto.Install,
  174. SafetyStk = dto.SafetyStk.GetValueOrDefault(),
  175. DaysBetweenPM = dto.DaysBetweenPM,
  176. ExpireAlarmDay = dto.ExpireAlarmDay,
  177. StockTurnOver = dto.StockTurnOver.GetValueOrDefault(),
  178. LotSerialControl = dto.LotSerialControl,
  179. AllocateSingleLot = dto.AllocateSingleLot,
  180. Planner = dto.Planner,
  181. TraceDetail = dto.TraceDetail,
  182. IsMainMas = dto.IsMainMas,
  183. Remark = dto.Remark,
  184. EMTType = dto.EMTType,
  185. OwnerApplication = dto.OwnerApplication,
  186. BomVersion = dto.BomVersion,
  187. EnableBomVersion = dto.EnableBomVersion,
  188. BOMDesign = dto.BOMDesign,
  189. RoutingDes = dto.RoutingDes,
  190. CustSupplied = dto.CustSupplied,
  191. DrawingDesign = dto.DrawingDesign,
  192. DesignOwner = dto.DesignOwner,
  193. IsActive = dto.IsActive,
  194. IsConfirm = dto.IsConfirm,
  195. MRPRequired = dto.MRPRequired,
  196. CreateUser = dto.CreateUser,
  197. CreateTime = now,
  198. UpdateUser = dto.UpdateUser,
  199. UpdateTime = null
  200. };
  201. private static void ApplyDtoToEntity(AdoS0ItemMaster entity, AdoS0ItemMasterUpsertDto dto)
  202. {
  203. entity.CompanyRefId = dto.CompanyRefId;
  204. entity.FactoryRefId = dto.FactoryRefId;
  205. entity.DomainCode = dto.DomainCode;
  206. entity.ItemNum = dto.ItemNum;
  207. entity.Descr = dto.Descr;
  208. entity.Descr1 = dto.Descr1;
  209. entity.PkgCode = dto.PkgCode;
  210. entity.UM = dto.UM;
  211. entity.PurMfg = dto.PurMfg;
  212. entity.Drawing = dto.Drawing;
  213. entity.ItemType = dto.ItemType;
  214. entity.Location = dto.Location;
  215. entity.DefaultShelf = dto.DefaultShelf;
  216. entity.KeyItem = dto.KeyItem;
  217. entity.NetWeight = dto.NetWeight;
  218. entity.NetWeightUM = dto.NetWeightUM;
  219. entity.Inspect = dto.Inspect;
  220. entity.PurLT = dto.PurLT.GetValueOrDefault();
  221. entity.InsLT = dto.InsLT.GetValueOrDefault();
  222. entity.MfgLT = dto.MfgLT;
  223. entity.Length = dto.Length;
  224. entity.Size = dto.Size;
  225. entity.SizeUM = dto.SizeUM;
  226. entity.IssuePolicy = dto.IssuePolicy;
  227. entity.MfgMttr = dto.MfgMttr;
  228. entity.LocationType = dto.LocationType;
  229. entity.CommodityCode = dto.CommodityCode;
  230. entity.Rev = dto.Rev;
  231. entity.MaxOrd = dto.MaxOrd.GetValueOrDefault();
  232. entity.MinOrd = dto.MinOrd.GetValueOrDefault();
  233. entity.OrdMult = dto.OrdMult.GetValueOrDefault();
  234. entity.MinOrdSales = dto.MinOrdSales.GetValueOrDefault();
  235. entity.AutoLotNums = dto.AutoLotNums;
  236. entity.Install = dto.Install;
  237. entity.SafetyStk = dto.SafetyStk.GetValueOrDefault();
  238. entity.DaysBetweenPM = dto.DaysBetweenPM;
  239. entity.ExpireAlarmDay = dto.ExpireAlarmDay;
  240. entity.StockTurnOver = dto.StockTurnOver.GetValueOrDefault();
  241. entity.LotSerialControl = dto.LotSerialControl;
  242. entity.AllocateSingleLot = dto.AllocateSingleLot;
  243. entity.Planner = dto.Planner;
  244. entity.TraceDetail = dto.TraceDetail;
  245. entity.IsMainMas = dto.IsMainMas;
  246. entity.Remark = dto.Remark;
  247. entity.EMTType = dto.EMTType;
  248. entity.OwnerApplication = dto.OwnerApplication;
  249. entity.BomVersion = dto.BomVersion;
  250. entity.EnableBomVersion = dto.EnableBomVersion;
  251. entity.BOMDesign = dto.BOMDesign;
  252. entity.RoutingDes = dto.RoutingDes;
  253. entity.CustSupplied = dto.CustSupplied;
  254. entity.DrawingDesign = dto.DrawingDesign;
  255. entity.DesignOwner = dto.DesignOwner;
  256. entity.IsActive = dto.IsActive;
  257. entity.IsConfirm = dto.IsConfirm;
  258. entity.MRPRequired = dto.MRPRequired;
  259. entity.UpdateUser = dto.UpdateUser;
  260. }
  261. }