生产订单开工批号匹配问题
客户需求
Bom子项的发料方式为【开工倒冲】时,开工后,生成领料单时子项匹配不到对应的批号!客户的批号基本是手工维护,在做期初或者杂收单等单据时已维护号批号,需要根据拣货规则取出当前生产订单备料表料品对应的批号!
开发方式
开工后会自动创建领料单,在领料单的setDefaultValue事件,去匹配对应的批号,拆行或不拆行
错误点
- 不拆行没问题,但是只符合当前批号库存满足需求量的情况
- 需要拆行的话,错误层出不穷!
拆行错误展示
//错误代码展示:
//备份表体信息
IssueDocLine hisIss = holder.IssueDocLines[0];
//删除原先所有表体
for (int curIdx = holder.IssueDocLines.Count - 1; curIdx >= 0; curIdx--)
{
holder.IssueDocLines.RemoveAt(curIdx);
}
//重新定义表体行!!
IssueDocLine iss1 = IssueDocLine.Create(holder);
long iss1ID = iss1.ID;
//从备份的hislss复制到新的iss1
Base.BusinessEntityHelper.CopyTo(hisIss, iss1, false);
LotMaster lotMaster2 = LotMaster.Finder.Find(" LotCode = 'Test01'");
iss1.LotMaster= lotMaster2;
iss1.ID = iss1ID;
iss1.IssueQty = 80;
iss1.IssuedQty = 80;
iss1.LineNum = 10;
Session.Current.InList(iss1);
IssueDocLine iss2 = IssueDocLine.Create(holder);
long iss2ID = iss2.ID;
//从备份的hislss复制到新的iss2
Base.BusinessEntityHelper.CopyTo(hisIss, iss2, false);
iss2.ID = iss2ID;
iss2.LineNum = 20;
iss2.IssueQty = 20;
iss2.IssuedQty = 20;
LotMaster lotMaster = LotMaster.Finder.Find(" LotCode = 'Test002'");
iss2.LotMaster = lotMaster;
Session.Current.InList(iss2);
错误点:
- 删除了之前的行记录
- 重新赋值了ID
LotMaster lotMaster2 = LotMaster.Finder.Find(" LotCode = 'Test01'");
holder.IssueDocLines[0].LotMaster = lotMaster2;
holder.IssueDocLines[0].IssueQty = 80;
holder.IssueDocLines[0].IssuedQty = 80;
IssueDocLine iss2 = IssueDocLine.Create(holder);
long iss2ID = iss2.ID;
Base.BusinessEntityHelper.CopyTo(holder.IssueDocLines[0], iss2, false);
iss2.ID = iss2ID;
iss2.LineNum = 20;
iss2.IssueQty = 20;
iss2.IssuedQty = 20;
LotMaster lotMaster = LotMaster.Finder.Find(" LotCode = 'Test002'");
iss2.LotMaster = lotMaster;
Session.Current.InList(iss2);
错误点:
3. 这里其实代码没错误!,但是最严重的错误就是,setDefalutValue会再给赋值的情况下不断地进入此方法,所以每进入一次,则插入一条记录,导致提示行号重复!!!
详细代码
namespace UFIDA.U9.Cust.SongYuan.LingLiaoPluginBE
{
using System;
using System.Collections.Generic;
using System.Text;
using UFIDA.U9.Base;
using UFSoft.UBF.Business;
using UFIDA.U9.Complete.RCVRpt;
using UFSoft.UBF.Util.DataAccess;
using UFSoft.UBF.Util.Log;
using UFSoft.UBF.Util.Context;
using UFIDA.U9.CBO.SCM;
using UFSoft.UBF.Transactions;
using UFSoft.UBF.Eventing;
using UFIDA.U9.MO.MO;
using UFIDA.U9.Complete.CompleteRpt;
using UFIDA.U9.MO.SFC.DispatchOrder;
using UFIDA.U9.MO.Issue;
using UFIDA.U9.CBO.SCM.Item;
using UFIDA.U9.CBO.MFG.BOM;
using UFIDA.U9.PM.Rcv;
using System.Linq;
using UFIDA.U9.Complete.Enums;
using UFIDA.U9.InvDoc.TransferIn;
using UFIDA.U9.Lot;
using UFIDA.U9.InvDoc.MiscRcv;
using UFIDA.U9.InvDoc.Enums;
using UFIDA.U9.MO.Enums;
using UFIDA.U9.CBO.SCM.Warehouse;
using UFIDA.U9.Base.Organization;
using UFIDA.U9.InvDoc.WhInit;
using UFIDA.U9.InvDoc.TransferForm;
using System.Data;
using UFSoft.UBF.Sys.Database;
using UFIDA.U9.Base.Profile;
public partial class ShipUpdatedExtend : IEventSubscriber
{
ILogger logger = LoggerManager.GetLogger("=======赋值完工倒冲物料批号================");
public long issueDocID;
public void Notify(object[] args)
{
#region 从事件参数中取得当前业务实体
if (args == null || args.Length == 0 || !(args[0] is UFSoft.UBF.Business.EntityEvent))
return;
BusinessEntity.EntityKey key = ((UFSoft.UBF.Business.EntityEvent)args[0]).EntityKey;
if (key == null)
return;
IssueDoc holder = key.GetEntity() as IssueDoc;
if (holder == null)
return;
//判断第几次进来! 如果当前领料行大于备料行,代表进行过拆行操作,则直接退出
MOPickList.EntityList validateMOPickList = MOPickList.Finder.FindAll(" MO = '" + holder.IssueDocLines[0].MO.ID + "'");
if (holder.IssueDocLines.Count > validateMOPickList.Count)
{
return;
}
int docLineC = holder.IssueDocLines.Count;
//这里要对holder.IssueDocLines先赋值到另一个对象,再遍历另一个对象,否则后面拆行后holder.IssueDocLines值会不断增加,陷入死循环!!
for (int b = 0;b < docLineC; b++)
{
//
MO mo = holder.IssueDocLines[b].MO;
if (mo == null)
{
logger.Error("生产订单获取为空");
return;
}
ItemMaster item = holder.IssueDocLines[b].Item;
if (item == null)
{
logger.Error("料品获取为空,生产订单单号:" + holder.IssueDocLines[b].MO.DocNo);
return;
}
//判断物料的批号参数是否为空,为空进行下一个备料的批号匹配 2023-06-27
if (item.InventoryInfo == null || item.InventoryInfo.LotParam == null)
{
continue;
}
//查找批号:
//1. 先判断Bom里是否是完工倒冲
//查找生产订单的Bom母项
BOMMaster bOMMaster = mo.BOMMaster;
if (bOMMaster == null)
{
logger.Error("Bom母项获取为空,生产订单单号:" + holder.IssueDocLines[b].MO.DocNo);
return;
}
//获取母项子项 -- 2023 08 25修改 不是根据Bom子项来做 ,而是根据备料表来做
MOPickList.EntityList mOPickList = MOPickList.Finder.FindAll(" MO = '" + mo.ID + "'");
if (mOPickList == null || mOPickList.Count == 0)
{
logger.Error("备料获取为空,生产订单单号:" + holder.IssueDocLines[b].MO.DocNo);
return;
}
UFIDA.U9.Lot.LotMaster rcvLot = null;
foreach (MOPickList mOPick in mOPickList)
{
//判断子项发料方式是否为开工倒冲
if (mOPick.IssueStyle != null && UFIDA.U9.CBO.MFG.Enums.IssueStyleEnum.StartWorkingPull == mOPick.IssueStyle)
{
//判断领料表体此行物料是否是同一物料 --
if (item.ID == mOPick.ItemMaster.ID)
{
//判断料品为采购件或者是制造件
if (ItemTypeAttributeEnum.PurchasePart == item.ItemFormAttribute)
{
//按照顺序,期初库存-》生产退料—》杂收-》调入-》收货-》形态转换
//20230-8-25新逻辑
//开发逻辑:遍历所有的单子,按照单子和时间排序创建一个Map,Map存储批号,批号对应的库存,然后遍历Map,
//1. 先判断map的所有库存加起来是否满足需求量,满足继续遍历,不满足则直接提示错误
//2. 如果单个批号满足库存需求,则直接赋值批号,结束逻辑
//3. 如果单个不满足,则一直遍历,遍历一次加一行
//result的值为目前料品匹配单子里的所有有库存的批号,和批号对应的库存量
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
result = getAllLotInfo(item, mOPick.SupplyWh, "采购");
chaihang(holder, mOPick, result, b);
}
else if (ItemTypeAttributeEnum.MakePart == item.ItemFormAttribute)
{
//按照顺序,期初库存-》生产退料—》杂收-》调入-》入库-》形态转换
//20230-8-25新逻辑
//开发逻辑:遍历所有的单子,按照单子和时间排序创建一个Map,Map存储批号,批号对应的库存,然后遍历Map,
//1. 先判断map的所有库存加起来是否满足需求量,满足继续遍历,不满足则直接提示错误
//2. 如果单个批号满足库存需求,则直接赋值批号,结束逻辑
//3. 如果单个不满足,则一直遍历,遍历一次加一行
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
result = getAllLotInfo(item, mOPick.SupplyWh, "制造");
chaihang(holder, mOPick, result, b);
}
}
}
}
}
}
private void chaihang(IssueDoc holder, MOPickList mOPick, Dictionary<string, decimal> result, int b)
{
#region 执行上面第一步
decimal xiancunliang = 0;
foreach (var ele in result)
{
xiancunliang += ele.Value;
}
//查找当前Bom子项对应的备料信息的实际需求数量
if (xiancunliang < mOPick.ActualReqQty)
{
//总现存量小于需求量
logger.Error("现存量为" + xiancunliang + ", 不满足需求量:" + mOPick.ActualReqQty);
return;
}
#endregion 第一步执行结束
#region 执行第二,三步
//拣货规则:map为按照时间顺序获取的批号和现存量,从中取得满足需求量的批号。有两种情况
// 1. 先进先出,从头开发遍历,如果一开始现存量就能直接满足需求量,则不需要拆行,直接赋值批号即可
// 2. 若前面现存量未能满足需求量,则需要拆行,将当前的批号和库存量赋值给当前的领料行,后面继续遍历批号,加行赋值批号
//算法需求:按照顺序找到能满足需求量的批号信息
//最终批号结果
Dictionary<string, decimal> endResult = new Dictionary<string, decimal>();
//创建一个值,用于记录已经放入endResult里的批号的库存量总和
decimal allowXianCun = 0;
for (int i = 0; i < result.Count; i++)
{
if (i == 0 && result.ElementAt(0).Value >= mOPick.ActualReqQty)
{
//如果第一个批号库存量大于需求量,代表已经满足算法条件,跳出
endResult.Add(result.ElementAt(0).Key, result.ElementAt(0).Value);
break;
}
else
{
if (allowXianCun >= mOPick.ActualReqQty)
{
break;
}
endResult.Add(result.ElementAt(i).Key, result.ElementAt(i).Value);
allowXianCun += result.ElementAt(i).Value;
}
}
if (endResult.Count == 1)
{
//查找批号
LotMaster lotMaster = LotMaster.Finder.Find(" LotCode = '" + endResult.ElementAt(0).Key + "'");
holder.IssueDocLines[b].LotMaster = lotMaster;
}
else
{
//首先确定当前领料行号最大为多少 按照排序最后一样行号最大
int lineNum = holder.IssueDocLines[holder.IssueDocLines.Count - 1].LineNum + 10;
//赋值此次需求量,每次生成领料单行扣减,便于最后一个领料行只占用剩余需求量的库存,而不是赋值整个批号的库存
decimal xuqiuliang = mOPick.ActualReqQty;
for (int a = 0; a < result.Count; a++)
{
//第一行赋值原来行的批号
if (a == 0)
{
//查找批号
LotMaster lotMaster = LotMaster.Finder.Find(" LotCode = '" + endResult.ElementAt(0).Key + "'");
//第一行
holder.IssueDocLines[b].LotMaster = lotMaster;
holder.IssueDocLines[b].IssueQty = endResult[endResult.ElementAt(0).Key];
holder.IssueDocLines[b].IssuedQty = endResult[endResult.ElementAt(0).Key];
holder.IssueDocLines[b].IssuedQtyByWhUOM = holder.IssueDocLines[b].WhUOM.Round.GetRoundValue(holder.IssueDocLines[b].IssuedQty * holder.IssueDocLines[b].IBUToSBURate);
holder.IssueDocLines[b].IssuedQtyByCoUOM = holder.IssueDocLines[b].CoUOM.Round.GetRoundValue(holder.IssueDocLines[b].IssuedQtyByWhUOM * holder.IssueDocLines[b].IBUToCBURate);
xuqiuliang -= holder.IssueDocLines[b].IssueQty;
continue;
}
//新增领料行
IssueDocLine newIssueLine = IssueDocLine.Create(holder);
Base.BusinessEntityHelper.CopyTo(holder.IssueDocLines[b], newIssueLine, false);
//举例:假如批号三个一共 80 100 100 需求量为200,第一次遍历赋值数量为批号数量80,xuqiuliang = 200-80 = 120
//第二次遍历判断xuqiuliang-批号库存量 = 120 -100 = 20,第二次遍历赋值数量为批号数量100
//第二次遍历判断xuqiuliang-批号库存量 = 20 -100 = -80,第二次遍历赋值数量为剩余需求数量20
if (xuqiuliang - endResult[endResult.ElementAt(a).Key] > 0)
{
newIssueLine.IssueQty = endResult[endResult.ElementAt(a).Key];
newIssueLine.IssuedQty = endResult[endResult.ElementAt(a).Key];
}
else
{
newIssueLine.IssueQty = xuqiuliang;
newIssueLine.IssuedQty = xuqiuliang;
}
newIssueLine.IssuedQtyByWhUOM = newIssueLine.WhUOM.Round.GetRoundValue(newIssueLine.IssuedQty * newIssueLine.IBUToSBURate);
newIssueLine.IssuedQtyByCoUOM = newIssueLine.CoUOM.Round.GetRoundValue(newIssueLine.IssuedQtyByWhUOM * newIssueLine.IBUToCBURate);
newIssueLine.LineNum = lineNum;
//查找批号
LotMaster lotMaster2 = LotMaster.Finder.Find(" LotCode = '" + endResult.ElementAt(a).Key + "'");
newIssueLine.LotMaster = lotMaster2;
Session.Current.InList(newIssueLine);
lineNum += 10;
}
}
#endregion 第二步执行结束
}
private decimal getKeYong(string itemCode, string wareCode, string lotCode)
{
var dataSet = new DataSet();
string org = UFIDA.U9.Base.Context.LoginOrg.Code;
string sql = "select " +
" A5.[Name] as [Wh_Name], " +
" A.[ItemInfo_ItemCode] as [Item_ItemCode], " +
" A.[LotInfo_LotCode] as [TempLotCode], " +
" sum( case when ((((A.[IsProdCancel] = 1) or (A.[MO_EntityID] != 0)) or A.[ProductDate] is not null) or (A.[WP_EntityID] != 0)) " +
" then A.[StoreQty] else convert(decimal(24,9),0) end ) as [NotUseQty]," +
" sum((((A.[StoreQty] - A.[ResvStQty]) - A.[ResvOccupyStQty]) - " +
" case when ((((A.[IsProdCancel] = 1) or (A.[MO_EntityID] != 0)) or A.[ProductDate] is not null) or (A.[WP_EntityID] != 0)) " +
" then A.[StoreQty] else convert(decimal(24,9),0) end )) as [CanUseQty], " +
" sum((A.[StoreQty] + A.[ToRetStQty])) as [BalQty]," +
" sum((A.[StoreMainQty] + A.[ToRetStMainQty])) as [BalQty_Main], " +
" sum((((((A.[StoreQty] - A.[ResvStQty]) - A.[ResvOccupyStQty]) - " +
" case when ((((A.[IsProdCancel] = 1) or (A.[MO_EntityID] != 0)) or A.[ProductDate] is not null) or (A.[WP_EntityID] != 0))" +
" then A.[StoreQty] else convert(decimal(24,9),0) end ) + A.[SupplyQtySU]) - A.[DemandQtySU])) as [Temp_PAB] " +
" from InvTrans_WhQoh as A " +
" left join [CBO_Wh] as A1 on (A.[Wh] = A1.[ID])" +
" left join [CBO_ItemMaster] as A2 on (A.[ItemInfo_ItemID] = A2.[ID]) " +
" left join [Base_UOM] as A3 on (A.[StoreUOM] = A3.[ID]) " +
" left join [Base_Organization] as A4 on (A.[LogisticOrg] = A4.[ID]) " +
" left join [CBO_Wh_Trl] as A5 on (A5.SysMlFlag = 'zh-CN') and (A1.[ID] = A5.[ID])" +
" where ((((A2.[Name] is not null and (A2.[Name] != '')) and (A4.[Code] = N'"+ org + "'))" +
" and (A1.[Code] = N'" + wareCode + "')) and (A.[ItemInfo_ItemCode] = N'" + itemCode + "')) and A.[LotInfo_LotCode] = '" + lotCode + "'" +
"group by A5.[Name], A.[ItemInfo_ItemCode], isnull( case when A2.[ItemFormAttribute] in (16, 22) " +
" then A.[ItemInfo_ItemName] else A2.[Name] end ,''), A.[LotInfo_LotCode], A3.[Round_Precision]";
DataAccessor.RunSQL(DatabaseManager.GetCurrentConnection(), sql.ToString(), null, out dataSet);
decimal kyl = decimal.Parse(dataSet.Tables[0].Rows[0][7] == null? "0":dataSet.Tables[0].Rows[0][7].ToString());
return kyl;
}
public Dictionary<string, decimal> getAllLotInfo(ItemMaster item, Warehouse supplyWareHouse,String type) {
Dictionary<string, decimal> endDic = new Dictionary<string, decimal>() ;
Dictionary<string, decimal> dic1 = getWhInitLineDic(item, supplyWareHouse);
Dictionary<string, decimal> dic2 = getSCTLRotLotDic(item, supplyWareHouse);
Dictionary<string, decimal> dic3 = getMiscRcvTransRotDic(item, supplyWareHouse);
Dictionary<string, decimal> dic4 = getTransferInRotDic(item, supplyWareHouse);
Dictionary<string, decimal> dic5 = new Dictionary<string, decimal>() ;
if ("采购".Equals(type))
{
dic5 = getRcvRotDic(item, supplyWareHouse);
}
else {
dic5 = getRcvRptRotLotDic(item, supplyWareHouse);
}
Dictionary<string, decimal> dic6 = getTransferFormDic(item, supplyWareHouse);
if (dic1.Count > 0) {
foreach (var ele in dic1) //拿到dict1
{
if (ele.Value > 0) {
endDic.Add(ele.Key, ele.Value);
}
}
}
if (dic2.Count > 0) {
foreach (var ele in dic2) //拿到dict2
{
if (ele.Value > 0)
{
endDic.Add(ele.Key, ele.Value);
}
}
}
if (dic3.Count > 0) {
foreach (var ele in dic3) //拿到dict2
{
if (ele.Value > 0)
{
endDic.Add(ele.Key, ele.Value);
}
}
}
if (dic4.Count > 0) {
foreach (var ele in dic4) //拿到dict2
{
if (ele.Value > 0)
{
endDic.Add(ele.Key, ele.Value);
}
}
}
if (dic5.Count > 0) {
foreach (var ele in dic5) //拿到dict2
{
if (ele.Value > 0)
{
endDic.Add(ele.Key, ele.Value);
}
}
}
if (dic6.Count > 0) {
foreach (var ele in dic6) //拿到dict2
{
if (ele.Value > 0)
{
endDic.Add(ele.Key, ele.Value);
}
}
}
return endDic;
}
//根据库存期初单获取批号
public Dictionary<string, decimal> getWhInitLineDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
WhInitLine.EntityList rcvLines = WhInitLine.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID +" and Wh = '" + warehouse.ID+"'");
if (rcvLines == null || rcvLines.Count == 0) {
logger.Error("库存期初单信息获取为空,料号:" + item.Code);
return result;
}
//进入方法默认查到了列表
List<WhInitLine> rcList = new List<WhInitLine>();
foreach (WhInitLine rt in rcvLines)
{
WhInit re = rt.WhInit;
if (INVDocStatus.Approved != re.Status)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("库存期初单信息获取为空,或者未审核,料号:" + item.Code);
return result;
}
//先进先出,所以顺序排序
List<WhInitLine> endRcv = rcList.OrderBy(r => r.CreatedOn).ToList();
foreach (WhInitLine whInitLine in endRcv) {
if (whInitLine.LotMaster == null)
{
logger.Error("库存期初单批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.LotMaster.LotMaster.LotCode);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0) {
result.Add(whInitLine.LotMaster.LotMaster.LotCode, xiancunliang);
}
}
return result;
}
//根据形态转换单获取批号
public Dictionary<string, decimal> getTransferFormDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
TransferFormL.EntityList rcvLines = TransferFormL.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (rcvLines == null || rcvLines.Count == 0)
{
logger.Error("形态转换单信息获取为空,料号:" + item.Code);
return result;
}
//进入方法默认查到了列表
List<TransferFormL> rcList = new List<TransferFormL>();
foreach (TransferFormL rt in rcvLines)
{
TransferForm re = rt.TransferForm;
if (INVDocStatus.Approved != re.Status)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("形态转换单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<TransferFormL> endRcv = rcList.OrderBy(r => r.CreatedOn).ToList();
foreach (TransferFormL whInitLine in endRcv)
{
if (whInitLine.LotInfo == null)
{
logger.Error("形态转换单批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.LotInfo.LotMaster.LotCode);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0)
{
result.Add(whInitLine.LotInfo.LotMaster.LotCode, xiancunliang);
}
}
return result;
}
//根据收货单获取批号
public Dictionary<string, decimal> getRcvRotDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
Organization organization = UFIDA.U9.Base.Context.LoginOrg;
RcvLine.EntityList rcvLines = RcvLine.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (rcvLines == null || rcvLines.Count == 0)
{
logger.Error("收货单信息获取为空,料号:" + item.Code);
return result;
}
//进入方法默认查到了列表
List<RcvLine> rcList = new List<RcvLine>();
foreach (RcvLine rt in rcvLines)
{
Receivement re = rt.Receivement;
if (RcvStatusEnum.Closed != re.Status)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("收货单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<RcvLine> endRcv = rcList.OrderBy(r => r.CreatedOn).ToList();
foreach (RcvLine whInitLine in endRcv)
{
if (whInitLine.InvLot == null)
{
logger.Error("收货单信息批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.InvLot.Code);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0)
{
result.Add(whInitLine.InvLot.Code, xiancunliang);
}
}
return result;
}
//根据调入单获取批号
public Dictionary<string, decimal> getTransferInRotDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
TransInLine.EntityList transferInList = TransInLine.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID + " and TransInWh = '" + warehouse.ID + "'");
if (transferInList == null || transferInList.Count == 0)
{
logger.Error("调入单单信息获取为空,料号:" + item.Code);
return result;
}
else
{
List<TransInLine> transInLines = new List<TransInLine>();
foreach (TransInLine rt in transferInList)
{
TransferIn ti = rt.TransferIn;
if (TransInStatus.Approved != ti.Status)
{
continue;
}
transInLines.Add(rt);
}
if (transInLines.Count == 0)
{
logger.Error("调入单单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<TransInLine> endRcv = transInLines.OrderBy(r => r.CreatedOn).ToList();
foreach (TransInLine whInitLine in endRcv)
{
if (whInitLine.LotInfo == null)
{
logger.Error("调入单单批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.LotInfo.LotMaster.LotCode);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0)
{
result.Add(whInitLine.LotInfo.LotMaster.LotCode, xiancunliang);
}
}
return result;
}
}
//根据杂收单获取批号
public Dictionary<string, decimal> getMiscRcvTransRotDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
MiscRcvTransL.EntityList mis = MiscRcvTransL.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (mis == null || mis.Count == 0)
{
logger.Error("杂收单单信息获取为空,料号:" + item.Code);
return result;
}
else
{
List<MiscRcvTransL> miss = new List<MiscRcvTransL>();
foreach (MiscRcvTransL rt in mis)
{
MiscRcvTrans ti = rt.MiscRcvTrans;
if (INVDocStatus.Approved != ti.Status)
{
continue;
}
miss.Add(rt);
}
if (miss.Count == 0)
{
logger.Error("杂收单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<MiscRcvTransL> endRcv = miss.OrderBy(r => r.CreatedOn).ToList();
foreach (MiscRcvTransL whInitLine in endRcv)
{
if (whInitLine.LotInfo == null)
{
logger.Error("杂收单批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.LotInfo.LotMaster.LotCode);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0)
{
result.Add(whInitLine.LotInfo.LotMaster.LotCode, xiancunliang);
}
}
return result;
}
}
//根据生产退料单获取批号
public Dictionary<string, decimal> getSCTLRotLotDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
//判断退料方式,退料理由不为空
IssueDocLine.EntityList mis = IssueDocLine.Finder.FindAll(" RecedeReason is not null and RecedeReason != '' and Item.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (mis == null || mis.Count == 0)
{
logger.Error("生产退料单信息获取为空,料号:" + item.Code);
return result;
}
else
{
List<IssueDocLine> miss = new List<IssueDocLine>();
foreach (IssueDocLine rt in mis)
{
IssueDoc ti = rt.IssueDoc;
if (IssueTXNStateEnum.Closed != ti.DocState)
{
continue;
}
miss.Add(rt);
}
if (miss.Count == 0)
{
logger.Error("生产退料单信息获取为空,或者未审核,料号:" + item.Code);
return result;
}
//先进先出,所以顺序排序,取第一个
List<IssueDocLine> endRcv = miss.OrderBy(r => r.CreatedOn).ToList();
foreach (IssueDocLine whInitLine in endRcv)
{
if (whInitLine.LotMaster == null)
{
logger.Error("生产退料单批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.LotMaster.LotCode);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0)
{
result.Add(whInitLine.LotMaster.LotCode, xiancunliang);
}
}
return result;
}
}
//根据成品入库获取批号
public Dictionary<string, decimal> getRcvRptRotLotDic(ItemMaster item, Warehouse warehouse)
{
Dictionary<string, decimal> result = new Dictionary<string, decimal>();
//制造件 - 批号取对应的生产订单的生产批号,此处取得是入库单的生产批号,因为和生产订单批号一一对应
//1.查找对应的入库单
RcvRptDocLine.EntityList rcvRptDocLine = RcvRptDocLine.Finder.FindAll(" Item.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (rcvRptDocLine.Count == 0)
{
logger.Error("入库单信息获取为空,料号:" + item.Code);
return result;
}
List<RcvRptDocLine> rcList = new List<RcvRptDocLine>();
foreach (RcvRptDocLine rt in rcvRptDocLine)
{
RcvRptDoc re = rt.RcvRptDoc;
if (RcvRptDocStateEnum.Approved != re.DocState)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("入库单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//2.先进先出,所以顺序排序,取第一个
List<RcvRptDocLine> endRcvList = rcList.OrderBy(r => r.CreatedOn).ToList();
foreach (RcvRptDocLine whInitLine in endRcvList)
{
if (whInitLine.RcvLotMaster == null)
{
logger.Error("入库单批号信息获取为空,料号:" + item.Code);
continue;
}
//1. 查询批号的现存量
decimal xiancunliang = getKeYong(item.Code, warehouse.Code, whInitLine.RcvLotMaster.LotCode);
//把有现存量的批号先都遍历出来
if (xiancunliang > 0)
{
result.Add(whInitLine.RcvLotMaster.LotCode, xiancunliang);
}
}
return result;
}
//根据库存期初单获取批号
public LotMaster getWhInitLineLotByItemInfo(ItemMaster item, Warehouse warehouse)
{
WhInitLine.EntityList rcvLines = WhInitLine.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (rcvLines == null || rcvLines.Count == 0)
{
logger.Error("库存期初单信息获取为空,料号:" + item.Code);
return null;
}
//进入方法默认查到了列表
List<WhInitLine> rcList = new List<WhInitLine>();
foreach (WhInitLine rt in rcvLines)
{
WhInit re = rt.WhInit;
if (INVDocStatus.Approved != re.Status)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("库存期初单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序
List<WhInitLine> endRcv = rcList.OrderBy(r => r.CreatedOn).ToList();
return endRcv[0].LotMaster.LotMaster;
}
//根据形态转换单获取批号
public LotMaster getTransferFormLLotByItemInfo(ItemMaster item, Warehouse warehouse)
{
TransferFormL.EntityList rcvLines = TransferFormL.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID +" and Wh = '" + warehouse.ID+"'");
if (rcvLines == null || rcvLines.Count == 0) {
logger.Error("形态转换单信息获取为空,料号:" + item.Code);
return null;
}
//进入方法默认查到了列表
List<TransferFormL> rcList = new List<TransferFormL>();
foreach (TransferFormL rt in rcvLines)
{
TransferForm re = rt.TransferForm;
if (INVDocStatus.Approved != re.Status)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("形态转换单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<TransferFormL> endRcv = rcList.OrderBy(r => r.CreatedOn).ToList();
return endRcv[0].LotInfo.LotMaster;
}
//根据收货单获取批号
public LotMaster getRcvRotLotByItemInfo(ItemMaster item, Warehouse warehouse)
{
Organization organization = UFIDA.U9.Base.Context.LoginOrg;
RcvLine.EntityList rcvLines = RcvLine.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID +" and Wh = '" + warehouse.ID+"'");
if (rcvLines == null || rcvLines.Count == 0) {
logger.Error("收货单信息获取为空,料号:" + item.Code);
return null;
}
//进入方法默认查到了列表
List<RcvLine> rcList = new List<RcvLine>();
foreach (RcvLine rt in rcvLines)
{
Receivement re = rt.Receivement;
if (RcvStatusEnum.Closed != re.Status)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("收货单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<RcvLine> endRcv = rcList.OrderBy(r => r.CreatedOn).ToList();
return endRcv[0].InvLot;
}
//根据调入单获取批号
public LotMaster getTransferInRotLotByItemInfo(ItemMaster item, Warehouse warehouse)
{
TransInLine.EntityList transferInList = TransInLine.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID+ " and TransInWh = '" + warehouse .ID+ "'");
if (transferInList == null || transferInList.Count == 0)
{
logger.Error("调入单单信息获取为空,料号:" + item.Code);
return null;
}
else {
List<TransInLine> transInLines = new List<TransInLine>();
foreach (TransInLine rt in transferInList)
{
TransferIn ti = rt.TransferIn;
if (TransInStatus.Approved != ti.Status)
{
continue;
}
transInLines.Add(rt);
}
if (transInLines.Count == 0)
{
logger.Error("调入单单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<TransInLine> endRcv = transInLines.OrderBy(r => r.CreatedOn).ToList();
if (endRcv[0].LotInfo == null) {
logger.Error("调入单单信息获取为空,料号:" + item.Code);
return null;
}
return endRcv[0].LotInfo.LotMaster;
}
}
//根据杂收单获取批号
public LotMaster getMiscRcvTransRotLotByItemInfo(ItemMaster item, Warehouse warehouse)
{
MiscRcvTransL.EntityList mis = MiscRcvTransL.Finder.FindAll(" ItemInfo.ItemID.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (mis == null || mis.Count == 0)
{
logger.Error("杂收单单信息获取为空,料号:" + item.Code);
return null;
}
else
{
List<MiscRcvTransL> miss = new List<MiscRcvTransL>();
foreach (MiscRcvTransL rt in mis)
{
MiscRcvTrans ti = rt.MiscRcvTrans;
if (INVDocStatus.Approved != ti.Status)
{
continue;
}
miss.Add(rt);
}
if (miss.Count == 0)
{
logger.Error("杂收单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<MiscRcvTransL> endRcv = miss.OrderBy(r => r.CreatedOn).ToList();
if (endRcv[0].LotInfo == null)
{
logger.Error("杂收单信息获取批号为空,料号:" + item.Code);
return null;
}
return endRcv[0].LotInfo.LotMaster;
}
}
//根据生产退料单获取批号
public LotMaster getSCTLRotLotByItemInfo(ItemMaster item,Warehouse warehouse)
{
//判断退料方式,退料理由不为空
IssueDocLine.EntityList mis = IssueDocLine.Finder.FindAll(" RecedeReason is not null and RecedeReason != '' and Item.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (mis == null || mis.Count == 0)
{
logger.Error("生产退料单信息获取为空,料号:" + item.Code);
return null;
}
else
{
List<IssueDocLine> miss = new List<IssueDocLine>();
foreach (IssueDocLine rt in mis)
{
IssueDoc ti = rt.IssueDoc;
if (IssueTXNStateEnum.Closed != ti.DocState)
{
continue;
}
miss.Add(rt);
}
if (miss.Count == 0)
{
logger.Error("生产退料单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//先进先出,所以顺序排序,取第一个
List<IssueDocLine> endRcv = miss.OrderBy(r => r.CreatedOn).ToList();
return endRcv[0].LotMaster;
}
}
//根据成品入库获取批号
public LotMaster getRcvRptRotLotByItemInfo(ItemMaster item, Warehouse warehouse)
{
//制造件 - 批号取对应的生产订单的生产批号,此处取得是入库单的生产批号,因为和生产订单批号一一对应
//1.查找对应的入库单
RcvRptDocLine.EntityList rcvRptDocLine = RcvRptDocLine.Finder.FindAll(" Item.ID = " + item.ID + " and Wh = '" + warehouse.ID + "'");
if (rcvRptDocLine.Count == 0)
{
logger.Error("入库单信息获取为空,料号:" + item.Code);
return null;
}
List<RcvRptDocLine> rcList = new List<RcvRptDocLine>();
foreach (RcvRptDocLine rt in rcvRptDocLine)
{
RcvRptDoc re = rt.RcvRptDoc;
if (RcvRptDocStateEnum.Approved != re.DocState)
{
continue;
}
rcList.Add(rt);
}
if (rcList.Count == 0)
{
logger.Error("入库单信息获取为空,或者未审核,料号:" + item.Code);
return null;
}
//2.先进先出,所以顺序排序,取第一个
List<RcvRptDocLine> endRcvList = rcList.OrderBy(r => r.CreatedOn).ToList();
return endRcvList[0].RcvLotMaster;
}
}
}
#endregion
代码核心逻辑
- 每一个getXXXDIc都是通过料品,存储地点按照先进先出顺序找到对应单子的行记录,并取到还有库存的批号!每一个方法都返回一个Map,存着批号和库存数量的对应关系
- getAllLotInfo对所有单子里查出来的Map进行组装,最后返回的就是这个料品对应的按照先进先出顺序排列的批号,库存对应
- 拆行逻辑单据摘出来
拆行算法逻辑
private void chaihang(IssueDoc holder, MOPickList mOPick, Dictionary<string, decimal> result, int b)
{
//定义一个现存量值,遍历所有的Map,赋值当前料品对应的所有批号的库存量和
decimal xiancunliang = 0;
foreach (var ele in result)
{
xiancunliang += ele.Value;
}
//如果总库存数小于需求数,直接报错
if (xiancunliang < mOPick.ActualReqQty)
{
//总现存量小于需求量
logger.Error("现存量为" + xiancunliang + ", 不满足需求量:" + mOPick.ActualReqQty);
return;
}
//拣货规则:
//map为按照时间顺序获取的批号和现存量,从中取得满足需求量的批号。有两种情况
// 1. 先进先出,从头开发遍历,如果一开始现存量就能直接满足需求量,则不需要拆行,直接赋值批号即可
// 2. 若前面现存量未能满足需求量,则需要拆行,将当前的批号和库存量赋值给当前的领料行,后面继续遍历批号,加行赋值批号
//算法需求:按照顺序找到能满足需求量的批号信息
//因为料品返回的批号库存可能多于需求量,在这重新定义一个Map对象,用于保存满足此次需求量的所用的批号
Dictionary<string, decimal> endResult = new Dictionary<string, decimal>();
//创建一个值,用于记录已经放入endResult里的批号的库存量总和,通过这个总和可以判断,如果遍历过程中这个和大于等于需求量了,就可以退出赋值了
decimal allowXianCun = 0;
for (int i = 0; i < result.Count; i++)
{
if (i == 0 && result.ElementAt(0).Value >= mOPick.ActualReqQty)
{
//如果第一个批号库存量大于需求量,代表已经满足算法条件,跳出
endResult.Add(result.ElementAt(0).Key, result.ElementAt(0).Value);
break;
}
else
{
if (allowXianCun >= mOPick.ActualReqQty)
{
break;
}
endResult.Add(result.ElementAt(i).Key, result.ElementAt(i).Value);
allowXianCun += result.ElementAt(i).Value;
}
}
//如果只有一个批号,则代表第一个批号库存即可满足条件,直接赋值批号信息
if (endResult.Count == 1)
{
//查找批号
LotMaster lotMaster = LotMaster.Finder.Find(" LotCode = '" + endResult.ElementAt(0).Key + "'");
holder.IssueDocLines[b].LotMaster = lotMaster;
}
else
{
//到这,代表需要拆行了!
//定义一个行号变量,用于拆行后新建行的行号赋值
//赋值逻辑:确定当前领料行号最大为多少 按照排序最后一样行号最大
int lineNum = holder.IssueDocLines[holder.IssueDocLines.Count - 1].LineNum + 10;
//定义一个变量赋值此料品需求量,每次生成领料单行扣减,便于最后一个领料行只占用剩余需求量的库存,而不是赋值整个批号的库存
//举例:假如批号三个一共 80 100 100 需求量为200,
//第一次遍历赋值数量为批号数量80,xuqiuliang = 200-80 = 120
//第二次遍历判断xuqiuliang-批号库存量 = 120 -100 = 20,第二次遍历赋值数量为批号数量100
//第二次遍历判断xuqiuliang-批号库存量 = 20 -100 = -80,第二次遍历赋值数量为剩余需求数量20
decimal xuqiuliang = mOPick.ActualReqQty;
for (int a = 0; a < result.Count; a++)
{
//第一行赋值第一行的批号
if (a == 0)
{
//查找批号
LotMaster lotMaster = LotMaster.Finder.Find(" LotCode = '" + endResult.ElementAt(0).Key + "'");
//第一行
holder.IssueDocLines[b].LotMaster = lotMaster;
//修改原有数量为批号数量
holder.IssueDocLines[b].IssueQty = endResult[endResult.ElementAt(0).Key];
//修改原有数量为批号数量
holder.IssueDocLines[b].IssuedQty = endResult[endResult.ElementAt(0).Key];
holder.IssueDocLines[b].IssuedQtyByWhUOM = holder.IssueDocLines[b].WhUOM.Round.GetRoundValue(holder.IssueDocLines[b].IssuedQty * holder.IssueDocLines[b].IBUToSBURate);
holder.IssueDocLines[b].IssuedQtyByCoUOM = holder.IssueDocLines[b].CoUOM.Round.GetRoundValue(holder.IssueDocLines[b].IssuedQtyByWhUOM * holder.IssueDocLines[b].IBUToCBURate);
//第一次遍历赋值数量为批号数量
xuqiuliang -= endResult.ElementAt(0).Value;
continue;
}
//新增领料行
IssueDocLine newIssueLine = IssueDocLine.Create(holder);
Base.BusinessEntityHelper.CopyTo(holder.IssueDocLines[b], newIssueLine, false);
if (xuqiuliang - endResult[endResult.ElementAt(a).Key] > 0)
{
//当剩余的需求量减去当前批号的库存量大于0,代表当前批号库存扔不满足需求量,此时拆的行赋值数量为批号库存量
newIssueLine.IssueQty = endResult[endResult.ElementAt(a).Key];
newIssueLine.IssuedQty = endResult[endResult.ElementAt(a).Key];
}
else
{
//当剩余的需求量减去当前批号的库存量小于0,代表当前批号库存满足需求量,即拆到了当前物料的最后一行,此时拆的行赋值数量为剩余需求量库存量
newIssueLine.IssueQty = xuqiuliang;
newIssueLine.IssuedQty = xuqiuliang;
}
newIssueLine.IssuedQtyByWhUOM = newIssueLine.WhUOM.Round.GetRoundValue(newIssueLine.IssuedQty * newIssueLine.IBUToSBURate);
newIssueLine.IssuedQtyByCoUOM = newIssueLine.CoUOM.Round.GetRoundValue(newIssueLine.IssuedQtyByWhUOM * newIssueLine.IBUToCBURate);
newIssueLine.LineNum = lineNum;
//查找批号
LotMaster lotMaster2 = LotMaster.Finder.Find(" LotCode = '" + endResult.ElementAt(a).Key + "'");
newIssueLine.LotMaster = lotMaster2;
xuqiuliang -= endResult.ElementAt(a).Value;
Session.Current.InList(newIssueLine);
lineNum += 10;
}
}
#endregion 第二步执行结束
}