using Admin.NET.Plugin.AiDOP.Dto.S0.Manufacturing; using Admin.NET.Plugin.AiDOP.Dto.S0.Sales; using Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing; using Admin.NET.Plugin.AiDOP.Entity.S0.Sales; using Admin.NET.Plugin.AiDOP.Infrastructure; namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Manufacturing; [ApiController] [Route("api/s0/manufacturing/boms")] [AllowAnonymous] [NonUnify] public class AdoS0MfgBomsController : ControllerBase { private readonly SqlSugarRepository _bomRep; private readonly SqlSugarRepository _itemRep; private readonly SqlSugarRepository _materialRep; public AdoS0MfgBomsController( SqlSugarRepository bomRep, SqlSugarRepository itemRep, SqlSugarRepository materialRep) { _bomRep = bomRep; _itemRep = itemRep; _materialRep = materialRep; } [HttpGet] public async Task GetPagedAsync([FromQuery] AdoS0MfgBomQueryDto q) { var page = q.EffectivePage; var pageSize = q.PageSize; (page, pageSize) = PagingGuard.Normalize(page, pageSize); var query = _bomRep.AsQueryable() .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId!.Value) .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId!.Value) .WhereIF(!string.IsNullOrWhiteSpace(q.Keyword), x => x.Code.Contains(q.Keyword!) || x.Name.Contains(q.Keyword!)) .WhereIF(q.IsEnabled.HasValue, x => x.IsEnabled == q.IsEnabled!.Value); var total = await query.CountAsync(); var list = await query.OrderByDescending(x => x.CreatedAt).Skip((page - 1) * pageSize).Take(pageSize).ToListAsync(); return Ok(new { total, page, pageSize, list }); } [HttpGet("{id:long}")] public async Task GetDetailAsync(long id) { var header = await _bomRep.GetByIdAsync(id); if (header == null) return NotFound(); var items = await _itemRep.AsQueryable() .Where(x => x.BomId == id) .OrderBy(x => x.SortNo) .ToListAsync(); return Ok(new { header, items }); } [HttpPost] public async Task CreateAsync([FromBody] AdoS0MfgBomUpsertDto dto) { var err = AdoS0MfgBomRules.ValidateUpsert(dto); if (err != null) return BadRequest(new { message = err }); var fkErr = await ValidateMaterialRefsAsync(dto.CompanyRefId, dto.FactoryRefId, dto.MaterialId, dto.Items.Select(i => i.MaterialId)); if (fkErr != null) return BadRequest(new { message = fkErr }); var db = _bomRep.Context; await db.Ado.BeginTranAsync(); try { var bom = new AdoS0MfgBom { CompanyRefId = dto.CompanyRefId, FactoryRefId = dto.FactoryRefId, Code = dto.Code, Name = dto.Name, MaterialId = dto.MaterialId, BizVersion = dto.BizVersion, DocStatus = dto.DocStatus, Remark = dto.Remark, IsEnabled = dto.IsEnabled, CreatedAt = DateTime.Now }; bom = await _bomRep.AsInsertable(bom).ExecuteReturnEntityAsync(); foreach (var line in dto.Items) { var row = MapBomItem(bom.Id, dto.CompanyRefId, dto.FactoryRefId, line); await _itemRep.AsInsertable(row).ExecuteCommandAsync(); } await db.Ado.CommitTranAsync(); return await GetDetailAsync(bom.Id); } catch { await db.Ado.RollbackTranAsync(); throw; } } [HttpPut("{id:long}")] public async Task UpdateAsync(long id, [FromBody] AdoS0MfgBomUpsertDto dto) { var err = AdoS0MfgBomRules.ValidateUpsert(dto); if (err != null) return BadRequest(new { message = err }); var header = await _bomRep.GetByIdAsync(id); if (header == null) return NotFound(); var fkErr = await ValidateMaterialRefsAsync(dto.CompanyRefId, dto.FactoryRefId, dto.MaterialId, dto.Items.Select(i => i.MaterialId)); if (fkErr != null) return BadRequest(new { message = fkErr }); var db = _bomRep.Context; await db.Ado.BeginTranAsync(); try { header.CompanyRefId = dto.CompanyRefId; header.FactoryRefId = dto.FactoryRefId; header.Code = dto.Code; header.Name = dto.Name; header.MaterialId = dto.MaterialId; header.BizVersion = dto.BizVersion; header.DocStatus = dto.DocStatus; header.Remark = dto.Remark; header.IsEnabled = dto.IsEnabled; header.UpdatedAt = DateTime.Now; await _bomRep.AsUpdateable(header).ExecuteCommandAsync(); await _itemRep.AsDeleteable().Where(x => x.BomId == id).ExecuteCommandAsync(); foreach (var line in dto.Items) { var row = MapBomItem(id, dto.CompanyRefId, dto.FactoryRefId, line); await _itemRep.AsInsertable(row).ExecuteCommandAsync(); } await db.Ado.CommitTranAsync(); return await GetDetailAsync(id); } catch { await db.Ado.RollbackTranAsync(); throw; } } [HttpPatch("{id:long}/toggle-enabled")] public async Task ToggleEnabledAsync(long id, [FromBody] AdoS0ToggleEnabledDto dto) { var entity = await _bomRep.GetByIdAsync(id); if (entity == null) return NotFound(); entity.IsEnabled = dto.IsEnabled; entity.UpdatedAt = DateTime.Now; await _bomRep.AsUpdateable(entity).ExecuteCommandAsync(); return Ok(entity); } [HttpDelete("{id:long}")] public async Task DeleteAsync(long id) { var header = await _bomRep.GetByIdAsync(id); if (header == null) return NotFound(); var db = _bomRep.Context; await db.Ado.BeginTranAsync(); try { await _itemRep.AsDeleteable().Where(x => x.BomId == id).ExecuteCommandAsync(); await _bomRep.DeleteAsync(header); await db.Ado.CommitTranAsync(); return Ok(new { message = "删除成功" }); } catch { await db.Ado.RollbackTranAsync(); throw; } } private static AdoS0MfgBomItem MapBomItem(long bomId, long companyRefId, long factoryRefId, AdoS0MfgBomItemUpsertDto line) { return new AdoS0MfgBomItem { CompanyRefId = companyRefId, FactoryRefId = factoryRefId, BomId = bomId, MaterialId = line.MaterialId, Qty = line.Qty, QtyNumerator = line.QtyNumerator, QtyDenominator = line.QtyDenominator, LossRate = line.LossRate, FixedLossQty = line.FixedLossQty, OperationId = line.OperationId, IsPhantom = line.IsPhantom, Unit = line.Unit, SortNo = line.SortNo, Remark = line.Remark, IsEnabled = line.IsEnabled, CreatedAt = DateTime.Now }; } private async Task ValidateMaterialRefsAsync(long companyRefId, long factoryRefId, long? headerMaterialId, IEnumerable itemMaterialIds) { var ids = new HashSet(); if (headerMaterialId.HasValue) ids.Add(headerMaterialId.Value); foreach (var mid in itemMaterialIds) ids.Add(mid); if (ids.Count == 0) return null; var count = await _materialRep.AsQueryable() .Where(m => ids.Contains(m.Id) && m.CompanyRefId == companyRefId && m.FactoryRefId == factoryRefId) .CountAsync(); return count == ids.Count ? null : "存在无效的物料主数据引用(公司/工厂或 Id 不匹配)"; } }