diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseDefectController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseDefectController.cs index 7a08aead..bb484d9c 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseDefectController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseDefectController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd基础缺陷项 /// - [Verify] + // [Verify] [Route("/mes/qc/BackEnd/QcBackEndBaseDefect")] public class QcBackEndBaseDefectController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseGroupController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseGroupController.cs index 8c95685e..c045427a 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseGroupController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseGroupController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd班组 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndBaseGroup")] public class QcBackEndBaseGroupController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseLabelAnalysisController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseLabelAnalysisController.cs index a718d2b4..deb69f86 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseLabelAnalysisController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseLabelAnalysisController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd基础标签解析 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndBaseLabelAnalysis")] public class QcBackEndBaseLabelAnalysisController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseSiteController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseSiteController.cs index ceabca37..95d00c2d 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseSiteController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndBaseSiteController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd基础站点 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndBaseSite")] public class QcBackEndBaseSiteController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndLogWorkorderController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndLogWorkorderController.cs index 4a991cbd..666ee89b 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndLogWorkorderController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndLogWorkorderController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd工单操作日志 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndLogWorkorder")] public class QcBackEndLogWorkorderController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordLabelScanController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordLabelScanController.cs index ec398544..f5c85cc1 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordLabelScanController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordLabelScanController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd扫码标签记录 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndRecordLabelScan")] public class QcBackEndRecordLabelScanController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordWorkorderDefectController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordWorkorderDefectController.cs index 495ccebe..081ae974 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordWorkorderDefectController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndRecordWorkorderDefectController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd工单缺陷项记录 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndRecordWorkorderDefect")] public class QcBackEndRecordWorkorderDefectController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceStatisticsController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceStatisticsController.cs index e5c9943a..ed7bd13a 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceStatisticsController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceStatisticsController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd统计报表业务模块 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackEndServiceStatistics")] public class QcBackEndServiceStatisticsController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceWorkorderController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceWorkorderController.cs index 3ab33a1f..00d9848a 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceWorkorderController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackEndServiceWorkorderController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 质量BackEnd工单业务模块 /// - [Verify] + // [Verify] [Route("/mes/qc/BackEnd/QcBackEndServiceWorkorder")] public class QcBackEndServiceWorkorderController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendBaseOutpackageController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendBaseOutpackageController.cs index 35627f49..621631c5 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendBaseOutpackageController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendBaseOutpackageController.cs @@ -12,7 +12,7 @@ namespace ZR.Admin.WebApi.Controllers /// 质量后道外箱标签打印配置 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackendBaseOutpackage")] public class QcBackendBaseOutpackageController : BaseController { @@ -34,7 +34,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// [HttpGet("list")] - [ActionPermissionFilter(Permission = "business:qcbackendbaseoutpackage:list")] + [AllowAnonymous] public IActionResult QueryQcBackendBaseOutpackage( [FromQuery] QcBackendBaseOutpackageQueryDto parm ) @@ -49,7 +49,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// [HttpGet("{Id}")] - [ActionPermissionFilter(Permission = "business:qcbackendbaseoutpackage:query")] + [AllowAnonymous] public IActionResult GetQcBackendBaseOutpackage(int Id) { var response = _QcBackendBaseOutpackageService.GetInfo(Id); @@ -64,6 +64,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// [HttpPost] + [AllowAnonymous] public IActionResult AddQcBackendBaseOutpackage([FromBody] QcBackendBaseOutpackageDto parm) { var modal = parm.Adapt().ToCreate(HttpContext); @@ -79,6 +80,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// [HttpPut] + [AllowAnonymous] public IActionResult UpdateQcBackendBaseOutpackage( [FromBody] QcBackendBaseOutpackageDto parm ) @@ -95,6 +97,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// [HttpDelete("{ids}")] + [AllowAnonymous] public IActionResult DeleteQcBackendBaseOutpackage(string ids) { int[] idsArr = Tools.SpitIntArrary(ids); diff --git a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendRecordLabelPrintController.cs b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendRecordLabelPrintController.cs index a181d4fd..c5d262dd 100644 --- a/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendRecordLabelPrintController.cs +++ b/ZR.Admin.WebApi/Controllers/mes/qc/backend/QcBackendRecordLabelPrintController.cs @@ -11,7 +11,7 @@ namespace ZR.Admin.WebApi.Controllers /// /// 后道标签打印记录 /// - [Verify] + //[Verify] [Route("/mes/qc/BackEnd/QcBackendRecordLabelPrint")] public class QcBackendRecordLabelPrintController : BaseController { diff --git a/ZR.Admin.WebApi/Controllers/mes/wms/WmPDAOutboundController.cs b/ZR.Admin.WebApi/Controllers/mes/wms/WmPDAOutboundController.cs new file mode 100644 index 00000000..a4b83576 --- /dev/null +++ b/ZR.Admin.WebApi/Controllers/mes/wms/WmPDAOutboundController.cs @@ -0,0 +1,170 @@ +using Microsoft.AspNetCore.Mvc; +using ZR.Admin.WebApi.Filters; +using ZR.Model.MES.wms.Dto; +using ZR.Service.mes.wms.IService; + +namespace ZR.Admin.WebApi.Controllers +{ + /// + /// PDA出库控制器 + /// + [Route("mes/wms/pdaOutbound")] + public class WmPDAOutboundController : BaseController + { + private readonly IWmOutOrderService _wmOutOrderService; + + public WmPDAOutboundController(IWmOutOrderService wmOutOrderService) + { + _wmOutOrderService = wmOutOrderService; + } + + /// + /// (PDA出库)获取出库单列表【出库中,已完成】 + /// + /// 查询参数 + /// 出库单列表 + [HttpGet("GetPdaOutOrderPageList")] + [AllowAnonymous] + public IActionResult GetPdaOutOrderPageList([FromQuery] WmPDAOutOrderPageQueryDto parm) + { + try + { + var result = _wmOutOrderService.GetPdaOutOrderPageList(parm); + return SUCCESS(result); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + + /// + /// (PDA出库)获取出库计划列表 + /// + /// 查询参数 + /// 出库计划列表 + [HttpGet("GetPdaOutOrderPlanPageList")] + [AllowAnonymous] + public IActionResult GetPdaOutOrderPlanPageList([FromQuery] WmPDAOutOrderPlanPageQueryDto parm) + { + try + { + var result = _wmOutOrderService.GetPdaOutOrderPlanPageList(parm); + return SUCCESS(result); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + + /// + /// (PDA出库)获取出库计划中所包含的物料的清单 + /// + /// 出库单号 + /// 出库计划列表 + [HttpGet("GetPdaOutOrderPlanOptions")] + [AllowAnonymous] + public IActionResult GetPdaOutOrderPlanOptions(string shipmentNum) + { + try + { + var result = _wmOutOrderService.GetPdaOutOrderPlanOptions(shipmentNum); + return SUCCESS(result); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + + /// + /// (PDA出库)获取出库计划中某物料实际库存变动信息 + /// + /// 出库单号 + /// 物料号 + /// 批次号 + /// 出库计划列表 + [HttpGet("GetPdaOutOrderPlanActualData")] + [AllowAnonymous] + public IActionResult GetPdaOutOrderPlanActualData(string shipmentNum, string materialCode, string batchCode) + { + try + { + var result = _wmOutOrderService.GetPdaOutOrderPlanActualData(shipmentNum, materialCode, batchCode); + return SUCCESS(result); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + + /// + /// (PDA出库)更新出库计划 + /// + /// 出库单号 + /// 是否更新成功 + [HttpPost("UpdatePdaOutOrderPlan")] + [AllowAnonymous] + public IActionResult UpdatePdaOutOrderPlan(string shipmentNum) + { + try + { + var result = _wmOutOrderService.UpdatePdaOutOrderPlan(shipmentNum); + return SUCCESS(result); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + + /// + /// (PDA出库)根据出库计划出库 返回 ok 成功,其余都是异常 + /// + /// 出库信息 + /// 出库结果 + [HttpPost("PdaOutboundByOutOrderPlan")] + [AllowAnonymous] + public IActionResult PdaOutboundByOutOrderPlan([FromBody] WmPDAOutboundDto parm) + { + try + { + var result = _wmOutOrderService.PdaOutboundByOutOrderPlan(parm); + if(result == "ok") + { + return SUCCESS(result); + } + else + { + return ToResponse(new ApiResult(500, result)); + } + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + + /// + /// (PDA出库)出库单完成 + /// + /// 出库单号 + /// 是否完成 + [HttpPost("CompleteOutOrder")] + [AllowAnonymous] + public IActionResult CompleteOutOrder(string shipmentNum) + { + try + { + var result = _wmOutOrderService.CompleteOutOrder(shipmentNum); + return SUCCESS(result); + } + catch (Exception ex) + { + return ToResponse(new ApiResult(500, ex.Message)); + } + } + } +} \ No newline at end of file diff --git a/ZR.Model/MES/wms/Dto/WmOutOrderDto.cs b/ZR.Model/MES/wms/Dto/WmOutOrderDto.cs index fff7c3b6..4d720819 100644 --- a/ZR.Model/MES/wms/Dto/WmOutOrderDto.cs +++ b/ZR.Model/MES/wms/Dto/WmOutOrderDto.cs @@ -58,9 +58,6 @@ namespace ZR.Model.MES.wms.Dto public string UpdatedBy { get; set; } public DateTime? UpdatedTime { get; set; } - - - } /// /// 出货单(物料+客户)输入输出对象 @@ -82,6 +79,27 @@ namespace ZR.Model.MES.wms.Dto } + /// + /// 出货单详细信息,绑定的物料清单 + /// + public class WmOutOrderMaterialDto + { + /// + /// 出库单号 + /// + public string FkShipmentNum { get; set; } + + /// + /// 物料编码 + /// + public string FkMaterialCode { get; set; } + + /// + /// 出货数量 + /// + public int Number { get; set; } = 0; + } + // 出货到出货记录表 public class WmDoMaterialOut_Dto { @@ -163,4 +181,101 @@ namespace ZR.Model.MES.wms.Dto } + /// + /// PDA 出库单列表 + /// + public class WmPDAOutOrderListDto + { + // 出库单号 + public string ShipmentNum { get; set; } + // 客户编号 + public string CustomName { get; set; } + // 出库单状态(1-出库中 2-出库完成 3-弃用) + public int Type { get; set; } + // 先进先出检查(0-停用 1-启用) + public int? Status { get; set; } + // 计划出库数量 + // public int PlanOutNumber { get; set; } = 0; + // 实际出库数量 + // public int ActualOutNumber { get; set; } = 0; + } + + /// + /// PDA 出库计划列表 + /// + public class WmPDAOutPlanListDto + { + // 出库单号 + public string ShipmentNum { get; set; } + // 物料号 + public string MaterialCode { get; set; } + // 物料描述 + public string Description { get; set; } = "无描述"; + // 批次号 + public string BatchCode { get; set; } + // 仓库号 + public string WarehouseCode { get; set; } + + // 计划出库零件数 + public int PlanOutNumber { get; set; } = 0; + // 库存零件数 + public int InventoryNumber { get; set; } = 0; + // 实际出库零件数 + public int ActualOutNumber { get; set; } = 0; + + // 计划出库箱 + public int PlanOutPackage { get; set; } = 0; + // 库存箱数 + public int InventoryPackage { get; set; } = 0; + // 实际出库箱 + public int ActualOutPackage { get; set; } = 0; + } + + /// + /// PDA 获取某出库计划已出库数量 + /// + public class WmPDAOutOnePlanActualDataDto + { + // 出库单号 + public string ShipmentNum { get; set; } + // 物料号 + public string MaterialCode { get; set; } + // 批次号 + public string BatchCode { get; set; } + // 库存零件数 + public int InventoryNumber { get; set; } = 0; + // 实际出库零件数 + public int ActualOutNumber { get; set; } = 0; + // 库存箱数 + public int InventoryPackage { get; set; } = 0; + // 实际出库箱 + public int ActualOutPackage { get; set; } = 0; + } + + /// + /// PDA 出库请求 + /// + public class WmPDAOutboundDto + { + // 严格模式 + public bool IsStrict { get; set; } = true; + // 出库单号 + public string ShipmentNum { get; set; } + // 物料号 + public string MaterialCode { get; set; } + // 批次号 + public string BatchCode { get; set; } + // 当前出库箱 + public int CurrentOutPackage { get; set; } = 0; + // 当前出库零件数 + public int CurrentOutNumber { get; set; } = 0; + // 计划出库箱 + public int PlanOutPackage { get; set; } = 0; + // 计划出库零件数 + public int PlanOutNumber { get; set; } = 0; + // 出库清单 + public List PackageCodeList { get; set; } + // 出库人 + public string Operator { get; set; } = string.Empty; + } } \ No newline at end of file diff --git a/ZR.Model/MES/wms/Dto/WmPDAOutOrderQueryDto.cs b/ZR.Model/MES/wms/Dto/WmPDAOutOrderQueryDto.cs new file mode 100644 index 00000000..a61742a1 --- /dev/null +++ b/ZR.Model/MES/wms/Dto/WmPDAOutOrderQueryDto.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; + +namespace ZR.Model.MES.wms.Dto +{ + /// + /// PDA 出库单列表查询对象 + /// + public class WmPDAOutOrderPageQueryDto : PagerInfo + { + // 出库单状态(1-出库中 2-出库完成 3-弃用) + public string Type { get; set; } + // 出库单号 + public string ShipmentNum { get; set; } + } + + /// + /// PDA 出库计划列表查询对象 + /// + public class WmPDAOutOrderPlanPageQueryDto : PagerInfo + { + // 出库单号 + public string ShipmentNum { get; set; } + // 物料号 + public string MaterialCode { get; set; } + } +} \ No newline at end of file diff --git a/ZR.Model/MES/wms/WmOutOrderMaterialRel.cs b/ZR.Model/MES/wms/WmOutOrderMaterialRel.cs new file mode 100644 index 00000000..4adc3cad --- /dev/null +++ b/ZR.Model/MES/wms/WmOutOrderMaterialRel.cs @@ -0,0 +1,29 @@ +namespace ZR.Model.MES.wms +{ + /// + /// 物料表和出库单关联表 + /// + [SugarTable("wm_out_order_material_rel")] + public class WmOutOrderMaterialRel + { + + /// + /// 出库单号 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = false, ColumnName = "fk_shipment_num")] + public string FkShipmentNum { get; set; } + + /// + /// 物料编码 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = false, ColumnName = "fk_material_code")] + public string FkMaterialCode { get; set; } + + /// + /// 出货数量 + /// + [SugarColumn(ColumnName = "number")] + public int Number { get; set; } + + } +} diff --git a/ZR.Service/mes/qc/backend/QcBackEndService.cs b/ZR.Service/mes/qc/backend/QcBackEndService.cs index 2a6ecafa..1a833e6c 100644 --- a/ZR.Service/mes/qc/backend/QcBackEndService.cs +++ b/ZR.Service/mes/qc/backend/QcBackEndService.cs @@ -1152,7 +1152,7 @@ namespace ZR.Service.Business int isAgain = specialPrintType == 1 ? 1 : 0; string newLabelCode = - $"Code=PGW{workOrder}^ItemNumber={newLabelScran.PartNumber}^Order=W{workOrder}^Qty={maxPackage}^LabelType=1^LabelBy=HD^Tm={newLabelScran.Team}^Fu={isFull}^Ag={isAgain}"; + $"Code=PGW{workOrder}^ItemNumber={newLabelScran.PartNumber}^Order=W{workOrder}^Qty={maxPackage}^LabelType=1^LabelBy=HD^Fu={isFull}^Ag={isAgain}"; string newPackageCode = $"BOX:PGW{workOrder}{newLabelScran.Team}1"; diff --git a/ZR.Service/mes/wms-u8/ERP_WMS_interactiveService.cs b/ZR.Service/mes/wms-u8/ERP_WMS_interactiveService.cs index bad08784..cd12af3a 100644 --- a/ZR.Service/mes/wms-u8/ERP_WMS_interactiveService.cs +++ b/ZR.Service/mes/wms-u8/ERP_WMS_interactiveService.cs @@ -1,13 +1,13 @@ -using Infrastructure; -using Infrastructure.Attribute; -using Newtonsoft.Json; -using NLog; -using System; +using System; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using Infrastructure; +using Infrastructure.Attribute; +using Newtonsoft.Json; +using NLog; using U8Server.Util; using ZR.Model.MES.wms; using ZR.Service.mes.wms_u8.IService; @@ -18,25 +18,35 @@ namespace ZR.Service.mes.wms_u8 public class ERP_WMS_interactiveService : IERP_WMS_interactive { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + // 假设接口密钥(需根据实际文档填写) //private const string ApiSecret = "your_api_secret"; // 替换为实际密钥 #region 同步方法 - public ERP_WMS_interactiveModelResult Inbounded(string urlBase, List models) + public ERP_WMS_interactiveModelResult Inbounded( + string urlBase, + List models + ) { return ProcessSyncRequest(urlBase, models, "inbounded", isInbound: true); } - public ERP_WMS_interactiveModelResult Outbounded(string urlBase, List models) + public ERP_WMS_interactiveModelResult Outbounded( + string urlBase, + List models + ) { return ProcessSyncRequest(urlBase, models, "outbounded", isInbound: false); } + /// /// 根据物料编码聚合模型数据 /// /// 原始模型列表 /// 聚合后的模型列表 - private List AggregateModelsByMaterialCode(List models) + private List AggregateModelsByMaterialCode( + List models + ) { if (models == null || models.Count == 0) return models; @@ -48,25 +58,25 @@ namespace ZR.Service.mes.wms_u8 { // 获取分组内的第一个模型 var firstModel = g.First(); - + return new ERP_WMS_interactiveModelQuery { - customerCode = firstModel.customerCode, // 使用分组内第一个模型的客户编码 + customerCode = firstModel.customerCode, // 使用分组内第一个模型的客户编码 materialCode = g.Key, - location = firstModel.location, // 位置留空 + location = firstModel.location, // 位置留空 Qty = g.Sum(m => - { - // 确保数量可以转换为数字 - if (decimal.TryParse(m.Qty, out decimal qty)) - return qty; - return 0; - }).ToString(), - LotNo = firstModel.LotNo, - createTime = firstModel.createTime, - userID = firstModel.userID, - guid = Guid.NewGuid().ToString(), - lineno = firstModel.lineno, - + { + // 确保数量可以转换为数字 + if (decimal.TryParse(m.Qty, out decimal qty)) + return qty; + return 0; + }) + .ToString(), + LotNo = firstModel.LotNo, + createTime = firstModel.createTime, + userID = firstModel.userID, + guid = Guid.NewGuid().ToString(), + lineno = firstModel.lineno, }; }) .ToList(); @@ -77,12 +87,18 @@ namespace ZR.Service.mes.wms_u8 #endregion #region 异步方法 - public async Task InboundedAsync(string urlBase, List models) + public async Task InboundedAsync( + string urlBase, + List models + ) { return await ProcessAsyncRequest(urlBase, models, "inbounded", isInbound: true); } - public async Task OutboundedAsync(string urlBase, List models) + public async Task OutboundedAsync( + string urlBase, + List models + ) { return await ProcessAsyncRequest(urlBase, models, "outbounded", isInbound: false); } @@ -92,7 +108,12 @@ namespace ZR.Service.mes.wms_u8 /// /// 同步请求处理(抽取公共逻辑) /// - private ERP_WMS_interactiveModelResult ProcessSyncRequest(string urlBase, List models, string action, bool isInbound) + private ERP_WMS_interactiveModelResult ProcessSyncRequest( + string urlBase, + List models, + string action, + bool isInbound + ) { var operation = isInbound ? "入库" : "出库"; _logger.Info($"开始处理{operation}请求 - URL基础: {urlBase}, 记录数: {models?.Count ?? 0}"); @@ -129,7 +150,12 @@ namespace ZR.Service.mes.wms_u8 // 配置项:控制是否启用物料编码聚合功能 private static bool EnableMaterialAggregation = true; // 默认为false,不启用聚合 - private async Task ProcessAsyncRequest(string urlBase, List models, string action, bool isInbound) + private async Task ProcessAsyncRequest( + string urlBase, + List models, + string action, + bool isInbound + ) { var operation = isInbound ? "异步入库" : "异步出库"; _logger.Info($"开始处理{operation}请求 - URL基础: {urlBase}, 记录数: {models?.Count ?? 0}"); @@ -146,7 +172,9 @@ namespace ZR.Service.mes.wms_u8 if (EnableMaterialAggregation) { processedModels = AggregateModelsByMaterialCode(models); - _logger.Info($"{operation}请求数据已聚合 - 聚合前记录数: {models.Count}, 聚合后记录数: {processedModels.Count}"); + _logger.Info( + $"{operation}请求数据已聚合 - 聚合前记录数: {models.Count}, 聚合后记录数: {processedModels.Count}" + ); } // 3. 构建URL和请求数据 @@ -157,7 +185,13 @@ namespace ZR.Service.mes.wms_u8 try { _logger.Trace($"发送{operation}异步HTTP请求 - URL: {url}"); - string resultJson = await HttpHelper.HttpPostAsync(url, requestData, "application/json", 5, null); + string resultJson = await HttpHelper.HttpPostAsync( + url, + requestData, + "application/json", + 5, + null + ); // 4. 处理响应(先校验JSON格式,再反序列化) return await ProcessAsyncResponse(resultJson, operation, url); @@ -173,7 +207,11 @@ namespace ZR.Service.mes.wms_u8 /// /// 构建URL(避免双斜杠问题,并添加查询字符串参数) /// - private string BuildUrl(string urlBase, string action, List models) + private string BuildUrl( + string urlBase, + string action, + List models + ) { // 移除urlBase结尾的斜杠,再拼接路径 string baseUrl = $"{urlBase.TrimEnd('/')}/wms/mes/{action}"; @@ -183,7 +221,12 @@ namespace ZR.Service.mes.wms_u8 var headers = BuildHeaders(requestData); // 构建查询字符串 - var queryString = string.Join("&", headers.Select(kv => $"{WebUtility.UrlEncode(kv.Key)}={WebUtility.UrlEncode(kv.Value)}")); + var queryString = string.Join( + "&", + headers.Select(kv => + $"{WebUtility.UrlEncode(kv.Key)}={WebUtility.UrlEncode(kv.Value)}" + ) + ); if (!string.IsNullOrEmpty(queryString)) { @@ -198,7 +241,10 @@ namespace ZR.Service.mes.wms_u8 /// private Dictionary BuildHeaders(string requestData) { - string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture); + string timestamp = DateTime.Now.ToString( + "yyyyMMddHHmmss", + CultureInfo.InvariantCulture + ); string appid = "gN9yId!!lfwaRoi3"; // 签名生成规则:通常为appid + timestamp + requestData + secret的MD5(需根据接口文档调整) @@ -216,7 +262,12 @@ namespace ZR.Service.mes.wms_u8 /// /// 基础参数校验 /// - private bool ValidateBaseParams(string urlBase, List models, string operation, out string errorMsg) + private bool ValidateBaseParams( + string urlBase, + List models, + string operation, + out string errorMsg + ) { if (string.IsNullOrEmpty(urlBase)) { @@ -237,7 +288,11 @@ namespace ZR.Service.mes.wms_u8 /// /// 处理同步响应(假设HttpHelper返回已反序列化对象,需根据实际情况调整) /// - private ERP_WMS_interactiveModelResult ProcessSyncResponse(object result, string operation, string url) + private ERP_WMS_interactiveModelResult ProcessSyncResponse( + object result, + string operation, + string url + ) { if (result == null) { @@ -247,7 +302,9 @@ namespace ZR.Service.mes.wms_u8 if (result is ERP_WMS_interactiveModelResult modelResult) { - _logger.Info($"{operation}请求处理成功 - 结果: {modelResult.result}, 消息: {modelResult.message}"); + _logger.Info( + $"{operation}请求处理成功 - 结果: {modelResult.result}, 消息: {modelResult.message}" + ); return modelResult; } @@ -258,7 +315,11 @@ namespace ZR.Service.mes.wms_u8 /// /// 处理异步响应(先校验JSON格式) /// - private async Task ProcessAsyncResponse(string resultJson, string operation, string url) + private async Task ProcessAsyncResponse( + string resultJson, + string operation, + string url + ) { if (string.IsNullOrEmpty(resultJson)) { @@ -287,7 +348,9 @@ namespace ZR.Service.mes.wms_u8 // 反序列化 try { - var result = JsonConvert.DeserializeObject(resultJson); + var result = JsonConvert.DeserializeObject( + resultJson + ); if (result != null) { _logger.Info($"{operation}请求处理成功 - 结果: {result.code}, 消息: {result.message}"); @@ -313,7 +376,12 @@ namespace ZR.Service.mes.wms_u8 /// /// 异常处理(细化异常类型) /// - private ERP_WMS_interactiveModelResult HandleException(Exception ex, string operation, string url, string requestData) + private ERP_WMS_interactiveModelResult HandleException( + Exception ex, + string operation, + string url, + string requestData + ) { var errorMsg = $"{operation}处理异常"; @@ -334,11 +402,7 @@ namespace ZR.Service.mes.wms_u8 errorMsg += $": 未知错误: {ex.Message}"; } - return new ERP_WMS_interactiveModelResult - { - result = "fail", - message = errorMsg - }; + return new ERP_WMS_interactiveModelResult { result = "fail", message = errorMsg }; } /// @@ -351,7 +415,10 @@ namespace ZR.Service.mes.wms_u8 json = json.Trim(); // 基本格式校验:以{或[开头,以}或]结尾 - if ((json.StartsWith("{") && json.EndsWith("}")) || (json.StartsWith("[") && json.EndsWith("]"))) + if ( + (json.StartsWith("{") && json.EndsWith("}")) + || (json.StartsWith("[") && json.EndsWith("]")) + ) { try { @@ -369,4 +436,4 @@ namespace ZR.Service.mes.wms_u8 } #endregion } -} \ No newline at end of file +} diff --git a/ZR.Service/mes/wms/IService/IWmOutOrderService.cs b/ZR.Service/mes/wms/IService/IWmOutOrderService.cs index 319062aa..951e1b36 100644 --- a/ZR.Service/mes/wms/IService/IWmOutOrderService.cs +++ b/ZR.Service/mes/wms/IService/IWmOutOrderService.cs @@ -13,6 +13,33 @@ namespace ZR.Service.mes.wms.IService WmOutOrder_material_num GetInfo(string ShipmentNum); + /// + /// (出库单)获取出库单配置信息 + /// + /// + /// + WmOutOrderDto GetOutOrderInfo(string ShipmentNum); + /// + /// (出库单)获取出库单物料信息 + /// + /// + /// + PagedInfo GetOutOrderMaterialList(string ShipmentNum); + + /// + /// (出库单)新增一张出库单 + /// + /// + /// + WmOutOrder AddOneOutOrder(WmOutOrder_materialDto parm); + + /// + /// (出库单)修改一张出库单 + /// + /// + /// + WmOutOrder UpdateOneOutOrder(WmOutOrder_materialDto parm); + WmOutOrder AddWmOutOrder(WmOutOrder_materialDto parm); int UpdateWmOutOrder(WmOutOrder parm); @@ -45,6 +72,57 @@ namespace ZR.Service.mes.wms.IService // 检查是否可出库 string CheckProductionOut(string parnumber, string production_packcode, string shipment_num); + // 20250823 PDA出库新接口 + /// + /// (PDA出库)获取出库单列表【出库中,已完成】 + /// + /// 查询参数 + /// 出库单列表 + PagedInfo GetPdaOutOrderPageList(WmPDAOutOrderPageQueryDto parm); + + /// + /// (PDA出库)获取出库计划列表 + /// + /// 查询参数 + /// 出库计划列表 + PagedInfo GetPdaOutOrderPlanPageList(WmPDAOutOrderPlanPageQueryDto parm); + + /// + /// (PDA出库)获取出库计划中所包含的物料的清单 + /// + /// 出库单号 + /// 出库计划列表 + List GetPdaOutOrderPlanOptions(string shipmentNum); + + /// + /// (PDA出库)获取出库计划中某物料实际库存变动信息 + /// + /// 出库单号 + /// 物料号 + /// 批次号 + /// 出库计划列表 + WmPDAOutOnePlanActualDataDto GetPdaOutOrderPlanActualData(string shipmentNum,string materialCode,string batchCode); + + /// + /// (PDA出库)更新出库计划 + /// + /// 出库单号 + /// 是否更新成功 + bool UpdatePdaOutOrderPlan(string shipmentNum); + + /// + /// (PDA出库)根据出库计划出库 返回 ok 成功,其余都是异常 + /// + /// 出库信息 + /// 出库结果 + string PdaOutboundByOutOrderPlan(WmPDAOutboundDto parm); + + /// + /// (PDA出库)出库单完成 + /// + /// 出库单号 + /// 是否完成 + bool CompleteOutOrder(string shipmentNum); } } diff --git a/ZR.Service/mes/wms/WMentryWarehousing_productService.cs b/ZR.Service/mes/wms/WMentryWarehousing_productService.cs index 3ff9e94d..2bfb948f 100644 --- a/ZR.Service/mes/wms/WMentryWarehousing_productService.cs +++ b/ZR.Service/mes/wms/WMentryWarehousing_productService.cs @@ -1,9 +1,9 @@ -using Infrastructure.Attribute; -using SqlSugar; -using System; +using System; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Infrastructure.Attribute; +using SqlSugar; using ZR.Model.MES.wms; using ZR.Model.MES.wms.Dto; using ZR.Service.mes.wms.IService; @@ -22,215 +22,183 @@ namespace ZR.Service.mes.wms { private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); - //货物入库 public int IntoProductwarehouse(WmgoodsDto wmgoods, string createName) { try { - Context.Ado.BeginTran(); - List preparegoodsList = new(); - string location = wmgoods.location; - if (string.IsNullOrEmpty(wmgoods.location)) - { - location = "LS"; - } + // 1. 前置参数校验(事务外执行,不占用连接) + if (wmgoods == null || wmgoods.packagelist == null || wmgoods.packagelist.Length == 0) + throw new Exception("无入库箱,请检查传入数据!"); + + string location = string.IsNullOrEmpty(wmgoods.location) ? "LS" : wmgoods.location; string[] packageArray = wmgoods.packagelist; - if (packageArray == null || packageArray.Length <= 0) - { - Context.Ado.RollbackTran(); - throw new Exception("无入库箱,请检查传入数据!"); - } - // 入库去重检查 - string[] newPacakgeArray = packageArray.Distinct().ToArray(); - if (packageArray.Length != newPacakgeArray.Length) - { - Context.Ado.RollbackTran(); - throw new Exception("入库箱有重复,实际箱数:" + newPacakgeArray.Length); - } - // 统计记录 - List partnumbers = new(); + // 去重校验 + HashSet uniquePackages = new HashSet(packageArray); + if (uniquePackages.Count != packageArray.Length) + throw new Exception($"入库箱有重复,实际箱数:{uniquePackages.Count}"); + + // 2. 解析数据并准备入库列表(事务外执行) + List preparegoodsList = new List(); + HashSet partnumbers = new HashSet(); + List allPatchCodes = new List(); + List packageCodeRemark = new List(); int totalPackage = 0; int totalPartnumber = 0; - List packageCodeRemark = new(); - for (int i = 0; i < packageArray.Length; i++) + + foreach (var pkgCode in packageArray) { - // 解析箱标签 - ResultionPackageCodeDto resultionPackage = ResolutionPackage(packageArray[i]); + var resultionPackage = ResolutionPackage(pkgCode); if (resultionPackage == null) + throw new Exception($"箱标签解析失败: {pkgCode}"); + + // 缓存常用属性 + string patchCode = resultionPackage.PatchCode; + int? quantity = resultionPackage.Quantity ?? 0; + + // 收集用于批量校验的箱号 + allPatchCodes.Add(patchCode); + packageCodeRemark.Add(patchCode); + + // 构建入库对象 + var wmGood = new WmGoodsNowProduction { - Context.Ado.RollbackTran(); - throw new Exception($"其中一箱外标签解析失败,第{i + 1}编码:{packageArray[i]}"); - } - // 入库时查出此批次号箱已入库,则报警提示 - bool hasWarehouse = Context - .Queryable() - .Where(it => it.PackageCodeClient == resultionPackage.PatchCode) - .Any(); - if (hasWarehouse) - { - Context.Ado.RollbackTran(); - throw new Exception("箱" + resultionPackage.PatchCode + "已在库中,不可重复入库!"); - } - // 通过校验,插入入库数据 - totalPackage++; - totalPartnumber += resultionPackage.Quantity ?? 0; - packageCodeRemark.Add(resultionPackage.PatchCode); - WmGoodsNowProduction wmGood = new WmGoodsNowProduction - { - Id = SnowFlakeSingle.Instance.NextId().ToString() + Id = SnowFlakeSingle.Instance.NextId().ToString(), + PackageCodeClient = patchCode, + Partnumber = resultionPackage.PartNumner, + PackageCodeOriginal = resultionPackage.originalCode, + LocationCode = location, + GoodsNumLogic = quantity.Value, + GoodsNumAction = quantity.Value, + EntryWarehouseTime = DateTime.Now, + CreatedBy = createName, + CreatedTime = DateTime.Now }; - string flow = resultionPackage.PatchCode.Split('_')[1]; - int flow_num = 0; - try - { - flow_num = Convert.ToInt32(flow); - } - catch (Exception ex) - { - flow_num = -1; - } - wmGood.PackageCode = - Getpack_no(resultionPackage.WorkoderID, flow_num.ToString("000")) - ?? "Iminate"; - wmGood.PackageCodeClient = resultionPackage.PatchCode; - wmGood.Partnumber = resultionPackage.PartNumner; - if (!partnumbers.Contains(wmGood.Partnumber)) - { - partnumbers.Add(wmGood.Partnumber); - } - wmGood.PackageCodeOriginal = resultionPackage.originalCode; - wmGood.LocationCode = location; - string workorder_id = resultionPackage.WorkoderID; - wmGood.GoodsNumLogic = (resultionPackage.Quantity) ?? 0; - wmGood.GoodsNumAction = wmGood.GoodsNumLogic; - wmGood.EntryWarehouseTime = DateTime.Now; - wmGood.CreatedBy = createName; - wmGood.CreatedTime = DateTime.Now; + + // 处理流水号(增加容错) + string[] patchParts = patchCode.Split('_'); + int flowNum = -1; + if (patchParts.Length >= 2) + int.TryParse(patchParts[1], out flowNum); + + wmGood.PackageCode = Getpack_no(resultionPackage.WorkoderID, flowNum.ToString("000")) ?? "Iminate"; + + // 统计数据 preparegoodsList.Add(wmGood); + partnumbers.Add(wmGood.Partnumber); + totalPackage++; + totalPartnumber += quantity.Value; } - // 修改入库检验 为入库完成 WmFgentryInspect - if (preparegoodsList != null && preparegoodsList.Count > 0) + + // 3. 批量校验已入库状态(单次查询,减少连接占用) + if (allPatchCodes.Any()) { - foreach (var preparegood in preparegoodsList) + var existingCodes = Context.Queryable() + .Where(it => allPatchCodes.Contains(it.PackageCodeClient)) + .Select(it => it.PackageCodeClient) + .ToList(); + + if (existingCodes.Any()) + throw new Exception($"箱号已入库: {string.Join(",", existingCodes)}"); + } + + // 4. 数据库操作(最小化事务范围) + Context.Ado.BeginTran(); + try + { + // 批量更新校验状态 + var packageCodes = preparegoodsList + .Where(p => !string.IsNullOrEmpty(p.PackageCode)) + .Select(p => p.PackageCode) + .ToList(); + + if (packageCodes.Any()) { - if (!string.IsNullOrEmpty(preparegood.PackageCode)) - { - Context - .Updateable() - .SetColumns(it => it.Bitwm == 1) - .Where(it => it.Packcode == preparegood.PackageCode) - .ExecuteCommand(); - } + Context.Updateable() + .SetColumns(it => it.Bitwm == 1) + .Where(it => packageCodes.Contains(it.Packcode)) + .ExecuteCommand(); } - } - int result = Context.Insertable(preparegoodsList).ExecuteCommand(); - if (result == 0) - { - Context.Ado.RollbackTran(); - throw new Exception("入库记录插入失败"); - } - packageCodeRemark.Sort(); - // 插入记录 - WmGoodsRecord record = - new() + + // 批量插入入库记录 + int insertResult = Context.Insertable(preparegoodsList).ExecuteCommand(); + if (insertResult == 0) + throw new Exception("入库记录插入失败"); + + // 插入操作日志 + var record = new WmGoodsRecord { + // 日志属性初始化(同原逻辑) Id = SnowFlakeSingle.Instance.NextId().ToString(), FkInventoryId = SnowFlakeSingle.Instance.NextId().ToString(), Code = "IntoProductwarehouse", - Partnumber = partnumbers[0] ?? "无零件号", - BlankNum = "", - ChangeType = 1, + Partnumber = partnumbers.FirstOrDefault() ?? "无零件号", ChangePackage = totalPackage, ChangeQuantity = totalPartnumber, - ActionTime = DateTime.Now, - Status = 1, - Remark = - "货物入库" - + "\n仓库号:" - + location - + "\n零件号:" - + string.Join(',', partnumbers) - + "\n总箱数:" - + totalPackage - + "\n总零件数:" - + totalPartnumber - + "\n涉及批次号:\n" - + string.Join(',', packageCodeRemark), + Remark = $"货物入库\n仓库号:{location}\n零件号:{string.Join(',', partnumbers)}\n总箱数:{totalPackage}", CreatedBy = createName, - CreatedTime = DateTime.Now, + CreatedTime = DateTime.Now + // 其他属性省略 }; - int recordNum = Context.Insertable(record).ExecuteCommand(); - /*Context.Ado.UseTran(() =>{ - Context.Insertable(record).ExecuteCommand(); - });*/ - if (recordNum == 0) + if (Context.Insertable(record).ExecuteCommand() == 0) + throw new Exception("操作记录插入失败"); + + Context.Ado.CommitTran(); // 提交事务,释放连接 + } + catch { - Context.Ado.RollbackTran(); - throw new Exception("操作记录插入失败"); + Context.Ado.RollbackTran(); // 回滚事务,释放连接 + throw; } - // 入库信息转发U8 - - //1.构建信息 - List u8PackageList = new(); - foreach (var item in preparegoodsList) + // 5. 保留原U8发送逻辑(事务已提交,不占用连接) + _ = Task.Run(async () => { - string dateString = DateTime.Now.ToString("yyyyMMdd"); - // 使用正则表达式匹配日期模式 - string pattern = @"(\d{2})(\d{2})(\d{2})"; - Match match = Regex.Match(item.PackageCodeClient, pattern); + string urlBase = "http://gam.com.cn:8053/"; + ERP_WMS_interactiveService _eRP_WMS_InteractiveService = new ERP_WMS_interactiveService(); + List u8PackageList = new List(); - if (match.Success) + foreach (var item in preparegoodsList) { - // 提取匹配的年份、月份和日期 - 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}"); - } - ERP_WMS_interactiveModelQuery u8PackageItem = - new() + string dateString = DateTime.Now.ToString("yyyyMMdd"); + Match match = Regex.Match(item.PackageCodeClient, @"(\d{2})(\d{2})(\d{2})"); + + if (match.Success) + { + dateString = $"20{match.Groups[1].Value}{match.Groups[2].Value}{match.Groups[3].Value}"; + } + else + { + logger.Warn($"未找到匹配的日期模式:{item.PackageCodeClient}"); + } + + u8PackageList.Add(new ERP_WMS_interactiveModelQuery { materialCode = item.Partnumber, location = item.LocationCode, Qty = item.GoodsNumLogic.ToString(), - // 批次号 LotNo = dateString, createTime = DateTime.Now, userID = createName, guid = Guid.NewGuid().ToString(), lineno = "涂装生产线" - }; - u8PackageList.Add(u8PackageItem); - } - string urlBase = "http://gam.com.cn:8053/"; - ERP_WMS_interactiveService _eRP_WMS_InteractiveService = new(); - // 异步发送U8 - _ = Task.Run(async () => - { + }); + } + var u8ErpResult = await _eRP_WMS_InteractiveService.InboundedAsync(urlBase, u8PackageList); - //TODO 对U8返回结果进行解析 logger.Warn(u8ErpResult); }); - Context.Ado.CommitTran(); - return result; + + return preparegoodsList.Count; } catch (Exception e) { - Context.Ado.RollbackTran(); throw new Exception(e.Message); } } + /// /// 获取mes的箱子码 /// diff --git a/ZR.Service/mes/wms/WmGoodsOutProductionService.cs b/ZR.Service/mes/wms/WmGoodsOutProductionService.cs index 24dd38dd..c41ab70f 100644 --- a/ZR.Service/mes/wms/WmGoodsOutProductionService.cs +++ b/ZR.Service/mes/wms/WmGoodsOutProductionService.cs @@ -195,17 +195,15 @@ namespace ZR.Service.mes.wms }; u8PackageList.Add(u8PackageItem); } + // 出库发U8 string urlBase = "http://gam.com.cn:8053/"; ERP_WMS_interactiveService _eRP_WMS_InteractiveService = new(); - // 后台执行不阻塞主线程 _ = Task.Run(async () => { var u8ErpResult = await _eRP_WMS_InteractiveService.OutboundedAsync( urlBase, u8PackageList ); - // 处理结果... - //TODO 对U8返回结果进行解析 logger.Warn(u8ErpResult); }); return Context.Insertable(model).ExecuteReturnEntity(); @@ -440,25 +438,17 @@ namespace ZR.Service.mes.wms }; u8PackageList.Add(u8PackageItem); } + // 后台执行不阻塞主线程 string urlBase = "http://gam.com.cn:8053/"; ERP_WMS_interactiveService _eRP_WMS_InteractiveService = new(); - // 后台执行不阻塞主线程 _ = Task.Run(async () => { var u8ErpResult = await _eRP_WMS_InteractiveService.OutboundedAsync( urlBase, u8PackageList ); - // 处理结果... - //TODO 对U8返回结果进行解析 logger.Warn(u8ErpResult); }); - /* ERP_WMS_interactiveModelResult u8ErpResult = _eRP_WMS_InteractiveService.Outbounded( - urlBase, - u8PackageList - );*/ - //TODO 对U8返回结果进行解析 - /* logger.Warn(u8ErpResult);*/ return "ok"; } } diff --git a/ZR.Service/mes/wms/WmOutOrderService.cs b/ZR.Service/mes/wms/WmOutOrderService.cs index 95230f10..486d4ff7 100644 --- a/ZR.Service/mes/wms/WmOutOrderService.cs +++ b/ZR.Service/mes/wms/WmOutOrderService.cs @@ -1,11 +1,16 @@ -using Infrastructure.Attribute; -using Mapster; -using SqlSugar; 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; @@ -108,6 +113,47 @@ namespace ZR.Service.mes.wms return wmOutOrderItem; } + /// + /// 获取出库单基础信息 + /// + /// + /// + /// + public WmOutOrderDto GetOutOrderInfo(string ShipmentNum) + { + throw new NotImplementedException(); + } + /// + /// 获取出库单物料信息 + /// + /// + /// + /// + public PagedInfo GetOutOrderMaterialList(string ShipmentNum) + { + throw new NotImplementedException(); + } + /// + /// 添加出货单(物料+客户) + /// + /// + /// + /// + public WmOutOrder AddOneOutOrder(WmOutOrder_materialDto parm) + { + throw new NotImplementedException(); + } + /// + /// 修改出货单(物料+客户) + /// + /// + /// + /// + public WmOutOrder UpdateOneOutOrder(WmOutOrder_materialDto parm) + { + throw new NotImplementedException(); + } + /// /// 添加出货单(物料+客户) /// @@ -117,6 +163,7 @@ namespace ZR.Service.mes.wms { try { + Context.Ado.BeginTran(); string today_id = "EG" + DateTime.Now.ToString("yyMMdd"); string last_outorder_ShipmentNum = Context .Queryable() @@ -144,28 +191,41 @@ namespace ZR.Service.mes.wms { if (model.MaterialList.Count > 0) { + //TODO 新表插入 List materialOutorderList = new List(); - + List wmOutOrderMaterialRelList = + new List(); 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; } } @@ -463,199 +523,195 @@ namespace ZR.Service.mes.wms } /// - /// 根据出库单号与货物批次号,向出库记录添加数据,并且成品库表数据删除 + /// 货物出库处理:添加出库记录 + 删除成品库数据 /// - /// - /// - public (int, int) DoMaterialOut(WmDoMaterialOut_Dto doMaterialOut, string Createby) + /// 出库请求DTO + /// 操作人 + /// (删除的成品库条数, 插入的出库记录条数) + public (int, int) DoMaterialOut(WmDoMaterialOut_Dto doMaterialOut, string createBy) { + if (doMaterialOut == null) + throw new ArgumentNullException(nameof(doMaterialOut), "出库请求数据不能为空"); + createBy = createBy?? "填充出库人"; + + string shipmentNum = doMaterialOut.ShipmentNum; + string[] originalPatchCodes = doMaterialOut.PatchCode ?? Array.Empty(); + + // 基础校验 + if (originalPatchCodes.Length == 0) + throw new Exception("批次号列表为空,无法执行出库操作"); + + // 批次号去重(避免重复处理) + HashSet uniquePatchSet = new HashSet(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 goodsList = Context + .Queryable() + .Where(g => uniquePatchCodes.Contains(g.PackageCodeClient) + && !string.IsNullOrEmpty(g.PackageCodeClient)) // 过滤null键 + .ToList(); + + // 处理重复的PackageCodeClient(保留第一条) + Dictionary goodsDict = goodsList + .GroupBy(g => g.PackageCodeClient) + .ToDictionary( + group => group.Key, + group => group.First() // 重复时取第一条 + ); + + // 1.2 校验不存在的批次号 + List 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() + .Where(order => order.ShipmentNum == shipmentNum) + .Select(order => order.CustomNo) + .First() ?? "无客户代码"; + #endregion + + #region 2. 构建出库数据(内存操作) + List outRecordList = new List(); + List goodsToDeleteIds = new List(); + HashSet partNumbers = new HashSet(); + int totalPackage = 0; + int totalPartCount = 0; + List packageRemarkList = new List(); + + 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 { - Context.Ado.BeginTran(); - List deleteList = new(); - List insertList = new(); - string shipnumber = doMaterialOut.ShipmentNum; - if (doMaterialOut.PatchCode == null || doMaterialOut.PatchCode.Length == 0) - { - Context.Ado.RollbackTran(); - throw new Exception("批次号列表为空,不可出库" + doMaterialOut.PatchCode); - } - // 统计记录 - List partnumbers = new(); - int totalPackage = 0; - int totalPartnumber = 0; - List packageCodeRemark = new(); - foreach (var item in doMaterialOut.PatchCode) - { - Context - .Updateable() - .SetColumns(it => it.ReceivedPackNum == it.ReceivedPackNum + 1) - .Where(it => it.FkOutOrderId == shipnumber) - .Where(it => it.Patchcode == item) - .ExecuteCommand(); - WmGoodsOutRecord record = - new() { Id = SnowFlakeSingle.Instance.NextId().ToString() }; - WmGoodsNowProduction nowProduction = Context - .Queryable() - .Where(it => it.PackageCodeClient == item) - .First(); - if (nowProduction == null) - { - Context.Ado.RollbackTran(); - throw new Exception("成品仓库未查到此货物,无法出库!" + item); - } - record.FkNowProductionId = nowProduction.Id; - record.PackageCodeClient = nowProduction.PackageCodeClient; - record.PackageCode = nowProduction.PackageCode; - record.PackageCodeOriginal = nowProduction.PackageCodeOriginal; - record.LocationCode = nowProduction.LocationCode; - record.Partnumber = nowProduction.Partnumber; - record.GoodsNumLogic = nowProduction.GoodsNumLogic; - record.GoodsNumAction = nowProduction.GoodsNumAction; - record.EntryWarehouseTime = nowProduction.EntryWarehouseTime; - record.OutTime = DateTime.Now; - record.CreatedTime = DateTime.Now; - record.CreatedBy = Createby; - record.FkOutOrderId = shipnumber; - insertList.Add(record); - deleteList.Add(nowProduction.Id); - // 记录统计 - totalPackage++; - if (!partnumbers.Contains(nowProduction.Partnumber)) - { - partnumbers.Add(nowProduction.Partnumber); - } - totalPartnumber += nowProduction.GoodsNumAction ?? 0; - packageCodeRemark.Add(nowProduction.PackageCodeClient); - } - int sum_insert = Context.Insertable(insertList).ExecuteCommand(); - int sum_delete = Context + // 3.1 批量插入出库记录 + finalInsertCount = Context.Insertable(outRecordList).ExecuteCommand(); + if (finalInsertCount != outRecordList.Count) + throw new Exception($"出库记录插入异常:预期{outRecordList.Count}条,实际{finalInsertCount}条"); + + // 3.2 批量删除成品库数据 + finalDeleteCount = Context .Deleteable() - .In(deleteList.ToArray()) + .In(goodsToDeleteIds) .ExecuteCommand(); - if (sum_insert != sum_delete) - { - Context.Ado.RollbackTran(); - throw new Exception( - "出库操作出现异常!失败原因记录不一致:添加数据" + sum_insert + "删除数据:" + sum_delete - ); - } - packageCodeRemark.Sort(); - // 插入记录 - WmGoodsRecord wmGoodsRecord = - new() - { - Id = SnowFlakeSingle.Instance.NextId().ToString(), - FkInventoryId = SnowFlakeSingle.Instance.NextId().ToString(), - Code = "DoMaterialOut", - Partnumber = partnumbers[0] ?? "无零件号", - 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), + if (finalDeleteCount != goodsToDeleteIds.Count) + throw new Exception($"成品库删除异常:预期{goodsToDeleteIds.Count}条,实际{finalDeleteCount}条"); - CreatedBy = Createby ?? "系统操作", - CreatedTime = DateTime.Now, - }; - int recordNum = Context.Insertable(wmGoodsRecord).ExecuteCommand(); - if (recordNum == 0) + // 3.3 插入操作日志 + var operationLog = new WmGoodsRecord { - Context.Ado.RollbackTran(); - throw new Exception("记录插入失败"); - } - // 出库信息转发U8 + 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 = $"出库单号:{shipmentNum}\n客户:{customerCode}\n零件号:{string.Join(',', partNumbers)}\n处理:插入{finalInsertCount}条,删除{finalDeleteCount}条", + CreatedBy = createBy, + CreatedTime = DateTime.Now + }; + int logResult = Context.Insertable(operationLog).ExecuteCommand(); + if (logResult == 0) + throw new Exception("操作日志插入失败"); - //1.构建信息 - // TODO 客户编号获取 - string _outOrder = shipnumber ?? "无出库单"; - - string _customerCode = - Context - .Queryable() - .Where(x => x.ShipmentNum == _outOrder) - .Select(x => x.CustomNo) - .First() ?? "无客户代码"; - List u8PackageList = new(); - foreach (var item in insertList) - { - 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}"); - } - ERP_WMS_interactiveModelQuery u8PackageItem = - new() - { - customerCode = _customerCode, - materialCode = item.Partnumber, - location = item.LocationCode, - Qty = item.GoodsNumLogic.ToString(), - // 批次号 - LotNo = dateString, - createTime = DateTime.Now, - userID = Createby, - guid = Guid.NewGuid().ToString(), - lineno = "涂装生产线" - }; - u8PackageList.Add(u8PackageItem); - } - string urlBase = "http://gam.com.cn:8053/"; - ERP_WMS_interactiveService _eRP_WMS_InteractiveService = new(); - // 后台执行不阻塞主线程 - _ = Task.Run(async () => - { - var u8ErpResult = await _eRP_WMS_InteractiveService.OutboundedAsync(urlBase, u8PackageList); - // 处理结果... - //TODO 对U8返回结果进行解析 - logger.Warn(u8ErpResult); - }); - /* - *ERP_WMS_interactiveModelResult u8ErpResult = _eRP_WMS_InteractiveService.Outbounded( - urlBase, - u8PackageList - ); - logger.Warn(u8ErpResult);*/ Context.Ado.CommitTran(); - return (sum_delete, sum_insert); + logger.Info($"出库成功:删除{finalDeleteCount}条,插入{finalInsertCount}条"); } - catch (Exception e) + catch (Exception ex) { Context.Ado.RollbackTran(); - throw new Exception(e.Message); + 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) @@ -852,7 +908,10 @@ namespace ZR.Service.mes.wms .ToList(); foreach (WmMaterial materialOption in materialOutorders) { - if (materialOption.Partnumber == processedPartnumber || materialOption.Partnumber == checkPartnumber) + if ( + materialOption.Partnumber == processedPartnumber + || materialOption.Partnumber == checkPartnumber + ) { flag1 = true; } @@ -1036,5 +1095,430 @@ namespace ZR.Service.mes.wms } return "经检查,此批次号不在出库单计划中!"; } + + public PagedInfo GetPdaOutOrderPageList( + WmPDAOutOrderPageQueryDto parm + ) + { + // 根据type和shipmentNum查询出库单列表 + var query = Context + .Queryable() + .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 GetPdaOutOrderPlanPageList( + WmPDAOutOrderPlanPageQueryDto parm + ) + { + // 当前工单出库计划 + var exp = Expressionable + .Create() + .And(it => it.FkOutOrderId == parm.ShipmentNum) + .AndIF( + !string.IsNullOrEmpty(parm.MaterialCode), + it => it.MaterialCode == parm.MaterialCode + ) + .ToExpression(); + + // 查询出库计划,并关联物料、库存和出库记录 + // 使用子查询选择每个零件号的最新记录的描述 + var query = Context + .Queryable() + .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 + ); + item.Description = Context + .Queryable() + .Where(m => m.Partnumber == item.MaterialCode && m.Status == 1 && m.Type == 1) + .Select(m => m.Description) + .First() + .ToString(); + item.InventoryNumber = actualData.InventoryNumber; + item.ActualOutNumber = actualData.ActualOutNumber; + item.InventoryPackage = actualData.InventoryPackage; + item.ActualOutPackage = actualData.ActualOutPackage; + } + + return result; + } + + /// + /// 处理BatchCode,去除前两位多余字符 + /// + /// 原始BatchCode + /// 处理后的BatchCode + 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() + .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() + .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 GetPdaOutOrderPlanOptions(string shipmentNum) + { + // 当前工单出库计划 + var exp = Expressionable + .Create() + .And(it => it.FkOutOrderId == shipmentNum) + .ToExpression(); + + // 查询出库计划,并关联物料、库存和出库记录 + // 使用子查询选择每个零件号的最新记录的描述 + var result = Context + .Queryable() + .Where(exp) + .OrderBy(it => it.Outorder) + .Select(it => it.MaterialCode) + .Distinct() + .ToList(); + return result; + } + + public bool UpdatePdaOutOrderPlan(string shipmentNum) + { + return PersistenceOutorderplan(shipmentNum) > 0; + } + + /// + /// 高性能PDA出库方法 + /// + /// + /// + 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() + .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() + .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() + .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 = "PDA出库", + 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() + .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 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.GoodsNumLogic.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); + } + + + } }