手动配货
不知道配货流程的朋友可以看一下前面的文章链接: 深入浅出WMS之出库流程里面有对出库的解释说明,其中也有对配货的解释。前端页面也可以在前面的那篇文章中看到,这里我们来说一下后端部分。
查
手动配货是选中出库单的某条数据,然后点击手动配货,这个时候前端会把这个出库单的guid传给后端,这个时候我们有guid了,就可以去出库单表里去找到这条数据,取出其中的订单号、批次号等等信息。这个时候我们要做的就很简单,去库存里找符合我们可以出库的信息。
public async Task<PageData> QueryPeiH(int pages, int limit, string matno, string batch, string cflag)
{
PageData pageData = new PageData();
var list = _fsql.Select<LogMstore, LOGMATERIAL,LOGSTORAGE>()
.LeftJoin((a, b, c) => a.MATNO == b.MATNO)
.LeftJoin((a, b, c) => a.ADDRE == c.ADDRE);
if (!string.IsNullOrWhiteSpace(matno))
{
list = list.Where((a,b,c) => a.MATNO == matno);
}
if (!string.IsNullOrWhiteSpace(batch))
{
list = list.Where((a, b, c) => a.BATCH == batch);
}
if (!string.IsNullOrWhiteSpace(cflag))
{
list = list.Where((a, b, c) => a.CFLAG == cflag);
}
//在这里添加我们对库存信息的限制条件
list = list.Where((a, b, c) => Convert.ToDouble(a.QUANT) - Convert.ToDouble(a.QUANTOUT) > 0);
list = list.Where((a, b, c) => c.PkFlag != "PK");
//
if (limit <= 0)
{
//pageData.pageData = await list.Count(out var total).ToListAsync();
//pageData.total = (int)total;
pageData.pageData = await list.Count(out var total).ToListAsync((a, b, c) => new {
a.MATNO,
b.MNAME,
b.SZNO,
a.BATCH,
keyong = Convert.ToDouble(a.QUANT) - Convert.ToDouble(a.QUANTOUT),
a.ADDRE,
a.PALNO,
a.SECTN,
a.CFLAG
});
pageData.total = (int)total;
}
else
{
pageData.pageData = await list.Count(out var total).Skip((pages - 1) * limit).Take(limit).ToListAsync((a, b, c) => new {
a.MATNO,
b.MNAME,
b.SZNO,
a.BATCH,
keyong = Convert.ToDouble(a.QUANT) - Convert.ToDouble(a.QUANTOUT),
a.ADDRE,
a.PALNO,
a.SECTN,
a.CFLAG
});
pageData.total = (int)total;
}
return pageData;
}
当我们查到所有符合条件的库存信息后,把数据返回给前端。这个时候也就是当前端选中出库单点击手动配货按钮时,会弹出一个页面,也就是我们上面执行的查询结果。这个时候又操作人员来进行货位分配。例如我们要出750个A货物,然后有符合出库的8个托盘,每个托盘上有100个A物品,这个时候我们手动配货是可以选择每个托盘出多少的,比如说第一个托盘出90个,第二个托盘出80等。当配完货后会生成相对应的出库任务,那么接下来我们看一下这个业务是怎么用代码来实现的。
配
[HttpPost]
public async Task<IActionResult> QueryPeiH(List<LogMstore> logMstores)
{
return Ok(await _erpOutService.PeiHuo2(logMstores));
}
这个是我们的配货接口,这个时候前端会发一个list过来,写list的目的是为了把配货的数据一次性发给后端,而不是一直调用接口。
这个时候可以看到我们调用了server层。我们来看一下server层对业务的处理。
public async Task<ResultData> PeiHuo2(List<LogMstore> logMstores)
{
try
{
//循环list
LOGOUTTASK lOGOUTTASK = new LOGOUTTASK();
for (int i = 0; i < logMstores.Count; i++)
{
string quant = logMstores[i].QUANTOUT;
string palno = logMstores[i].PALNO;
string ordno = logMstores[i].DEMO1;
string itmno = logMstores[i].DEMO2;
string keyong = logMstores[i].DEMO3;
//通过订单号和行号查询出库表
var ErpOut = await _erpOutRepository.QueryMI(ordno, itmno);
//通过托盘号查询库存表
var OutMstore = await _mstoreRepository.QueryPalno(palno);
#region 赋值给出库任务做添加
lOGOUTTASK.ErpoutId = ErpOut.Id.ToString();
lOGOUTTASK.TICNO = "";
lOGOUTTASK.SEQNO = "";
lOGOUTTASK.INTASKID = OutMstore.INTASKID;
lOGOUTTASK.ORDNO = ordno;
lOGOUTTASK.ITMNO = itmno;
lOGOUTTASK.RECTYPE = ErpOut.RECTYPE;
lOGOUTTASK.MATNO = ErpOut.MATNO;
lOGOUTTASK.MUNIT = ErpOut.MUNIT;
lOGOUTTASK.QUANT = quant;
lOGOUTTASK.QUANT0 = keyong;
lOGOUTTASK.QuantQy = "0";
lOGOUTTASK.JIAN = ErpOut.JIAN;
lOGOUTTASK.STANO = "0";
lOGOUTTASK.ADDRESRC = OutMstore.ADDRE;
lOGOUTTASK.ADDREDESC = "-";
lOGOUTTASK.PALNO = OutMstore.PALNO;
lOGOUTTASK.MstoreId = OutMstore.MstoreId.ToString();
lOGOUTTASK.CFLAG = ErpOut.CFLAG;
lOGOUTTASK.BATCH = ErpOut.BATCH;
lOGOUTTASK.LOTNO = ErpOut.LOTNO;
lOGOUTTASK.CUSTOM = ErpOut.CUSTOM;
lOGOUTTASK.CustAddress = "-";
lOGOUTTASK.WORKS = "-";
lOGOUTTASK.STORE = ErpOut.STORE;
lOGOUTTASK.VCDSCR = ErpOut.VCDSCR;
lOGOUTTASK.PONO = ErpOut.PONO;
lOGOUTTASK.POITEM = ErpOut.POITEM;
lOGOUTTASK.STYPE = "N";
lOGOUTTASK.ATTACHMENT = "N";
lOGOUTTASK.SECTN = ErpOut.SECTN;
lOGOUTTASK.PRDAT = ErpOut.PRDAT;
lOGOUTTASK.QUDAT = ErpOut.QUDAT;
lOGOUTTASK.PRICE = "-";
lOGOUTTASK.KEEPER = "-";
lOGOUTTASK.PRNNO = "-";
lOGOUTTASK.TKDAT = DateTime.Now;
lOGOUTTASK.COMDAT = DateTime.Now;
lOGOUTTASK.IFDO = "O";
lOGOUTTASK.OPUSER = ErpOut.OPUSER;
lOGOUTTASK.USERID = "";
lOGOUTTASK.OutPort = "";
lOGOUTTASK.OutportJ = "";
lOGOUTTASK.Deviceno = "";
lOGOUTTASK.DEMO1 = ordno;
lOGOUTTASK.DEMO2 = itmno;
lOGOUTTASK.DEMO3= logMstores[i].DEMO3;
lOGOUTTASK.Tasktype = "N";
lOGOUTTASK.DEMO19 = "自建";
lOGOUTTASK.DEMO24 = ErpOut.DEMO24;
#endregion
if (Convert.ToDouble(OutMstore.QUANT) <Convert.ToDouble(quant))
{
throw new Exception("库存数量不足!");
}
if (await _erpOutRepository.PeiHuo(lOGOUTTASK) == 0)
{
resultData.code = 0;
resultData.message = "配货成功!";
}
}
}
catch (Exception ex)
{
resultData.code = 1;
resultData.message = ex.Message;
}
return resultData;
}
在这里面我们又调用了一层,因为这层是处理业务的,会有另外一层专门写增删改查等操作,这样的话也是为了提高代码的利用率,这也符合我们写代码的原则“高内聚,低耦合”。
public async Task<int> PeiHuo(LOGOUTTASK lOGOUTTASK)
{
try
{
var ErpOut = await _fsql.Select<LogErpOut>().Where(x => x.ORDNO == lOGOUTTASK.ORDNO && x.ITMNO == lOGOUTTASK.ITMNO).FirstAsync();
string quant0 = ErpOut.QUANT0;
var Pdian = await _fsql.Select<LOGSTORAGE>().Where(x => x.ADDRE == lOGOUTTASK.ADDRESRC).FirstAsync();
if (Pdian.PkFlag == "PK") return 6001;
quant0 = (Convert.ToDouble(quant0) + Convert.ToDouble(lOGOUTTASK.QUANT)).ToString();
_fsql.Transaction(() => {
//添加出库任务
var inrows = _fsql.Insert(lOGOUTTASK).ExecuteAffrows();
if (inrows <= 0) throw new Exception("出库任务添加失败!");
//var erpin = _fsql.Select<LogErpOut>().Where(x => x.Id == Convert.ToDecimal(lOGOUTTASK.ErpoutId)).First();
//修改出库单已配货数量
var uprows = _fsql.Update<LogErpOut>().Set(x =>x.QUANT0 == quant0)
.Set(x => x.IfDo == "O")
.Where(x => x.ORDNO == lOGOUTTASK.ORDNO && x.ITMNO == lOGOUTTASK.ITMNO).ExecuteAffrows();
if (uprows <= 0) throw new Exception("配货数量回写失败!");
//库存表可用数量加上
});
return 0;
}
catch (Exception ex)
{
return 1;
}
}
这里我们需要使用到事务,以防数据出现错误。手动配货的相比较自动配货来说比较简单,了解业务之后就可以一步一步的往下写。而且一般来说不会出现什么错误,只需要多注意数据的增减。例如配货成功后出库单的已配货数量,未配货数量,出库任务的出库数量,库存的剩余数量,可用数量等等。假如对这个配货还不太懂的话,可以看一下前面的文章,里面有出库的整个流程,其中对配货也有一定说明。接下来我们来说一下重点-自动配货。
自动配货
自动配货前端写起来比较简单,因为只需要写一个按钮就可以。自动配货相对手动配货来说第一步的查询是一样的,只不过区别是手动配货需要我们把查出来的数据返回给前端,让操作人员进行分配,然后再把分配完的数据再传给后端。自动配货的话就是我们查出来数据后,我们后端自己处理。
上面的是我初次写的自动配货,如今已弃用,下面的是优化过的。还是那么一个简简单单的接口,只需要前端给我们传一个guid,我们就去server层自己玩。说明我写在下面代码的注释里,方便大家了解流程。
public async Task<ResultData> AutomaticPicking(string guid)
{
ResultData resultData = new ResultData()
{
code = 0,
message = "success",
};
try
{
List<LogMstore> logMstores = new List<LogMstore>();
var ErpOut = await _erpOutRepository.Queryguid(guid);//根据guid查询出库单
string quantout = ErpOut.QUANT;//出库单计划出库数量
string matno = ErpOut.MATNO;
string batch = ErpOut.BATCH;
string cflag = ErpOut.CFLAG;
//查询符合条件的库存信息
var logmstore = await _mstoreRepository.QueryZiDong(1, 20, matno, batch, cflag);
//序列化查询符合条件的库存信息
var json = JsonConvert.SerializeObject(logmstore.pageData, Formatting.Indented);
var newdynamicData = JsonConvert.DeserializeObject<List<dynamic>>(json);
//未配货数量 = 计划数量 - 已配货数量
Double UnshippedQuantity = Convert.ToDouble(ErpOut.QUANT) - Convert.ToDouble(ErpOut.QUANT0);
//符合条件的数据循环插入到List集合中
for (int i = 0; i < newdynamicData.Count; i++)
{
//如果大于0说明是整托出库,例如750个的货,每个托盘100个,那么前7个托盘就是整托出库,最后的一个托盘就是拣选出库,因为我们只需要50,剩下的50还需要再回库。
if((UnshippedQuantity - Convert.ToDouble(newdynamicData[i].QUANT)) >= 0)
{
UnshippedQuantity = UnshippedQuantity - Convert.ToDouble(newdynamicData[i].QUANT);
logMstores.Add(new LogMstore
{
ADDRE = newdynamicData[i].ADDRE,//货位地址
BATCH = newdynamicData[i].BATCH,//批次号
CFLAG = newdynamicData[i].CFLAG,
DEMO1 = ErpOut.ORDNO,//订单号
DEMO2 = ErpOut.ITMNO,//行号
MATNO = newdynamicData[i].MATNO,//物料编码
PALNO = newdynamicData[i].PALNO,
QUDAT = newdynamicData[i].QUDAT,
SECTN = newdynamicData[i].SECTN,
//QUANT = newdynamicData[i].QUANT //库存数量
QUANTOUT = newdynamicData[i].QUANT
});
}
else
{
logMstores.Add(new LogMstore
{
ADDRE = newdynamicData[i].ADDRE,//货位地址
BATCH = newdynamicData[i].BATCH,//批次号
CFLAG = newdynamicData[i].CFLAG,
DEMO1 = ErpOut.ORDNO,//订单号
DEMO2 = ErpOut.ITMNO,//行号
MATNO = newdynamicData[i].MATNO,//物料编码
PALNO = newdynamicData[i].PALNO,
QUDAT = newdynamicData[i].QUDAT,
SECTN = newdynamicData[i].SECTN,
//QUANT = newdynamicData[i].QUANT //库存数量
QUANTOUT = UnshippedQuantity.ToString()
});
break;
}
}
await ZiDong(logMstores);//这一步和手动配货一样
}
catch (Exception ex)
{
resultData.code = 1;
resultData.message = ex.Message;
}
return resultData;
}
自动配货的话一般有先入先出原则,也有随机出库原则等等,这个都是根据甲方需求来写。先入先出原则比较简单,查询的时候按时间进行排序查询结果就可以。这里我们用的也是先入先出原则。自动配货的话一般来说比较常用。