| | |
| | | using MES.Service.Modes; |
| | | using MES.Service.util; |
| | | using SqlSugar; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | |
| | | namespace MES.Service.service; |
| | | |
| | | public class MesCgthSqManager : Repository<MesCgthSq> |
| | | { |
| | | // 明细表管理器(用于处理明细数据) |
| | | //private readonly MesCgthSqDetailManager _detailManager = new(); |
| | | /// <summary> |
| | | /// 批量保存采购退货单(主表+明细) |
| | | /// </summary> |
| | | public bool SaveList(List<ErpCgth> erpCgthList) |
| | | { |
| | | // 逐条处理,全部成功才返回true |
| | | if (erpCgthList == null || !erpCgthList.Any()) |
| | | throw new ArgumentNullException(nameof(erpCgthList), "待保存的退货单列表不能为空"); |
| | | |
| | | // 逐条处理,全部成功才返回true(事务内批量处理更优,此处保持原有逻辑) |
| | | var result = erpCgthList.Select(Save).ToList(); |
| | | return result.All(b => b); |
| | | } |
| | |
| | | /// </summary> |
| | | public bool Save(ErpCgth erpCgth) |
| | | { |
| | | // 从ERP数据中提取主表和明细表DTO |
| | | if (erpCgth == null) |
| | | throw new ArgumentNullException(nameof(erpCgth), "退货单数据不能为空"); |
| | | if (erpCgth.ErpCgtha == null) |
| | | throw new ArgumentNullException(nameof(erpCgth.ErpCgtha), "退货单主表数据不能为空"); |
| | | |
| | | // 提取主表和明细表DTO |
| | | var erpMain = erpCgth.ErpCgtha; |
| | | var erpDetails = erpCgth.ErpCgthB; |
| | | var erpDetails = erpCgth.ErpCgthB ?? new List<ErpCgthB>(); // 避免明细为null |
| | | |
| | | // 映射主表和明细表实体 |
| | | // 步骤1:映射主表基础数据(暂不处理Id) |
| | | var mesMain = MapErpCgthaToMesCgthSq(erpMain); |
| | | var mesDetails = MapErpCgthBToMesCgthSqDetail(erpDetails, mesMain.Id); // 关联主表ID |
| | | |
| | | // 使用事务处理主从表联动操作 |
| | | // 步骤2:提前确定主表Id(核心修正:在映射明细表前获取/生成Id) |
| | | Guid mainId = GetOrCreateMainId(mesMain); |
| | | |
| | | // 步骤3:用确定的mainId映射明细表(确保Mid=mainId) |
| | | var mesDetails = MapErpCgthBToMesCgthSqDetail(erpDetails, mainId); |
| | | |
| | | // 步骤4:事务内处理主从表保存 |
| | | return UseTransaction(db => |
| | | { |
| | | // 根据操作类型(TYPE)执行不同逻辑(假设TYPE=1新增,2更新,3删除) |
| | | switch (erpMain.TYPE) |
| | | { |
| | | case "1": // 新增 |
| | | case "2": // 更新(统一走新增或更新逻辑) |
| | | case "4": |
| | | return SaveOrUpdateData(db, mesMain, mesDetails) ? 1 : 0; |
| | | case "2": // 更新 |
| | | case "4": // 其他新增/更新类型 |
| | | return SaveOrUpdateData(db, mesMain, mesDetails, mainId) ? 1 : 0; |
| | | case "3": // 删除 |
| | | return DeleteData(db, mesMain, mesDetails) ? 1 : 0; |
| | | return DeleteData(db, mesMain, mesDetails, mainId) ? 1 : 0; |
| | | default: |
| | | throw new NotImplementedException($"操作类型[{erpMain.TYPE}]未实现"); |
| | | throw new NotImplementedException($"未实现的操作类型:{erpMain.TYPE}"); |
| | | } |
| | | }) > 0; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 提前获取或生成主表Id(确保在映射明细表前确定) |
| | | /// </summary> |
| | | private Guid GetOrCreateMainId(MesCgthSq mesMain) |
| | | { |
| | | // 优先通过单据号查询已有主表(更新场景) |
| | | var existingMain = Context.Queryable<MesCgthSq>() |
| | | .Where(m => m.BillNo == mesMain.BillNo) |
| | | .First(); |
| | | |
| | | if (existingMain != null) |
| | | { |
| | | // 已存在:返回数据库中的Id |
| | | return existingMain.Id; |
| | | } |
| | | else |
| | | { |
| | | // 不存在:生成新Id(新增场景) |
| | | return Guid.NewGuid(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 新增或更新数据(主表+明细) |
| | | /// </summary> |
| | | private bool SaveOrUpdateData(SqlSugarScope db, MesCgthSq mesMain, List<MesCgthSqDetail> mesDetails) |
| | | private bool SaveOrUpdateData(SqlSugarScope db, MesCgthSq mesMain, List<MesCgthSqDetail> mesDetails, Guid mainId) |
| | | { |
| | | // 1. 处理主表:若已存在则更新,不存在则新增 |
| | | bool isMainExist = db.Queryable<MesCgthSq>().Where(m => m.BillNo == mesMain.BillNo).Any(); |
| | | // 绑定主表Id(使用提前确定的mainId) |
| | | mesMain.Id = mainId; |
| | | |
| | | // 处理主表:新增或更新 |
| | | bool isExist = db.Queryable<MesCgthSq>().Where(m => m.BillNo == mesMain.BillNo).Any(); |
| | | int mainResult; |
| | | if (isMainExist) |
| | | |
| | | if (isExist) |
| | | { |
| | | // 更新主表(忽略空字段) |
| | | // 更新:按Id匹配(避免单据号重复导致错误) |
| | | mainResult = db.Updateable(mesMain) |
| | | .IgnoreColumns(m => m.Id) // 不更新主键 |
| | | .Where(m => m.BillNo == mesMain.BillNo) |
| | | .IgnoreColumns(m => new { m.CreateDate }) // 不更新创建时间 |
| | | .Where(m => m.Id == mainId) |
| | | .ExecuteCommand(); |
| | | } |
| | | else |
| | | { |
| | | // 新增主表(生成主键) |
| | | mesMain.Id = Guid.NewGuid(); |
| | | // 新增:使用提前生成的Id |
| | | mainResult = db.Insertable(mesMain).ExecuteCommand(); |
| | | } |
| | | |
| | | // 2. 处理明细表:先删除旧明细,再插入新明细(确保数据同步) |
| | | // (通过主表ERPID关联旧明细) |
| | | int oldDetailCount = db.Deleteable<MesCgthSqDetail>() |
| | | .Where(d => d.Mid == mesMain.Id) |
| | | // 处理明细表:先删旧数据,再插新数据(确保数据同步) |
| | | // 1. 删除当前主表关联的旧明细 |
| | | int deleteOldDetailResult = db.Deleteable<MesCgthSqDetail>() |
| | | .Where(d => d.Mid == mainId) |
| | | .ExecuteCommand(); |
| | | |
| | | // 插入新明细(关联主表ID) |
| | | int detailResult = mesDetails.Count > 0 |
| | | // 2. 插入新明细(Mid已=mainId) |
| | | int insertDetailResult = mesDetails.Count > 0 |
| | | ? db.Insertable(mesDetails).ExecuteCommand() |
| | | : 1; // 无明细时默认成功 |
| | | : 1; // 无明细时视为成功 |
| | | |
| | | // 3. 校验结果 |
| | | if (mainResult > 0 && detailResult > 0) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | throw new NotImplementedException("主表或明细保存失败"); |
| | | // 校验结果(主表必须成功,明细删除/插入至少有一个成功) |
| | | return mainResult > 0 && (deleteOldDetailResult >= 0 && insertDetailResult > 0); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 删除数据(主表+明细) |
| | | /// </summary> |
| | | private bool DeleteData(SqlSugarScope db, MesCgthSq mesMain, List<MesCgthSqDetail> mesDetails) |
| | | private bool DeleteData(SqlSugarScope db, MesCgthSq mesMain, List<MesCgthSqDetail> mesDetails, Guid mainId) |
| | | { |
| | | // 处理ERPID为空的情况,转换为统一的空字符串进行比较 |
| | | string mainErpId = mesMain.ErpId?.ToString() ?? string.Empty; |
| | | |
| | | // 1. 删除明细(通过主表ERPID关联) |
| | | // 1. 删除明细(按主表Id删除) |
| | | int detailResult = db.Deleteable<MesCgthSqDetail>() |
| | | .Where(d => (d.Eid.ToString() ?? string.Empty) == mainErpId) |
| | | .Where(d => d.Mid == mainId) |
| | | .ExecuteCommand(); |
| | | |
| | | // 2. 删除主表(通过ERPID关联) |
| | | // 2. 删除主表(按Id删除) |
| | | int mainResult = db.Deleteable<MesCgthSq>() |
| | | .Where(m => (m.ErpId.ToString() ?? string.Empty) == mainErpId) |
| | | .Where(m => m.Id == mainId) |
| | | .ExecuteCommand(); |
| | | |
| | | return mainResult > 0 && detailResult >= 0; // 允许明细原本不存在(>=0) |
| | | // 允许明细原本不存在(detailResult=0),但主表必须删除成功 |
| | | return mainResult > 0; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ErpCgtha 映射到 MesCgthSq(仅映射ErpCgtha中存在的字段) |
| | | /// 主表映射(仅处理基础字段,Id由GetOrCreateMainId确定) |
| | | /// </summary> |
| | | private MesCgthSq MapErpCgthaToMesCgthSq(ErpCgtha erpMain) |
| | | { |
| | | return new MesCgthSq |
| | | { |
| | | // 主表核心字段(仅从ErpCgtha取值) |
| | | // Id暂不赋值(由GetOrCreateMainId后续确定) |
| | | ErpId = erpMain.ERPID, // ERP主表ID |
| | | BillNo = erpMain.billNo, // 单据编号 |
| | | Type = erpMain.TYPE, // 操作类型 |
| | | FDate = erpMain.FDate, // 退料日期 |
| | | FDocumentStatus = erpMain.FDocumentStatus, // 单据状态 |
| | | FSupplierId = erpMain.FSupplierID, // 供应商ID |
| | | FBillTypeId = erpMain.FBillTypeID, // 单据类型 |
| | | FBusinessType = erpMain.FBusinessType, // 业务类型 |
| | | ReturnType = erpMain.FMRTYPE, // 退料类型(对应ErpCgtha的退料类型) |
| | | ReturnMethod = erpMain.FMRMODE, // 退料方式(对应ErpCgtha的退料方式) |
| | | CreateBy = erpMain.FCreatorId, // 创建人 |
| | | FPurchaseOrgId = erpMain.FPurchaseOrgId, // 采购组织 |
| | | ThOrgId = erpMain.FStockOrgId, // 退料组织 |
| | | FRequireOrgId = erpMain.FRequireOrgId, // 需求组织 |
| | | FMRDeptId = erpMain.FMRDeptId, // 退料部门 |
| | | FStockerId = erpMain.FSTOCKERID, // 仓管员 |
| | | FPurchaserId = erpMain.FPURCHASERID, // 采购员 |
| | | FMRReason = erpMain.FMRREASON, // 退料原因 |
| | | FPurchaseDeptId = erpMain.FPURCHASEDEPTID, // 采购部门 |
| | | FPurchaserGroupId = erpMain.FPURCHASERGROUPID, // 采购组 |
| | | FACCTYPE = erpMain.FACCTYPE, // 验收方式 |
| | | FCreateDate = erpMain.FCreateDate, // 创建日期 |
| | | FWPVTINTEGERL6W = erpMain.F_WPVT_INTEGER_L6W, // 扫码标识 |
| | | // DepotId = string.IsNullOrEmpty(erpMain.FSTOCKID) |
| | | // ? null |
| | | // : Convert.ToInt32(erpMain.FSTOCKID), // 仓库ID(转换为int) |
| | | SuppId = string.IsNullOrEmpty(erpMain.FSupplierID) |
| | | ? null |
| | | : Convert.ToInt32(erpMain.FSupplierID), // 供应商ID(转换为int) |
| | | // 系统自动赋值字段 |
| | | BillNo = erpMain.billNo ?? throw new ArgumentNullException(nameof(erpMain.billNo), "单据编号不能为空"), |
| | | Type = erpMain.TYPE, |
| | | FDate = erpMain.FDate, |
| | | FDocumentStatus = erpMain.FDocumentStatus, |
| | | FSupplierId = erpMain.FSupplierID, |
| | | FBillTypeId = erpMain.FBillTypeID, |
| | | FBusinessType = erpMain.FBusinessType, |
| | | ReturnType = erpMain.FMRTYPE, |
| | | ReturnMethod = erpMain.FMRMODE, |
| | | CreateBy = erpMain.FCreatorId, |
| | | FPurchaseOrgId = erpMain.FPurchaseOrgId, |
| | | ThOrgId = erpMain.FStockOrgId, |
| | | FRequireOrgId = erpMain.FRequireOrgId, |
| | | FMRDeptId = erpMain.FMRDeptId, |
| | | FStockerId = erpMain.FSTOCKERID, |
| | | FPurchaserId = erpMain.FPURCHASERID, |
| | | FMRReason = erpMain.FMRREASON, |
| | | FPurchaseDeptId = erpMain.FPURCHASEDEPTID, |
| | | FPurchaserGroupId = erpMain.FPURCHASERGROUPID, |
| | | FACCTYPE = erpMain.FACCTYPE, |
| | | FCreateDate = erpMain.FCreateDate, |
| | | FWPVTINTEGERL6W = erpMain.F_WPVT_INTEGER_L6W, |
| | | |
| | | // 供应商ID(安全转换) |
| | | SuppId = !string.IsNullOrEmpty(erpMain.FSupplierID) |
| | | && int.TryParse(erpMain.FSupplierID, out int suppId) |
| | | ? suppId |
| | | : null, |
| | | |
| | | // 系统字段(创建时间仅新增时赋值,更新时不覆盖) |
| | | CreateDate = DateTime.Now, |
| | | LastUpdateTime = DateTime.Now |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ErpCgthB 映射到 MesCgthSqDetail(仅映射ErpCgthB中存在的字段) |
| | | /// 明细表映射(使用提前确定的mainId作为Mid) |
| | | /// </summary> |
| | | private List<MesCgthSqDetail> MapErpCgthBToMesCgthSqDetail(List<ErpCgthB> erpDetails, Guid mainId) |
| | | { |
| | | return erpDetails.Select(erpDetail => new MesCgthSqDetail |
| | | { |
| | | // 关联主表ID |
| | | Mid= mainId, // 主表ID(MesCgthSq的Id) |
| | | // 明细核心字段(仅从ErpCgthB取值) |
| | | ErpId = string.IsNullOrEmpty(erpDetail.ERPID) ? null : Convert.ToInt32(erpDetail.ERPID), // ERP明细ID |
| | | Eid = string.IsNullOrEmpty(erpDetail.EID) ? null : Convert.ToInt32(erpDetail.EID), // 外部系统ID |
| | | FsrcBillNo = erpDetail.FSRCBillNo, // 源单单号 |
| | | FsrcBillTypeId = erpDetail.FSRCBillTypeId, // 源单类型 |
| | | ItemId = string.IsNullOrEmpty(erpDetail.FMATERIALID) |
| | | ? null |
| | | : Convert.ToInt32(erpDetail.FMATERIALID), // 物料ID |
| | | FUnitId = erpDetail.FUnitID, // 库存单位 |
| | | DepotId = string.IsNullOrEmpty(erpDetail.FSTOCKID) ? null : Convert.ToInt64(erpDetail.FSTOCKID), // 仓库ID |
| | | FstockLocId = erpDetail.FSTOCKLOCID, // 仓位 |
| | | FLot = erpDetail.FLot, // 批号 |
| | | SqNum = erpDetail.FRMREALQTY, // 实退数量 |
| | | Remark = erpDetail.FNOTE, // 备注 |
| | | FMtoNo = erpDetail.FMtoNo, // 计划跟踪号 |
| | | // 系统自动赋值字段 |
| | | Mid = mainId, // 直接使用提前确定的mainId(确保非全零) |
| | | |
| | | // ERP明细ID(安全转换) |
| | | ErpId = !string.IsNullOrEmpty(erpDetail.ERPID) |
| | | && int.TryParse(erpDetail.ERPID, out int erpId) |
| | | ? erpId |
| | | : null, |
| | | |
| | | // 外部系统ID(安全转换) |
| | | Eid = !string.IsNullOrEmpty(erpDetail.EID) |
| | | && int.TryParse(erpDetail.EID, out int eid) |
| | | ? eid |
| | | : null, |
| | | |
| | | FsrcBillNo = erpDetail.FSRCBillNo, |
| | | FsrcBillTypeId = erpDetail.FSRCBillTypeId, |
| | | |
| | | // 物料ID(安全转换) |
| | | ItemId = !string.IsNullOrEmpty(erpDetail.FMATERIALID) |
| | | && int.TryParse(erpDetail.FMATERIALID, out int itemId) |
| | | ? itemId |
| | | : null, |
| | | |
| | | FUnitId = erpDetail.FUnitID, |
| | | |
| | | // 仓库ID(安全转换) |
| | | DepotId = !string.IsNullOrEmpty(erpDetail.FSTOCKID) |
| | | && long.TryParse(erpDetail.FSTOCKID, out long depotId) |
| | | ? depotId |
| | | : null, |
| | | |
| | | FstockLocId = erpDetail.FSTOCKLOCID, |
| | | FLot = erpDetail.FLot, |
| | | SqNum = erpDetail.FRMREALQTY, // 假设FRMREALQTY是数值类型(如decimal) |
| | | Remark = erpDetail.FNOTE, |
| | | FMtoNo = erpDetail.FMtoNo, |
| | | IsFinish = false // 初始未完成 |
| | | }).ToList(); |
| | | } |
| | | |
| | | } |
| | | } |