| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- namespace Admin.NET.Plugin.AiDOP.Order;
- /// <summary>
- /// 出货计划服务 📦
- /// 路由前缀:/api/Order/shippingplan/...
- /// </summary>
- [ApiDescriptionSettings(Order = 290, Description = "出货计划")]
- [Route("api/Order")]
- [AllowAnonymous]
- [NonUnify]
- public class ShippingPlanService : IDynamicApiController, ITransient
- {
- private readonly ISqlSugarClient _db;
- private readonly SqlSugarRepository<ShippingPlan> _planRep;
- private readonly SqlSugarRepository<ShippingPlanDetail> _detailRep;
- private readonly UserManager _userManager;
- public ShippingPlanService(
- ISqlSugarClient db,
- SqlSugarRepository<ShippingPlan> planRep,
- SqlSugarRepository<ShippingPlanDetail> detailRep,
- UserManager userManager)
- {
- _db = db;
- _planRep = planRep;
- _detailRep = detailRep;
- _userManager = userManager;
- }
- // ══════════════════════════════════════════════════════════════
- // 列表 GET /api/Order/shippingplan/list
- // ══════════════════════════════════════════════════════════════
- /// <summary>获取出货计划分页列表 📦</summary>
- [DisplayName("获取出货计划列表")]
- [HttpGet("shippingplan/list")]
- public async Task<object> GetShippingPlanList([FromQuery] ShippingPlanListInput input)
- {
- var pars = new List<SugarParameter>();
- var conditions = new List<string>();
- if (!string.IsNullOrWhiteSpace(input.OrdNbr))
- {
- conditions.Add("b.OrdNbr LIKE @OrdNbr");
- pars.Add(new SugarParameter("@OrdNbr", $"%{input.OrdNbr.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.BillNo))
- {
- conditions.Add("b.bill_no LIKE @BillNo");
- pars.Add(new SugarParameter("@BillNo", $"%{input.BillNo.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.Country))
- {
- conditions.Add("b.Country = @Country");
- pars.Add(new SugarParameter("@Country", input.Country.Trim()));
- }
- if (!string.IsNullOrWhiteSpace(input.ShippingDateFrom))
- {
- conditions.Add("a.ShippingDate >= @ShippingDateFrom");
- pars.Add(new SugarParameter("@ShippingDateFrom", input.ShippingDateFrom.Trim()));
- }
- if (!string.IsNullOrWhiteSpace(input.CustomNo))
- {
- conditions.Add("b.CustomNo LIKE @CustomNo");
- pars.Add(new SugarParameter("@CustomNo", $"%{input.CustomNo.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.ItemNum))
- {
- conditions.Add("b.ItemNum LIKE @ItemNum");
- pars.Add(new SugarParameter("@ItemNum", $"%{input.ItemNum.Trim()}%"));
- }
- var whereClause = conditions.Count > 0 ? "WHERE " + string.Join(" AND ", conditions) : "";
- var baseSql = $"""
- SELECT
- a.ShippingSite,
- a.LotSerial,
- a.ShippingDate,
- a.ShippingAddress,
- a.Consignee,
- a.Telephone,
- a.RecID AS Id,
- b.OrdNbr,
- b.bill_no AS BillNo,
- b.OrdDate,
- b.Country,
- b.CustomNo,
- b.CustomName,
- b.ItemNum,
- b.ItemName,
- b.Specification,
- b.Qty,
- b.Packaging,
- CAST(c.RecID AS CHAR) AS AsnRecID,
- CAST(b.RecID AS CHAR) AS Sid,
- b.Volume,
- b.Weight
- FROM ShippingPlan a
- LEFT JOIN ShippingPlanDetail b ON a.RecID = b.plan_id
- LEFT JOIN ASNBOLShipperDetail c ON b.ItemNum = c.ContainerItem
- AND b.bill_no = c.ordnbr
- AND c.shtype = 'SH'
- AND c.Typed <> 'S'
- AND c.IsActive = 1
- {whereClause}
- """;
- var offset = (input.Page - 1) * input.PageSize;
- var total = await _db.Ado.GetIntAsync(
- $"SELECT COUNT(*) FROM ({baseSql}) AS t", pars);
- var list = await _db.Ado.SqlQueryAsync<ShippingPlanListRow>(
- $"SELECT * FROM ({baseSql}) AS t ORDER BY t.Id DESC LIMIT {input.PageSize} OFFSET {offset}",
- pars);
- return new { total, page = input.Page, pageSize = input.PageSize, list };
- }
- // ══════════════════════════════════════════════════════════════
- // 详情 GET /api/Order/shippingplan/{id}
- // ══════════════════════════════════════════════════════════════
- /// <summary>获取出货计划详情(含明细)📦</summary>
- [DisplayName("获取出货计划详情")]
- [HttpGet("shippingplan/{id}")]
- public async Task<object> GetShippingPlanDetail(int id)
- {
- var plan = await _planRep.GetFirstAsync(u => u.RecID == id)
- ?? throw Oops.Oh("出货计划不存在");
- var details = await _detailRep.GetListAsync(u => u.PlanId == id);
- return new
- {
- recID = plan.RecID,
- lotSerial = plan.LotSerial,
- shippingDate = plan.ShippingDate?.ToString("yyyy-MM-dd"),
- shippingSite = plan.ShippingSite,
- consignee = plan.Consignee,
- shippingAddress = plan.ShippingAddress,
- telephone = plan.Telephone,
- remark = plan.Remark,
- details = details.Select(d => new
- {
- recID = d.RecID,
- ordNbr = d.OrdNbr,
- billNo = d.BillNo,
- customNo = d.CustomNo,
- customName = d.CustomName,
- ordDate = d.OrdDate?.ToString("yyyy-MM-dd"),
- country = d.Country,
- itemNum = d.ItemNum,
- itemName = d.ItemName,
- specification = d.Specification,
- qty = d.Qty,
- weight = d.Weight,
- volume = d.Volume,
- packaging = d.Packaging,
- remark = d.Remark,
- })
- };
- }
- // ══════════════════════════════════════════════════════════════
- // 保存(新增/编辑)POST /api/Order/shippingplan/save
- // ══════════════════════════════════════════════════════════════
- /// <summary>保存出货计划(新增或编辑)📦</summary>
- [DisplayName("保存出货计划")]
- [ApiDescriptionSettings(Name = "SaveShippingPlan"), HttpPost("shippingplan/save")]
- public async Task<object> SaveShippingPlan([FromBody] ShippingPlanSaveInput input)
- {
- var now = DateTime.Now;
- var user = _userManager.Account ?? "system";
- if (input.RecID is null or 0)
- {
- // ── 新增:参照 SysJobService.AddJobDetail ──
- var nextPlanId = await _db.Ado.GetIntAsync("SELECT IFNULL(MAX(RecID), 0) FROM ShippingPlan") + 1;
- var entity = new ShippingPlan
- {
- RecID = nextPlanId,
- Domain = "8010",
- Priority = 0,
- LotSerial = input.LotSerial,
- ShippingDate = string.IsNullOrWhiteSpace(input.ShippingDate) ? null : DateTime.Parse(input.ShippingDate),
- ShippingSite = input.ShippingSite,
- Consignee = input.Consignee,
- ShippingAddress = input.ShippingAddress,
- Telephone = input.Telephone,
- Remark = input.Remark,
- IsActive = 1,
- IsConfirm = 1,
- CreateUser = user,
- CreateTime = now,
- };
- await _planRep.InsertAsync(entity);
- await SaveDetailsAsync(nextPlanId, input.Details, user, now, isNew: true);
- return new { id = nextPlanId, message = "新增成功" };
- }
- else
- {
- // ── 编辑:参照 SysJobService.UpdateJobDetail ──
- var entity = await _planRep.GetFirstAsync(u => u.RecID == input.RecID.Value)
- ?? throw Oops.Oh("出货计划不存在");
- entity.LotSerial = input.LotSerial;
- entity.ShippingDate = string.IsNullOrWhiteSpace(input.ShippingDate) ? null : DateTime.Parse(input.ShippingDate);
- entity.ShippingSite = input.ShippingSite;
- entity.Consignee = input.Consignee;
- entity.ShippingAddress = input.ShippingAddress;
- entity.Telephone = input.Telephone;
- entity.Remark = input.Remark;
- entity.UpdateUser = user;
- entity.UpdateTime = now;
- await _planRep.UpdateAsync(entity);
- await SaveDetailsAsync(input.RecID.Value, input.Details, user, now, isNew: false);
- return new { id = input.RecID, message = "编辑成功" };
- }
- }
- /// <summary>
- /// 明细三路合并(参照 SeOrderService.SaveEntriesAsync):
- /// ① DB有且入参有(按 RecID 匹配)→ 更新
- /// ② DB无但入参有 → 新增
- /// ③ DB有但入参无 → 删除
- /// </summary>
- private async Task SaveDetailsAsync(int planId, List<ShippingPlanDetailInput> details,
- string user, DateTime now, bool isNew)
- {
- var dbDetails = isNew ? new List<ShippingPlanDetail>()
- : await _detailRep.GetListAsync(u => u.PlanId == planId);
- var dbById = dbDetails.ToDictionary(u => u.RecID);
- var inputIds = new HashSet<int>(details.Where(d => d.RecID is > 0).Select(d => d.RecID!.Value));
- // RecID 列无 AUTO_INCREMENT,手动取当前最大值后顺序递增
- var nextId = await _db.Ado.GetIntAsync("SELECT IFNULL(MAX(RecID), 0) FROM ShippingPlanDetail") + 1;
- foreach (var d in details)
- {
- if (d.RecID > 0 && dbById.TryGetValue(d.RecID!.Value, out var existing))
- {
- // ① 更新
- existing.OrdNbr = d.OrdNbr;
- existing.BillNo = d.BillNo;
- existing.CustomNo = d.CustomNo;
- existing.CustomName = d.CustomName;
- existing.OrdDate = string.IsNullOrWhiteSpace(d.OrdDate) ? null : DateTime.Parse(d.OrdDate);
- existing.Country = d.Country;
- existing.ItemNum = d.ItemNum;
- existing.ItemName = d.ItemName;
- existing.Specification = d.Specification;
- existing.Qty = d.Qty;
- existing.Weight = d.Weight;
- existing.Volume = d.Volume;
- existing.Packaging = d.Packaging;
- existing.Remark = d.Remark;
- existing.UpdateUser = user;
- existing.UpdateTime = now;
- await _detailRep.UpdateAsync(existing);
- }
- else
- {
- // ② 新增
- var detail = new ShippingPlanDetail
- {
- RecID = nextId++,
- PlanId = planId,
- Domain = "8010",
- SentryId = long.TryParse(d.SentryId, out var sid) ? sid : null,
- OrdNbr = d.OrdNbr,
- BillNo = d.BillNo,
- CustomNo = d.CustomNo,
- CustomName = d.CustomName,
- OrdDate = string.IsNullOrWhiteSpace(d.OrdDate) ? null : DateTime.Parse(d.OrdDate),
- Country = d.Country,
- ItemNum = d.ItemNum,
- ItemName = d.ItemName,
- Specification = d.Specification,
- Qty = d.Qty,
- Weight = d.Weight,
- Volume = d.Volume,
- Packaging = d.Packaging,
- Remark = d.Remark,
- IsActive = 1,
- IsConfirm = 1,
- CreateUser = user,
- CreateTime = now,
- };
- await _detailRep.InsertAsync(detail);
- }
- }
- // ③ 删除:DB有、入参无
- foreach (var toDelete in dbDetails.Where(u => !inputIds.Contains(u.RecID)))
- {
- await _detailRep.DeleteAsync(u => u.RecID == toDelete.RecID);
- }
- }
- // ══════════════════════════════════════════════════════════════
- // 销售出库 POST /api/Order/shippingplan/ship
- // ══════════════════════════════════════════════════════════════
- /// <summary>销售出库(调用存储过程 pr_WMS_AutoCreateShipper)📦</summary>
- [DisplayName("销售出库")]
- [ApiDescriptionSettings(Name = "ShipShippingPlan"), HttpPost("shippingplan/ship")]
- public async Task<object> ShipShippingPlan([FromBody] ShippingPlanShipInput input)
- {
- var orgNo = ""; // 组织编号,可从配置或 UserManager 获取
- var account = _userManager.Account ?? "system";
- var ids = input.Ids;
- await _db.Ado.ExecuteCommandAsync(
- "CALL pr_WMS_AutoCreateShipper(@OrgNo, @Account, @Ids)",
- new SugarParameter("@OrgNo", orgNo),
- new SugarParameter("@Account", account),
- new SugarParameter("@Ids", ids));
- return new { message = "销售出库执行成功" };
- }
- // ──────────────── 内部查询结果映射类 ────────────────
- private sealed class ShippingPlanListRow
- {
- public int Id { get; set; }
- public string? LotSerial { get; set; }
- public string? OrdNbr { get; set; }
- public string? BillNo { get; set; }
- public DateTime? OrdDate { get; set; }
- public string? Country { get; set; }
- public string? CustomNo { get; set; }
- public string? CustomName { get; set; }
- public string? ItemNum { get; set; }
- public string? ItemName { get; set; }
- public string? Specification { get; set; }
- public decimal? Qty { get; set; }
- public decimal? Weight { get; set; }
- public decimal? Volume { get; set; }
- public string? Packaging { get; set; }
- public string? ShippingSite { get; set; }
- public DateTime? ShippingDate { get; set; }
- public string? ShippingAddress { get; set; }
- public string? Consignee { get; set; }
- public string? Telephone { get; set; }
- public string? AsnRecID { get; set; }
- public string? Sid { get; set; }
- }
- }
|