Files
shgx_tz_mes_backend_sync/ZR.Service/mes/wms/WmOutOrderService.cs

1545 lines
64 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Numerics;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Aliyun.OSS;
using Infrastructure.Attribute;
using Infrastructure.Extensions;
using Mapster;
using Microsoft.AspNetCore.Http.HttpResults;
using SqlSugar;
using ZR.Model;
using ZR.Model.MES.wms;
using ZR.Model.MES.wms.Dto;
using ZR.Repository;
using ZR.Service.mes.wms.IService;
using ZR.Service.mes.wms_u8;
using ZR.Service.Utils;
namespace ZR.Service.mes.wms
{
/// <summary>
/// 出货单(物料+客户Service业务层处理
/// </summary>
[AppService(ServiceType = typeof(IWmOutOrderService), ServiceLifetime = LifeTime.Transient)]
public class WmOutOrderService : BaseService<WmOutOrder>, IWmOutOrderService
{
private NLog.Logger logger;
public WmOutOrderService()
{
logger = NLog.LogManager.GetCurrentClassLogger();
}
/// <summary>
/// 查询出货单(物料+客户)列表
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public PagedInfo<WmOutOrderDto> GetList(WmOutOrderQueryDto parm)
{
var predicate = Expressionable
.Create<WmOutOrder>()
.AndIF(
!string.IsNullOrEmpty(parm.ShipmentNum),
it => it.ShipmentNum.Contains(parm.ShipmentNum)
)
.AndIF(
!string.IsNullOrEmpty(parm.CustomNo),
it => it.CustomNo.Contains(parm.CustomNo)
)
.AndIF(parm.Type > 0, it => it.Type == parm.Type)
.AndIF(parm.Status > -1, it => it.Status == parm.Status)
.AndIF(parm.Year > 0, it => it.Year == parm.Year)
.AndIF(parm.Week > 0, it => it.Year == parm.Week)
.AndIF(parm.Date > 0, it => it.Year == parm.Date);
var response = Queryable()
.Where(predicate.ToExpression())
.OrderByDescending(it => it.CreatedTime)
.ToPage<WmOutOrder, WmOutOrderDto>(parm);
return response;
}
/// <summary>
/// 获取详情
/// </summary>
/// <param name="ShipmentNum"></param>
/// <returns></returns>
public WmOutOrder_material_num GetInfo(string ShipmentNum)
{
WmOutOrder WmOutOrderList = Context
.Queryable<WmOutOrder>()
.Where(it => it.ShipmentNum == ShipmentNum)
.First();
WmOutOrder_material_num wmOutOrderItem = null;
if (WmOutOrderList != null)
{
wmOutOrderItem = WmOutOrderList.Adapt<WmOutOrder_material_num>();
List<WmMaterialOutorder> moList = Context
.Queryable<WmMaterialOutorder>()
.Where(it => it.FkOutorderId == WmOutOrderList.ShipmentNum)
.ToList();
if (moList != null && moList.Count > 0)
{
List<WmMaterialQuery_stockQuantityDto2> Material_stock =
new List<WmMaterialQuery_stockQuantityDto2>();
foreach (var moItem in moList)
{
WmMaterial material = Context
.Queryable<WmMaterial>()
.Where(it => it.Id == moItem.FkMaterialId)
.First();
if (material != null)
{
WmMaterialQuery_stockQuantityDto2 dto2 =
material.Adapt<WmMaterialQuery_stockQuantityDto2>();
dto2.requireOutNum = moItem.OuthouseNum;
Material_stock.Add(dto2);
}
}
wmOutOrderItem.MaterialList = Material_stock;
}
}
return wmOutOrderItem;
}
/// <summary>
/// 获取出库单基础信息
/// </summary>
/// <param name="ShipmentNum"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public WmOutOrderDto GetOutOrderInfo(string ShipmentNum)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取出库单物料信息
/// </summary>
/// <param name="ShipmentNum"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public PagedInfo<WmOutOrderMaterialDto> GetOutOrderMaterialList(string ShipmentNum)
{
throw new NotImplementedException();
}
/// <summary>
/// 添加出货单(物料+客户)
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public WmOutOrder AddOneOutOrder(WmOutOrder_materialDto parm)
{
throw new NotImplementedException();
}
/// <summary>
/// 修改出货单(物料+客户)
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public WmOutOrder UpdateOneOutOrder(WmOutOrder_materialDto parm)
{
throw new NotImplementedException();
}
/// <summary>
/// 添加出货单(物料+客户)
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public WmOutOrder AddWmOutOrder(WmOutOrder_materialDto model)
{
try
{
Context.Ado.BeginTran();
string today_id = "EG" + DateTime.Now.ToString("yyMMdd");
string last_outorder_ShipmentNum = Context
.Queryable<WmOutOrder>()
.Where(it => it.ShipmentNum.StartsWith(today_id))
.Max(it => it.ShipmentNum);
if (string.IsNullOrEmpty(last_outorder_ShipmentNum))
{
model.ShipmentNum = today_id + "001";
}
else
{
int flow =
int.Parse(
last_outorder_ShipmentNum.Substring(
last_outorder_ShipmentNum.Length - 3,
3
)
) + 1;
model.ShipmentNum = today_id + flow.ToString("000");
}
WmOutOrder wmOutOrder = model.Adapt<WmOutOrder>();
// 关联表也要新增
if (model.MaterialList != null)
{
if (model.MaterialList.Count > 0)
{
//TODO 新表插入
List<WmMaterialOutorder> materialOutorderList =
new List<WmMaterialOutorder>();
List<WmOutOrderMaterialRel> wmOutOrderMaterialRelList =
new List<WmOutOrderMaterialRel>();
foreach (var item in model.MaterialList)
{
WmMaterialOutorder materialOutorder = new WmMaterialOutorder();
materialOutorder.FkMaterialId = item.Id;
materialOutorder.FkOutorderId = model.ShipmentNum;
materialOutorder.OuthouseNum = item.requireOutNum;
materialOutorder.CreatedBy = model.CreatedBy;
materialOutorder.CreatedTime = DateTime.Now;
materialOutorderList.Add(materialOutorder);
WmOutOrderMaterialRel wmOutOrderMaterial = new WmOutOrderMaterialRel();
wmOutOrderMaterial.FkShipmentNum = model.ShipmentNum;
wmOutOrderMaterial.FkMaterialCode = item.Partnumber;
wmOutOrderMaterial.Number = item.requireOutNum;
wmOutOrderMaterialRelList.Add(wmOutOrderMaterial);
}
;
int result = Context.Insertable(materialOutorderList).ExecuteCommand();
int result2 = Context
.Insertable(wmOutOrderMaterialRelList)
.ExecuteCommand();
}
}
Context.Ado.CommitTran();
return Context.Insertable(wmOutOrder).ExecuteReturnEntity();
}
catch (Exception)
{
Context.Ado.RollbackTran();
return null;
}
}
/// <summary>
/// 删除出货单关联的物料出货单
/// </summary>
/// <param name="primarys"></param>
/// <returns></returns>
public int Delete_fk_matrial(string[] primarys)
{
if (primarys.Count() > 0)
{
for (int i = 0; i < primarys.Length; i++)
{
Context
.Deleteable<WmMaterialOutorder>()
.Where(it => it.FkOutorderId == primarys[i])
.ExecuteCommand();
}
}
return 1;
}
/// <summary>
/// 修改出货单(物料+客户)
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public int UpdateWmOutOrder(WmOutOrder model)
{
//var response = Update(w => w.ShipmentNum == model.ShipmentNum, it => new WmOutOrder()
//{
// CustomNo = model.CustomNo,
// CustomName = model.CustomName,
// CustomAddress = model.CustomAddress,
// Remarks = model.Remarks,
// Type = model.Type,
// Status = model.Status,
// Year = model.Year,
// Week = model.Week,
// Date = model.Date,
// CreatedBy = model.CreatedBy,
// CreatedTime = model.CreatedTime,
// UpdatedBy = model.UpdatedBy,
// UpdatedTime = model.UpdatedTime,
//});
//return response;
return Update(model, true);
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <returns></returns>
public List<WmCustom> GetCustominfo()
{
return Context.Queryable<WmCustom>().ToList();
}
/// <summary>
/// 查询物料记录表列表
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public (List<WmMaterialQuery_stockQuantityDto>, int) GetmaterialList(
WmMaterialQueryDto parm
)
{
int total = 0;
var predicate = Expressionable
.Create<WmMaterial>()
.AndIF(parm.Partnumber != null, it => it.Partnumber.Contains(parm.Partnumber))
.AndIF(
parm.U8InventoryCode != null,
it => it.U8InventoryCode.Contains(parm.U8InventoryCode)
)
.AndIF(parm.ProductName != null, it => it.ProductName.Contains(parm.ProductName))
.AndIF(parm.Color != null, it => it.Color.Contains(parm.Color))
.AndIF(
parm.Specification != null,
it => it.Specification.Contains(parm.Specification)
)
.AndIF(parm.Description != null, it => it.Description.Contains(parm.Description))
.AndIF(
parm.Search1 != null,
it => it.Search1.Contains(parm.Search1) || it.Search2.Contains(parm.Search1)
)
.AndIF(parm.Status > -1, it => it.Status == parm.Status);
List<WmMaterial> materialList = Context
.Queryable<WmMaterial>()
.Where(predicate.ToExpression())
.OrderByDescending(it => it.CreatedTime)
.ToPageList(parm.PageNum, parm.PageSize, ref total);
List<WmMaterialQuery_stockQuantityDto> material_stockQuantity_list =
new List<WmMaterialQuery_stockQuantityDto>();
if (materialList.Count > 0)
{
foreach (WmMaterial item in materialList)
{
WmMaterialQuery_stockQuantityDto wmMaterialQuery_Stock_item =
item.Adapt<WmMaterialQuery_stockQuantityDto>();
int material_num = 0;
List<WmGoodsNowProduction> productioList = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.Partnumber == item.Partnumber)
.ToList();
if (productioList.Count > 0)
{
foreach (var product in productioList)
{
int num = product.GoodsNumAction ?? 0;
material_num += num;
}
}
wmMaterialQuery_Stock_item.stockQuantity = material_num;
material_stockQuantity_list.Add(wmMaterialQuery_Stock_item);
}
}
return (material_stockQuantity_list, total);
}
/// <summary>
/// 查询出货单的物料信息
/// </summary>
/// <param name="shipment_num"></param>
/// <returns></returns>
public List<WmMaterialQuery_print> Queryoutoder_matrials(string shipment_num)
{
List<WmMaterialQuery_print> stockList = Context
.Queryable<WmMaterialOutorder>()
.LeftJoin<WmMaterial>((mo, m) => mo.FkMaterialId == m.Id)
.Where(mo => mo.FkOutorderId == shipment_num)
.Select(
(mo, m) =>
new WmMaterialQuery_print()
{
//物料号
Partnumber = m.Partnumber,
// 描述
ProductName = m.ProductName,
//需求零件数
RequireOutNum = mo.OuthouseNum
}
)
.ToList();
if (stockList != null && stockList.Count > 0)
{
foreach (var stock in stockList)
{
//库存箱数
stock.PackageNum = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.Partnumber == stock.Partnumber)
.Count();
//库存零件数
int? num = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.Partnumber == stock.Partnumber)
.Sum(it => it.GoodsNumAction);
stock.ItemNum = num ?? 0;
// 需求箱数
List<WmGoodsNowProduction> list = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.Partnumber == stock.Partnumber)
.OrderByDescending(it => it.PackageCodeClient)
.ToList();
int sum = 0;
foreach (var item in list)
{
sum = sum + item.GoodsNumLogic ?? 0;
if (sum >= stock.RequireOutNum)
{
stock.Require_pack_num = list.IndexOf(item) + 1;
break;
}
}
}
}
return stockList;
}
/// <summary>
/// 生成出库单的出货计划
/// </summary>
/// <param name="shipment_num">出货单号</param>
/// <returns></returns>
public List<WmOutOrderPlan> Generate_outorderplan(string shipment_num)
{
// 最终结果
List<WmOutOrderPlan> planList = new List<WmOutOrderPlan>();
// 获取当前出货单下的物料信息
List<WmMaterialQuery_print> materialQuery_Prints = this.Queryoutoder_matrials(
shipment_num
);
if (materialQuery_Prints != null && materialQuery_Prints.Count > 0)
{
// 物料解析
foreach (var material in materialQuery_Prints)
{
//1. 物料需求量
int require_num = material.RequireOutNum;
// 物料号
string partnumber = material.Partnumber;
// 该物料下 ,现有货物列表,零件号相同,根据批次号从小到大排序
List<WmGoodsNowProduction> wmGoodsNowsList = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.Partnumber == partnumber)
.Where(it => it.LocationCode != "Z1-01")
.OrderBy(it => it.PackageCodeClient)
.ToList();
// 判断要出多少批次的货
List<WmOutOrderPlan> wmOutOrderPlans = new List<WmOutOrderPlan>();
// 当前累计批次货物总数
int accumulation_num = 0;
foreach (var witem in wmGoodsNowsList)
{
//TODO 修改>= 为 > 查看情况
if (require_num > accumulation_num)
{
WmOutOrderPlan orderPlan = new WmOutOrderPlan();
orderPlan.FkOutOrderId = shipment_num;
orderPlan.Patchcode = witem.PackageCodeClient;
orderPlan.Patchcode_short = witem.PackageCodeClient.Split("_")[0];
orderPlan.MaterialCode = witem.Partnumber;
orderPlan.WarehouseCode = witem.LocationCode;
orderPlan.PackageNum = material.PackageNum;
// 获得批次的总零件数
orderPlan.PartnumberNum = witem.GoodsNumLogic;
orderPlan.RequireNum = require_num;
orderPlan.ReceivedPackNum = 0;
orderPlan.Patchtime = Resolution_bath(witem.PackageCodeOriginal);
wmOutOrderPlans.Add(orderPlan);
// 实际值计算
accumulation_num = accumulation_num + witem.GoodsNumLogic ?? 0;
}
}
// 进行聚合
if (wmOutOrderPlans.Count > 0)
{
// 根据批次号下划线前进行聚合
List<WmOutOrderPlan> material_plan_item = wmOutOrderPlans
.GroupBy(it => it.Patchcode_short)
.Select(g => new WmOutOrderPlan
{
FkOutOrderId = g.Max(p => p.FkOutOrderId),
Patchcode = g.Max(p => p.Patchcode_short),
Patchcode_short = g.Max(p => p.Patchcode_short),
WarehouseCode = g.Max(p => p.WarehouseCode),
MaterialCode = g.Max(p => p.MaterialCode),
PackageNum = g.Count(),
PartnumberNum = g.Sum(p => p.PartnumberNum),
RequireNum = g.Max(p => p.RequireNum),
ReceivedPackNum = 0,
Patchtime = g.Max(p => p.Patchtime),
})
.ToList();
planList = planList.Concat(material_plan_item).ToList();
}
}
}
// 添加序号
int count = 1;
foreach (var witem in planList)
{
witem.Id = count;
witem.Outorder = count;
count++;
witem.Inventory_pack_num = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.PackageCodeClient.Contains(witem.Patchcode))
.Count();
witem.Inventory_num =
Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.PackageCodeClient.Contains(witem.Patchcode))
.Sum(it => it.GoodsNumLogic) ?? 0;
}
return planList;
}
/// <summary>
/// 货物出库处理:添加出库记录 + 删除成品库数据
/// </summary>
/// <param name="doMaterialOut">出库请求DTO</param>
/// <param name="createBy">操作人</param>
/// <returns>(删除的成品库条数, 插入的出库记录条数)</returns>
public (int, int) DoMaterialOut(WmDoMaterialOut_Dto doMaterialOut, string createBy)
{
Console.WriteLine("开始执行货物出库处理" + DateTime.Now.ToString());
if (doMaterialOut == null)
throw new ArgumentNullException(nameof(doMaterialOut), "出库请求数据不能为空");
createBy = createBy?? "填充出库人";
string shipmentNum = doMaterialOut.ShipmentNum;
string[] originalPatchCodes = doMaterialOut.PatchCode ?? Array.Empty<string>();
// 基础校验
if (originalPatchCodes.Length == 0)
throw new Exception("批次号列表为空,无法执行出库操作");
// 批次号去重(避免重复处理)
HashSet<string> uniquePatchSet = new HashSet<string>(originalPatchCodes);
string[] uniquePatchCodes = uniquePatchSet.ToArray();
if (uniquePatchCodes.Length != originalPatchCodes.Length)
{
string duplicates = string.Join(",", originalPatchCodes.Except(uniquePatchCodes));
logger.Warn($"出库批次号存在重复,已去重:{duplicates}");
}
#region 1.
// 1.1 查询成品库数据避免重复键和null键
List<WmGoodsNowProduction> goodsList = Context
.Queryable<WmGoodsNowProduction>()
.Where(g => uniquePatchCodes.Contains(g.PackageCodeClient)
&& !string.IsNullOrEmpty(g.PackageCodeClient)) // 过滤null键
.ToList();
// 处理重复的PackageCodeClient保留第一条
Dictionary<string, WmGoodsNowProduction> goodsDict = goodsList
.GroupBy(g => g.PackageCodeClient)
.ToDictionary(
group => group.Key,
group => group.First() // 重复时取第一条
);
// 1.2 校验不存在的批次号
List<string> notExistsCodes = uniquePatchCodes
.Where(code => !goodsDict.ContainsKey(code))
.ToList();
if (notExistsCodes.Any())
throw new Exception($"成品库未查询到以下批次号:{string.Join(",", notExistsCodes)}");
// 1.3 暂不批量更新出库计划
// 1.4 查询客户编号
string customerCode = Context
.Queryable<WmOutOrder>()
.Where(order => order.ShipmentNum == shipmentNum)
.Select(order => order.CustomNo)
.First() ?? "无客户代码";
#endregion
#region 2.
List<WmGoodsOutRecord> outRecordList = new List<WmGoodsOutRecord>();
List<string> goodsToDeleteIds = new List<string>();
HashSet<string> partNumbers = new HashSet<string>();
int totalPackage = 0;
int totalPartCount = 0;
List<string> packageRemarkList = new List<string>();
foreach (string patchCode in uniquePatchCodes)
{
if (!goodsDict.TryGetValue(patchCode, out WmGoodsNowProduction goods))
continue; // 已校验,理论上不会进入
// 构建出库记录
var outRecord = new WmGoodsOutRecord
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
FkNowProductionId = goods.Id,
PackageCodeClient = goods.PackageCodeClient,
PackageCode = goods.PackageCode,
PackageCodeOriginal = goods.PackageCodeOriginal,
LocationCode = goods.LocationCode,
Partnumber = goods.Partnumber,
GoodsNumLogic = goods.GoodsNumLogic,
GoodsNumAction = goods.GoodsNumAction ?? 0,
EntryWarehouseTime = goods.EntryWarehouseTime,
OutTime = DateTime.Now,
CreatedBy = createBy,
CreatedTime = DateTime.Now,
FkOutOrderId = shipmentNum
};
// 统计信息
outRecordList.Add(outRecord);
goodsToDeleteIds.Add(goods.Id);
totalPackage++;
partNumbers.Add(goods.Partnumber);
totalPartCount += outRecord.GoodsNumAction.Value;
packageRemarkList.Add(patchCode);
}
packageRemarkList.Sort();
#endregion
#region 3.
int finalDeleteCount = 0;
int finalInsertCount = 0;
Context.Ado.BeginTran();
try
{
// 3.1 批量插入出库记录
finalInsertCount = Context.Insertable(outRecordList).ExecuteCommand();
if (finalInsertCount != outRecordList.Count)
throw new Exception($"出库记录插入异常:预期{outRecordList.Count}条,实际{finalInsertCount}条");
// 3.2 批量删除成品库数据
finalDeleteCount = Context
.Deleteable<WmGoodsNowProduction>()
.In(goodsToDeleteIds)
.ExecuteCommand();
if (finalDeleteCount != goodsToDeleteIds.Count)
throw new Exception($"成品库删除异常:预期{goodsToDeleteIds.Count}条,实际{finalDeleteCount}条");
// 3.3 插入操作日志
var operationLog = new WmGoodsRecord
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
FkInventoryId = SnowFlakeSingle.Instance.NextId().ToString(),
Code = "DoMaterialOut",
Partnumber = partNumbers.FirstOrDefault() ?? "无零件号",
ChangeType = 2,
ChangePackage = totalPackage,
ChangeQuantity = totalPartCount,
ActionTime = DateTime.Now,
Status = 1,
Remark =
"货物出库"
+ "\n出库单:"
+ shipmentNum
+ "\n零件号:"
+ string.Join(',', partNumbers)
+ "\n总箱数:"
+ totalPackage
+ "\n总零件数:"
+ totalPartCount
+ "\n涉及批次号:\n"
+ string.Join(',', uniquePatchCodes)
+ $"\n处理插入{ finalInsertCount }条,删除{ finalDeleteCount }条",
CreatedBy = createBy,
CreatedTime = DateTime.Now
};
int logResult = Context.Insertable(operationLog).ExecuteCommand();
if (logResult == 0)
throw new Exception("操作日志插入失败");
Context.Ado.CommitTran();
Console.WriteLine($"出库成功:删除{finalDeleteCount}条,插入{finalInsertCount}条");
logger.Info($"出库成功:删除{finalDeleteCount}条,插入{finalInsertCount}条");
}
catch (Exception ex)
{
Context.Ado.RollbackTran();
logger.Error($"出库事务回滚:{ex.Message}", ex);
throw;
}
finally
{
Context.Ado.Dispose();
}
#endregion
#region 4. U8
_ = Task.Run(async () =>
{
try
{
// U8发送逻辑保持不变
var u8Data = outRecordList.Select(record => new ERP_WMS_interactiveModelQuery
{
customerCode = customerCode,
materialCode = record.Partnumber,
location = record.LocationCode,
Qty = record.GoodsNumLogic.ToString(),
LotNo = DateTime.Now.ToString("yyyyMMdd"), // 简化日期处理
userID = createBy,
lineno = "涂装生产线",
guid = Guid.NewGuid().ToString(),
createTime = DateTime.Now
}).ToList();
var result = await new ERP_WMS_interactiveService()
.OutboundedAsync("http://gam.com.cn:8053/", u8Data);
logger.Info($"U8同步结果{result}");
}
catch (Exception ex)
{
logger.Error($"U8同步失败{ex.Message}", ex);
}
});
#endregion
return (finalDeleteCount, finalInsertCount);
}
public bool OverOutorderplan(string shipment_num)
{
int reult = Context
.Updateable<WmOutOrder>()
.Where(it => it.ShipmentNum == shipment_num)
.SetColumns(it => it.Type == 2)
.ExecuteCommand();
if (reult > 0)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 传入批次号 解析出时间 BNW240318007_105
/// </summary>
/// <param name="production_packcode">原始编码</param>
/// <returns></returns>
private string Resolution_bath(string production_packcode)
{
MaterialUtils materialToos = new MaterialUtils();
ResultionPackageCodeDto resultionPackage = materialToos.ResolutionPackage(
production_packcode
);
if (resultionPackage == null)
{
return "时间解析异常";
}
return resultionPackage.ProductionTime;
}
/// <summary>
/// 8.5 PDA端 获取出库单的持久化存储出库计划并计算计划批次当前已出库数量
/// </summary>
/// <param name="shipment_num">出库单号</param>
/// <returns></returns>
public List<WmOutPlanAndGoodsOutProductionNumDto> GetOutOrderPlanAndOutProductionNum(
string shipment_num,
string partnumber
)
{
try
{
List<WmOutPlanAndGoodsOutProductionNumDto> result = new();
// 当前工单出库计划
var exp = Expressionable
.Create<WmOutOrderPlan>()
.And(it => it.FkOutOrderId == shipment_num)
.AndIF(!string.IsNullOrEmpty(partnumber), it => it.MaterialCode == partnumber)
.ToExpression();
List<WmOutOrderPlan> wmOutOrderPlan = Context
.Queryable<WmOutOrderPlan>()
.Where(exp)
.OrderBy(it => it.Outorder)
.ToList();
// 查询每个计划具体出库数据
foreach (WmOutOrderPlan item in wmOutOrderPlan)
{
// 库存记录
List<WmGoodsNowProduction> nowProductionList = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.PackageCodeClient.Contains(item.Patchcode))
.ToList();
int nowPackageNum = 0;
int nowPartnumberNum = 0;
foreach (WmGoodsNowProduction outItem in nowProductionList)
{
nowPackageNum += 1;
nowPartnumberNum += (int)outItem.GoodsNumAction;
}
// 出库记录
List<WmGoodsOutRecord> outPackageList = Context
.Queryable<WmGoodsOutRecord>()
.Where(it => it.PackageCodeClient.Contains(item.Patchcode))
.Where(it => it.FkOutOrderId == shipment_num)
.ToList();
int outPackageNum = 0;
int outPartnumberNum = 0;
foreach (WmGoodsOutRecord outItem in outPackageList)
{
outPackageNum += 1;
outPartnumberNum += (int)outItem.GoodsNumAction;
}
WmMaterial material = Context
.Queryable<WmMaterial>()
.Where(it => it.Partnumber.Contains(item.MaterialCode))
.First();
WmOutPlanAndGoodsOutProductionNumDto newItem =
new()
{
OutOrder = (int)item.Outorder,
PackageCode = item.Patchcode,
Partnumber = item.MaterialCode,
Description = !string.IsNullOrEmpty(material.Description)
? material.Description
: material.ProductName,
WarehouseCode = item.WarehouseCode,
RequireNum = (int)item.RequireNum,
PackageNum = (int)nowPackageNum,
PartnumberNum = (int)nowPartnumberNum,
PackagePlanNum = (int)item.PackageNum,
PartnumberPlanNum = (int)item.PartnumberNum,
OutPackageNum = outPackageNum,
OutPartnumberNum = outPartnumberNum,
IsError = (nowPartnumberNum + outPartnumberNum) != item.PartnumberNum,
IsOver = outPartnumberNum > item.PartnumberNum
};
result.Add(newItem);
}
return result;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 检查是否可出库
/// </summary>
/// <param name="production_packcode"></param>
/// <param name="shipment_num"></param>
/// <returns></returns>
public string CheckProductionOut(
string parnumber,
string production_packcode,
string shipment_num
)
{
try
{
if (shipment_num == "" || production_packcode == "")
{
return "传入数据为空!请检查参数";
}
// 1.判断工单是否处于已完成状态
bool isOutOrderCanUse = Context
.Queryable<WmOutOrder>()
.Where(it => it.ShipmentNum == shipment_num)
.Where(it => it.Type == 1)
.Any();
if (!isOutOrderCanUse)
{
return "出库单已完成或已弃用!请检查出库单";
}
MaterialUtils materialToos = new MaterialUtils();
// 2.解析标签编码
ResultionPackageCodeDto resultionPackage = materialToos.ResolutionPackage(
production_packcode
);
if (resultionPackage == null)
{
return "标签解析异常!请检查标签";
}
//3 判断箱子是否配置零件号
string checkPartnumber = resultionPackage.PartNumner;
// 使用正则表达式匹配并移除特殊后缀
string processedPartnumber = Regex.Replace(
checkPartnumber,
@"-(FL|FR|RR|RL)$",
"",
RegexOptions.IgnoreCase
);
if (parnumber != processedPartnumber)
{
return "此箱子不是选择的物料号";
}
// 3.1判断是否已入库
bool isExistedWarehouse = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.PackageCodeClient == resultionPackage.PatchCode)
.Any();
if (!isExistedWarehouse)
{
return "该箱号未入库!请先入库";
}
// 3.2 判断是否是计划中的物料(大概率不执行)
bool flag1 = false;
List<WmMaterial> materialOutorders = Context
.Queryable<WmMaterialOutorder>()
.LeftJoin<WmMaterial>((wmo, wm) => wmo.FkMaterialId == wm.Id)
.Where(wmo => wmo.FkOutorderId == shipment_num)
.Select((wmo, wm) => new WmMaterial { Partnumber = wm.Partnumber })
.ToList();
foreach (WmMaterial materialOption in materialOutorders)
{
if (
materialOption.Partnumber == processedPartnumber
|| materialOption.Partnumber == checkPartnumber
)
{
flag1 = true;
}
}
if (!flag1)
{
return "该箱标签物料号不在出库单物料清单内!";
}
// 4. 判断出库单是否启用出库规则
var shipment = Context
.Queryable<WmOutOrder>()
.Where(it => it.ShipmentNum == shipment_num)
.First();
if (shipment.Status == 0)
{
return "ok";
}
// 5. 确保出最早批次
string short_path = resultionPackage.PatchCode.Split('_')[0];
// 严格规则(出库规则判断)
return CheckRuleJudgmentFirstInFirstOut(shipment_num, short_path, parnumber);
/*WmOutOrderPlan plan_item = Context.Queryable<WmOutOrderPlan>()
.Where(it => it.FkOutOrderId == shipment_num)
.Where(it => it.Patchcode == short_path)
.OrderBy(it => it.Outorder)
.First();
if (plan_item != null)
{
// 查看此批次号在此工单下已出库箱子数量
int patchInNum = Context.Queryable<WmGoodsOutRecord>()
.Where(it => it.PackageCodeClient.Contains(short_path))
.Where(it => it.FkOutOrderId == shipment_num)
.Count();
var plan_earliest = Context.Queryable<WmOutOrderPlan>()
.Where(it => it.FkOutOrderId == shipment_num)
.Where(it => it.MaterialCode == plan_item.MaterialCode)
.Where(it => it.PackageNum > patchInNum)
.OrderBy(it => it.Outorder)
.First();
//已经出库完成,没有可以出库的了
if (plan_earliest == null)
{
return "此物料在计划中已经全部出库完成,无法继续出库";
}
// 批次号是最早批次
*/
/*if (plan_earliest.Id == plan_item.Id)
{
return "ok";
}*/
/*
if (plan_earliest.Patchcode == short_path)
{
return "ok";
}
else
{
return "不是此物料最早批次,无法出库";
}
}*/
// 6 .还差一个 数量超过要出库的箱子
return "此箱标签不可出库,批次号不在出库单计划内!请检查出库单计划!";
}
catch (Exception)
{
return "此箱标签存在异常不可出库!";
}
}
/// <summary>
/// 持久化存储
/// </summary>
/// <param name="shipment_num"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public int PersistenceOutorderplan(string shipment_num)
{
List<WmOutOrderPlan> orderPlans = Generate_outorderplan(shipment_num);
var x = Context
.Storageable(orderPlans)
.WhereColumns(it => it.FkOutOrderId)
.WhereColumns(it => it.MaterialCode)
.WhereColumns(it => it.Patchcode)
.ToStorage();
int result = x.AsInsertable.ExecuteCommand(); //执行插入
x.AsUpdateable.ExecuteCommand(); //执行更新 
return result;
}
/// <summary>
/// 出库单先进先出规则判断(严格复杂版)
/// </summary>
/// <param name="shipment_num">工单号</param>
/// <param name="shortPackageCode">短批次号</param>
/// <returns> "ok" 代表通过,其余返回错误提示</returns>
/// <exception cref="NotImplementedException"></exception>
public string CheckRuleJudgmentFirstInFirstOut(
string shipment_num,
string shortPackageCode,
string parnumber
)
{
// 1.检查是否有记录
var exp1 = Expressionable
.Create<WmOutOrderPlan>()
.And(it => it.FkOutOrderId == shipment_num)
.And(it => it.Patchcode == shortPackageCode)
.ToExpression();
bool hasRecord = Context.Queryable<WmOutOrderPlan>().Where(exp1).Any();
if (!hasRecord)
{
return "此批次号不在出库单计划中!";
}
// 1.取出出库单计划
List<WmOutOrderPlan> plans = Context
.Queryable<WmOutOrderPlan>()
.Where(it => it.FkOutOrderId == shipment_num)
.OrderBy(it => it.Outorder)
.ToList();
foreach (WmOutOrderPlan plan in plans)
{
// 不是同零件号的跳过
if (plan.MaterialCode != parnumber)
{
continue;
}
// 2.此出库单下的批次号的已出库记录
var exp2 = Expressionable
.Create<WmGoodsOutRecord>()
.And(it => it.PackageCodeClient.Contains(plan.Patchcode))
.And(it => it.Partnumber == plan.MaterialCode)
.And(it => it.FkOutOrderId == shipment_num)
.ToExpression();
List<WmGoodsOutRecord> outPackageList = Context
.Queryable<WmGoodsOutRecord>()
.Where(exp2)
.ToList();
int outPackageNum = 0;
int outPartnumberNum = 0;
foreach (WmGoodsOutRecord outItem in outPackageList)
{
outPackageNum += 1;
outPartnumberNum += (int)outItem.GoodsNumAction;
}
// 按顺序检查是否出完
if (plan.Patchcode != shortPackageCode)
{
// 此批次是否还有库存
bool hasAny = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => it.PackageCodeClient.Contains(plan.Patchcode))
.Where(it => it.Partnumber == plan.MaterialCode)
.Any();
// 检查此批次是否出完
if (outPartnumberNum >= plan.RequireNum || !hasAny)
{
// 出完了或仓库里没了
continue;
}
else
{
return "不是出库单计划中此物料的最早批次,无法出库! 计划中批次:" + plan.Patchcode + "未出完!";
}
}
else
{
// 检查此批次是否出完
if (outPartnumberNum < plan.RequireNum)
{
return "ok";
}
else
{
// 出完了
return "此批次已在出库计划中出完! 当前已出库:" + outPartnumberNum + "个零件!";
}
}
}
return "经检查,此批次号不在出库单计划中!";
}
public PagedInfo<WmPDAOutOrderListDto> GetPdaOutOrderPageList(
WmPDAOutOrderPageQueryDto parm
)
{
// 根据type和shipmentNum查询出库单列表
var query = Context
.Queryable<WmOutOrder>()
.WhereIF(!string.IsNullOrEmpty(parm.Type), it => it.Type == int.Parse(parm.Type))
.WhereIF(
!string.IsNullOrEmpty(parm.ShipmentNum),
it => it.ShipmentNum.Contains(parm.ShipmentNum)
);
// 查询结果并映射到WmPDAOutOrderListDto
var result = query
.Select(it => new WmPDAOutOrderListDto
{
ShipmentNum = it.ShipmentNum,
CustomName = it.CustomName,
Type = it.Type,
Status = it.Status
})
.OrderByDescending(it => it.ShipmentNum)
.ToPage(parm);
return result;
}
public PagedInfo<WmPDAOutPlanListDto> GetPdaOutOrderPlanPageList(
WmPDAOutOrderPlanPageQueryDto parm
)
{
// 当前工单出库计划
var exp = Expressionable
.Create<WmOutOrderPlan>()
.And(it => it.FkOutOrderId == parm.ShipmentNum)
.AndIF(
!string.IsNullOrEmpty(parm.MaterialCode),
it => it.MaterialCode == parm.MaterialCode
)
.ToExpression();
// 查询出库计划,并关联物料、库存和出库记录
// 使用子查询选择每个零件号的最新记录的描述
var query = Context
.Queryable<WmOutOrderPlan>()
.Where(exp)
.Select(plan => new WmPDAOutPlanListDto
{
ShipmentNum = plan.FkOutOrderId,
MaterialCode = plan.MaterialCode,
BatchCode = plan.Patchtime,
WarehouseCode = plan.WarehouseCode,
PlanOutNumber = plan.PartnumberNum.Value,
PlanOutPackage = plan.PackageNum.Value,
Description = ""
});
// 分页查询
var result = query
.OrderBy(dto => dto.BatchCode) // 根据需要调整排序
.ToPage(parm);
foreach (var item in result.Result)
{
WmPDAOutOnePlanActualDataDto actualData = GetPdaOutOrderPlanActualData(
item.ShipmentNum,
item.MaterialCode,
item.BatchCode
);
var material = Context
.Queryable<WmMaterial>()
.Where(m => m.Partnumber == item.MaterialCode && m.Status == 1 && m.Type == 1)
.First();
if(material == null)
{
item.Description = "无描述";
}
else
{
item.Description = material.Description;
}
item.InventoryNumber = actualData.InventoryNumber;
item.ActualOutNumber = actualData.ActualOutNumber;
item.InventoryPackage = actualData.InventoryPackage;
item.ActualOutPackage = actualData.ActualOutPackage;
}
return result;
}
/// <summary>
/// 处理BatchCode去除前两位多余字符
/// </summary>
/// <param name="batchCode">原始BatchCode</param>
/// <returns>处理后的BatchCode</returns>
private string ProcessBatchCode(string batchCode)
{
if (string.IsNullOrEmpty(batchCode) || batchCode.Length <= 2)
{
return batchCode;
}
return batchCode.Substring(2);
}
public WmPDAOutOnePlanActualDataDto GetPdaOutOrderPlanActualData(
string shipmentNum,
string materialCode,
string batchCode
)
{
// 处理BatchCode去除前两位多余字符
string processedBatchCode = ProcessBatchCode(batchCode);
// 零件该批次库存箱数和库存零件数
var nowProductionQuery = Context
.Queryable<WmGoodsNowProduction>()
.Where(it =>
it.Partnumber == materialCode && it.PackageCodeClient.Contains(processedBatchCode)
);
var nowProductionData = new {
PackageCount = nowProductionQuery.Count(),
PartNumberSum = nowProductionQuery.Sum(it => it.GoodsNumAction) ?? 0
};
// 该零件出库箱数和出库数量
var outRecordQuery = Context
.Queryable<WmGoodsOutRecord>()
.Where(it =>
it.FkOutOrderId == shipmentNum
&& it.Partnumber == materialCode
&& it.PackageCodeClient.Contains(processedBatchCode)
);
var outRecordData = new {
PackageCount = outRecordQuery.Count(),
PartNumberSum = outRecordQuery.Sum(it => it.GoodsNumAction) ?? 0
};
WmPDAOutOnePlanActualDataDto result =
new()
{
ShipmentNum = shipmentNum,
MaterialCode = materialCode,
BatchCode = batchCode,
InventoryNumber = nowProductionData.PartNumberSum,
ActualOutNumber = outRecordData.PartNumberSum,
InventoryPackage = nowProductionData.PackageCount,
ActualOutPackage = outRecordData.PackageCount
};
return result;
}
public List<string> GetPdaOutOrderPlanOptions(string shipmentNum)
{
// 当前工单出库计划
var exp = Expressionable
.Create<WmOutOrderPlan>()
.And(it => it.FkOutOrderId == shipmentNum)
.ToExpression();
// 查询出库计划,并关联物料、库存和出库记录
// 使用子查询选择每个零件号的最新记录的描述
var result = Context
.Queryable<WmOutOrderPlan>()
.Where(exp)
.OrderBy(it => it.Outorder)
.Select(it => it.MaterialCode)
.Distinct()
.ToList();
return result;
}
public bool UpdatePdaOutOrderPlan(string shipmentNum)
{
return PersistenceOutorderplan(shipmentNum) > 0;
}
/// <summary>
/// 高性能PDA出库方法
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
public string PdaOutboundByOutOrderPlan(WmPDAOutboundDto parm)
{
// 输入验证
if (parm == null)
{
throw new ArgumentNullException(nameof(parm), "参数不能为空");
}
if (string.IsNullOrEmpty(parm.Operator))
{
throw new ArgumentException("操作员不能为空", nameof(parm.Operator));
}
if (string.IsNullOrEmpty(parm.ShipmentNum))
{
throw new ArgumentException("出库单号不能为空", nameof(parm.ShipmentNum));
}
if (string.IsNullOrEmpty(parm.MaterialCode))
{
throw new ArgumentException("物料编码不能为空", nameof(parm.MaterialCode));
}
if (parm.PackageCodeList == null || parm.PackageCodeList.Count() == 0)
{
return "无出库箱";
}
bool IsStrict = parm.IsStrict;
string CreatedBy = parm.Operator;
string shipnumber = parm.ShipmentNum;
try
{
// 使用单次查询获取所有需要的数据
var nowProductionList = Context
.Queryable<WmGoodsNowProduction>()
.Where(it => parm.PackageCodeList.Contains(it.PackageCodeClient))
.ToList();
if (nowProductionList.Count != parm.CurrentOutPackage && IsStrict)
{
return $"提醒:当前成品库数与出库箱数对不上,成品库箱数:{nowProductionList.Count}";
}
// 处理BatchCode去除前两位多余字符
string processedBatchCode = ProcessBatchCode(parm.BatchCode);
// 批量获取已出库统计数据
var outRecordData = Context
.Queryable<WmGoodsOutRecord>()
.Where(it =>
it.FkOutOrderId == shipnumber
&& it.Partnumber == parm.MaterialCode
&& it.PackageCodeClient.Contains(processedBatchCode)
)
.Select(g => new { PackageCodeClient = g.PackageCodeClient, GoodsNumAction = g.GoodsNumAction })
.ToList()
.Aggregate(new { PackageCount = 0, PartNumberSum = 0.0 }, (acc, x) => new { PackageCount = acc.PackageCount + 1, PartNumberSum = acc.PartNumberSum + (x.GoodsNumAction ?? 0) });
if (
parm.PlanOutPackage < parm.CurrentOutPackage + outRecordData.PackageCount
&& IsStrict
)
{
return "提醒:总出库箱超出计划";
}
if (
parm.PlanOutNumber < parm.CurrentOutNumber + outRecordData.PartNumberSum
&& IsStrict
)
{
return "提醒:总出库零件数超出计划";
}
Context.Ado.BeginTran();
// 批量构建出库记录
var outRecords = nowProductionList
.Select(wmGoods => new WmGoodsOutRecord
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
FkNowProductionId = wmGoods.Id,
PackageCodeClient = wmGoods.PackageCodeClient,
PackageCode = wmGoods.PackageCode,
PackageCodeOriginal = wmGoods.PackageCodeOriginal,
LocationCode = wmGoods.LocationCode,
Partnumber = wmGoods.Partnumber,
GoodsNumLogic = wmGoods.GoodsNumLogic,
GoodsNumAction = wmGoods.GoodsNumAction,
EntryWarehouseTime = wmGoods.EntryWarehouseTime,
OutTime = DateTime.Now,
CreatedTime = DateTime.Now,
CreatedBy = CreatedBy,
FkOutOrderId = shipnumber
})
.ToList();
// 批量插入出库记录
int sum_insert = Context.Insertable(outRecords).ExecuteCommand();
// 批量删除成品库记录
var deleteIds = nowProductionList.Select(x => x.Id).ToArray();
int sum_delete = Context
.Deleteable<WmGoodsNowProduction>()
.In(deleteIds)
.ExecuteCommand();
if (sum_insert != sum_delete && IsStrict)
{
Context.Ado.RollbackTran();
throw new Exception(
"出库操作出现异常!失败原因记录不一致:添加数据" + sum_insert + "删除数据:" + sum_delete
);
}
// 构建统计信息
var partnumbers = nowProductionList.Select(x => x.Partnumber).Distinct().ToList();
int totalPackage = nowProductionList.Count;
int totalPartnumber = nowProductionList.Sum(x => x.GoodsNumAction ?? 0);
var packageCodeRemark = nowProductionList
.Select(x => x.PackageCodeClient)
.OrderBy(x => x)
.ToList();
// 插入操作记录
WmGoodsRecord wmGoodsRecord =
new()
{
Id = SnowFlakeSingle.Instance.NextId().ToString(),
FkInventoryId = SnowFlakeSingle.Instance.NextId().ToString(),
Code = "PdaOutboundByOutOrderPlan",
Partnumber = partnumbers.FirstOrDefault() ?? "无零件号",
BlankNum = "",
ChangeType = 2,
ChangePackage = totalPackage,
ChangeQuantity = totalPartnumber,
ActionTime = DateTime.Now,
Status = 1,
Remark =
"货物出库"
+ "\n出库单:"
+ shipnumber
+ "\n零件号:"
+ string.Join(',', partnumbers)
+ "\n数据库变动:插入成功"
+ sum_insert
+ "删除成功"
+ sum_delete
+ "\n总箱数:"
+ totalPackage
+ "\n总零件数:"
+ totalPartnumber
+ "\n涉及批次号:\n"
+ string.Join(',', packageCodeRemark),
CreatedBy = CreatedBy ?? "系统操作",
CreatedTime = DateTime.Now,
};
int recordNum = Context.Insertable(wmGoodsRecord).ExecuteCommand();
if (recordNum == 0)
{
Context.Ado.RollbackTran();
throw new Exception("记录插入失败");
}
// 获取客户代码
string _customerCode =
Context
.Queryable<WmOutOrder>()
.Where(x => x.ShipmentNum == shipnumber)
.Select(x => x.CustomNo)
.First() ?? "无客户代码";
// 异步处理U8系统交互
_ = Task.Run(async () =>
{
try
{
string urlBase = "http://gam.com.cn:8053/";
ERP_WMS_interactiveService _eRP_WMS_InteractiveService = new();
// 构建U8包列表
List<ERP_WMS_interactiveModelQuery> u8PackageList = outRecords
.Select(item =>
{
string dateString = DateTime.Now.ToString("yyyyMMdd");
// 使用正则表达式匹配日期模式
string pattern = @"(\d{2})(\d{2})(\d{2})";
Match match = Regex.Match(item.PackageCodeClient, pattern);
if (match.Success)
{
// 提取匹配的年份、月份和日期
string year = match.Groups[1].Value;
string month = match.Groups[2].Value;
string day = match.Groups[3].Value;
// 转换为四位数年份假设2000年代
string fullYear = "20" + year;
// 组合为yyyyMMdd格式
dateString = fullYear + month + day;
}
else
{
logger.Warn($"未找到匹配的日期模式:{item.PackageCodeClient}");
}
return new ERP_WMS_interactiveModelQuery
{
customerCode = _customerCode,
materialCode = item.Partnumber,
location = item.LocationCode,
Qty = item.GoodsNumAction.ToString(),
LotNo = dateString,
createTime = DateTime.Now,
userID = parm.Operator,
guid = Guid.NewGuid().ToString(),
lineno = "涂装生产线"
};
})
.ToList();
var u8ErpResult = await _eRP_WMS_InteractiveService
.OutboundedAsync(urlBase, u8PackageList)
.ConfigureAwait(false);
logger.Warn(u8ErpResult);
}
catch (Exception ex)
{
logger.Error($"U8系统交互异常: {ex.Message}", ex);
}
})
.ConfigureAwait(false);
Context.Ado.CommitTran();
return "ok";
}
catch (Exception e)
{
Context.Ado.RollbackTran();
logger.Error($"出库操作异常: {e.Message}", e);
throw new Exception($"出库操作失败: {e.Message}");
}
}
public bool CompleteOutOrder(string shipmentNum)
{
return OverOutorderplan(shipmentNum);
}
}
}