using System.Globalization;
|
using Masuit.Tools;
|
using MES.Service.DB;
|
using MES.Service.Dto.webApi;
|
using MES.Service.Modes;
|
using SqlSugar;
|
|
namespace MES.Service.service.BasicData;
|
|
public class MesCgthSqManager : Repository<MesCgthSq>
|
{
|
/// <summary>
|
/// 批量保存采购退货单(主表+明细)
|
/// </summary>
|
public bool SaveList(List<ErpCgth> erpCgthList)
|
{
|
if (erpCgthList == null || !erpCgthList.Any())
|
throw new ArgumentNullException(nameof(erpCgthList),
|
"待保存的退货单列表不能为空");
|
|
// 逐条处理,全部成功才返回true(事务内批量处理更优,此处保持原有逻辑)
|
var result = erpCgthList.Select(Save).ToList();
|
return result.All(b => b);
|
}
|
|
/// <summary>
|
/// 保存采购退货单(主表+明细)
|
/// </summary>
|
public bool Save(ErpCgth erpCgth)
|
{
|
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.ErpCgthBList ?? new List<ErpCgthBList>(); // 避免明细为null
|
|
// 步骤1:映射主表基础数据(暂不处理Id)
|
var mesMain = MapErpCgthaToMesCgthSq(erpMain);
|
|
// 步骤2:提前确定主表Id(核心修正:在映射明细表前获取/生成Id)
|
Guid mainId = GetOrCreateMainId(mesMain);
|
|
// 步骤3:用确定的mainId映射明细表(确保Mid=mainId)
|
var mesDetails = MapErpCgthBToMesCgthSqDetail(erpDetails, mainId);
|
|
// 步骤4:事务内处理主从表保存
|
return UseTransaction(db =>
|
{
|
switch (erpMain.TYPE)
|
{
|
case "1": // 新增
|
case "2": // 更新
|
case "4": // 其他新增/更新类型
|
return SaveOrUpdateData(db, mesMain, mesDetails, mainId)
|
? 1
|
: 0;
|
case "3": // 删除
|
return DeleteData(db, mesMain, mesDetails, mainId) ? 1 : 0;
|
default:
|
throw new NotImplementedException(
|
$"未实现的操作类型:{erpMain.TYPE}");
|
}
|
}) > 0;
|
}
|
|
/// <summary>
|
/// 提前获取或生成主表Id(确保在映射明细表前确定)
|
/// </summary>
|
private Guid GetOrCreateMainId(MesCgthSq mesMain)
|
{
|
// 优先通过单据号查询已有主表(更新场景)
|
var existingMain = Context.Queryable<MesCgthSq>()
|
.Where(m => m.PaperBillNo == 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, Guid mainId)
|
{
|
// 绑定主表Id(使用提前确定的mainId)
|
mesMain.Id = mainId;
|
|
// 处理主表:新增或更新
|
bool isExist = db.Queryable<MesCgthSq>()
|
.Where(m => m.PaperBillNo == mesMain.BillNo).Any();
|
int mainResult;
|
|
if (isExist)
|
{
|
// 更新:按Id匹配(避免单据号重复导致错误)
|
mainResult = db.Updateable(mesMain)
|
.IgnoreColumns(m => new { m.CreateDate }) // 不更新创建时间
|
.Where(m => m.Id == mainId)
|
.ExecuteCommand();
|
}
|
else
|
{
|
// 新增:使用提前生成的Id
|
mainResult = db.Insertable(mesMain).ExecuteCommand();
|
}
|
|
// 处理明细表:先删旧数据,再插新数据(确保数据同步)
|
// 1. 删除当前主表关联的旧明细
|
int deleteOldDetailResult = db.Deleteable<MesCgthSqDetail>()
|
.Where(d => d.Mid == mainId)
|
.ExecuteCommand();
|
|
// 2. 插入新明细(Mid已=mainId)
|
int insertDetailResult = mesDetails.Count > 0
|
? db.Insertable(mesDetails).ExecuteCommand()
|
: 1; // 无明细时视为成功
|
|
// 校验结果(主表必须成功,明细删除/插入至少有一个成功)
|
return mainResult > 0 &&
|
(deleteOldDetailResult >= 0 && insertDetailResult > 0);
|
}
|
|
/// <summary>
|
/// 删除数据(主表+明细)
|
/// </summary>
|
private bool DeleteData(SqlSugarScope db, MesCgthSq mesMain,
|
List<MesCgthSqDetail> mesDetails, Guid mainId)
|
{
|
// 1. 删除明细(按主表Id删除)
|
int detailResult = db.Deleteable<MesCgthSqDetail>()
|
.Where(d => d.Mid == mainId)
|
.ExecuteCommand();
|
|
// 2. 删除主表(按Id删除)
|
int mainResult = db.Deleteable<MesCgthSq>()
|
.Where(m => m.Id == mainId)
|
.ExecuteCommand();
|
|
// 允许明细原本不存在(detailResult=0),但主表必须删除成功
|
return mainResult > 0;
|
}
|
|
/// <summary>
|
/// 主表映射(仅处理基础字段,Id由GetOrCreateMainId确定)
|
/// </summary>
|
private MesCgthSq MapErpCgthaToMesCgthSq(ErpCgtha erpMain)
|
{
|
//主表还需要补充这些字段的值STATUS=>false,IS_FINISH=>false,fMrtype =>库存退料,fHasLink=>0
|
var mesCgthSq = new MesCgthSq
|
{
|
// Id暂不赋值(由GetOrCreateMainId后续确定)
|
PaperBillNo = erpMain.billNo ??
|
throw new ArgumentNullException(nameof(erpMain.billNo),
|
"单据编号不能为空"),
|
ReturnMethod = erpMain.FMRMODE,
|
CreateBy = erpMain.FCreatorId,
|
ThOrgId = "1",
|
FRequireOrgId = erpMain.FRequireOrgId,
|
FPurchaserId = erpMain.FPURCHASERID,
|
Remark = erpMain.FMRREASON,
|
FPurchaseDeptId = erpMain.FPURCHASEDEPTID,
|
|
// 委外标识:判断FType是否为"1"或"true"
|
IsOut = !string.IsNullOrEmpty(erpMain.FType) &&
|
(erpMain.FType == "1" || erpMain.FType.ToLower() == "true"),
|
|
// 补充的固定字段
|
Status = false, // 审核状态默认为未审核
|
IsFinish = false, // 完成标识默认为未完成
|
FMrtype = "库存退料", // 退料类型固定值
|
FHasLink = false, // 是否存在源单:0是1否(false=0=是)
|
|
// 系统字段(创建时间仅新增时赋值,更新时不覆盖)
|
LastUpdateTime = DateTime.Now
|
};
|
|
|
// 调用存储过程获取BillNo
|
var hNoParam = new SugarParameter("@hNo", null, true); // 声明输出参数
|
Db.Ado.UseStoredProcedure().ExecuteCommand(
|
"getOrderNo",
|
new SugarParameter("@wntype",
|
"CGTHSQ(采购退货申请)"), // 传入业务类型(例如收料单传"SL")
|
new SugarParameter("@itemNo", "0"), // 传入物料编码(若有)
|
hNoParam // 输出参数
|
);
|
// 获取存储过程返回的单据号
|
var billNo = TrimOrNull(hNoParam.Value?.ToString()) ??
|
throw new ArgumentException("收料单单据号生成失败");
|
|
mesCgthSq.BillNo = billNo;
|
|
// 处理创建日期
|
if (!erpMain.FCreateDate.IsNullOrEmpty())
|
{
|
if (!DateTime.TryParseExact(erpMain.FCreateDate, "yyyy-MM-dd",
|
CultureInfo.InvariantCulture,
|
DateTimeStyles.None, out DateTime purchaseDate))
|
{
|
throw new FormatException(
|
$"采购日期(FCreateDate)解析失败!值:【{erpMain.FCreateDate}】,支持格式:yyyy-MM-dd");
|
}
|
|
mesCgthSq.CreateDate = purchaseDate;
|
}
|
else
|
{
|
mesCgthSq.CreateDate = DateTime.Now;
|
}
|
|
// 通过退料部门编码查找仓库ID
|
if (!string.IsNullOrEmpty(erpMain.FMRDeptId))
|
{
|
var depot = Db.Queryable<MesDepots>()
|
.Where(d => d.DepotCode == erpMain.FMRDeptId)
|
.Select(d => d.DepotId)
|
.First();
|
mesCgthSq.DepotId = (int)depot;
|
}
|
|
// 通过供应商ID查找供应商
|
if (!string.IsNullOrEmpty(erpMain.FSupplierID))
|
{
|
// var supp = Convert.ToInt32(erpMain.FSupplierID);
|
var suppId = Db.Queryable<MesSupplier>()
|
.Where(d => d.SuppNo == erpMain.FSupplierID)
|
.Select(d => d.Id)
|
.First();
|
mesCgthSq.SuppId = (int)suppId;
|
}
|
|
return mesCgthSq;
|
}
|
|
/// <summary>
|
/// 明细表映射(使用提前确定的mainId作为Mid)
|
/// </summary>
|
private List<MesCgthSqDetail> MapErpCgthBToMesCgthSqDetail(
|
List<ErpCgthBList> erpDetails, Guid mainId)
|
{
|
if (erpDetails == null || !erpDetails.Any())
|
return new List<MesCgthSqDetail>();
|
|
// 批量提取物料编码,用于批量查询物料ID(性能优化)
|
var materialCodes = erpDetails
|
.Where(d => !string.IsNullOrEmpty(d.FMATERIALID))
|
.Select(d => d.FMATERIALID!)
|
.Distinct()
|
.ToList();
|
|
// 批量查询物料信息,构建编码->ID字典
|
var itemDict = materialCodes.Any()
|
? Db.Queryable<MesItems>()
|
.Where(i => materialCodes.Contains(i.ItemNo))
|
.Select(i => new { i.ItemNo, i.Id })
|
.ToList()
|
.ToDictionary(x => x.ItemNo!, x => (int)x.Id)
|
: new Dictionary<string, int>();
|
|
// 批量提取仓库编码
|
var depotCodes = erpDetails
|
.Where(d => !string.IsNullOrEmpty(d.FSTOCKID))
|
.Select(d => d.FSTOCKID!)
|
.Distinct()
|
.ToList();
|
|
// 批量查询仓库信息,构建编码->ID字典
|
var depotDict = depotCodes.Any()
|
? Db.Queryable<MesDepots>()
|
.Where(d => depotCodes.Contains(d.DepotCode))
|
.Select(d => new { d.DepotCode, d.DepotId })
|
.ToList()
|
.ToDictionary(x => x.DepotCode, x => (long)x.DepotId)
|
: new Dictionary<string, long>();
|
|
List<MesCgthSqDetail> entitys = new List<MesCgthSqDetail>();
|
int orderNo = 1; // 序号从1开始
|
|
foreach (var erpDetail in erpDetails)
|
{
|
//子表还需要补充这些字段的值ORDER_NO,RKMX_GUID,EID,ERPID,SQ_NUM
|
var mesCgthSqDetail = new MesCgthSqDetail
|
{
|
Id = Guid.NewGuid(), // 明细表主键
|
Mid = mainId, // 直接使用提前确定的mainId(确保非全零)
|
|
// Erpid取FSrcBillLine(源单分录内码)
|
Erpid = !string.IsNullOrEmpty(erpDetail.FSrcBillLine)
|
&& int.TryParse(erpDetail.FSrcBillLine, out int erpId)
|
? erpId
|
: null,
|
|
// Eid应该取FSRCBillNo(源单单号转换为int)
|
Eid = !string.IsNullOrEmpty(erpDetail.FSRCBillNo)
|
&& int.TryParse(erpDetail.FSRCBillNo, out int eid)
|
? eid
|
: null,
|
|
// 申请数量:取实退数量FRMREALQTY(转为正数)
|
SqNum = erpDetail.FRMREALQTY.HasValue ? Math.Abs(erpDetail.FRMREALQTY.Value) : 0,
|
YsNum = 0,
|
|
// 备注
|
Remark = erpDetail.FNOTE,
|
|
// 完成标识:初始为未完成
|
IsFinish = false,
|
|
OrderNo = Convert.ToInt32(erpDetail.LineNo),
|
};
|
|
//Ebeln
|
var mesRohIn = Db.Queryable<MesRohIn>()
|
.Where(s => s.EbelnK3id == mesCgthSqDetail.Eid)
|
.First();
|
|
if (mesRohIn != null)
|
{
|
mesCgthSqDetail.Ebeln = mesRohIn.BillNo;
|
}
|
|
//MesInvItemInCItems
|
// RkmxGuid = null // 如果有入库单明细关联逻辑,这里需要查询
|
var mesInvItemInCItems = Db.Queryable<MesInvItemInCItems>()
|
.Where(s => s.Remark == "采购入库"
|
&& s.EbelnK3id == mesCgthSqDetail.Eid
|
&& s.LineK3id == mesCgthSqDetail.Erpid).First();
|
|
if (mesInvItemInCItems != null)
|
{
|
mesCgthSqDetail.RkmxGuid = mesInvItemInCItems.Guid;
|
mesCgthSqDetail.InvBillNo = mesInvItemInCItems.BillNo;
|
}
|
|
// 通过仓库编码查找仓库ID
|
if (!string.IsNullOrEmpty(erpDetail.FSTOCKID))
|
{
|
if (depotDict.TryGetValue(erpDetail.FSTOCKID, out var depotId))
|
{
|
mesCgthSqDetail.DepotId = depotId;
|
}
|
else
|
{
|
throw new KeyNotFoundException(
|
$"未找到仓库编码【{erpDetail.FSTOCKID}】对应的仓库信息");
|
}
|
}
|
|
// 通过物料编码查找物料ID
|
if (!string.IsNullOrEmpty(erpDetail.FMATERIALID))
|
{
|
if (itemDict.TryGetValue(erpDetail.FMATERIALID, out var itemId))
|
{
|
mesCgthSqDetail.ItemId = itemId;
|
}
|
else
|
{
|
throw new KeyNotFoundException(
|
$"未找到物料编码【{erpDetail.FMATERIALID}】对应的物料信息");
|
}
|
}
|
|
entitys.Add(mesCgthSqDetail);
|
}
|
|
return entitys;
|
}
|
|
|
private static string TrimOrNull(string s) =>
|
string.IsNullOrWhiteSpace(s) ? null : s.Trim();
|
}
|