using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Hh.Mes.Common.config; using Hh.Mes.Common.log; using Infrastructure; using SqlSugar; using WebRepository; using WebRepository.DomainSugars; using WebRepository.EnumEntitys; using Hh.Mes.Service.Repository; namespace Hh.Mes.Service.Material { public class NestingService : RepositorySqlSugar<inventory> { #region 属性 /// <summary> /// 切割工单主表 /// </summary> public List<cutter_head_work_order> OriginalCutterHeadWorkOrders { get; set; } = new List<cutter_head_work_order>(); /// <summary> /// 切割工单子表 /// </summary> public List<cutter_detail_work_order> OriginalCutterDetailWorkOrders { get; set; } = new List<cutter_detail_work_order>(); /// <summary> /// 原材料 (原数据) /// </summary> public List<inventory> OriginalInventorys { get; set; } = new List<inventory>(); /// <summary> /// 记录套料中明细工单 状态 /// </summary> public List<cutter_detail_work_order> UpdateCutterDetails { get; set; } = new List<cutter_detail_work_order>(); /// <summary> /// 记录配料单已套料的 /// </summary> public List<api_material_demand_footer> MaterialDemandFooters { get; set; } = new List<api_material_demand_footer>(); /// <summary> /// 记录使用中库存 修改库存状态 /// </summary> public List<inventory> UpdateInventorys { get; set; } = new List<inventory>(); public List<Record> Records { get; set; } = new List<Record>(); /// <summary> /// 记录使用的工单 用于数据筛选 /// </summary> public List<int> CuttingDetailId { get; set; } = new List<int>(); public TotalModel TotalData { get; set; } = new TotalModel(); #endregion #region PC页面 /// <summary> /// 废料长度 /// </summary> public dynamic CuttingWasteLength() { return ExceptionsHelp.Instance.ExecuteT(() => { return GetDictionaryDictValue("废料长度", "CuttingWasteLength"); }, catchRetrunValue: "catchRetrunValue"); } /// <summary> /// 下料尺寸数据 /// </summary> public dynamic GetInventorys() { return ExceptionsHelp.Instance.ExecuteT(() => { OriginalInventorys = Context.Queryable<inventory>().Where(x => x.useState == (int)InventoryPlanStatus.初始 && x.status == InventoryStatus.良品).ToList(); if (OriginalInventorys.Count == 0) return ReturnTableData("原材料无", -1); return ReturnTableData("", 0, OriginalInventorys, OriginalInventorys.Count); }, catchRetrunValue: "catchRetrunValue"); } /// <summary> /// 导入下料数据 /// </summary> public dynamic GetCutterInfo() { OriginalCutterDetailWorkOrders = Context.Queryable<cutter_detail_work_order>().Where(x => x.State == (int)EnumCutBodyStatus.发布).ToList(); if (OriginalCutterDetailWorkOrders.Count == 0) return ReturnTableData("切割工单明细查询无数据", -1); return ReturnTableData("", 0, OriginalCutterDetailWorkOrders, OriginalCutterDetailWorkOrders.Count); } /// <summary> /// 套料计算 /// </summary> public dynamic NestingCalculation() { OriginalInventorys = Context.Queryable<inventory>().Where(x => x.useState == (int)InventoryPlanStatus.初始 && x.status == InventoryStatus.良品).ToList(); if (OriginalInventorys.Count == 0) return ReturnTableData("原材料库存查询无数据状态", -1); OriginalCutterHeadWorkOrders = Context.Queryable<cutter_head_work_order>().Where(x => x.State == (int)EnumCutHeadStatus.发布 || x.State == (int)EnumCutHeadStatus.部分套料).ToList(); if (OriginalCutterHeadWorkOrders.Count == 0) return ReturnTableData("切割工单头查询无数据状态:【发布或者已套料】", -1); OriginalCutterDetailWorkOrders = Context.Queryable<cutter_detail_work_order>().Where(x => x.State == (int)EnumCutBodyStatus.发布).ToList(); if (OriginalCutterDetailWorkOrders.Count == 0) return ReturnTableData("切割工单明细查询无数据状态:【发布或者已套料】", -1); var DetailMaterialCode = OriginalCutterDetailWorkOrders.Select(x => x.MaterialCode).Distinct().ToList(); //如果工单中所有需要的物料,一个都不在库存中,那就提示报错返回 foreach (var item in DetailMaterialCode) { if (OriginalInventorys.Any(x => x.materialCode == item)) break; if (item == DetailMaterialCode[DetailMaterialCode.Count - 1]) return ReturnTableData("没有切割工单所需的物料库存!", -1); } var inventories = new List<inventory>(); inventories.AddRange(OriginalInventorys); var cutDetail = new List<cutter_detail_work_order>(); cutDetail.AddRange(OriginalCutterDetailWorkOrders); var cuttingWasteLength = GetDictionaryDictValue("废料长度", "CuttingWasteLength"); #region 切割计算 decimal loss = 0; //记录切割方案 List<SchemeOperation> schemes = new List<SchemeOperation>(); var CutterList = new List<List<cutter_detail_work_order>>(); #region 数据筛选 通过切割工单的图纸以及切割工单明细的页码 进行分组操作。 LineNo,ShopDesignDrawingNo foreach (var order in OriginalCutterHeadWorkOrders.OrderBy(x => x.LineNo).ThenBy(x => x.ShopDesignDrawingNo)) { //先添加 相同的切割工单 var cutters = cutDetail.Where(t => t.headKeys == order.keys).OrderBy(x => x.CuttingLength).ToList(); CutterList.Add(cutters); var count = cutters.Count; Records.Add(new Record() { Guid = order.keys, Count = count, EidtCount = count }); } #endregion #region 计算 foreach (var Cutters in CutterList.OrderByDescending(x => x.Sum(t => t.CuttingLength))) { bool isok = false; //跳过 防止报错为null if (Cutters.Count == 0) continue; var LineCode = OriginalCutterHeadWorkOrders.FirstOrDefault(x => x.keys == Cutters.FirstOrDefault().headKeys).LineCode; var inventorys = inventories.Where(x => x.materialCode == Cutters.FirstOrDefault().MaterialCode && x.lineCode == LineCode).OrderBy(x => x.pipeLength).ToList(); if (inventorys.Count == 0) continue; #region 不需要切割 查找直接匹配管材 //套料过的切割工单明细 var WorkOrderList = new List<cutter_detail_work_order>(); foreach (var Cutter in Cutters) { //找出相同的 刚好匹配 var Equalinventory = inventorys.FirstOrDefault(x => x.pipeLength == Cutter.CuttingLength); if (Equalinventory != null) { Equalinventory.pipeLength = 0; Cutter.PipeSN = Equalinventory.pipeSN; //添加套料计划头 var cut_plan_head = new cut_plan_head(); cut_plan_head.PipeLength = Convert.ToDecimal(Equalinventory.pipeLength); cut_plan_head.OldSN = Equalinventory.oldSN ?? Equalinventory.pipeSN; cut_plan_head.PipeSN = Equalinventory.pipeSN; cut_plan_head.MaterialCode = Equalinventory.materialCode; cut_plan_head.InventoryId = Equalinventory.id; cut_plan_head.LineCode = Equalinventory.lineCode; cut_plan_head.State = (int)CutPlanExcuteStatus.初始; cut_plan_head.createBy = sysUser?.Account; cut_plan_head.createTime = DateTime.Now; var cutPlanDetail = AddCutPlanDetail(Cutter, 0, true); var scheme = schemes.Where(x => x.CutPlanHead.InventoryId == Equalinventory.id).FirstOrDefault(); WorkOrderList.Add(Cutter); //如果管材没有套料,就添加套料头和明细,否则就添加找到的套料的明细 if (scheme == null) { scheme = new SchemeOperation(); scheme.CutPlanHead = cut_plan_head; scheme.CutPlanDetails.Add(cutPlanDetail); schemes.Add(scheme); } else { scheme.CutPlanDetails.Add(cutPlanDetail); } } } foreach (var WorkOrder in WorkOrderList) { Cutters.Remove(WorkOrder); } #endregion //统计次数 var CuttersCount = Cutters.Count; if (CuttersCount == 0) continue; #region 执行单管组合 if (CuttersCount != 1) { //从最短的管材进行配比 foreach (var inventory in inventorys) { //查询当前损耗 var material = Context.Queryable<material>().First(x => x.MaterialCode == inventory.materialCode); loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss)); //损耗合计 var CuttersSum = Cutters.Sum(x => x.CuttingLength) + loss * CuttersCount; if (inventory.pipeLength >= CuttersSum) { //切割方案实体 SchemeOperation scheme = new SchemeOperation(); //添加计算过的切割下料尺寸数据 scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(inventory.pipeLength), OldSN = inventory.oldSN ?? inventory.pipeSN, PipeSN = inventory.pipeSN, MaterialCode = inventory.materialCode, InventoryId = inventory.id, LineCode = inventory.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now }; inventory.pipeLength -= CuttersSum; Cutters.ForEach(t => { t.PipeSN = inventory.pipeSN; }); if (schemes.Any(x => x.CutPlanHead.InventoryId == inventory.id)) { schemes.Where(x => x.CutPlanHead.InventoryId == inventory.id).FirstOrDefault().CutPlanDetails.AddRange(AddCutPlanDetail(Cutters, loss)); } else { scheme.CutPlanDetails = AddCutPlanDetail(Cutters, loss); schemes.Add(scheme); } isok = true; break; } } } #endregion //false 组合没有执行成功 if (isok) continue; //单管找出最短的进行配比 inventorys.ForEach(t => { //切割方案实体 SchemeOperation scheme = new SchemeOperation(); var material = Context.Queryable<material>().First(x => x.MaterialCode == t.materialCode); loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss)); //切割方案 取原材料数据 scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(t.pipeLength), OldSN = t.oldSN ?? t.pipeSN, PipeSN = t.pipeSN, MaterialCode = t.materialCode, InventoryId = t.id, LineCode = t.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now }; for (int i = 0; i < CuttersCount; i++) { //没有下料切割数据那么就不用执行了 if (Cutters.Count == 0) { //跳出for循环 break; } //当前下料尺寸 拿最短的 var Cutter = Cutters.First(); //当前物料长度够 就进行切割方案添加 当不够那么后面的数据也将不会进入。 需要更换一个物料进行分配 if ((t.pipeLength - loss) >= Cutter.CuttingLength) { Cutter.PipeSN = t.pipeSN; t.pipeLength -= Cutter.CuttingLength + loss; if (schemes.Exists(x => x.CutPlanHead.InventoryId == t.id)) { schemes.First(x => x.CutPlanHead.InventoryId == t.id).CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss)); } else { scheme.CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss)); } //删除 计算后面下料数据 Cutters.Remove(Cutter); } else { //当前物料长度 不够 就换一个 break; } } if (scheme.CutPlanDetails.Count > 0) { schemes.Add(scheme); } }); } #endregion foreach (var scheme in schemes) { scheme.CutPlanHead.LossCount = scheme.CutPlanDetails.Sum(x => x.Loss); var SumProducts = scheme.CutPlanDetails.Sum(x => x.Loss + x.CuttingLength); double Percentage = Convert.ToDouble(SumProducts) / Convert.ToDouble(scheme.CutPlanHead.PipeLength); scheme.Percentage = Percentage.ToString("0.00%"); foreach (var CutPlanDetail in scheme.CutPlanDetails) { //进行删除 切割方案当中的下料数据 cutDetail.RemoveAll(x => x.id == CutPlanDetail.CuttingDetailId); } #region 余料计算 foreach (var t in inventories.Where(x => x.id == scheme.CutPlanHead.InventoryId)) { if (t.pipeLength < Convert.ToDecimal(cuttingWasteLength)) { t.pipeSN = "废料"; } else { if (t.pipeSN.Contains("-YL")) { var inedx = t.pipeSN.IndexOf("-YL"); var onepieceName = t.pipeSN.Substring(0, inedx); var YLNo = t.pipeSN.Substring(t.pipeSN.Length - 2, 2); if (YLNo.Contains("0")) { int Nos = Convert.ToInt32(YLNo) + 1; t.pipeSN = onepieceName + $"-YL-0{Nos}"; } else { int Nos = Convert.ToInt32(YLNo) + 1; t.pipeSN = onepieceName + $"-YL-{Nos}"; } } else { t.oldSN = t.pipeSN; t.pipeSN += "-YL-01"; } } scheme.CutPlanHead.OddmentsLength = Convert.ToDecimal(t.pipeLength); scheme.CutPlanHead.OddmentsCode = t.pipeSN; #region 更新当前使用的库存状态 t.useState = (int)InventoryPlanStatus.已经套料; t.updateTime = DateTime.Now; t.updateBy = sysUser?.Account; UpdateInventorys.Add(t); #endregion } #endregion } //切割方案 TotalData.SchemeOperations = schemes; //余材料 TotalData.Inventories = inventories.OrderBy(x => x.pipeLength).ToList(); //未切割工单 TotalData.CutterDetailWorkOrders = cutDetail; #endregion return ReturnTableData("计算成功!", 0, new { Middle = TotalData.SchemeOperations, RightUp = TotalData.Inventories, RightDown = TotalData.CutterDetailWorkOrders }); } /// <summary> /// 保存套料信息 /// </summary> public dynamic SaveNestingProgramme() { Init(); var result = NestingCalculation(); if (result.code != 0) return result; if (TotalData.SchemeOperations.Count == 0) return ReturnTableData("切割方案数据为空", -1); if (Context.Queryable<cut_plan_detail>().Any(x => x.CuttingDetailId == CuttingDetailId.FirstOrDefault())) return ReturnTableData("已添加", -1); #region 修改工单信息 var UpdateCutterHead = new List<cutter_head_work_order>(); foreach (var Record in Records) { if (Record.EidtCount == Record.Count) continue; var cutter = OriginalCutterHeadWorkOrders.Where(x => x.keys == Record.Guid).FirstOrDefault(); if (Record.Count > Record.EidtCount && Record.EidtCount > 0) cutter.State = (int)EnumCutHeadStatus.部分套料; else if (Record.EidtCount == 0) cutter.State = (int)EnumCutHeadStatus.已套料; cutter.updateBy = sysUser?.Account; cutter.updateTime = DateTime.Now; UpdateCutterHead.Add(cutter); } #endregion #region 记录到之前余料数据当中 var AddCutPlanDetail = new List<cut_plan_detail>(); var AddCutPlanHead = new List<cut_plan_head>(); foreach (var SchemeOperations in TotalData.SchemeOperations) { var guid = Guid.NewGuid(); SchemeOperations.CutPlanHead.Keys = guid; SchemeOperations.CutPlanHead.createBy = sysUser?.Account; SchemeOperations.CutPlanHead.createTime = DateTime.Now; AddCutPlanHead.Add(SchemeOperations.CutPlanHead); SchemeOperations.CutPlanDetails.ForEach(t => { t.HeadKeys = guid; t.createBy = sysUser?.Account; t.createTime = DateTime.Now; }); AddCutPlanDetail.AddRange(SchemeOperations.CutPlanDetails); } #endregion var saveResults = UpdateCuttingPlan(UpdateInventorys, UpdateCutterHead, UpdateCutterDetails, AddCutPlanHead, AddCutPlanDetail); if (saveResults.code != 0) return ReturnTableData($"保存失败,原因:{saveResults.msg}", -1); return ReturnTableData("保存成功", 0); } /// <summary> /// 套料保存 /// </summary> /// <param name="updateInventory">修改库存状态</param> /// <param name="cutterHeadWorkOrder">修改切割工单主状态</param> /// <param name="cutterDetailWorkOrder">修改切割工单子状态</param> /// <param name="addcutPlans">添加方案主</param> /// <param name="addcutdetail">添加方案子</param> /// <returns></returns> public TableData UpdateCuttingPlan(List<inventory> updateInventory, List<cutter_head_work_order> cutterHeadWorkOrder, List<cutter_detail_work_order> cutterDetailWorkOrder, List<cut_plan_head> addcutPlans, List<cut_plan_detail> addcutdetail) { Context.Ado.BeginTran(); var result = new TableData { code = 0, data = null, count = 0, msg = "" }; if (Context.Updateable(updateInventory).UpdateColumns(t => new { t.useState, t.updateBy, t.updateTime }).ExecuteCommand() <= 0) result.msg += "更新库存数据失败"; if (Context.Updateable(cutterHeadWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).ExecuteCommand() <= 0) result.msg += "更新切割头表数据失败"; if (Context.Updateable(cutterDetailWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).ExecuteCommand() <= 0) result.msg += "更新切割明细数据失败"; if (Context.Insertable(addcutPlans).ExecuteCommand() <= 0) result.msg += "新增套料方案头表数据失败"; if (Context.Insertable(addcutdetail).ExecuteCommand() <= 0) result.msg += "新增套料方案明细数据失败"; if (result.msg != "") { Context.Ado.RollbackTran(); result.code = -1; } Context.Ado.CommitTran(); return result; } /// <summary> /// 切割明细 下料切割工单 /// </summary> /// <param name="cutter"></param> /// <param name="Loss"></param> /// <returns></returns> public cut_plan_detail AddCutPlanDetail(cutter_detail_work_order cutter, decimal Loss = 0, bool Notcutting = false) { #region 切割工单状态修改 cutter.State = (int)EnumCutHeadStatus.已套料; cutter.updateBy = sysUser?.Account; cutter.updateTime = DateTime.Now; UpdateCutterDetails.Add(cutter); CuttingDetailId.Add(cutter.id); Records.FirstOrDefault(x => x.Guid == cutter.headKeys).EidtCount--; #endregion cut_plan_detail cutPlanDetail = new cut_plan_detail(); cutPlanDetail.PipeSN = cutter.PipeSN; cutPlanDetail.BarCode = cutter.BarCode; cutPlanDetail.CuttingLength = cutter.CuttingLength; cutPlanDetail.Loss = Loss; cutPlanDetail.CuttingDetailId = cutter.id; cutPlanDetail.CuttingHeadId = cutter.headKeys; cutPlanDetail.State = Notcutting ? (int)CutPlanExcuteStatus.无需切割 : (int)CutPlanExcuteStatus.初始; cutPlanDetail.createBy = sysUser?.Account; cutPlanDetail.createTime = DateTime.Now; return cutPlanDetail; } /// <summary> /// 切割明细 切割工单 /// </summary> /// <param name="cutters"></param> /// <param name="Loss"></param> /// <returns></returns> public List<cut_plan_detail> AddCutPlanDetail(List<cutter_detail_work_order> cutters, decimal Loss = 0) { List<cut_plan_detail> list = new List<cut_plan_detail>(); foreach (var cutter in cutters) { CuttingDetailId.Add(cutter.id); Records.FirstOrDefault(x => x.Guid == cutter.headKeys).EidtCount--; var cutt = new cut_plan_detail() { PipeSN = cutter.PipeSN, BarCode = cutter.BarCode, CuttingLength = Convert.ToInt32(cutter.CuttingLength), Loss = Loss, CuttingDetailId = cutter.id, CuttingHeadId = cutter.headKeys, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now }; list.Add(cutt); cutter.updateBy = sysUser?.Account; cutter.updateTime = DateTime.Now; cutter.State = (int)EnumCutHeadStatus.已套料; UpdateCutterDetails.Add(cutter); } return list; } /// <summary> /// 导出方法 /// </summary> /// <returns></returns> public dynamic Export() { return ExceptionsHelp.Instance.ExecuteT(() => { var result = new TableData(); var nestingCalculation = NestingCalculation(); List<dynamic> heads = new List<dynamic>(); List<dynamic> details = new List<dynamic>(); if (nestingCalculation.code > -1) { foreach (var item in nestingCalculation.data.Middle) { item.CutPlanHead.Percentage = item.Percentage; heads.Add(item.CutPlanHead); foreach (var items in item.CutPlanDetails) { details.Add(items); } } } result.data = new { LeftUp = GetInventorys().data, LeftDown = GetCutterInfo().data, Middle = nestingCalculation.code > -1 ? heads : null, MiddleDesc = nestingCalculation.code > -1 ? details : null, RightUp = nestingCalculation.code > -1 ? nestingCalculation.data.RightUp : null, RightDown = nestingCalculation.code > -1 ? nestingCalculation.data.RightDown : null }; return result; }, catchRetrunValue: "actionList"); } /// <summary> /// 返回信息 /// </summary> public TableData ReturnTableData(string msg = "", int? code = 0, dynamic data = null, int count = 0) { return new TableData() { code = Convert.ToInt32(code == null ? 0 : code), msg = msg, data = data, count = count }; } #endregion #region 执行已呼叫的单管进行套料 /// <summary> /// 套料计算 /// item1=true 套料停止往下执行 ,如果为true 还要继续往下走 mes 加-$ 【面向过程遗留问题】 /// </summary> public Tuple<bool, string> CalledNestingCalculation(List<Guid> bodyKeys) { #region 查询 工单物料需求表 List<api_material_demand_footer> demandFooters; if (bodyKeys == null) { //执行已呼叫单管操作 var huJiao = (int)MaterialDemandFooterState.已呼叫; demandFooters = base.Context.Queryable<api_material_demand_footer>() .Where(t => t.Unit.ToUpper() == "MM" && t.State == huJiao).ToList(); } else { //执行选中的叫料数据套料 var paiChan = (int)MaterialDemandFooterState.已排产; demandFooters = base.Context.Queryable<api_material_demand_footer>() .Where(t => t.Unit.ToUpper() == "MM" && t.State == paiChan && bodyKeys.Contains(t.bodyKeys)).ToList(); } if (demandFooters.Count == 0) { Log4NetHelper.Instance.Error("CalledNestingCalculation:工单物料需求查询无数据!"); return new Tuple<bool, string>(true, "工单物料需求查询无数据!"); } MaterialDemandFooters.AddRange(demandFooters); #endregion #region 查找切割工单 //查找工单有问题,既然用了bodyKeys,就应该按照body对应的单管号来赛选工单,如果用IWPNo来筛选范围一下扩大了,超出了bodyKeys的范围 var iWPNos = demandFooters.Select(x => x.IWPNo).ToList(); OriginalCutterHeadWorkOrders = Context.Queryable<cutter_head_work_order>().Where(x => x.State == (int)EnumCutHeadStatus.发布 || x.State == (int)EnumCutHeadStatus.部分套料 && iWPNos.Contains(x.IWPNo)).ToList(); if (OriginalCutterHeadWorkOrders.Count == 0) { Log4NetHelper.Instance.Error("CalledNestingCalculation:切割工单头信息查询无数据"); return new Tuple<bool, string>(true, "未查询到切割工单头状态【发布】的数据"); } var cutterHeadKeys = OriginalCutterHeadWorkOrders.Select(x => x.keys).ToList(); OriginalCutterDetailWorkOrders = Context.Queryable<cutter_detail_work_order>().Where(t => t.State == (int)EnumCutBodyStatus.发布 && cutterHeadKeys.Contains(t.headKeys)).ToList(); OriginalCutterDetailWorkOrders = OriginalCutterDetailWorkOrders.Where(t => demandFooters.Exists(x => x.SpoolNo == t.SpoolNo && x.PartCode == t.PartCode && x.ProjectCode == t.ProjectCode)).ToList(); if (OriginalCutterDetailWorkOrders.Count == 0) { Log4NetHelper.Instance.Error("CalledNestingCalculation:下料尺寸数据无"); return new Tuple<bool, string>(true, "下料尺寸数据(切割工单明细)信息查询无数据"); } #endregion var detailMaterialCode = OriginalCutterDetailWorkOrders.Select(x => x.MaterialCode).Distinct().ToList(); var isCallWms = false; //如果工单中所有的物料,一个都不在库存中,那就提示报错返回 foreach (var item in detailMaterialCode) { if (OriginalInventorys.Any(x => x.materialCode == item)) { isCallWms = true; break; } //if (detailMaterialCode.IndexOf(item) + 1 == detailMaterialCode.Count) //{ // Log4NetHelper.Instance.Error("没有切割工单所需的物料库存"); // return new Tuple<bool, string>(true, "没有切割工单所需的物料库存"); //} } //仓库没有切割工单的物料 需要叫料仓储Wms 程序继续往下走 lsw if (!isCallWms) { Log4NetHelper.Instance.Error("没有切割工单所需的物料库存"); return new Tuple<bool, string>(true, "没有切割工单所需的物料库存-$"); } var inventories = new List<inventory>(); OriginalInventorys.ForEach(i => inventories.Add(i)); var cutDetail = new List<cutter_detail_work_order>(); OriginalCutterDetailWorkOrders.ForEach(i => cutDetail.Add(i)); var cuttingWasteLength = GetDictionaryDictValue("废料长度", "CuttingWasteLength"); #region 切割计算 decimal loss = 0; //记录切割方案 var schemes = new List<SchemeOperation>(); var CutterList = new List<List<cutter_detail_work_order>>(); #region 数据筛选 通过切割工单的图纸以及切割工单明细的页码 进行分组操作。 LineNo,ShopDesignDrawingNo foreach (var order in OriginalCutterHeadWorkOrders.OrderBy(x => x.LineNo).ThenBy(x => x.ShopDesignDrawingNo)) { //先添加 相同的切割工单 var cutters = cutDetail.Where(t => t.headKeys == order.keys).OrderBy(x => x.CuttingLength).ToList(); CutterList.Add(cutters); var count = cutters.Count; Records.Add(new Record() { Guid = order.keys, Count = count, EidtCount = count }); } #endregion #region 计算 foreach (var Cutters in CutterList.OrderByDescending(x => x.Sum(t => t.CuttingLength))) { bool isok = false; //跳过 防止报错为null if (Cutters.Count == 0) continue; var LineCode = OriginalCutterHeadWorkOrders.FirstOrDefault(x => x.keys == Cutters.FirstOrDefault().headKeys).LineCode; var materialCodes = Cutters.Select(t => t.MaterialCode).ToList(); var inventorys = inventories.Where(x => materialCodes.Contains(x.materialCode) && x.lineCode == LineCode).OrderBy(x => x.pipeLength).ToList(); if (inventorys.Count == 0) continue; #region 不需要切割 查找直接匹配管材 var WorkOrderList = new List<cutter_detail_work_order>(); foreach (var Cutter in Cutters) { //找出相同的 刚好匹配 var Equalinventory = inventorys.FirstOrDefault(x => x.pipeLength == Cutter.CuttingLength); if (Equalinventory != null) { SchemeOperation scheme = new SchemeOperation(); scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(Equalinventory.pipeLength), OldSN = Equalinventory.oldSN ?? Equalinventory.pipeSN, PipeSN = Equalinventory.pipeSN, MaterialCode = Equalinventory.materialCode, InventoryId = Equalinventory.id, LineCode = Equalinventory.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now }; Equalinventory.pipeLength = 0; Cutter.PipeSN = Equalinventory.pipeSN; if (schemes.Any(x => x.CutPlanHead.InventoryId == Equalinventory.id)) { schemes.FirstOrDefault(x => x.CutPlanHead.InventoryId == Equalinventory.id).CutPlanDetails.Add(AddCutPlanDetail(Cutter, 0, true)); WorkOrderList.Add(Cutter); } else { scheme.CutPlanDetails.Add(AddCutPlanDetail(Cutter, 0, true)); schemes.Add(scheme); WorkOrderList.Add(Cutter); } } } foreach (var WorkOrder in WorkOrderList) { Cutters.Remove(WorkOrder); } #endregion //统计次数 var CuttersCount = Cutters.Count; if (CuttersCount == 0) continue; #region 执行单管组合 if (CuttersCount != 1) { //从最短的管材进行配比 foreach (var inventory in inventorys) { //查询当前损耗 var material = Context.Queryable<material>().First(x => x.MaterialCode == inventory.materialCode); loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss)); //损耗合计 var CuttersSum = Cutters.Sum(x => x.CuttingLength) + loss * CuttersCount; if (inventory.pipeLength >= CuttersSum) { //切割方案实体 SchemeOperation scheme = new SchemeOperation(); //添加计算过的切割下料尺寸数据 scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(inventory.pipeLength), OldSN = inventory.oldSN ?? inventory.pipeSN, PipeSN = inventory.pipeSN, MaterialCode = inventory.materialCode, InventoryId = inventory.id, LineCode = inventory.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now }; inventory.pipeLength -= CuttersSum; Cutters.ForEach(t => { t.PipeSN = inventory.pipeSN; }); if (schemes.Any(x => x.CutPlanHead.InventoryId == inventory.id)) { schemes.Where(x => x.CutPlanHead.InventoryId == inventory.id).FirstOrDefault().CutPlanDetails.AddRange(AddCutPlanDetail(Cutters, loss)); } else { scheme.CutPlanDetails = AddCutPlanDetail(Cutters, loss); schemes.Add(scheme); } isok = true; break; } } } #endregion //false 组合没有执行成功 if (isok) continue; //单管找出最短的进行配比 inventorys.ForEach(t => { //切割方案实体 SchemeOperation scheme = new SchemeOperation(); var material = Context.Queryable<material>().First(x => x.MaterialCode == t.materialCode); loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss)); //切割方案 取原材料数据 scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(t.pipeLength), OldSN = t.oldSN ?? t.pipeSN, PipeSN = t.pipeSN, MaterialCode = t.materialCode, InventoryId = t.id, LineCode = t.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now }; for (int i = 0; i < CuttersCount; i++) { //没有下料切割数据那么就不用执行了 if (Cutters.Count == 0) { //跳出for循环 break; } //当前下料尺寸 拿最短的 var Cutter = Cutters.First(); //当前物料长度够 就进行切割方案添加 当不够那么后面的数据也将不会进入。 需要更换一个物料进行分配 if ((t.pipeLength - loss) >= Cutter.CuttingLength) { Cutter.PipeSN = t.pipeSN; t.pipeLength -= Cutter.CuttingLength + loss; if (schemes.Exists(x => x.CutPlanHead.InventoryId == t.id)) { schemes.First(x => x.CutPlanHead.InventoryId == t.id).CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss)); } else { scheme.CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss)); } //删除 计算后面下料数据 Cutters.Remove(Cutter); } else { //当前物料长度 不够 就换一个 break; } } if (scheme.CutPlanDetails.Count > 0) { schemes.Add(scheme); } }); } #endregion foreach (var scheme in schemes) { scheme.CutPlanHead.LossCount = scheme.CutPlanDetails.Sum(x => x.Loss); var SumProducts = scheme.CutPlanDetails.Sum(x => x.Loss + x.CuttingLength); double Percentage = Convert.ToDouble(SumProducts) / Convert.ToDouble(scheme.CutPlanHead.PipeLength); scheme.Percentage = Percentage.ToString("0.00%"); foreach (var CutPlanDetail in scheme.CutPlanDetails) { //进行删除 切割方案当中的下料数据 cutDetail.RemoveAll(x => x.id == CutPlanDetail.CuttingDetailId); } #region 余料计算 foreach (var t in inventories.Where(x => x.id == scheme.CutPlanHead.InventoryId)) { if (t.pipeLength < Convert.ToDecimal(cuttingWasteLength)) { t.pipeSN = "废料"; } else { if (t.pipeSN.Contains("-YL")) { var inedx = t.pipeSN.IndexOf("-YL"); var onepieceName = t.pipeSN.Substring(0, inedx); var YLNo = t.pipeSN.Substring(t.pipeSN.Length - 2, 2); if (YLNo.Contains("0")) { int Nos = Convert.ToInt32(YLNo) + 1; t.pipeSN = onepieceName + $"-YL-0{Nos}"; } else { int Nos = Convert.ToInt32(YLNo) + 1; t.pipeSN = onepieceName + $"-YL-{Nos}"; } } else { t.oldSN = t.pipeSN; t.pipeSN += "-YL-01"; } } scheme.CutPlanHead.OddmentsLength = Convert.ToDecimal(t.pipeLength); scheme.CutPlanHead.OddmentsCode = t.pipeSN; #region 更新当前使用的库存状态 t.useState = (int)InventoryPlanStatus.已经套料; t.updateTime = DateTime.Now; t.updateBy = sysUser?.Account; UpdateInventorys.Add(t); #endregion } #endregion } //切割方案 TotalData.SchemeOperations = schemes; //余材料 TotalData.Inventories = inventories.OrderBy(x => x.pipeLength).ToList(); //未切割工单 TotalData.CutterDetailWorkOrders = cutDetail; #endregion return new Tuple<bool, string>(false, ""); } /// <summary> /// 执行已呼叫的切割工单 /// </summary> public Tuple<bool, string> ExecuteCalledCuttingWorkOrder(List<Guid> bodyKeys) { Init(); #region 库存信息查询 OriginalInventorys = Context.Queryable<inventory>().Where(x => x.useState == (int)InventoryPlanStatus.初始 && x.status == InventoryStatus.良品).ToList(); if (OriginalInventorys.Count == 0) { Log4NetHelper.Instance.Error("CalledNestingCalculation:库存信息需求查询无数据!"); return new Tuple<bool, string>(false, "库存信息需求查询无数据!"); } #endregion var result = CalledNestingCalculation(bodyKeys); if (result.Item1) return result; if (TotalData.SchemeOperations.Count == 0) { Log4NetHelper.Instance.Error("切割方案数据为空"); return new Tuple<bool, string>(true, "切割方案数据为空"); } foreach (var item in CuttingDetailId) { if (Context.Queryable<cut_plan_detail>().Any(it => it.CuttingDetailId == item)) { Log4NetHelper.Instance.Error("套料方案数据已经存在切割明细工单套料数据:" + item); return new Tuple<bool, string>(true, "套料方案数据已经存在切割明细工单套料数据" + item); } } #region 修改工单信息 var UpdateCutterHead = new List<cutter_head_work_order>(); foreach (var Record in Records) { if (Record.EidtCount == Record.Count) continue; var cutter = OriginalCutterHeadWorkOrders.FirstOrDefault(x => x.keys == Record.Guid); if (Record.Count > Record.EidtCount && Record.EidtCount > 0) cutter.State = (int)EnumCutHeadStatus.部分套料; else if (Record.EidtCount == 0) cutter.State = (int)EnumCutHeadStatus.已套料; cutter.updateBy = sysUser?.Account; cutter.updateTime = DateTime.Now; UpdateCutterHead.Add(cutter); } #endregion #region 记录到之前余料数据当中 var AddCutPlanDetail = new List<cut_plan_detail>(); var AddCutPlanHead = new List<cut_plan_head>(); foreach (var SchemeOperations in TotalData.SchemeOperations) { var guid = Guid.NewGuid(); SchemeOperations.CutPlanHead.Keys = guid; SchemeOperations.CutPlanHead.createBy = sysUser?.Account; SchemeOperations.CutPlanHead.createTime = DateTime.Now; AddCutPlanHead.Add(SchemeOperations.CutPlanHead); SchemeOperations.CutPlanDetails.ForEach(t => { t.HeadKeys = guid; t.createBy = sysUser?.Account; t.createTime = DateTime.Now; }); AddCutPlanDetail.AddRange(SchemeOperations.CutPlanDetails); } #endregion #region 修改配料单明细状态改为已套料 var demand_Footers = MaterialDemandFooters.Where(x => UpdateCutterDetails.Exists(t => t.SpoolNo == x.SpoolNo && t.PartCode == x.PartCode)); //demand_Footers.ForEach(t => t.State = (int)MaterialDemandFooterState.已套料); var demandfooters = demand_Footers.Where(t => t.State == (int)MaterialDemandFooterState.已套料).ToList(); #endregion var resultSave = UpdateCalledCuttingPlan(UpdateInventorys, UpdateCutterHead, UpdateCutterDetails, AddCutPlanHead, AddCutPlanDetail, demandfooters); return resultSave; } /// <summary> /// 套料保存 /// </summary> /// <param name="updateInventory">修改库存状态</param> /// <param name="cutterHeadWorkOrder">修改切割工单主状态</param> /// <param name="cutterDetailWorkOrder">修改切割工单子状态</param> /// <param name="addcutPlans">添加方案主</param> /// <param name="addcutdetail">添加方案子</param> /// <param name="demand_Footers"></param> /// <returns></returns> public Tuple<bool, string> UpdateCalledCuttingPlan(List<inventory> updateInventory, List<cutter_head_work_order> cutterHeadWorkOrder, List<cutter_detail_work_order> cutterDetailWorkOrder, List<cut_plan_head> addcutPlans, List<cut_plan_detail> addcutdetail, List<api_material_demand_footer> demand_Footers) { //更新库存数据失败 Context.Updateable(updateInventory).UpdateColumns(t => new { t.useState, t.updateBy, t.updateTime }).AddQueue(); //更新切割头表数据失败 Context.Updateable(cutterHeadWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).AddQueue(); //更新切割明细数据失败 Context.Updateable(cutterDetailWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).AddQueue(); //新增套料方案头表数据失败 Context.Insertable(addcutPlans).AddQueue(); //新增套料方案明细数据失败 Context.Insertable(addcutdetail).AddQueue(); //修改配料单明显状态失败 Context.Updateable(demand_Footers).AddQueue(); Context.SaveQueues(); return new Tuple<bool, string>(false, "套料保存"); } #endregion public void Init() { OriginalCutterHeadWorkOrders.Clear(); OriginalCutterDetailWorkOrders.Clear(); OriginalInventorys.Clear(); UpdateCutterDetails.Clear(); UpdateInventorys.Clear(); Records.Clear(); CuttingDetailId.Clear(); MaterialDemandFooters.Clear(); } } }