using Rhea.Common;
using Microsoft.AspNetCore.Http;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tiger.Model;
using Apache.NMS;
using System.Drawing.Drawing2D;
using Tiger.Model.Sharetronic.Shelf;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Microsoft.CodeAnalysis.Operations;
using Tiger.IBusiness;
namespace Tiger.Business.WMS.Transaction
{
///
/// 生产领料事务
///
public class ProductionMaterialReq : WMSTransactionBase, IProductionMaterialReq
{
public IProductionMaterialReq Init(string id, string userCode, string apiHost, string orgCode, string fty)
{
TransID = id;
UserCode = userCode;
ApiHost = apiHost;
OrgCode = orgCode;
FTY_CODE = fty;
Logger.Console.Info($"Start {this.GetType().Name} Transaction[ID: {TransID}]");
return this;
}
#region Propertys & Variables
public string UserCode { get; set; }
public long UserId { get; set; }
public string OrgCode { get; set; }
public string FTY_CODE { get; set; }
public AuthOption SelectOrderOption { get; set; }
public List Suggests { get; set; } = new();
public List CurPoolList => Suggests.Where(q => !q.poolItem.IsNullOrEmpty()).Select(q => q.poolItem).ToList();
public List LocationHis { get; set; } = new();
public Inventory CurInv { get; set; }
public BIZ_ERP_PROD_OUT req { get; set; }
public ReqType CurReqType { get; set; }
public List dtls { get; set; } = new();
public bool isExceed { get; set; }
public ProductionPickToMes toMes { get; set; }
public bool his_isComplete { get; set; }
public bool isCutting { get; set; }
public string ScanAfCut { get; set; }
public decimal cutQty { get; set; }
public decimal remainQty { get; set; }
public string QrCode { get; set; }
public BIZ_WMS_TRANSFER transferH = null;
public BIZ_WMS_TRANSFER cTransferH = null;
public BIZ_WMS_TRANSFER_DTL transferDtl = null;
public BIZ_WMS_TRANSFER_SN transferSn = null;
#endregion Propertys & Variables
#region Functions
///
/// 获取生产领料单明细
///
///
///
public async Task>> GetProdMaterialReqDetails(Model.Minsun.ProdMaterialReqInput input)
{
var action = new ApiAction>();
action.Data = await Biz.Db.Queryable((t, m, w) =>
new JoinQueryInfos(
JoinType.Left, t.MATERIALCODE == m.MCode,
JoinType.Left, t.WAREHOUSECODE == w.WHCODE
))
.Where((t, m, w) => t.BILLCODE.ToUpper() == input.BillCode.ToUpper() && t.PRQTY > 0)
.WhereIF(!string.IsNullOrEmpty(input.Material), (t, m, w) => t.MATERIALCODE.Contains(input.Material) || m.MName.Contains(input.Material))
.Select((t, m, w) => new Model.Minsun.ProdMaterialReqOutput
{
BillCode = t.BILLCODE,
BillLine = t.BILLLINE,
MaterialCode = t.MATERIALCODE,
MaterialName = m.MName,
MaterialStandard = m.MDesc,
UnitCode = t.UNITCODE,
PRQty = t.PRQTY,
Qty = t.QTY,
DeliveryDate = t.DELIVERYDATE,
WarehouseCode = t.WAREHOUSECODE,
WarehouseName = w.WHNAME
})
.OrderBy(x => x.BillLine).ToListAsync();
return action;
}
///
/// 领料单选择事件
///
///
///
public async Task> SelectOrder(ProdReqInput input)
{
var action = new ApiAction();
try
{
SelectOrderOption = input.AuthOption;
req = await Biz.Db.Queryable().Where(x => x.BILLCODE == input.ReqNo).Includes(q => q.DtlsWithGhost, d => d.ItemInfo).IncludesAllFirstLayer().FirstAsync();
//验证明细是否正确
if (!req.Dtls.Any())
{
action.IsSuccessed = false;
action.LocaleMsg = new("WMS.ProdMReq.SelectOrder.DtlsException", input.ReqNo);
return action;
}
Biz.Db.Deleteable().Where(q => q.TRANS_CODE == req.BILLCODE).ExecuteCommand();
//如果上一次推荐有数据,则先灭掉亮的灯
if (Suggests.Any())
{
await CloseLight(LocationHis);
}
CurReqType = input.ReqType < 0 ? (!req.SOURCECODE.IsNullOrEmpty() ? ReqType.IsAgv : ReqType.IsFirst) : input.ReqType.GetEnum();
//如果是Agv叫料,只发一盘
if (CurReqType == ReqType.IsAgv)
{
//推荐物料
Result> result = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, req.Dtls[0].ITEM_CODE, req.Dtls[0].WAREHOUSECODE, null, null, null, input.AuthOption, 1);
action.LocaleMsg = result.LocaleMsg;
if (result.IsException)
{
action.IsSuccessed = false;
return action;
}
Suggests.Clear();
Suggests = result.Data;
if (Suggests.Count > 0 && req.Dtls.Count > 0)
{
var inv = Suggests.First();
var actQty = req.Dtls.First().PRQTY - req.Dtls.First().QTY;
if (inv != null && actQty > 0)
{
inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(req), req.BILLCODE, req.Dtls[0].BILLLINE, actQty, false);
}
else
{
inv.poolItem = null;
}
}
Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty());
}
//如果是首套每个料一盘
if (CurReqType == ReqType.IsFirst)
{
var begin = DateTime.Now;
var ElapsedTime = 0.0;
//Debug.WriteLine($"Async suggest begin at {begin:T}");
//var dic = new Dictionary>>();
var sumDtls = req.Dtls.Where(q => q.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue())
.GroupBy(x => new { x.ITEM_CODE }).Select(x => new { ItemCode = x.Key.ITEM_CODE.ToString(), WarehouseCode = x.Max(t => t.WAREHOUSECODE), actQty = x.Sum(t => t.PRQTY - t.QTY), lockObj = new object() });
var dic = sumDtls.ToDictionary(k => k, v => new Result>());
foreach (var item in dic)
{
var key = item.Key;
Work.DoAsync(() =>
{
lock (item.Key.lockObj)
{
dic[item.Key] = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, item.Key.ItemCode, item.Key.WarehouseCode, null, null, null, input.AuthOption, 1);
}
});
}
while (dic.Any() && dic.Any(q => q.Value.IsNormal)) ;
ElapsedTime = (DateTime.Now - begin).TotalSeconds;
//Debug.WriteLine($"Async suggest end, elapsed total {ElapsedTime} seconds");
if (dic.Any(q => q.Value.IsException))
{
action.IsSuccessed = false;
action.LocaleMsg = dic.Where(q => q.Value.IsException).Select(q => q.Value.LocaleMsg).First();
return action;
}
if (dic.Any(q => q.Value.IsWarning))
{
action.IsSuccessed = false;
action.LocaleMsg = new("WMS.WmsItem.Suggest.Warning", string.Join(", ", dic.Where(q => q.Value.IsWarning).Select(q => q.Key.ItemCode)));
}
Suggests.Clear();
dic.Select(x => x.Value.Data).ToList().ForEach((item) =>
{
Suggests.AddRange(item);
});
Suggests.ForEach((inv) =>
{
var actQty = sumDtls.Where(x => x.ItemCode == inv.Item.ITEM_CODE).Select(x => x.actQty).First();
if (actQty > 0)
{
inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(req), req.BILLCODE, null, actQty, false);
}
else
{
inv.poolItem = null;
}
});
Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty());
}
//如果是整单发料
if (CurReqType == ReqType.IsWhole)
{
var begin = DateTime.Now;
var ElapsedTime = 0.0;
var sumDtls = req.Dtls.Where(q => q.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue())
.GroupBy(x => new { x.ITEM_CODE }).Select(x => new { ItemCode = x.Key.ITEM_CODE.ToString(), WarehouseCode = x.Max(t => t.WAREHOUSECODE), actQty = x.Sum(t => t.PRQTY - t.QTY), lockObj = new object() });
var dic = sumDtls.ToDictionary(k => k, v => new Result>());
foreach (var item in dic)
{
var key = item.Key;
Work.DoAsync(() =>
{
lock (item.Key.lockObj)
{
dic[item.Key] = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, item.Key.ItemCode, item.Key.WarehouseCode, null, null, null, input.AuthOption, item.Key.actQty);
}
});
}
while (dic.Any() && dic.Any(q => q.Value.IsNormal)) ;
ElapsedTime = (DateTime.Now - begin).TotalSeconds;
//Debug.WriteLine($"Async suggest end, elapsed total {ElapsedTime} seconds");
if (dic.Any(q => q.Value.IsException))
{
action.IsSuccessed = false;
action.LocaleMsg = dic.Where(q => q.Value.IsException).Select(q => q.Value.LocaleMsg).First();
return action;
}
if (dic.Any(q => q.Value.IsWarning))
{
action.IsSuccessed = false;
action.LocaleMsg = new("WMS.WmsItem.Suggest.Warning", string.Join(", ", dic.Where(q => q.Value.IsWarning).Select(q => q.Key.ItemCode)));
}
Suggests.Clear();
dic.Select(x => x.Value.Data).ToList().ForEach((item) =>
{
Suggests.AddRange(item);
});
Suggests.ForEach((inv) =>
{
var actQty = sumDtls.Where(x => x.ItemCode == inv.Item.ITEM_CODE).Select(x => x.actQty).First();
if (actQty > 0)
{
inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(req), req.BILLCODE, null, actQty, false);
}
else
{
inv.poolItem = null;
}
});
Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty());
}
//保存物料池到数据库
Biz.Db.Insertable(CurPoolList).ExecuteCommand();
action = await LightAll(new() { AuthOption = input.AuthOption, ReqType = CurReqType, Color = LedColor.Blue });
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"领料单选择异常");
}
return action;
}
///
/// 领料单推荐所有物料亮灯
///
///
///
public async Task> LightAll(LightEntityInput light)
{
var action = new ApiAction();
try
{
var list = Suggests.WhereIF(!light.ItemCode.IsNullOrEmpty(), q => q.Item.ITEM_CODE == light.ItemCode);
if (list.Any())
{
list = list.Where(q => q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.Smart.GetValue() || q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.QRCode.GetValue());
if (list.Any())
{
list = list.Where(q => q.poolItem.STATUS < WMS_ITEM_POOL.STATUSs.WaitSend.GetValue());
if (list.Any())
{
var locs = list.Select(x => x.Location.AddShelf(x.Shelf)).ToList();
LocationHis.AddRange(locs);
//亮灯前先灭一遍
foreach (var shelf in list.Select(q => q.Shelf))
{
await Share.Shelf.DownAll(shelf);
}
await Share.Shelf.LightMulti(TransID, light.Color, locs);
action.LocaleMsg = Biz.L("亮灯成功,亮灯颜色[{0}]", light.Color.GetDesc());
}
else
{
action.LocaleMsg = Biz.L($"无需亮灯,推荐的物料已全部下架");
}
}
else
{
action.LocaleMsg = Biz.L($"无需亮灯,推荐的物料不在智能货架上");
}
}
else
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L($"亮灯失败,未找到推荐下架的物料");
}
action.Data = new ProdReqOutput()
{
ReqNo = req.BILLCODE,
ReqType = light.ReqType.GetValue()
};
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"亮灯异常");
}
return action;
}
///
/// 领料单发料明细
///
///
public async Task ProdReqDtl(string itemcode)
{
var action = new ApiAction();
try
{
//如果是整单发料
if (false && !itemcode.IsNullOrEmpty() && CurReqType == ReqType.IsWhole)
{
var begin = DateTime.Now;
var ElapsedTime = 0.0;
//Debug.WriteLine($"Async suggest begin at {begin:T}");
//var dic = new Dictionary>>();
var sumDtls = req.Dtls.Where(q => q.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue())
.GroupBy(x => new { x.ITEM_CODE }).Select(x => new { ItemCode = x.Key.ITEM_CODE.ToString(), actQty = x.Sum(t => t.PRQTY - t.QTY), lockObj = new object() });
var dic = sumDtls.Where(q => q.ItemCode == itemcode).ToDictionary(k => k, v => new Result>());
foreach (var item in dic)
{
var key = item.Key;
Work.DoAsync(() =>
{
lock (item.Key.lockObj)
{
dic[item.Key] = WMS_ITEM_Biz.WmsItem.Suggest(req.BILLCODE, item.Key.ItemCode, req.Dtls[0].WAREHOUSECODE, null, null, null, SelectOrderOption, item.Key.actQty);
}
});
}
while (dic.Any() && dic.Any(q => q.Value.IsNormal)) ;
ElapsedTime = (DateTime.Now - begin).TotalSeconds;
//Debug.WriteLine($"Async suggest end, elapsed total {ElapsedTime} seconds");
if (dic.Any(q => q.Value.IsException))
{
action.IsSuccessed = false;
action.LocaleMsg = dic.Where(q => q.Value.IsException).Select(q => q.Value.LocaleMsg).First();
return action;
}
if (dic.Any(q => q.Value.IsWarning))
{
action.IsSuccessed = false;
action.LocaleMsg = new("WMS.WmsItem.Suggest.Warning", string.Join(", ", dic.Where(q => q.Value.IsWarning).Select(q => q.Key.ItemCode)));
}
Suggests.Clear();
dic.Select(x => x.Value.Data).ToList().ForEach((item) =>
{
Suggests.AddRange(item);
});
Suggests.ForEach((inv) =>
{
var actQty = sumDtls.Where(x => x.ItemCode == inv.Item.ITEM_CODE).Select(x => x.actQty).First();
if (actQty > 0)
{
inv.poolItem = inv.Item.GetPoolItem(OrgCode, nameof(req), req.BILLCODE, null, actQty, false);
}
else
{
inv.poolItem = null;
}
});
Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty());
//保存物料池到数据库
var db = Biz.Db;
var dbTran = db.UseTran(() =>
{
db.Deleteable().Where(x => x.TRANS_NO == req.BILLCODE).ExecuteCommand();
db.Insertable(CurPoolList).ExecuteCommand();
});
if (!dbTran.IsSuccess)
{
Logger.Default.Fatal(dbTran.ErrorException, "Database transaction save exception");
this.Close(!dbTran.IsSuccess);
throw dbTran.ErrorException;
}
}
var dtls = req.Dtls.GroupBy(x => new { x.BILLLINE, x.ITEM_CODE, x.ItemInfo.ITEM_NAME }).Select(x => new ProdReqDtl() { BillLine = x.Key.BILLLINE.ToInt32(), ItemCode = x.Key.ITEM_CODE, ItemName = x.Key.ITEM_NAME, Status = "", Items = new List() }).ToList();
foreach (var d in dtls)
{
d.Items = Suggests.Where(x => x.Item.ITEM_CODE == d.ItemCode).Select(x => new ProdReqDtlItems { WHCode = x.Warehouse.WH_CODE, LocationCode = x.Location.LOCATION_CODE, SN = x.Item.SN, QTY = x.Item.QTY }).ToList();
d.Status = $"{(double)req.Dtls.Where(x => x.ITEM_CODE == d.ItemCode).Sum(x => x.QTY)} / {(double)req.Dtls.Where(x => x.ITEM_CODE == d.ItemCode).Sum(x => x.PRQTY)} {Suggests.FirstOrDefault()?.Item?.UNIT} (还需{d.Items.Count}个)";
}
action.Data = itemcode.IsNullOrEmpty() ? dtls.OrderBy(q => q.BillLine) : dtls.Where(x => x.ItemCode == itemcode).FirstOrDefault()?.Items.OrderBy(q => q.SN);
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"获取领料单明细异常");
}
return action;
}
///
/// 发料明细提示信息
///
///
public async Task GetItemTips(string itemcode)
{
var action = new ApiAction();
try
{
action.Data = $"物料{itemcode}:已下架[{(double)req.Dtls.Where(x => x.ITEM_CODE == itemcode).Sum(x => x.QTY)}],共{(double)req.Dtls.Where(x => x.ITEM_CODE == itemcode).Sum(x => x.PRQTY)} {Suggests.FirstOrDefault()?.Item?.UNIT}";
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"获取发料明细提示信息异常");
}
return action;
}
///
/// 灭灯
///
///
public async Task CloseLight(List locations = null)
{
var action = new ApiAction();
try
{
if (locations != null)
{
foreach (var shelf in locations.Select(q => q.Shelf))
{
await Share.Shelf.DownAll(shelf);
}
}
//灭灯
await Share.Shelf.DownMulti(TransID, locations.IsNullOrEmpty(Suggests.Where(q => q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.Smart.GetValue() || q.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.QRCode.GetValue())
.Select(x => x.Location).ToList()));
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"灭灯异常");
}
return action;
}
///
/// 扫描物料领料下架
///
///
///
public async Task> ScanItem(BaseInput input)
{
var action = new ApiAction();
try
{
ProcessingSn = input.SN;
if (WMSContext.TransactionDic.Where(q => !string.IsNullOrWhiteSpace(q.Value.ProcessingSn)).Any(q => q.Value.ProcessingSn == ProcessingSn && q.Value.TransID != this.TransID))
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("当前条码正在执行中,无法扫描");
return action;
}
if (input.SN.IsNullOrEmpty())
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("条码不能为空");
return action;
}
//解析条码
Result result = WMS_ITEM_Biz.WmsItem.Get(input.SN, input.AuthOption, true);
if (!result.IsSuccessed)
{
action.IsSuccessed = false;
action.LocaleMsg = result.LocaleMsg;
return action;
}
CurInv = result.Data as Inventory;
//验证条码是否正确
if (!CurInv.isNormalStatus || CurInv.Status != WMS_ITEM.STATUSs.InStore)
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("WMS.ProdMReq.ScanItem.StatusException", string.Join(',', CurInv.StatusList.Select(x => x.GetDesc())));
return action;
}
//储位验证
if (CurInv.Location.IsNullOrEmpty())
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("WMS.ProdMReq.ScanItem.LocationIsNull", CurInv.CurPkg.SN, CurInv.CurPkg.LOCATION_ID);
return action;
}
//物料验证
if (CurInv.ItemInfo.IsNullOrEmpty() || CurInv.ItemInfo.IS_ACTIVE == "N")
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("WMS.ProdMReq.ScanItem.ItemCodeNotExistsOrNotActive", CurInv.ItemInfo.ITEM_CODE.IsNullOrEmpty(CurInv.Barcode.ItemCode));
return action;
}
//仓库卡控
if (!req.Dtls.Any(q => q.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE && q.WAREHOUSECODE == CurInv.Warehouse.WH_CODE))
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L($"条码[{CurInv.Barcode.SN}]的料号或者仓库跟单据[{req.BILLCODE}]上的不一致");
return action;
}
QrCode = input.SN;
//1.判断FIFO
//if (Suggests.Where(x => Barcode.IsReprint(x.Item.SN) && x.Item.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE).Any() && !CurInv.Barcode.IsReprintSn)
//{
// action.IsSuccessed = false;
// action.LocaleMsg = Biz.L("需要先出拆包的物料");
// return action;
//}
var MaxDC = CurPoolList.Any(x => x.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE) ? CurPoolList.Where(x => x.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE).Max(t => t.PROD_DATE) : DateTime.MinValue;
//判断系统参数是否设置先进先出
if (Cache.SysParam["YesOrNo"].PARAM_VALUE == "Y")
{
if (CurInv.Items.Max(x => x.PROD_DATE).Date > MaxDC.Date)
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("必需先进先出", CurInv.Items.Max(x => x.PROD_DATE).Date, MaxDC.Date);
return action;
}
}
//判断物料是否在超期日期范围内
//if (CurInv.ItemInfo.VALIDITY_DAYS>0)
//{
// var time = DateTime.Now;
// if (CurInv.Items.Count() > 0)
// {
// foreach (var item in CurInv.Items)
// {
// //判断是否有生产日期
// if (!item.PROD_DATE.IsNullOrEmpty() && item.PROD_DATE.ToString()!= "1900/1/1 0:00:00")
// {
// var t = time - item.PROD_DATE;
// decimal timeDifference = (decimal)t.TotalDays;
// if (timeDifference > CurInv.ItemInfo.VALIDITY_DAYS)
// {
// action.IsSuccessed = false;
// action.LocaleMsg = Biz.L("产品超出有效期范围",timeDifference);
// return action;
// }
// }
// //否则以首次入库时间
// else if (!item.FIRST_IN_DATE.IsNullOrEmpty() && item.FIRST_IN_DATE.ToString() != "1900/1/1 0:00:00")
// {
// var t = time - item.PROD_DATE;
// decimal timeDifference = (decimal)t.TotalDays;
// if (timeDifference > CurInv.ItemInfo.VALIDITY_DAYS)
// {
// action.IsSuccessed = false;
// action.LocaleMsg = Biz.L("产品超出有效期范围", timeDifference);
// return action;
// }
// }
// }
// }
//}
//2.是否超发
isExceed = false;
req = await Biz.Db.Queryable().Where(x => x.BILLCODE == req.BILLCODE).Includes(q => q.DtlsWithGhost, d => d.ItemInfo).IncludesAllFirstLayer().FirstAsync();
dtls = req.Dtls.Where(x => x.ITEM_CODE == CurInv.ItemInfo.ITEM_CODE).ToList();
if (dtls.IsNullOrEmpty())
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("领料单明细没有这个物料");
return action;
}
//如果状态完成
if (dtls.Where(x => x.LINESTATUS == BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()).Count() == dtls.Count)
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("料已经发完");
return action;
}
//实际已经扫描数量
var actQty = dtls.Sum(x => x.QTY > x.PRQTY ? 0 : x.PRQTY - x.QTY);
cutQty = 0;
if (actQty < CurInv.CurPkg.QTY)
{
isExceed = true;
cutQty = CurInv.CurPkg.QTY - actQty;
Logger.Interface.Info($"生产领料 =>单号[{req.BILLCODE}],条码[{CurInv.CurPkg.SN}],实际已经扫描数量:{actQty},剩余数量:{cutQty},超发提示");
}
//3.是否截料,不允许超发的物料,所有的单别都需要做拆包处理,自动调用拆包,允许超发的物料,单别5408或者5409的要拆包
isCutting = false;
transferSn = null;
var isFirstDtl = true;
remainQty = 0;
decimal curQty = CurInv.CurPkg.QTY;
foreach (var d in dtls)
{
var actPrQty = d.PRQTY - d.QTY;
if (actPrQty > 0 && curQty > 0)
{
if (actPrQty >= curQty)
{
d.QTY += curQty;
curQty = 0;
}
else
{
d.QTY = d.PRQTY;
curQty -= actPrQty;
}
d.LINESTATUS = d.QTY >= d.PRQTY ? BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue() : BIZ_ERP_PROD_OUT.STATUSs.WORKING.GetValue();
//d.WAREHOUSECODE = CurInv.Warehouse.WH_CODE;
//如果是Agv叫料
if (CurReqType == ReqType.IsAgv)
{
d.LINESTATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue();
Logger.Interface.Info($"生产领料 =>叫料发料,单号:[{req.BILLCODE}],Qty:{d.QTY},状态:{d.LINESTATUS},项次:[{d.BILLLINE}]");
}
//首套发料且是循环的第一次扣减数量的行,则标记该行完成,后续的行需要再扫一盘才标记完成
if (CurReqType == ReqType.IsFirst && isFirstDtl)
{
d.LINESTATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue();
}
isFirstDtl = false;
}
}
remainQty = curQty; //如果超发就是超发需要调拨的数量
var _isCutting = isCutting;
var _isExceed = isExceed;
if (isExceed && (CurInv.ItemInfo.DLVY_TYPE == BAS_ITEM.DLVY_TYPEs.ByDemand.GetValue() || req.BILLCODE.Substring(2, 4) == "5408" || req.BILLCODE.Substring(2, 4) == "5409"))
{
_isCutting = true;
_isExceed = false;
if (CurInv.Items.Count > 1)
{
action.IsSuccessed = false;
action.LocaleMsg = Biz.L("扫描的条码不是最小包装,不能拆包!");
return action;
}
}
//增加储位操作历史
if (!LocationHis.Any(q => q.SHELF_ID == CurInv.Location.SHELF_ID && q.LOCATION_CODE == CurInv.Location.LOCATION_CODE))
{
LocationHis.Add(CurInv.Location);
}
//action.LocaleMsg = Biz.L($"生产领料 =>料号:[{CurInv.ItemInfo.ITEM_CODE}],状态:{req.STATUS.GetEnumDesc()}");
action.Data = new ProdReqOutput()
{
SN = CurInv.SN,
ItemCode = CurInv.ItemInfo.ITEM_CODE,
Qty = CurInv.CurPkg.QTY,
CutQty = CurInv.CurPkg.QTY - cutQty,
isCutting = _isCutting,
isExceed = _isExceed,
ReqNo = req.BILLCODE,
regionCode = CurInv.Region.REGION_CODE,
locationCode = CurInv.Location?.LOCATION_CODE,
ScanAfCut = CurInv.Warehouse.SCAN_AF_CUT
};
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"扫描物料[{input.SN}]复核异常");
}
return action;
}
///
/// 确认超发后或者需要截料的
///
///
///
public async Task> ConfirmExceed(ProdReqInput input)
{
var action = new ApiAction();
try
{
if (isExceed)
{
if (CurInv.ItemInfo.DLVY_TYPE == BAS_ITEM.DLVY_TYPEs.ByDemand.GetValue() || req.BILLCODE.Substring(2, 4) == "5408" || req.BILLCODE.Substring(2, 4) == "5409")
{
isCutting = true;
isExceed = false;
}
else
{
//最大数量明细行用于调拨,该行发料数量加上应截料数量
var tfdtl = dtls.OrderBy(q => q.BILLLINE.ToDecimal()).Last();
tfdtl.QTY += cutQty;
//调拨
transferH = Biz.Db.Queryable().Where(x => x.SOURCECODE == req.BILLCODE).First() ?? new()
{
BILLCODE = "TF-" + req.BILLCODE,
BILLDATE = DateTime.Now,
STATUS = BIZ_WMS_TRANSFER.STATUSs.INIT.GetValue(),
BIZTYPE = null,
OUTWAREHOUSECODE = CurInv.Warehouse.WH_CODE,
INWAREHOUSECODE = CurInv.Warehouse.TRANSFER_WH,
SOURCETYPE = 0,
SOURCECODE = req.BILLCODE,
AUTH_ORG = OrgCode
};
transferDtl = Biz.Db.Queryable().Where(x => x.BILLCODE == transferH.BILLCODE && x.BILLLINE == tfdtl.BILLLINE).First() ?? new()
{
BILLCODE = transferH.BILLCODE,
BILLLINE = tfdtl.BILLLINE,
LINESTATUS = BIZ_WMS_TRANSFER.STATUSs.FINISHED.GetValue(),
ITEM_CODE = CurInv.ItemInfo.ITEM_CODE,
UNITCODE = "",
PRQTY = remainQty,
OUTQTY = remainQty,
INQTY = remainQty,
OUTWAREHOUSECODE = CurInv.Warehouse.WH_CODE,
INWAREHOUSECODE = CurInv.Warehouse.TRANSFER_WH,
SOURCETYPE = transferH.SOURCETYPE,
SOURCECODE = transferH.SOURCECODE,
SOURCELINE = tfdtl.BILLLINE,
REMARK = "",
AUTH_ORG = OrgCode
};
Logger.Interface.Info($"生产领料 =>生成调拨明细,单号:[{transferH.BILLCODE}],Qty:{remainQty},状态:{transferDtl.LINESTATUS},项次:[{transferDtl.BILLLINE}]");
transferSn = new()
{
BILLCODE = transferH.BILLCODE,
BILLLINE = tfdtl.BILLLINE,
ITEM_CODE = CurInv.ItemInfo.ITEM_CODE,
SN = CurInv.CurPkg.SN,
QTY = CurInv.CurPkg.QTY,
AUTH_ORG = OrgCode
};
}
}
//灭灯
if (CurInv.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.Smart.GetValue() || CurInv.Shelf.SHELF_TYPE == WMS_SHELF.SHELF_TYPEs.QRCode.GetValue())
{
await Share.Shelf.DownSingle(TransID, CurInv.Location);
}
Suggests.Where(q => CurInv.Items.Select(x => x.SN).Contains(q.Item.SN)).ToList().ForEach((i) => { i.poolItem.STATUS = WMS_ITEM_POOL.STATUSs.WaitSend.GetValue(); });
var reqSn = new BIZ_ERP_PROD_OUT_SN()
{
AUTH_ORG = OrgCode,
ORDER_NO = req.BILLCODE,
SN = CurInv.CurPkg.SN,
ITEM_CODE = CurInv.ItemInfo.ITEM_CODE,
QTY = CurInv.CurPkg.QTY,
META_SN = CurInv.Barcode.MetaSn,
STATUS = WMS_ITEM.STATUSs.Sended.GetValue(),
LOCATION_CODE = CurInv.Location?.LOCATION_CODE,
ERP_WH = CurInv.Warehouse.WH_CODE,
ALLOC_QTY = CurInv.CurPkg.QTY - cutQty,
SOURCE_CODE = "WORK_ORDER",
SOURCE_ORDER = dtls.First().SOURCECODE,
IS_FIRST = CurReqType == ReqType.IsFirst ? "Y" : "N",
NEED_CUTTING = isCutting ? "Y" : "N",
};
//4.更新业务单据,BIZ_ERP_PROD_OUT、BIZ_ERP_PROD_OUT_DTL、BIZ_ERP_PROD_OUT_SN 判断明细行状态是否完成
//detail未完成的行数
//var isComplete = req.Dtls.Where(x => !(x.LINESTATUS == BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue() && x.PRQTY > 0)).Count() <= 0;
var isComplete = !req.Dtls.Any(x => x.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue() && x.PRQTY > 0);
his_isComplete = isComplete;
if (isComplete)
{
//detail全部完成了
req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue();
Logger.Interface.Info($"生产领料 =>领料单[{req.BILLCODE}]发料完成,单头状态:{req.STATUS}");
transferH = transferH != null ? transferH : Biz.Db.Queryable().Where(x => x.SOURCECODE == req.BILLCODE).First();
if (transferH != null)
{
transferH.STATUS = BIZ_WMS_TRANSFER.STATUSs.FINISHED.GetValue();
Logger.Interface.Info($"生产领料 =>更新调拨单[{transferH.BILLCODE}]状态:{transferH.STATUS}");
}
}
else
{
req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.WORKING.GetValue();
}
//如果是Agv
if (CurReqType == ReqType.IsAgv)
{
req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue();
}
//5.出库下架
foreach (var item in CurInv.Items)
{
item.TRANS_CODE = nameof(BIZ_ERP_PROD_OUT);
item.TRANS_NO = req.BILLCODE;
item.TRANS_LINE = string.Join(",", dtls.Select(x => x.BILLLINE));
item.SOURCE_CODE = "WORK_ORDER";
item.SOURCE_ORDER = dtls.First().SOURCECODE;
}
Result downResult = WMS_ITEM_Biz.WmsItem.TakeDown(CurInv, input.AuthOption, WMS_ITEM.STATUSs.Sended, !isCutting);
if (!downResult.IsSuccessed)
{
ProcessingSn = "";
action.IsSuccessed = false;
action.LocaleMsg = downResult.LocaleMsg;
return action;
}
TakeDownInfo downInfo = downResult.Data;
//物料池中删除已下架物料
var poolItems = CurPoolList.Where(q => downInfo.Items.Select(x => x.SN).Contains(q.SN)).ToList();
//5.保存数据库
var db = Business.Biz.Db;
var dbTran = db.UseTran(() =>
{
//入库
db.Updateable(downInfo.Items, UserCode).ExecuteCommand();
db.Insertable(downInfo.History, UserCode).ExecuteCommand();
db.Updateable(downInfo.Packages, UserCode).ExecuteCommand();
db.Deleteable(poolItems).ExecuteCommand();
db.Updateable(req, UserCode).ExecuteCommand();
db.Updateable(dtls, UserCode).ExecuteCommand();
if (transferH != null)
{
db.Storageable(transferH, UserCode).ExecuteCommand();
}
if (transferDtl != null)
{
db.Storageable(transferDtl, UserCode).ExecuteCommand();
}
if (transferSn != null)
{
db.Insertable(transferSn, UserCode).ExecuteCommand();
}
if (reqSn != null)
{
db.Insertable(reqSn, UserCode).ExecuteCommand();
}
db.Deleteable().Where(q => SqlFunc.Subqueryable().Where(p => p.ITEM_CODE == q.ITEM_CODE && p.BILLCODE == q.TRANS_NO &&
p.LINESTATUS != BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue()).NotAny()).ExecuteCommand();
});
if (!dbTran.IsSuccess)
{
Logger.Default.Fatal(dbTran.ErrorException, "Database transaction save exception");
this.Close(!dbTran.IsSuccess);
throw dbTran.ErrorException;
}
//6. 对接MES、Agv
//通过工厂带出 MES api 和 agv api
MES_FACTORY _factory = db.Queryable()
.Where(x => x.FTY_CODE == FTY_CODE)
.First();
//发送到MES的实体 2022/09/20
var query = db.Queryable().Where(x => x.ORIGINAL_WORKORDER == dtls.First().SOURCECODE).First();
var sctrwo = query != null ? (query.TARGET_WORKORDER.IsNullOrEmpty() ? dtls.First().SOURCECODE : query.TARGET_WORKORDER) : dtls.First().SOURCECODE;
toMes = new ProductionPickToMes()
{
qrcode = QrCode,
wo = sctrwo, // nCurrentLines.First().SourceCode,
pkgid = CurInv.SN,
mfrkp = CurInv.Barcode.OEMItemCode,
kpno = CurInv.ItemInfo.ITEM_CODE,
dc = CurInv.Barcode.ProdDateStr,
lc = CurInv.Barcode.LotNo,
qty = CurInv.CurPkg.QTY.ToString(),
erpBillCode = req.BILLCODE, //MES接口增加字段,返回备料完成的当前领料单号 2023/05/17 Ben Lin
taskid = ""
};
//调用agv
if (CurReqType == ReqType.IsAgv)
{
string[] agvData = req.SOURCECODE.Split(',');
toMes.taskid = agvData[0]; //MES接口增加字段,返回备料完成的当前任务ID 2023/05/17 Ben Lin
AgvMPInput agvMPInput = new AgvMPInput
{
taskId = agvData[0],
stationCode = agvData.Length >= 2 ? agvData[1] : "",
materialType = CurInv.ItemInfo.ITEM_CODE,
type = "done"
};
//JObject resp = PostMesApi(@"http://172.18.8.56:9533/api/WMS/Agv/materialPreparation", JsonConvert.SerializeObject(agvMPInput));
var agvurl = _factory != null ? _factory.AGV_API : @$"{Cache.SysParam["agvUrl", "AgvApiAddress"].PARAM_VALUE}"; ;
var agvResult = DI.Resolve().materialPreparation(agvMPInput, agvurl);
req.STATUS = BIZ_ERP_PROD_OUT.STATUSs.COMPLETE.GetValue();
Logger.Interface.Info($"生产领料 =>叫料发料完成,单号:[{req.BILLCODE}],状态:{req.STATUS.GetEnumDesc()}");
}
if (isComplete)
{
//领料单实时过账 MaterialReqToErpNew
MReqPostParam mReqPostParam = new()
{
BillCode = req.BILLCODE,
CompanyId = OrgCode
};
//异步调用T100过账
DI.Resolve().MaterialReqToErpNew(mReqPostParam);
}
//发送到MES
var isSend = Cache.SysParam["IsSend", "SendToMES"].PARAM_VALUE.ToString() == "Y";
if (isSend && !isCutting)
{
var mesApi = _factory != null ? _factory.MES_API : Cache.SysParam["apiUrl", "SendToMES"].PARAM_VALUE.ToString();
var response = HttpHelper.PostAsync(mesApi, JsonConvert.SerializeObject(toMes)).Result;
var _action = JsonConvert.DeserializeObject(response.Message);
Logger.Interface.Info($"生产领料 =>发料完成,单号[{req.BILLCODE}],条码[{CurInv.SN}],状态[{req.STATUS.GetEnumDesc()}],MES传入Json:{JsonConvert.SerializeObject(toMes)}, MES返回:{response.Message}");
}
action.LocaleMsg = Biz.L($"生产领料 =>发料完成,单号[{req.BILLCODE}],条码[{CurInv.SN}],状态[{req.STATUS.GetEnumDesc()}]");
action.Data = new ProdReqOutput()
{
SN = CurInv.SN,
ItemCode = CurInv.ItemInfo.ITEM_CODE,
Qty = CurInv.CurPkg.QTY,
CutQty = CurInv.CurPkg.QTY - cutQty,
isCutting = isCutting,
isExceed = isExceed,
ReqNo = req.BILLCODE,
regionCode = CurInv.Region.REGION_CODE,
locationCode = CurInv.Location?.LOCATION_CODE,
};
}
catch (Exception ex)
{
action.CatchExceptionWithLog(ex, $"亮灯异常");
}
ProcessingSn = "";
return action;
}
///
/// 添加一个ApiAction的历史记录
///
///
public override void AddHistory(Microsoft.AspNetCore.Http.HttpRequest request, ApiAction action)
{
var his = action.History();
his.TraceDic.Add("CurReqType", CurReqType);
his.TraceDic.Add("CurInv", CurInv);
his.TraceDic.Add("req", req);
his.TraceDic.Add("toMes", toMes);
his.TraceDic.Add("CurPoolList", CurPoolList);
his.TraceDic.Add("isComplete", his_isComplete);
ActionHistoryList.Add($"{request.HttpContext.TraceIdentifier} at {action.Timestamp:yyyy/MM/dd HH:mm:ss.fff}: {request.Path}", his);
LastActionTime = DateTime.Now;
}
#endregion Functions
public override bool Close(bool needSaveHistoryLog = false)
{
//needSaveHistoryLog = true;
CloseLight(LocationHis).Wait();
if (!(req?.BILLCODE ?? "").IsNullOrEmpty())
{
Biz.Db.Deleteable().Where(x => x.TRANS_NO == req.BILLCODE).ExecuteCommand();
}
Biz.Db.Deleteable().Where(q => CurPoolList.Select(q => q.SN).Contains(q.SN)).ExecuteCommand();
//保存操作日志
this.IsFinished = true;
return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
}
}//endClass
}