using Admin.NET.Plugin.AiDOP.ProcurementExecution.Dto; using Admin.NET.Plugin.AiDOP.ProcurementExecution.Entity; using Yitter.IdGenerator; namespace Admin.NET.Plugin.AiDOP.ProcurementExecution; /// /// S4 供应商发货单(列表 + 表单) /// [ApiDescriptionSettings(Order = 321, Description = "S4供应商发货单")] [Route("api/ProcurementExecution")] [AllowAnonymous] [NonUnify] public class SupplierShipmentService : IDynamicApiController, ITransient { private readonly ISqlSugarClient _db; private readonly SqlSugarRepository _masterRep; private readonly SqlSugarRepository _detailRep; private readonly UserManager _userManager; public SupplierShipmentService( ISqlSugarClient db, SqlSugarRepository masterRep, SqlSugarRepository detailRep, UserManager userManager) { _db = db; _masterRep = masterRep; _detailRep = detailRep; _userManager = userManager; } [DisplayName("供应商发货单列表")] [HttpGet("supplier-shipment/list")] public async Task GetList([FromQuery] SupplierShipmentListInput input) { var pars = new List(); var conditions = new List { "IFNULL(m.state, 1) <> 0" }; if (!string.IsNullOrWhiteSpace(input.JhshrqFrom)) { conditions.Add("m.jhshrq >= @jhshrqFrom"); pars.Add(new SugarParameter("@jhshrqFrom", input.JhshrqFrom.Trim())); } if (!string.IsNullOrWhiteSpace(input.Gysmc)) { conditions.Add("m.sh_purchase_name LIKE @gysmc"); pars.Add(new SugarParameter("@gysmc", $"%{input.Gysmc.Trim()}%")); } if (!string.IsNullOrWhiteSpace(input.Shddh)) { conditions.Add("m.shddh LIKE @shddh"); pars.Add(new SugarParameter("@shddh", $"%{input.Shddh.Trim()}%")); } if (!string.IsNullOrWhiteSpace(input.Wldh)) { conditions.Add("m.wldh LIKE @wldh"); pars.Add(new SugarParameter("@wldh", $"%{input.Wldh.Trim()}%")); } if (!string.IsNullOrWhiteSpace(input.PoBill)) { conditions.Add("m.po_bill LIKE @poBill"); pars.Add(new SugarParameter("@poBill", $"%{input.PoBill.Trim()}%")); } if (!string.IsNullOrWhiteSpace(input.ShMaterialCode)) { conditions.Add("m.sh_material_code LIKE @shMaterialCode"); pars.Add(new SugarParameter("@shMaterialCode", $"%{input.ShMaterialCode.Trim()}%")); } if (!string.IsNullOrWhiteSpace(input.Shzt)) { conditions.Add("m.shzt = @shzt"); pars.Add(new SugarParameter("@shzt", input.Shzt.Trim())); } if (!string.IsNullOrWhiteSpace(input.Shpc)) { conditions.Add("m.shpc LIKE @shpc"); pars.Add(new SugarParameter("@shpc", $"%{input.Shpc.Trim()}%")); } var where = $" WHERE {string.Join(" AND ", conditions)} "; var orderBy = BuildListOrderBy(input.SortField, input.SortOrder); var offset = (input.Page - 1) * input.PageSize; var wrapped = $"{BuildListSql()} {where}"; var total = await _db.Ado.GetIntAsync($"SELECT COUNT(*) FROM ({wrapped}) t", pars); var list = await _db.Ado.SqlQueryAsync( $"SELECT * FROM ({wrapped}) t {orderBy} LIMIT {input.PageSize} OFFSET {offset}", pars); return new { total, page = input.Page, pageSize = input.PageSize, list }; } [DisplayName("获取发货单详情")] [HttpGet("supplier-shipment/{id:long}")] public async Task GetDetail(long id) { var master = await _masterRep.GetFirstAsync(x => x.Id == id) ?? throw Oops.Oh("发货单不存在"); var details = await _detailRep.AsQueryable().Where(x => x.Glid == id.ToString()).OrderBy(x => x.Hh).ToListAsync(); return new { id = master.Id, shddh = master.Shddh, jhshrq = master.Jhshrq, wlsc = master.Wlsc, yjdhrq = master.Yjdhrq, shPurchaseName = master.ShPurchaseName, shPurchaseNum = master.ShPurchaseNum, wldh = master.Wldh, sfpc = master.Sfpc ?? 0, chbg = master.Chbg, pcsm = master.Pcsm, state = master.State ?? 1, details = details.Select(d => new { id = d.Id, hh = d.Hh, poBill = d.PoBill, poBillLine = d.PoBillLine, orderType = d.OrderType, shMaterialCode = d.ShMaterialCode, shMaterialName = d.ShMaterialName, th = d.Th, shDeliveryQuantity = d.ShDeliveryQuantity, bzsl = d.Bzsl, bqsl = d.Bqsl, shMaterialDw = d.ShMaterialDw, scrq = d.Scrq, scph = d.Scph, remarks = d.Remarks, djsl = d.Djsl, jybb = d.Jybb, jhdbh = d.Jhdbh }) }; } [DisplayName("发货单新增草稿")] [HttpGet("supplier-shipment/create-draft")] public async Task GetCreateDraft([FromQuery] string ids) { var pars = new List { new("@ids", ids) }; var rows = await _db.Ado.SqlQueryAsync( """ SELECT CAST(IFNULL(ds.id, p.RecID) AS CHAR(50)) AS sourceId, ds.suppliercode AS gysdm, ds.supplier AS gysmc, p.PurOrd AS poBill, p.Line AS poBillLine, p.Potype AS orderType, p.ItemNum AS shMaterialCode, im.Descr AS shMaterialName, IFNULL(im.Drawing, p.Drawing) AS th, IFNULL(ds.SchedQty, p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) AS shDeliveryQuantity, p.StdPackQty AS bzsl, p.UM AS shMaterialDw, (p.QtyOrded - p.RctQty - p.ReceiptQty + p.QtyReturned) AS djsl, ds.DSNum AS jhdbh, im.Descr1 AS shMaterialGgxh FROM PurOrdDetail p LEFT JOIN srm_polist_ds ds ON p.PurOrd = ds.ponumber AND p.Line = ds.poline LEFT JOIN ItemMaster im ON p.ItemNum = im.ItemNum WHERE FIND_IN_SET(CAST(IFNULL(ds.id, p.RecID) AS CHAR(50)), REPLACE(IFNULL(@ids, ''), ' ', '')) > 0 ORDER BY p.PurOrd, p.Line """, pars); if (rows.Count == 0) throw Oops.Oh("未找到可生成的交货明细"); return new { shddh = string.Empty, jhshrq = DateTime.Now.ToString("yyyy-MM-dd"), wlsc = string.Empty, yjdhrq = string.Empty, shPurchaseName = rows[0].Gysmc, shPurchaseNum = rows[0].Gysdm, wldh = string.Empty, sfpc = 0, chbg = string.Empty, pcsm = string.Empty, details = rows.Select((r, i) => new { id = (long?)null, hh = i + 1, poBill = r.PoBill, poBillLine = r.PoBillLine?.ToString(), orderType = r.OrderType, shMaterialCode = r.ShMaterialCode, shMaterialName = r.ShMaterialName, th = r.Th, shDeliveryQuantity = r.ShDeliveryQuantity, bzsl = r.Bzsl, bqsl = 0, shMaterialDw = r.ShMaterialDw, scrq = string.Empty, scph = string.Empty, remarks = string.Empty, djsl = r.Djsl, jybb = string.Empty, jhdbh = r.Jhdbh }) }; } [DisplayName("保存发货单")] [ApiDescriptionSettings(Name = "SaveSupplierShipment"), HttpPost("supplier-shipment/save")] public async Task Save([FromBody] SupplierShipmentSaveInput input) { var now = DateTime.Now; var userId = _userManager.UserId.ToString(); var userName = _userManager.Account ?? "system"; var shipDate = string.IsNullOrWhiteSpace(input.Jhshrq) ? now.ToString("yyyy-MM-dd") : input.Jhshrq!.Trim(); if (input.Id is null or 0) { var newId = YitIdHelper.NextId(); var entity = new ScmShd { Id = newId, Shddh = string.IsNullOrWhiteSpace(input.Shddh) ? $"SH{DateTime.Now:yyyyMMddHHmmss}" : input.Shddh!.Trim(), Jhshrq = shipDate, Wlsc = input.Wlsc, Yjdhrq = input.Yjdhrq, ShPurchaseName = input.ShPurchaseName, ShPurchaseNum = input.ShPurchaseNum, Wldh = input.Wldh, Sfpc = input.Sfpc ?? 0, Chbg = input.Chbg, Pcsm = input.Pcsm, State = 1, Shzt = "待收", Tjrid = userId, Tjrxm = userName, Tjrq = now.ToString("yyyy-MM-dd") }; await _masterRep.InsertAsync(entity); await SaveDetailsAsync(newId, input.Details); return new { id = newId, message = "新增成功" }; } var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id.Value) ?? throw Oops.Oh("发货单不存在"); master.Shddh = input.Shddh; master.Jhshrq = shipDate; master.Wlsc = input.Wlsc; master.Yjdhrq = input.Yjdhrq; master.ShPurchaseName = input.ShPurchaseName; master.ShPurchaseNum = input.ShPurchaseNum; master.Wldh = input.Wldh; master.Sfpc = input.Sfpc ?? 0; master.Chbg = input.Chbg; master.Pcsm = input.Pcsm; await _masterRep.UpdateAsync(master); await SaveDetailsAsync(input.Id.Value, input.Details); return new { id = input.Id, message = "编辑成功" }; } [DisplayName("删除发货单")] [ApiDescriptionSettings(Name = "DeleteSupplierShipment"), HttpPost("supplier-shipment/delete")] public async Task Delete([FromBody] SupplierShipmentDeleteInput input) { var master = await _masterRep.GetFirstAsync(x => x.Id == input.Id) ?? throw Oops.Oh("发货单不存在"); master.State = 0; await _masterRep.UpdateAsync(master); return new { message = "删除成功" }; } [DisplayName("生成标签(预留)")] [HttpPost("supplier-shipment/generate-label")] public Task GenerateLabel([FromBody] SupplierShipmentOperationInput input) => Task.FromResult(new { message = $"发货单{input.Id}:功能预留,暂未启用" }); [DisplayName("打印送货单(预留)")] [HttpPost("supplier-shipment/print-shipping-note")] public Task PrintShippingNote([FromBody] SupplierShipmentOperationInput input) => Task.FromResult(new { message = $"发货单{input.Id}:功能预留,暂未启用" }); [DisplayName("打印标签(预留)")] [HttpPost("supplier-shipment/print-label")] public Task PrintLabel([FromBody] SupplierShipmentOperationInput input) => Task.FromResult(new { message = $"发货单{input.Id}:功能预留,暂未启用" }); private async Task SaveDetailsAsync(long masterId, List inputDetails) { var dbDetails = await _detailRep.AsQueryable().Where(x => x.Glid == masterId.ToString()).ToListAsync(); var dbById = dbDetails.ToDictionary(x => x.Id); var inputIds = new HashSet(inputDetails.Where(d => d.Id is > 0).Select(d => d.Id!.Value)); for (var i = 0; i < inputDetails.Count; i++) { var d = inputDetails[i]; if (d.Id is > 0 && dbById.TryGetValue(d.Id.Value, out var existing)) { existing.Hh = d.Hh ?? (i + 1); existing.PoBill = d.PoBill; existing.PoBillLine = d.PoBillLine; existing.OrderType = d.OrderType; existing.ShMaterialCode = d.ShMaterialCode; existing.ShMaterialName = d.ShMaterialName; existing.Th = d.Th; existing.ShDeliveryQuantity = d.ShDeliveryQuantity; existing.Bzsl = d.Bzsl; existing.Bqsl = d.Bqsl; existing.ShMaterialDw = d.ShMaterialDw; existing.Scrq = d.Scrq; existing.Scph = d.Scph; existing.Remarks = d.Remarks; existing.Djsl = d.Djsl; existing.Jybb = d.Jybb; existing.Jhdbh = d.Jhdbh; await _detailRep.UpdateAsync(existing); } else { var detail = new ScmShdzb { Id = YitIdHelper.NextId(), Glid = masterId.ToString(), Hh = d.Hh ?? (i + 1), PoBill = d.PoBill, PoBillLine = d.PoBillLine, OrderType = d.OrderType, ShMaterialCode = d.ShMaterialCode, ShMaterialName = d.ShMaterialName, Th = d.Th, ShDeliveryQuantity = d.ShDeliveryQuantity, Bzsl = d.Bzsl, Bqsl = d.Bqsl, ShMaterialDw = d.ShMaterialDw, Scrq = d.Scrq, Scph = d.Scph, Remarks = d.Remarks, Djsl = d.Djsl, Jybb = d.Jybb, Jhdbh = d.Jhdbh }; await _detailRep.InsertAsync(detail); } } foreach (var old in dbDetails.Where(x => !inputIds.Contains(x.Id))) { await _detailRep.DeleteAsync(x => x.Id == old.Id); } } private static string BuildListOrderBy(string? sortField, string? sortOrder) { var map = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["shddh"] = "t.shddh", ["poBill"] = "t.po_bill", ["jhshrq"] = "t.jhshrq", ["shMaterialCode"] = "t.sh_material_code", ["shMaterialName"] = "t.sh_material_name", ["shDeliveryQuantity"] = "t.sh_delivery_quantity", ["sfpc"] = "t.sfpc", ["pcrksl"] = "t.pcrksl", ["shPurchaseName"] = "t.sh_purchase_name", ["shpc"] = "t.shpc", ["scph"] = "t.scph", ["wldh"] = "t.wldh", ["dycs"] = "t.dycs", ["shzt"] = "t.shzt" }; var field = map.TryGetValue(sortField ?? string.Empty, out var sqlField) ? sqlField : "t.mid"; var order = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC"; return $" ORDER BY {field} {order} "; } private static string BuildListSql() => """ SELECT m.mid, m.id, m.sh_purchase_id, m.sh_purchase_name, m.sh_purchase_num, m.sh_purchase_address, m.sh_purchase_lxr, m.sh_purchase_phone, m.client, m.delivery_Address, m.expected_consignee, m.consignee_phone, m.estimated_delivery_date, m.po_billno, m.shddh, m.jhshrq, m.tjrid, m.tjrxm, m.tjrq, m.scbq, m.chbg, m.sfpc, m.pcsm, m.wlsc, m.yjdhrq, m.state, m.shzt, m.wldh, m.dycs, m.gys, m.sh_material_code, m.sh_material_name, m.sh_material_ggxh, m.hh, m.po_bill, m.shpc, m.scph, m.sh_delivery_quantity, m.th, n.rksl, n.bhgsl, n.thsl, n.zshl, n.Delivery, n.pc, l.pcrksl, po.potype, po.Usage FROM ( SELECT a.id mid, b.id, a.sh_purchase_id, a.sh_purchase_name, a.sh_purchase_num, a.sh_purchase_address, a.sh_purchase_lxr, a.sh_purchase_phone, a.client, a.delivery_Address, a.expected_consignee, a.consignee_phone, a.estimated_delivery_date, a.po_billno, a.shddh, a.jhshrq, a.tjrid, a.tjrxm, a.tjrq, a.scbq, a.chbg, a.sfpc, a.pcsm, a.wlsc, a.yjdhrq, a.state, IFNULL(a.shzt, '待收') shzt, a.wldh, a.dycs, CONCAT(IFNULL(a.sh_purchase_num, ''), IFNULL(a.sh_purchase_name, '')) gys, b.sh_material_code, b.sh_material_name, b.sh_material_ggxh, b.hh, b.po_bill, c.shpc, b.scph, b.sh_delivery_quantity, b.th FROM scm_shd a LEFT JOIN scm_shdzb b ON a.id = b.glid LEFT JOIN (SELECT DISTINCT glid, shpc FROM scm_shbq) c ON b.id = c.glid ) m LEFT JOIN ( SELECT Delivery, LotSerial pc, SUM(ReceiptQty) zshl, AVG(yssl) rksl, SUM(QtyReturn) bhgsl, SUM(QtyReturned) thsl FROM vscm_cgshrk GROUP BY Delivery, LotSerial ) n ON m.shddh = n.Delivery AND m.shpc = n.pc LEFT JOIN ( SELECT LotSerial, SUM(QtyChange) pcrksl FROM InvTransHist WHERE QtyChange > 0 AND Reason LIKE '%收货' GROUP BY LotSerial ) l ON m.shpc = l.LotSerial LEFT JOIN PurOrdMaster po ON m.po_bill = po.purord """; private sealed class SupplierShipmentListRow { public long Mid { get; set; } public long Id { get; set; } public string? ShPurchaseName { get; set; } public string? ShPurchaseNum { get; set; } public string? Shddh { get; set; } public string? Jhshrq { get; set; } public int? Sfpc { get; set; } public string? Wldh { get; set; } public int? Dycs { get; set; } public string? Shzt { get; set; } public string? ShMaterialCode { get; set; } public string? ShMaterialName { get; set; } public decimal? ShDeliveryQuantity { get; set; } public decimal? Pcrksl { get; set; } public string? PoBill { get; set; } public string? Usage { get; set; } public string? Shpc { get; set; } public string? Scph { get; set; } public string? Th { get; set; } public int? State { get; set; } } private sealed class SupplierShipmentDraftRow { public string? SourceId { get; set; } public string? Gysdm { get; set; } public string? Gysmc { get; set; } public string? PoBill { get; set; } public int? PoBillLine { get; set; } public string? OrderType { get; set; } public string? ShMaterialCode { get; set; } public string? ShMaterialName { get; set; } public string? Th { get; set; } public decimal? ShDeliveryQuantity { get; set; } public decimal? Bzsl { get; set; } public string? ShMaterialDw { get; set; } public decimal? Djsl { get; set; } public string? Jhdbh { get; set; } public string? ShMaterialGgxh { get; set; } } }