using Rhea.Common;
|
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 Tiger.IBusiness;
|
using Tiger.Business.WMS.Common;
|
using Org.BouncyCastle.Ocsp;
|
using Tiger.Model.Sharetronic.Shelf;
|
using MailKit.Search;
|
using Tiger.Business.WMS.Extensions;
|
|
namespace Tiger.Business.WMS.Transaction
|
{
|
/// <summary>
|
/// 备料任务下架事务
|
/// </summary>
|
public class Out_BIZ_WMS_PREP : WmsTask, IOut_BIZ_WMS_PREP
|
{
|
public IOut_BIZ_WMS_PREP Init(string id, string userCode, string apiHost, string orgCode)
|
{
|
base.Init(id, userCode, apiHost, orgCode);
|
CurPREP = new(MainDB, userCode, orgCode);
|
Logger.Console.Info($"Start {this.GetType().Name} Transaction[ID: {TransID}]");
|
return this;
|
}
|
|
#region Propertys & Variables
|
public Preparation CurPREP { get; set; }
|
private List<BIZ_WMS_PREP_SN> CurSn = new();
|
private BIZ_WMS_PREP_DTL CurPrepDtl = new();
|
public List<SuggestItem> Suggests { get; set; } = new();
|
public List<WMS_ITEM_POOL> CurPoolList => Suggests.Where(q => !q.poolItem.IsNullOrEmpty()).Select(q => q.poolItem).ToList();
|
public List<WMS_LOCATION> LocationHis { get; set; } = new();
|
private bool isExceed = false;
|
private bool isNeedCut = false;
|
#endregion
|
|
#region Functions
|
/// <summary>
|
/// 扫描入口
|
/// </summary>
|
public async Task<ApiAction<ScanOutput>> Scan(BaseInput input)
|
{
|
var action = new ApiAction<ScanOutput>(new ScanOutput());
|
try
|
{
|
|
if (input.Command == "ScanOrder")
|
{
|
action = await ScanOrder(input);
|
}
|
else if (input.Command == "SelectItem")
|
{
|
action = await SelectItem(input);
|
}
|
else if (input.Command == "ConfirmExceed")
|
{
|
action = await ConfirmExceed(input);
|
}
|
else
|
{
|
if (CurPREP.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("备料前请先选择需要备料的单据");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanItem.NotScanOrder");
|
action.Data.Command = "ScanOrder";
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
if (CurPrepDtl.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("备料前请先选择需要备料的物料行");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanItem.NotSelectItem");
|
action.Data.Command = "SelectItem";
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
if (input.SN.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("条码不能为空");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.SnEmptyFailure");
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
//扫描物料并复核
|
{
|
action = await ScanItem(input);
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
//取消当前操作
|
ResetTrans();
|
//action.CatchExceptionWithLog(ex, $"扫描[{input.SN}]异常");
|
action.CatchExceptionWithLog(ex, Biz.L("WMS.Default.Scan.ScanException", input.SN));
|
}
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
|
/// <summary>
|
/// 扫描下架单据号码
|
/// </summary>
|
public async Task<ApiAction<ScanOutput>> ScanOrder(BaseInput input)
|
{
|
var action = new ApiAction<ScanOutput>(new ScanOutput());
|
try
|
{
|
var inputOrder = input?.Data?.JsonToObject<BIZ_WMS_PREP>();
|
if ((inputOrder?.BIZ_TYPE).IsNullOrEmpty() || (inputOrder?.SOURCE_ORDER ?? "").IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L($"请选择正确的备料任务类型和单号");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanOrder.InputOrderIsNull");
|
return action;
|
}
|
if (!CurPREP.Order.IsNullOrEmpty() && inputOrder.BIZ_TYPE != CurPREP.Order.BIZ_TYPE)
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L($"当前备料任务[{0}]的业务类型为{1},如需切换备料业务,请退出后重新选择");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanOrder.BizTypeError", CurPREP.Order.ORDER_NO, CurPREP.Order.BIZ_TYPE.GetEnumDesc<BIZ_WMS_PREP.BIZ_TYPEs>());
|
return action;
|
}
|
|
if (CurPREP.Order.IsNullOrEmpty() || !CurPREP.Order.SourceOrders.Any(q => q.SOURCE_ORDER == inputOrder.SOURCE_ORDER))
|
{
|
action = CurPREP.AddOrder(inputOrder.BIZ_TYPE.GetEnum<BIZ_WMS_PREP.BIZ_TYPEs>(), inputOrder.SOURCE_CODE);
|
}
|
else
|
{
|
//action.LocaleMsg = Biz.L($"当前备料任务[{0}]已包含单据[{1}],无需重复操作");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanOrder.Repeat", CurPREP.Order.ORDER_NO, inputOrder.SOURCE_ORDER);
|
}
|
action.Data.Command = "SelectItem";
|
action.Data.Data = CurPREP.Order;
|
}
|
catch (Exception ex)
|
{
|
//取消当前操作
|
ResetTrans();
|
//action.CatchExceptionWithLog(ex, $"备料任务扫描单据异常(Data: {0})");
|
action.CatchExceptionWithLog(ex, Biz.L("WMS.Out_BIZ_WMS_PREP.ScanOrderException", input?.Data));
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 获取当前备料的备料任务信息
|
/// </summary>
|
/// <returns></returns>
|
public async Task<ApiAction<BIZ_WMS_PREP>> GetPrepInfo()
|
{
|
var action = new ApiAction<BIZ_WMS_PREP>();
|
CurPREP.Order = MainDB.Queryable<BIZ_WMS_PREP>().Where(q => q.ID == CurPREP.Order.ID).IncludesAllFirstLayer().First();
|
|
action.Data = CurPREP.Order;
|
return action;
|
}
|
|
/// <summary>
|
/// 选择备料任务中要下架物料行
|
/// </summary>
|
public async Task<ApiAction<ScanOutput>> SelectItem(BaseInput input)
|
{
|
var action = new ApiAction<ScanOutput>(new ScanOutput());
|
try
|
{
|
var inputDtl = input?.Data?.JsonToObject<BIZ_WMS_PREP_DTL>();
|
var prepDtl = MainDB.Queryable<BIZ_WMS_PREP_DTL>().Where(q => q.ID == inputDtl.ID).First();
|
if (prepDtl.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("备料前请先选择需要备料的物料行");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanItem.NotSelectItem");
|
return action;
|
}
|
if (prepDtl.QTY_PREP > CurPREP.BizType.GetActReqQty(prepDtl))
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("当前选择物料行[{0}]已备料数量[{1}],以满足实际需求数量[{2}],无需继续备料");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.SelectItem.PrepFinish", prepDtl.ITEM_CODE, prepDtl.QTY_PREP, CurPREP.BizType.GetActReqQty(prepDtl));
|
return action;
|
}
|
//删除物料池中上一次分配的物料
|
MainDB.Deleteable<WMS_ITEM_POOL>().Where(q => q.TRANS_NO == prepDtl.ORDER_NO && q.TRANS_LINE == prepDtl.ORDER_LINE).ExecuteCommand();
|
if (!CurPrepDtl.IsNullOrEmpty())
|
{
|
MainDB.Deleteable<WMS_ITEM_POOL>().Where(q => q.TRANS_NO == CurPrepDtl.ORDER_NO && q.TRANS_LINE == CurPrepDtl.ORDER_LINE).ExecuteCommand();
|
}
|
CurPrepDtl = prepDtl;
|
|
//如果上一次推荐有数据,则先灭掉亮的灯
|
if (Suggests.Any())
|
{
|
//await CloseLight(LocationHis);
|
}
|
|
//如果是首套发料,则只推荐当前物料行的一盘物料
|
if (CurPREP.Order.CurBatch.DLVY_MODE == BIZ_WMS_PREP_BTH.DLVY_MODEs.First.GetValue())
|
{
|
//推荐物料
|
Result<List<SuggestItem>> result = Suggest(CurPrepDtl.ORDER_NO, CurPrepDtl.ITEM_CODE, null, inputDtl.WH_ID, inputDtl.REGION_ID, inputDtl.SHELF_ID, input.AuthOption, 1);
|
action.LocaleMsg = result.LocaleMsg;
|
if (result.IsException)
|
{
|
action.IsSuccessed = false;
|
return action;
|
}
|
|
Suggests.Clear();
|
Suggests = result.Data;
|
if (Suggests.Count > 0)
|
{
|
var inv = Suggests.First();
|
var actQty = CurPrepDtl.QTY_PREP > CurPREP.BizType.GetActReqQty(CurPrepDtl) ? 0 : (CurPREP.BizType.GetActReqQty(CurPrepDtl) - CurPrepDtl.QTY_PREP);
|
if (inv != null && actQty > 0)
|
{
|
inv.poolItem = inv.Item.GetPoolItem(OrgCode, nameof(CurPREP.Order), CurPrepDtl.ORDER_NO, CurPrepDtl.ORDER_LINE, actQty, true);
|
}
|
else
|
{
|
inv.poolItem = null;
|
}
|
}
|
Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty());
|
}
|
|
//如果是正常发料,则按实际需求数量推荐当前物料行的所有物料
|
if (CurPREP.Order.CurBatch.DLVY_MODE == BIZ_WMS_PREP_BTH.DLVY_MODEs.Supply.GetValue())
|
{
|
//推荐物料
|
var actQty = CurPrepDtl.QTY_PREP > CurPREP.BizType.GetActReqQty(CurPrepDtl) ? 0 : (CurPREP.BizType.GetActReqQty(CurPrepDtl) - CurPrepDtl.QTY_PREP);
|
Result<List<SuggestItem>> result = Suggest(CurPrepDtl.ORDER_NO, CurPrepDtl.ITEM_CODE, null, inputDtl.WH_ID, inputDtl.REGION_ID, inputDtl.SHELF_ID, input.AuthOption, actQty);
|
action.LocaleMsg = result.LocaleMsg;
|
if (result.IsException)
|
{
|
action.IsSuccessed = false;
|
return action;
|
}
|
|
Suggests.Clear();
|
var remain = actQty;
|
Suggests.ForEach((inv) =>
|
{
|
if (remain > 0)
|
{
|
inv.poolItem = inv.Item.GetPoolItem(input.AuthOption.OrgCode, nameof(CurPREP.Order), CurPrepDtl.ORDER_NO, CurPrepDtl.ORDER_LINE, remain, false);
|
remain -= inv.poolItem.ALLOC_QTY;
|
}
|
else
|
{
|
inv.poolItem = null;
|
}
|
});
|
Suggests.RemoveAll(q => q.poolItem.IsNullOrEmpty());
|
}
|
|
//保存物料池到数据库
|
MainDB.Insertable(CurPoolList).ExecuteCommand();
|
CurPrepDtl.Suggests = Suggests;
|
|
//action = await LightAll(new() { AuthOption = input.AuthOption, Color = LedColor.Blue });
|
|
action.Data.Command = "Normal";
|
action.Data.Data = CurPrepDtl;
|
//action.LocaleMsg = Biz.L($"已选择要备料物料行[{0},可以开始备料下架");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.SelectItem.Success", CurPrepDtl.ITEM_CODE);
|
}
|
catch (Exception ex)
|
{
|
//取消当前操作
|
ResetTrans();
|
//action.CatchExceptionWithLog(ex, $"备料任务选择要物料行异常(Data: {0})");
|
action.CatchExceptionWithLog(ex, Biz.L("WMS.Out_BIZ_WMS_PREP.SelectItemException", input?.Data));
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 获取当前备料的物料行信息
|
/// </summary>
|
/// <returns></returns>
|
public async Task<ApiAction<BIZ_WMS_PREP_DTL>> GetCurPrepItem()
|
{
|
var action = new ApiAction<BIZ_WMS_PREP_DTL>();
|
var prepDtl = MainDB.Queryable<BIZ_WMS_PREP_DTL>().Where(q => q.ID == CurPrepDtl.ID).First();
|
prepDtl.ERP_WH = CurPrepDtl.ERP_WH;
|
prepDtl.WH_ID = CurPrepDtl.WH_ID;
|
prepDtl.REGION_ID = CurPrepDtl.REGION_ID;
|
prepDtl.SHELF_ID = CurPrepDtl.SHELF_ID;
|
prepDtl.Suggests = Suggests;
|
CurPrepDtl = prepDtl;
|
CurPREP.Order.Details.RemoveAll(q => q.ID == CurPrepDtl.ID);
|
CurPREP.Order.Details.Add(CurPrepDtl);
|
action.Data = CurPrepDtl;
|
return action;
|
}
|
|
/// <summary>
|
/// 领料单推荐所有物料亮灯
|
/// </summary>
|
/// <param name="light"></param>
|
/// <returns></returns>
|
public async Task<ApiAction<ScanOutput>> LightAll(LightEntityInput light)
|
{
|
var action = new ApiAction<ScanOutput>();
|
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($"亮灯失败,未找到推荐下架的物料");
|
}
|
}
|
catch (Exception ex)
|
{
|
action.CatchExceptionWithLog(ex, $"亮灯异常");
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 灭灯
|
/// </summary>
|
/// <returns></returns>
|
public async Task<ApiAction> CloseLight(List<WMS_LOCATION> 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;
|
}
|
|
/// <summary>
|
/// 扫描物料并复核,如果物料已经完成移库则货架上亮灯提醒储位
|
/// </summary>
|
public async Task<ApiAction<ScanOutput>> ScanItem(BaseInput input)
|
{
|
var action = new ApiAction<ScanOutput>(new ScanOutput() { Command = Command });
|
try
|
{
|
//解析条码
|
Result<IInventory> result = GetInventory(input.SN, input.AuthOption, true);
|
if (!result.IsSuccessed)
|
{
|
action.IsSuccessed = false;
|
action.LocaleMsg = result.LocaleMsg;
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
var inv = result.Data as Inventory;
|
|
//验证条码是否正确
|
if (!inv.isNormalStatus || inv.Status != WMS_ITEM.STATUSs.InStore)
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("状态[{0}]异常,请重新扫描");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.StatusException", string.Join(',', inv.StatusList.Select(q => q.GetDesc())));
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
//验证条码是否被锁定
|
if (inv.Items.Any(q => q.IS_LOCKED == "Y"))
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("条码[{0}]已被锁定,请重新扫描");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.ItemIsLock", inv.CurPkg.SN);
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
//验证条码是否被其他用户和单据锁定
|
if (inv.Items.Any(q => q.IS_LOCKED == "Y"))
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("条码[{0}]已被锁定,请重新扫描");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.ItemIsLock", inv.CurPkg.SN);
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
//储位验证
|
if (inv.Location.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L($"条码[{0}]库存信息异常: 没有储位信息,请先上架后再扫描(储位Id: {1})");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.LocationIsNull", inv.CurPkg.SN, inv.CurPkg.LOCATION_ID);
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
//物料验证
|
if (inv.ItemInfo.IsNullOrEmpty() || inv.ItemInfo.IS_ACTIVE == "N")
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L($"物料编码[{0}]不存在或者该物料未启用");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.ItemCodeNotExistsOrNotActive", inv.ItemInfo.ITEM_CODE.IsNullOrEmpty(inv.Barcode.ItemCode));
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
//判断是否在备料中的物料
|
if (inv.ItemInfo.ITEM_CODE != CurPrepDtl.ITEM_CODE)
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L($"条码[{0}]不是当前选中的物料行[{1}],请放回原储位或者重新选择下架物料行");
|
action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanItem.NoNeedItemCode", inv.CurPkg.SN, CurPrepDtl.ITEM_CODE);
|
return action;
|
}
|
//判断是否在备料中的物料
|
//if (!CurPREP.Order.Details.Any(q => q.ITEM_CODE == inv.ItemInfo.ITEM_CODE))
|
//{
|
// action.IsSuccessed = false;
|
// //action.LocaleMsg = Biz.L($"当前备料任务[{0}]不包含条码[{1}]的物料编码[{2}],请放回原储位");
|
// action.LocaleMsg = Biz.L("WMS.Out_BIZ_WMS_PREP.ScanItem.NoNeedItemCode", inv.ItemInfo.ITEM_CODE.IsNullOrEmpty(inv.Barcode.ItemCode));
|
// return action;
|
//}
|
|
//ProcessingOrderDetail = 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;
|
//}
|
CurInvItem = inv;
|
CurSn = CurInvItem.Items.Select(q => new BIZ_WMS_PREP_SN() {
|
ORDER_NO = CurPREP.Order.ORDER_NO,
|
ORDER_LINE = CurPrepDtl.ORDER_LINE,
|
ORDER_BATCH = CurPREP.Order.CurBatch.BATCH.ToString(),
|
SN = q.SN,
|
ITEM_CODE = q.ITEM_CODE,
|
UNIT = q.UNIT,
|
QTY = q.QTY,
|
QTY_DLVY = q.QTY,
|
STATUS = q.STATUS,
|
LOTNO = q.LOTNO,
|
WH_CODE = CurInvItem.Warehouse.WH_CODE,
|
REGION_CODE = CurInvItem.Region.REGION_CODE,
|
SHELF_CODE = CurInvItem.Shelf.SHELF_CODE,
|
LOCATION_CODE = CurInvItem.Location.LOCATION_CODE,
|
FTY_CODE = CurPREP.Order.CurBatch.FTY_CODE,
|
WS_CODE = CurPREP.Order.CurBatch.WS_CODE,
|
LINE_CODE = CurPREP.Order.CurBatch.LINE_CODE,
|
POST_CODE = CurPREP.Order.CurBatch.POST_CODE,
|
OPER_CODE = CurPREP.Order.CurBatch.OPER_CODE,
|
SEGMENT = CurPREP.Order.CurBatch.SEGMENT,
|
SMT_NO = CurPrepDtl.SMT_NO,
|
STATION_NO = CurPrepDtl.STATION_NO,
|
FEEDER_NO = CurPrepDtl.FEEDER_NO,
|
FEEDER_TYPE = CurPrepDtl.FEEDER_TYPE,
|
IS_FIRST = CurPREP.Order.CurBatch.DLVY_MODE == BIZ_WMS_PREP_BTH.DLVY_MODEs.First.GetValue() ? "Y" : "N",
|
NEED_CUTTING = "N",
|
}).ToList();
|
|
|
//判断是否超发
|
isExceed = false;
|
//计算剩余需求数量和条码中的发出数量
|
var actQty = CurPrepDtl.QTY_PREP > CurPREP.BizType.GetActReqQty(CurPrepDtl) ? 0 : (CurPREP.BizType.GetActReqQty(CurPrepDtl) - CurPrepDtl.QTY_PREP);
|
if (actQty < CurInvItem.CurPkg.QTY)
|
{
|
isExceed = true;
|
var remain = actQty;
|
foreach (var sn in CurSn.OrderByDescending(q => q.QTY))
|
{
|
sn.QTY_DLVY = remain >= sn.QTY ? sn.QTY : remain;
|
sn.NEED_CUTTING = remain >= sn.QTY ? "N" : "Y";
|
remain -= sn.QTY_DLVY;
|
}
|
}
|
|
//判断物料是否允许超发,如果不允许则必须截料,否则由用户在PDA选择是否截料
|
isNeedCut = CurInvItem.ItemInfo.DLVY_TYPE == BAS_ITEM.DLVY_TYPEs.ByDemand.GetValue();
|
|
//如果超发则返回前端处理,否则直接发出
|
if (isExceed)
|
{
|
//action.LocaleMsg = Biz.L($"条码[{0}]扫描成功,备料物料[{1}]需求[{2}]还需备料[{3}],条码需要截料,发出数量[{4}]");
|
//action.LocaleMsg = Biz.L($"条码[{0}]扫描成功,备料物料[{1}]需求[{2}]还需备料[{3}],请选择要超发还是截料?");
|
action.LocaleMsg = Biz.L($"WMS.Out_BIZ_WMS_PREP.ScanItem.Success{(isNeedCut ? "" : "Confirm")}", CurInvItem.SN, CurInvItem.ItemInfo.ITEM_CODE, CurPREP.BizType.GetActReqQty(CurPrepDtl), actQty);
|
action.Data.Command = "ConfirmExceed";
|
action.Data.Data = new
|
{
|
isExceed,
|
isNeedCut,
|
CurSn,
|
};
|
}
|
else
|
{
|
input.Data = CurSn.ToJson();
|
action = await ConfirmExceed(input);
|
}
|
}
|
catch (Exception ex)
|
{
|
action.CatchExceptionWithLog(ex, $"扫描物料[{input.SN}]复核异常");
|
}
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
|
/// <summary>
|
/// 前端确认超发或者返回截料后的信息
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<ApiAction<ScanOutput>> ConfirmExceed(BaseInput input)
|
{
|
var action = new ApiAction<ScanOutput>(new ScanOutput() { Command = "Normal" });
|
try
|
{
|
var sns = (input.Data ?? "").JsonToObject<List<BIZ_WMS_PREP_SN>>() ?? new List<BIZ_WMS_PREP_SN>();
|
if (!sns.Any())
|
{
|
var actQty = CurPrepDtl.QTY_PREP > CurPREP.BizType.GetActReqQty(CurPrepDtl) ? 0 : (CurPREP.BizType.GetActReqQty(CurPrepDtl) - CurPrepDtl.QTY_PREP);
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L($"条码[{0}]扫描成功,备料物料[{1}]需求[{2}]还需备料[{3}],条码需要截料,发出数量[{4}]");
|
//action.LocaleMsg = Biz.L($"条码[{0}]扫描成功,备料物料[{1}]需求[{2}]还需备料[{3}],请选择要超发还是截料?");
|
action.LocaleMsg = Biz.L($"WMS.Out_BIZ_WMS_PREP.ScanItem.Success{(isNeedCut ? "" : "Confirm")}", CurInvItem.SN, CurInvItem.ItemInfo.ITEM_CODE, CurPREP.BizType.GetActReqQty(CurPrepDtl), actQty);
|
action.Data.Command = "ConfirmExceed";
|
action.Data.Data = new
|
{
|
isExceed,
|
isNeedCut,
|
CurSn,
|
};
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
else
|
{
|
foreach (var item in sns)
|
{
|
var sn = CurSn.First(q => q.ID == item.ID);
|
sn.NEED_CUTTING = item.NEED_CUTTING;//前端返回Y认为是截料发料,N认为是整盘发料
|
sn.DeliverySN = item.DeliverySN;
|
sn.ReserveSN = item.ReserveSN;
|
}
|
Command = "Normal";
|
}
|
|
//出库下架
|
foreach (var item in CurInvItem.Items)
|
{
|
item.SOURCE_CODE = item.TRANS_CODE;
|
item.SOURCE_ORDER = item.TRANS_NO;
|
item.SOURCE_LINE = item.TRANS_LINE;
|
item.TRANS_CODE = nameof(BIZ_WMS_PREP);
|
item.TRANS_NO = CurPREP.Order.ORDER_NO;
|
item.TRANS_LINE = CurPrepDtl.ORDER_LINE;
|
}
|
foreach (var item in CurInvItem.Packages)
|
{
|
item.SOURCE_CODE = item.TRANS_CODE;
|
item.SOURCE_ORDER = item.TRANS_NO;
|
item.SOURCE_LINE = item.TRANS_LINE;
|
item.TRANS_CODE = nameof(BIZ_WMS_PREP);
|
item.TRANS_NO = CurPREP.Order.ORDER_NO;
|
item.TRANS_LINE = CurPrepDtl.ORDER_LINE;
|
}
|
Result downResult = TakeDown(CurSn, input.AuthOption, WMS_ITEM.STATUSs.OffShelf);
|
if (!downResult.IsSuccessed)
|
{
|
action.IsSuccessed = false;
|
action.LocaleMsg = downResult.LocaleMsg;
|
ResetScan();
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
else//处理备料任务数据
|
{
|
foreach (var sn in CurSn)
|
{
|
sn.STATUS = WMS_ITEM.STATUSs.OffShelf.GetValue();
|
sn.DOWN_TIME = DateTime.Now;
|
if (sn.NEED_CUTTING == "Y")
|
{
|
sn.NEED_CUTTING = "N";
|
sn.SN = sn.DeliverySN;
|
sn.QTY = sn.QTY_DLVY;
|
}
|
}
|
CurPrepDtl.QTY_PREP += CurSn.Sum(q => q.QTY_DLVY);
|
CurPREP.Order.STATUS = BIZ_WMS_PREP.STATUSs.Picking.GetValue();
|
CurPREP.Order.CurBatch.STATUS = BIZ_WMS_PREP_BTH.STATUSs.Picking.GetValue();
|
CurPREP.Order.CurBatch.PICK_TIME = CurPREP.Order.CurBatch.PICK_TIME < new DateTime(2000, 1, 1) ? DateTime.Now : CurPREP.Order.CurBatch.PICK_TIME;
|
|
//创建变量克隆对象用于传入DBSubmitAction中保存当前需要暂存的数据值
|
var _CurSn = CurSn.Clone();
|
var _CurPrepDtl = CurPrepDtl.Clone();
|
var _CurPREPOrder = CurPREP.Order.Clone();
|
var _CurPREPCurBatch = CurPREP.Order.CurBatch.Clone();
|
AddCommitAction("UpdatePrep", () =>
|
{
|
//使用统一的事务DB对象
|
var db = GetCommitDB();
|
//数据保存逻辑
|
db.Insertable(_CurSn, UserCode).ExecuteCommand();
|
_CurPrepDtl.QTY_PREP = db.Queryable<BIZ_WMS_PREP_SN>().Where(q => q.ORDER_NO == _CurPrepDtl.ORDER_NO && q.ORDER_LINE == _CurPrepDtl.ORDER_LINE).Sum(q => q.QTY_DLVY);
|
db.Updateable(_CurPrepDtl, UserCode).UpdateColumns(q => new { q.QTY_PREP, q.UPDATE_TIME, q.UPDATE_USER }).ExecuteCommand();
|
db.Updateable(_CurPREPOrder, UserCode).UpdateColumns(q => new { q.STATUS, q.UPDATE_TIME, q.UPDATE_USER }).ExecuteCommand();
|
db.Updateable(_CurPREPCurBatch, UserCode).UpdateColumns(q => new { q.STATUS, q.PICK_TIME, q.UPDATE_TIME, q.UPDATE_USER }).ExecuteCommand();
|
});
|
|
//灭灯
|
if (CurInvItem.Shelf.IsLightShelf)
|
{
|
await Share.Shelf.DownSingle(TransID, CurInvItem.Location);
|
}
|
}
|
|
//完成所有处理后使用事务保存数据
|
action = DoIfFinish(action, input.Locale);
|
}
|
catch (Exception ex)
|
{
|
//取消当前操作
|
ResetTrans();
|
//action.CatchExceptionWithLog(ex, $"扫描条码[{input.SN}]复核异常");
|
action.CatchExceptionWithLog(ex, Biz.L("WMS.Default.ScanItem.ScanException", input.SN));
|
}
|
return SetOutPutMqttMsg(action, input.Locale);
|
}
|
|
/// <summary>
|
/// 扫描货架或者储位
|
/// </summary>
|
public async Task<ApiAction<ScanOutput>> ScanShelf(string Code, V_WH_UNIT whUnit)
|
{
|
var action = new ApiAction<ScanOutput>(new ScanOutput() { Command = Command });
|
try
|
{
|
if (Code.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("请输入或扫描有效的货架/储位码");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanShelf.ShelfCanNotEmpty");
|
return action;
|
}
|
|
CurScanShelf = new ScanShelfInfo();
|
|
// 查询货架信息
|
whUnit = whUnit.IsNullOrEmpty(await MainDB.Queryable<V_WH_UNIT>().Where(t => (t.SHELF_CODE.ToUpper() == Code || t.LOCATION_CODE.ToUpper() == Code) && t.AUTH_ORG == OrgCode).IncludesAllFirstLayer().FirstAsync());
|
|
// 扫描货架代码,且为智能货架
|
if (!whUnit.IsNullOrEmpty() && whUnit.SHELF_CODE == Code)
|
{
|
CurScanShelf.Shelf = whUnit.Shelf;
|
CurScanShelf.WarehouseCode = whUnit.WH_CODE;
|
CurScanShelf.RegionCode = whUnit.REGION_CODE;
|
CurScanShelf.ShelfCode = whUnit.SHELF_CODE;
|
CurScanShelf.ShelfType = whUnit.SHELF_TYPE;
|
CurScanShelf.IsSmartRack = true;
|
}
|
// 扫描库位代码
|
else if (!whUnit.IsNullOrEmpty() && whUnit.LOCATION_CODE == Code)
|
{
|
//var nLocation = await MainDB.Queryable<V_WH_UNIT>().Where(t => t.LOCATION_CODE.ToUpper() == Code.ToUpper() && t.AUTH_ORG == OrgCode).IncludesAllFirstLayer().FirstAsync();
|
//if (nLocation == null)
|
//{
|
// action.IsSuccessed = false;
|
// //action.LocaleMsg = Biz.L("储位[{0}]不存在");
|
// action.LocaleMsg = Biz.L("WMS.Default.ScanShelf.LocationNotExist", Code);
|
// return action;
|
//}
|
if (whUnit.IS_ACTIVE == "N")
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("扫描的储位[{0}]未启用");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanShelf.ShelfOrLocationDisabled", Code);
|
return action;
|
}
|
if (whUnit.Shelf.IsLightShelf || whUnit.Location.IS_SINGLE == "Y")
|
{
|
var locationData = MainDB.Queryable<WMS_ITEM>().Where(q => q.LOCATION_ID == whUnit.LOCATION_ID && q.AUTH_ORG == OrgCode).First();
|
if (!locationData.IsNullOrEmpty())
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("储位[{0}]已存有物料[{1}],请检查系统库存信息");
|
action.LocaleMsg = Biz.L($"WMS.Default.ScanShelf.ItemAlreadyExistsInLocation", Code, locationData.SN);
|
return action;
|
}
|
}
|
CurScanShelf.Shelf = whUnit.Shelf;
|
CurScanShelf.Location = whUnit.Location;
|
CurScanShelf.WarehouseCode = whUnit.WH_CODE;
|
CurScanShelf.RegionCode = whUnit.REGION_CODE;
|
CurScanShelf.ShelfCode = whUnit .SHELF_CODE;
|
CurScanShelf.LocationCode = whUnit.LOCATION_CODE;
|
CurScanShelf.IsSmartRack = false;
|
}
|
else
|
{
|
action.IsSuccessed = false;
|
//action.LocaleMsg = Biz.L("请输入或扫描有效的货架/储位码");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanShelf.ShelfCanNotEmpty");
|
return action;
|
}
|
|
CurScanShelf.IsScanShelf = true;
|
//action.LocaleMsg = Biz.L("扫描货架/储位[{0}]成功");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanShelf.ScanSucceeded", Code);
|
action.Data.Data = CurScanShelf;
|
}
|
catch (Exception ex)
|
{
|
//取消当前操作
|
ResetTrans();
|
//action.CatchExceptionWithLog(ex, $"扫描货架/储位[{0}]异常");
|
action.CatchExceptionWithLog(ex, Biz.L("WMS.Default.ScanShelf.ScanException", Code));
|
}
|
return action;
|
}
|
|
/// <summary>
|
/// 完成所有处理后使用事务保存数据
|
/// </summary>
|
/// <param name="action"></param>
|
/// <param name="locale"></param>
|
/// <param name="doAfterSave"></param>
|
/// <returns></returns>
|
public ApiAction<ScanOutput> DoIfFinish(ApiAction<ScanOutput> action, string locale, Action doAfterSave = null)
|
{
|
//保存数据库
|
SaveCommitListToDB();
|
|
//保存数据成功后执行
|
doAfterSave?.Invoke();
|
|
// 返回数据
|
action.Data.Data = new DefaultScanItemOutput()
|
{
|
SN = CurInvItem.SN,
|
ItemCode = CurInvItem.ItemInfo.ITEM_CODE,
|
MaterialName = CurInvItem.ItemInfo.ITEM_NAME,
|
Qty = CurInvItem.CurPkg.QTY,
|
Unit = CurInvItem.CurPkg.UNIT,
|
regionCode = CurInvItem.Region.REGION_CODE,
|
locationCode = CurInvItem.Location?.LOCATION_CODE,
|
DateCode = CurInvItem.Items[0].PROD_DATE,
|
ScanAfCut = CurInvItem.Warehouse.SCAN_AF_CUT
|
};
|
|
//action.LocaleMsg = Biz.L($"扫描条码[{0}]从储位[{1}]下架成功");
|
action.LocaleMsg = Biz.L("WMS.Default.ScanItem.TakeDownSucceeded", CurInvItem.SN, CurInvItem.Location?.LOCATION_CODE);
|
|
//重置工序
|
ResetScan();
|
return SetOutPutMqttMsg(action, locale);
|
}
|
|
/// <summary>
|
/// 调用发料单据的ERP接口
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<ApiAction> CommitToERP(BaseInput input)
|
{
|
var action = await CurPREP.Order.BIZ_TYPE.GetEnum<BIZ_WMS_PREP.BIZ_TYPEs>() .CommitToERP(this, input);
|
return action;
|
}
|
|
#endregion
|
|
/// <summary>
|
/// 重置事务数据,有需要则重写此方法
|
/// </summary>
|
public override void ResetTrans()
|
{
|
ResetScan();
|
CurPREP = new(MainDB, UserCode, OrgCode);
|
CurPrepDtl = new();
|
CurScanShelf = null;
|
base.ResetTrans();
|
}
|
|
/// <summary>
|
/// 重置本次扫码信息
|
/// </summary>
|
public override void ResetScan()
|
{
|
Command = "Normal";
|
CurInvItem = null;
|
CurSn = new();
|
base.ResetScan();
|
}
|
|
public override bool Close(bool needSaveHistoryLog = false)
|
{
|
//CloseLight(LocationHis).Wait();
|
if (!(CurPREP?.Order?.ORDER_NO ?? "").IsNullOrEmpty())
|
{
|
MainDB.Deleteable<WMS_ITEM_POOL>().Where(x => x.TRANS_NO == CurPREP.Order.ORDER_NO).ExecuteCommand();
|
}
|
|
this.IsFinished = true;
|
return IsFinished ? base.Close(needSaveHistoryLog) : IsFinished;
|
}
|
|
}//endClass
|
}
|