ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -217,7 +217,7 @@ { return Repository.QueryData(x => locationCodes.Contains(x.LocationCode)); } public List<LocationTypeDto> GetLocationTypes() { return _locationTypeRepository.Db.Queryable<Dt_LocationType>().Select(x => ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs
@@ -156,6 +156,18 @@ return ConvertAsync(materialData, quantity, materialData.PurchaseUnit, materialData.StockUnit); } public async Task<MaterialWithUnitConversionResult> ConvertFromToStockAsync(string materialCode,string fromUom, decimal quantity) { var materialData = await GetMaterialWithUnitsAsync(materialCode); // 妿颿åä½ååºååä½ç¸åï¼ç´æ¥è¿å if (fromUom.Equals(materialData.StockUnit, StringComparison.OrdinalIgnoreCase)) return new MaterialWithUnitConversionResult(quantity, materialData.StockUnit, false); return ConvertAsync(materialData, quantity, fromUom, materialData.StockUnit); } /// <summary> /// 颿åä½è½¬åºååä½ /// </summary> ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs
@@ -16,6 +16,17 @@ // å·²åºåº = 4, // å·²ååº = 5 //} // æä¸¾å®ä¹ public enum BatchStatusEnum { åé ä¸ = 0, æ§è¡ä¸ = 1, 已宿 = 2, å·²ååº = 3, 已忶 = 4 } public enum SplitPackageStatusEnum { å·²æå = 1, ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IBasicService/IMaterialUnitService.cs
@@ -20,6 +20,8 @@ Task<Dictionary<string, MaterialWithUnitConversionResult>> BatchConvertPurchaseToStockAsync(List<BatchConversionRequest> requests); Task<MaterialWithUnitConversionResult> ConvertAsync(string materialCode, decimal quantity, string fromUnit, string toUnit); Task<MaterialWithUnitConversionResult> ConvertIssueToStockAsync(string materialCode, decimal quantity); Task<MaterialWithUnitConversionResult> ConvertFromToStockAsync(string materialCode, string fromUom, decimal quantity); Task<MaterialWithUnitConversionResult> ConvertPurchaseToStockAsync(string materialCode, decimal quantity); Task<decimal?> GetConversionRatioAsync(string materialCode, string fromUnit, string toUnit); Task<string> GetIssueUnitAsync(string materialCode); ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutStockLockInfoService.cs
@@ -26,7 +26,7 @@ Task<List<Dt_OutStockLockInfo>> GetByPalletCode(string palletCode, int? status = null); Task<LockInfoDetailDto> GetLockInfoDetail(int lockInfoId); Dt_OutStockLockInfo GetOutStockLockInfo(Dt_OutboundOrder outboundOrder,Dt_OutboundOrderDetail outboundOrderDetail,Dt_StockInfo outStock, decimal assignQuantity, string barcode = null); Dt_OutStockLockInfo GetOutStockLockInfo(Dt_OutboundOrder outboundOrder,Dt_OutboundOrderDetail outboundOrderDetail,Dt_StockInfo outStock, decimal assignQuantity, string barcode = null, string outboundBatchNo = ""); List<Dt_OutStockLockInfo> GetOutStockLockInfos(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail outboundOrderDetail, List<Dt_StockInfo> outStocks, int? taskNum = null); Task<List<Dt_OutStockLockInfo>> GetPalletLockInfos(string palletCode); ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundOrderDetailService.cs
@@ -23,6 +23,6 @@ WebResponseContent LockOutboundStockDataUpdate(List<Dt_StockInfo> stockInfos, List<Dt_OutboundOrderDetail> outboundOrderDetails, List<Dt_OutStockLockInfo> outStockLockInfos, List<Dt_LocationInfo> locationInfos, LocationStatusEnum locationStatus = LocationStatusEnum.Lock, List<Dt_Task>? tasks = null); (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) AssignStockOutbound(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews); //List<Dt_OutboundOrderDetail> GetOutboundStockDataById(int id); Task<(List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>)> AssignStockForBatch(Dt_OutboundOrderDetail orderDetail, decimal batchQuantity, string batchNo); } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs
@@ -96,11 +96,11 @@ item.Unit = purchaseToStockResult.Unit; item.OrderQuantity = purchaseToStockResult.Quantity; } if (model .OrderType != InOrderTypeEnum.Allocat.ObjToInt()) if (model.OrderType != InOrderTypeEnum.Allocat.ObjToInt()) { model.InboundOrderNo = CreateCodeByRule(nameof(RuleCodeEnum.InboundOrderRule)); } Db.InsertNav(model).Include(x => x.Details).ExecuteCommand(); } return WebResponseContent.Instance.OK(); @@ -341,12 +341,12 @@ if (!result2.Item1) return content = WebResponseContent.Instance.Error(result2.Item2); // materielGroupDTO.WarehouseCode var code = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>().Where(x => x.Code == materielGroupDTO.WarehouseType).Select(x=>x.Code).First(); if(string.IsNullOrEmpty(code)) var code = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>().Where(x => x.Code == materielGroupDTO.WarehouseType).Select(x => x.Code).First(); if (string.IsNullOrEmpty(code)) { return content = WebResponseContent.Instance.Error($"ä»åºä¸æ²¡æè¯¥{materielGroupDTO.WarehouseType}ç¼å·ã"); } Dt_InboundOrder inboundOrder = GetInboundOrder(materielGroupDTO.OrderNo); @@ -368,10 +368,10 @@ if (stockInfo == null) { stockInfo = new Dt_StockInfo() { PalletType = (int)PalletTypeEnum.None,LocationType=materielGroupDTO.locationType.ObjToInt() }; stockInfo = new Dt_StockInfo() { PalletType = (int)PalletTypeEnum.None, LocationType = materielGroupDTO.locationType.ObjToInt() }; stockInfo.Details = new List<Dt_StockInfoDetail>(); } foreach (var item in dbinboundOrderDetails) { stockInfo.Details.Add(new Dt_StockInfoDetail @@ -385,15 +385,15 @@ SupplyCode = item.SupplyCode, WarehouseCode = materielGroupDTO.WarehouseType, StockQuantity = item.OrderQuantity, BarcodeQty=item.BarcodeQty, BarcodeUnit=item.BarcodeUnit, FactoryArea= inboundOrder.FactoryArea, Status = 0, BarcodeQty = item.BarcodeQty, BarcodeUnit = item.BarcodeUnit, FactoryArea = inboundOrder.FactoryArea, Status = 0, OrderNo = inboundOrder.InboundOrderNo, BusinessType = inboundOrder.BusinessType, }); item.ReceiptQuantity = item.BarcodeQty; item.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt(); item.WarehouseCode = materielGroupDTO.WarehouseType; @@ -440,7 +440,7 @@ WebResponseContent content = new WebResponseContent(); try { { (bool, string, object?) result2 = ModelValidate.ValidateModelData(materielGroupDTO); if (!result2.Item1) return content = WebResponseContent.Instance.Error(result2.Item2); @@ -450,7 +450,8 @@ return content = WebResponseContent.Instance.Error($"åºå䏿²¡æè¯¥{materielGroupDTO.WarehouseCode}ç¼å·ã"); } if(_stockRepository.QueryFirst(x=>x.PalletCode == materielGroupDTO.PalletCode)!=null){ if (_stockRepository.QueryFirst(x => x.PalletCode == materielGroupDTO.PalletCode) != null) { return WebResponseContent.Instance.Error("该æçå·²ç»ç»è¿ç"); } @@ -469,7 +470,7 @@ { if (stockInfo == null) { stockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.ç»çæå.ObjToInt(), PalletCode = materielGroupDTO.PalletCode,LocationType= materielGroupDTO.WarehouseCode.ObjToInt() }; stockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.ç»çæå.ObjToInt(), PalletCode = materielGroupDTO.PalletCode, LocationType = materielGroupDTO.WarehouseCode.ObjToInt() }; stockInfo.Details = new List<Dt_StockInfoDetail>(); } else @@ -650,13 +651,13 @@ { return WebResponseContent.Instance.Error("æçå·ä¸è½ä¸ºç©º"); } var stock= _stockRepository.Db.Queryable<Dt_StockInfo>().Includes(o=>o.Details).First(x => x.PalletCode == palletCode && x.StockStatus ==(int)StockStatusEmun.ç»çæå); var stock = _stockRepository.Db.Queryable<Dt_StockInfo>().Includes(o => o.Details).First(x => x.PalletCode == palletCode && x.StockStatus == (int)StockStatusEmun.ç»çæå); if (stock == null) { return WebResponseContent.Instance.Error($"æªæ¾å°æçå·{palletCode}对åºçåºåè®°å½"); } if(stock.Details == null || !stock.Details.Any()) if (stock.Details == null || !stock.Details.Any()) { _stockRepository.DeleteData(stock); return WebResponseContent.Instance.OK(); ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundBatch.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,68 @@ using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_Core.DB.Models; namespace WIDESEA_Model.Models.Outbound { /// <summary> /// åºåºæ¹æ¬¡è¡¨ /// </summary> [SugarTable("Dt_OutboundBatch")] public class Dt_OutboundBatch : BaseEntity { /// <summary> /// 主é®IDï¼èªå¢ï¼ /// </summary> [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] // public int Id { get; set; } /// <summary> /// æ¹æ¬¡å· /// </summary> [SugarColumn(ColumnName = "BatchNo", Length = 50, IsNullable = false)] public string BatchNo { get; set; } /// <summary> /// 订åå· /// </summary> [SugarColumn(ColumnName = "OrderNo", Length = 50, IsNullable = false)] public string OrderNo { get; set; } /// <summary> /// 订åæç»ID /// </summary> [SugarColumn(ColumnName = "OrderDetailId", IsNullable = false)] public int OrderDetailId { get; set; } /// <summary> /// æ¹æ¬¡åé æ°é /// </summary> [SugarColumn(ColumnName = "BatchQuantity", IsNullable = false)] // 精度18ï¼å°æ°ä½2 public decimal BatchQuantity { get; set; } /// <summary> /// 已宿æ°éï¼é»è®¤0ï¼ /// </summary> [SugarColumn(ColumnName = "CompletedQuantity", DefaultValue = "0")] // é»è®¤å¼0 public decimal CompletedQuantity { get; set; } = 0; // 代ç å±é»è®¤å¼ï¼ä¸æ°æ®åºé»è®¤å¼ä¸è´ /// <summary> /// æ¹æ¬¡ç¶æï¼é»è®¤0ï¼ /// </summary> [SugarColumn(ColumnName = "BatchStatus", DefaultValue = "0")] public int BatchStatus { get; set; } = 0; /// <summary> /// æä½äºº /// </summary> [SugarColumn(ColumnName = "Operator", Length = 50, IsNullable = true)] // å¯ç©º public string Operator { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundLockInfo.cs
@@ -136,6 +136,8 @@ public string BarcodeUnit { get; set; } public string OutboundBatchNo { get; set; } [Navigate(NavigateType.OneToOne, nameof(StockInfo))]//ä¸å¯¹ä¸ SchoolIdæ¯StudentAç±»éé¢ç public Dt_StockInfo StockInfo { get; set; } //ä¸è½èµå¼åªè½æ¯null ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs
@@ -145,5 +145,9 @@ public decimal PickedQty { get; set; } public string documentsNO { get; set; } public decimal AllocatedQuantity { get; set; } public string BatchAllocateStatus { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutStockLockInfoService.cs
@@ -43,7 +43,7 @@ Dt_OutboundOrderDetail outboundOrderDetail, Dt_StockInfo outStock, decimal assignQuantity, string barcode = null) string barcode = null,string outboundBatchNo = "") { // è·ååºåæç»ä¿¡æ¯ var stockDetails = outStock.Details @@ -107,9 +107,12 @@ IsSplitted = 0, MaterielCode = outboundOrderDetail.MaterielCode, BatchNo = firstAvailableDetail.BatchNo, Unit = firstAvailableDetail.BarcodeUnit, Unit = firstAvailableDetail.Unit, BarcodeQty = firstAvailableDetail.BarcodeQty, BarcodeUnit = firstAvailableDetail.BarcodeUnit, FactoryArea = firstAvailableDetail.FactoryArea, lineNo = outboundOrderDetail.lineNo, OutboundBatchNo= outboundBatchNo }; } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,571 @@ using Microsoft.Extensions.Logging; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_BasicService; using WIDESEA_Common.OrderEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.BaseServices; using WIDESEA_IAllocateService; using WIDESEA_IBasicService; using WIDESEA_IOutboundService; using WIDESEA_IStockService; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Basic; using WIDESEA_Model.Models.Outbound; namespace WIDESEA_OutboundService { public class OutboundBatchPickingService : ServiceBase<Dt_PickingRecord, IRepository<Dt_PickingRecord>> { private readonly IUnitOfWorkManage _unitOfWorkManage; public IRepository<Dt_PickingRecord> Repository => BaseDal; private readonly IStockInfoService _stockInfoService; private readonly IStockService _stockService; private readonly IOutStockLockInfoService _outStockLockInfoService; private readonly IStockInfoDetailService _stockInfoDetailService; private readonly ILocationInfoService _locationInfoService; private readonly IOutboundOrderDetailService _outboundOrderDetailService; private readonly IOutboundOrderService _outboundOrderService; private readonly ISplitPackageService _splitPackageService; private readonly IRepository<Dt_Task> _taskRepository; private readonly IESSApiService _eSSApiService; private readonly IInvokeMESService _invokeMESService; private readonly IDailySequenceService _dailySequenceService; private readonly IAllocateService _allocateService; private readonly IRepository<Dt_OutboundBatch> _outboundBatchRepository; private readonly ILogger<OutboundPickingService> _logger; private Dictionary<string, string> stations = new Dictionary<string, string> { {"2-1","2-9" }, {"3-1","3-9" }, }; private Dictionary<string, string> movestations = new Dictionary<string, string> { {"2-1","2-5" }, {"3-1","3-5" }, }; public OutboundBatchPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService, IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService, IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; _stockService = stockService; _outStockLockInfoService = outStockLockInfoService; _stockInfoDetailService = stockInfoDetailService; _locationInfoService = locationInfoService; _outboundOrderDetailService = outboundOrderDetailService; _splitPackageService = splitPackageService; _outboundOrderService = outboundOrderService; _taskRepository = taskRepository; _eSSApiService = eSSApiService; _logger = logger; _invokeMESService = invokeMESService; _dailySequenceService = dailySequenceService; _allocateService = allocateService; } #region 忹忣 /// <summary> /// åæ¹åæ£ç¡®è®¤ /// </summary> public async Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string batchNo, string palletCode, string barcode, decimal actualPickedQty) { try { _unitOfWorkManage.BeginTran(); // 1. éªè¯åæ£è¯·æ± var validationResult = await ValidateBatchPickingRequest(orderNo, batchNo, palletCode, barcode, actualPickedQty); if (!validationResult.IsValid) return WebResponseContent.Instance.Error(validationResult.ErrorMessage); var (lockInfo, orderDetail, stockDetail) = validationResult.Data; // 2. æ§è¡åæ£é»è¾ var pickingResult = await ExecuteBatchPickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty); // 3. æ´æ°æ¹æ¬¡å®ææ°é await UpdateBatchCompletedQuantity(batchNo, actualPickedQty); // 4. æ´æ°è®¢åç¸å ³æ°æ® await UpdateOrderRelatedData(orderDetail.Id, actualPickedQty, orderNo); // 5. è®°å½æ£éåå² await RecordPickingHistory(pickingResult, orderNo, palletCode, batchNo); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("忹忣æå"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¹åæ£å¤±è´¥ - OrderNo: {orderNo}, BatchNo: {batchNo}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¹åæ£å¤±è´¥ï¼{ex.Message}"); } } private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>> ValidateBatchPickingRequest( string orderNo, string batchNo, string palletCode, string barcode, decimal actualPickedQty) { // æ¥æ¾æ¹æ¬¡éå®ä¿¡æ¯ var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.BatchNo == batchNo && x.PalletCode == palletCode && x.CurrentBarcode == barcode && x.Status == (int)OutLockStockStatusEnum.åºåºä¸) .FirstAsync(); if (lockInfo == null) return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçæ¹æ¬¡éå®ä¿¡æ¯"); if (actualPickedQty <= 0 || actualPickedQty > lockInfo.AssignQuantity - lockInfo.PickedQty) return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("忣æ°éæ æ"); // è·å订åæç»ååºåæç» var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() .FirstAsync(x => x.Id == lockInfo.OrderDetailId); var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == barcode && x.StockId == lockInfo.StockId); return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Success((lockInfo, orderDetail, stockDetail)); } private async Task<PickingResult> ExecuteBatchPickingLogic( Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail, decimal actualPickedQty) { // æ´æ°éå®ä¿¡æ¯ lockInfo.PickedQty += actualPickedQty; if (lockInfo.PickedQty >= lockInfo.AssignQuantity) { lockInfo.Status = (int)OutLockStockStatusEnum.æ£é宿; } await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); // æ´æ°åºå stockDetail.StockQuantity -= actualPickedQty; stockDetail.OutboundQuantity += actualPickedQty; if (stockDetail.StockQuantity <= 0) { stockDetail.Status = (int)StockStatusEmun.åºåºå®æ; } await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); return new PickingResult { FinalLockInfo = lockInfo, ActualPickedQty = actualPickedQty }; } private async Task UpdateBatchCompletedQuantity(string batchNo, decimal pickedQty) { await _outboundBatchRepository.Db.Updateable<Dt_OutboundBatch>() .SetColumns(x => x.CompletedQuantity == x.CompletedQuantity + pickedQty) .Where(x => x.BatchNo == batchNo) .ExecuteCommandAsync(); // æ£æ¥æ¹æ¬¡æ¯å¦å®æ var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() .FirstAsync(x => x.BatchNo == batchNo); if (batch.CompletedQuantity >= batch.BatchQuantity) { batch.BatchStatus = (int)BatchStatusEnum.已宿; await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); } } #endregion #region æå¨æå /// <summary> /// æå¨æå /// </summary> public async Task<WebResponseContent> ManualSplitPackage(string orderNo, string batchNo, string originalBarcode, decimal splitQuantity) { try { _unitOfWorkManage.BeginTran(); // 1. éªè¯æå è¯·æ± var validationResult = await ValidateManualSplitRequest(orderNo, batchNo, originalBarcode, splitQuantity); if (!validationResult.IsValid) return WebResponseContent.Instance.Error(validationResult.ErrorMessage); var (lockInfo, stockDetail) = validationResult.Data; // 2. æ§è¡æå é»è¾ var splitResult = await ExecuteManualSplit(lockInfo, stockDetail, splitQuantity, batchNo); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("æå¨æå æå", new { NewBarcode = splitResult.NewBarcode }); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"æå¨æå 失败 - OrderNo: {orderNo}, BatchNo: {batchNo}, Barcode: {originalBarcode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"æå¨æå 失败ï¼{ex.Message}"); } } private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateManualSplitRequest( string orderNo, string batchNo, string originalBarcode, decimal splitQuantity) { var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.BatchNo == batchNo && x.CurrentBarcode == originalBarcode && x.Status == (int)OutLockStockStatusEnum.åºåºä¸) .FirstAsync(); if (lockInfo == null) return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId); if (stockDetail.StockQuantity < splitQuantity) return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå æ°éä¸è½å¤§äºåºåæ°é"); if (lockInfo.AssignQuantity - lockInfo.PickedQty < splitQuantity) return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå æ°éä¸è½å¤§äºæªæ£éæ°é"); return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail)); } private async Task<SplitResultDto> ExecuteManualSplit(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQuantity, string batchNo) { // çææ°æ¡ç string newBarcode = await GenerateNewBarcode(); // å建æ°åºåæç» var newStockDetail = new Dt_StockInfoDetail { StockId = stockDetail.StockId, MaterielCode = stockDetail.MaterielCode, OrderNo = stockDetail.OrderNo, BatchNo = stockDetail.BatchNo, StockQuantity = splitQuantity, OutboundQuantity = 0, Barcode = newBarcode, Status = (int)StockStatusEmun.åºåºéå®, SupplyCode = stockDetail.SupplyCode, Unit = stockDetail.Unit }; await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); // æ´æ°ååºåæç» stockDetail.StockQuantity -= splitQuantity; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); // å建æ°éå®ä¿¡æ¯ var newLockInfo = new Dt_OutStockLockInfo { OrderNo = lockInfo.OrderNo, OrderDetailId = lockInfo.OrderDetailId, BatchNo = batchNo, MaterielCode = lockInfo.MaterielCode, StockId = lockInfo.StockId, OrderQuantity = splitQuantity, AssignQuantity = splitQuantity, PickedQty = 0, LocationCode = lockInfo.LocationCode, PalletCode = lockInfo.PalletCode, Status = (int)OutLockStockStatusEnum.åºåºä¸, CurrentBarcode = newBarcode, Operator = App.User.UserName, }; await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); // æ´æ°åéå®ä¿¡æ¯ lockInfo.AssignQuantity -= splitQuantity; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); // è®°å½æå åå² await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode); return new SplitResultDto { NewBarcode = newBarcode }; } #endregion #region åæ¶æå /// <summary> /// åæ¶æå /// </summary> public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string batchNo, string newBarcode) { try { _unitOfWorkManage.BeginTran(); // æ¥æ¾æå è®°å½åæ°éå®ä¿¡æ¯ var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.NewBarcode == newBarcode && x.OrderNo == orderNo && !x.IsReverted) .FirstAsync(); if (splitRecord == null) return WebResponseContent.Instance.Error("æªæ¾å°æå è®°å½"); var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.CurrentBarcode == newBarcode && x.BatchNo == batchNo) .FirstAsync(); if (newLockInfo == null) return WebResponseContent.Instance.Error("æªæ¾å°æ°éå®ä¿¡æ¯"); // æ¢å¤ååºå var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); originalStock.StockQuantity += splitRecord.SplitQty; await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync(); // æ¢å¤åéå®ä¿¡æ¯ var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); originalLockInfo.AssignQuantity += splitRecord.SplitQty; await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync(); // å 餿°åºåæç» await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() .Where(x => x.Barcode == newBarcode) .ExecuteCommandAsync(); // å 餿°éå®ä¿¡æ¯ await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() .Where(x => x.Id == newLockInfo.Id) .ExecuteCommandAsync(); // æ è®°æå è®°å½ä¸ºå·²æ¤é splitRecord.IsReverted = true; splitRecord.RevertTime = DateTime.Now; splitRecord.Operator = App.User.UserName; await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("åæ¶æå æå"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¶æå 失败 - OrderNo: {orderNo}, BatchNo: {batchNo}, Barcode: {newBarcode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¶æå 失败ï¼{ex.Message}"); } } #endregion #region åæ¹ååº /// <summary> /// åæ¹ååº - éæ¾æªæ£éçåºå /// </summary> public async Task<WebResponseContent> BatchReturnStock(string orderNo, string batchNo) { try { _unitOfWorkManage.BeginTran(); // 1. æ¥æ¾æ¹æ¬¡æªå®æçéå®è®°å½ var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.BatchNo == batchNo && x.Status == (int)OutLockStockStatusEnum.åºåºä¸) .ToListAsync(); if (!unfinishedLocks.Any()) return WebResponseContent.Instance.Error("è¯¥æ¹æ¬¡æ²¡ææªå®æçéå®è®°å½"); // 2. éæ¾åºååéå®è®°å½ foreach (var lockInfo in unfinishedLocks) { await ReleaseLockAndStock(lockInfo); } // 3. æ´æ°æ¹æ¬¡ç¶æ await UpdateBatchStatusForReturn(batchNo); // 4. æ´æ°è®¢åæç»çå·²åé æ°é await UpdateOrderDetailAfterReturn(unfinishedLocks); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("åæ¹ååºæå"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¹ååºå¤±è´¥ - OrderNo: {orderNo}, BatchNo: {batchNo}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¹ååºå¤±è´¥ï¼{ex.Message}"); } } private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo) { // æ¢å¤åºåç¶æ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); if (stockDetail != null) { stockDetail.Status = (int)StockStatusEmun.å ¥åºå®æ; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); } // æ´æ°éå®è®°å½ç¶æä¸ºååº lockInfo.Status = (int)OutLockStockStatusEnum.ååºä¸; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); } private async Task UpdateBatchStatusForReturn(string batchNo) { await _outboundBatchRepository.Db.Updateable<Dt_OutboundBatch>() .SetColumns(x => new Dt_OutboundBatch { BatchStatus = (int)BatchStatusEnum.å·²ååº, Operator = App.User.UserName }) .Where(x => x.BatchNo == batchNo) .ExecuteCommandAsync(); } private async Task UpdateOrderDetailAfterReturn(List<Dt_OutStockLockInfo> returnedLocks) { var orderDetailGroups = returnedLocks.GroupBy(x => x.OrderDetailId); foreach (var group in orderDetailGroups) { var orderDetailId = group.Key; var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty); await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() .SetColumns(x => x.AllocatedQuantity == x.AllocatedQuantity - returnedQty) .Where(x => x.Id == orderDetailId) .ExecuteCommandAsync(); } } #endregion #region è¾ å©æ¹æ³ private async Task<string> GenerateNewBarcode() { var seq = await _dailySequenceService.GetNextSequenceAsync(); return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0'); } private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,decimal splitQty, string newBarcode) { var splitHistory = new Dt_SplitPackageRecord { OrderNo = lockInfo.OrderNo, OutStockLockInfoId = lockInfo.Id, StockId = stockDetail.StockId, Operator = App.User.UserName, OriginalBarcode = stockDetail.Barcode, NewBarcode = newBarcode, SplitQty = splitQty, SplitTime = DateTime.Now, Status = (int)SplitPackageStatusEnum.å·²æå }; await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync(); } private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode, string batchNo) { var pickingRecord = new Dt_PickingRecord { OrderNo = orderNo, // BatchNo = batchNo, OrderDetailId = result.FinalLockInfo.OrderDetailId, PalletCode = palletCode, Barcode = result.FinalLockInfo.CurrentBarcode, MaterielCode = result.FinalLockInfo.MaterielCode, PickQuantity = result.ActualPickedQty, PickTime = DateTime.Now, Operator = App.User.UserName, OutStockLockId = result.FinalLockInfo.Id }; await Db.Insertable(pickingRecord).ExecuteCommandAsync(); } private async Task UpdateOrderRelatedData(int orderDetailId, decimal pickedQty, string orderNo) { // æ´æ°è®¢åæç»çå·²åºåºæ°é await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() .SetColumns(x => new Dt_OutboundOrderDetail { OverOutQuantity = x.OverOutQuantity + pickedQty, AllocatedQuantity = x.AllocatedQuantity - pickedQty }) .Where(x => x.Id == orderDetailId) .ExecuteCommandAsync(); // æ£æ¥è®¢åç¶æ await CheckAndUpdateOrderStatus(orderNo); } private async Task CheckAndUpdateOrderStatus(string orderNo) { var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) .Where((o, item) => item.OrderNo == orderNo) .Select((o, item) => o) .ToListAsync(); bool allCompleted = orderDetails.All(x => x.OverOutQuantity >= x.NeedOutQuantity); if (allCompleted) { await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() .SetColumns(x => new Dt_OutboundOrder { OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ }) .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync(); } } #endregion } // æ¯æç±» public class SplitResultDto { public string NewBarcode { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -227,6 +227,189 @@ return (true, "åé æå"); } /// <summary> /// ä¸ºåæ¹åé åºå /// </summary> public async Task<(List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>)> AssignStockForBatch(Dt_OutboundOrderDetail orderDetail, decimal batchQuantity, string batchNo) { if (orderDetail == null) { throw new Exception("æªæ¾å°åºåºåæç»ä¿¡æ¯"); } var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>() .FirstAsync(x => x.Id == orderDetail.OrderId); List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>(); List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>(); List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>(); // æç©æåæ¹æ¬¡åç»å¤çï¼è¿éåªæä¸ä¸ªæç»ï¼ var groupDetails = new List<Dt_OutboundOrderDetail> { orderDetail } .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode }) .Select(x => new { MaterielCode = x.Key.MaterielCode, BatchNo = x.Key.BatchNo, SupplyCode = x.Key.SupplyCode, Details = x.ToList(), TotalNeedQuantity = batchQuantity // 使ç¨åæ¹æ°é }) .Where(x => x.TotalNeedQuantity > 0) .ToList(); foreach (var item in groupDetails) { var needQuantity = item.TotalNeedQuantity; // è·åå¯ç¨åºåï¼æå è¿å åºæåºï¼ List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, item.SupplyCode); if (!stockInfos.Any()) { throw new Exception($"ç©æ[{item.MaterielCode}]æ¹æ¬¡[{item.BatchNo}]æªæ¾å°å¯åé åºå"); } // åé åºåï¼æå è¿å åºï¼ var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks (stockInfos, item.MaterielCode, needQuantity, out decimal residueQuantity); // æ£æ¥åé ç»æ decimal allocatedQuantity = needQuantity - residueQuantity; if (allocatedQuantity <= 0) { throw new Exception($"ç©æ[{item.MaterielCode}]æ¹æ¬¡[{item.BatchNo}]åºåä¸è¶³ï¼éè¦{needQuantity}ï¼ä½æ æ³åé ä»»ä½åºå"); } outStocks.AddRange(autoAssignStocks); // æå è¿å åºåååé é宿°éå°å个æç» var distributionResult = DistributeLockQuantityByFIFO(item.Details, autoAssignStocks, stockAllocations, outStockLockInfos, outboundOrder, batchNo); if (!distributionResult.success) { throw new Exception(distributionResult.message); } // æ´æ°åºåºåæç»ç¶æ UpdateOrderDetailStatus(item.Details, allocatedQuantity, needQuantity); } if (outStocks.Any()) { locationInfos.AddRange(_locationInfoService.GetLocationInfos( outStocks.Select(x => x.LocationCode).Distinct().ToList())); } return (outStocks, groupDetails.SelectMany(x => x.Details).ToList(), outStockLockInfos, locationInfos); } /// <summary> /// æå è¿å åºåååé é宿°é /// </summary> private (bool success, string message) DistributeLockQuantityByFIFO( List<Dt_OutboundOrderDetail> details, List<Dt_StockInfo> assignStocks, Dictionary<int, decimal> stockAllocations, List<Dt_OutStockLockInfo> outStockLockInfos, Dt_OutboundOrder outboundOrder, string batchNo) { var sortedDetails = details .Where(d => d.OrderQuantity - d.OverOutQuantity - d.AllocatedQuantity > 0) .OrderBy(x => x.Id) .ToList(); if (!sortedDetails.Any()) return (true, "æ éåé "); // è·åææåé äºåºåçæç»ï¼æå è¿å åºæåº var allocatedStockDetails = assignStocks .SelectMany(x => x.Details) .Where(x => stockAllocations.ContainsKey(x.Id) && stockAllocations[x.Id] > 0) .OrderBy(x => x.CreateDate) .ThenBy(x => x.StockId) .ToList(); if (!allocatedStockDetails.Any()) { return (false, "没æå¯åé çåºåæç»"); } decimal totalNeedQuantity = sortedDetails.Sum(d => d.OrderQuantity - d.OverOutQuantity - d.AllocatedQuantity); decimal allocatedQuantity = 0; // 为æ¯ä¸ªåºåæç»å建åé è®°å½ foreach (var stockDetail in allocatedStockDetails) { if (!stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQuantityForStock)) continue; if (allocatedQuantityForStock <= 0) continue; var stockInfo = assignStocks.First(x => x.Id == stockDetail.StockId); decimal remainingAllocate = allocatedQuantityForStock; // æé¡ºåºåé ç»å个åºåºåæç» foreach (var detail in sortedDetails) { if (remainingAllocate <= 0) break; // 计ç®è¿ä¸ªæç»è¿éè¦åé çæ°é var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.AllocatedQuantity; if (detailNeed <= 0) continue; // åé æ°é var assignQuantity = Math.Min(remainingAllocate, detailNeed); // éªè¯æ¡ç æ¯å¦åå¨ if (string.IsNullOrEmpty(stockDetail.Barcode)) { return (false, $"åºåæç»ID[{stockDetail.Id}]çæ¡ç 为空"); } // å建åºåºéå®ä¿¡æ¯ var lockInfo = _outStockLockInfoService.GetOutStockLockInfo( outboundOrder, detail, stockInfo, assignQuantity, stockDetail.Barcode,batchNo); outStockLockInfos.Add(lockInfo); // æ´æ°æç»çå·²åé æ°é detail.AllocatedQuantity += assignQuantity; remainingAllocate -= assignQuantity; allocatedQuantity += assignQuantity; } // å¦æè¿æå©ä½åé æ°éï¼è®°å½è¦å if (remainingAllocate > 0) { _logger.LogWarning($"åºååé å仿å©ä½æ°éæªåé : {remainingAllocate}, æ¡ç : {stockDetail.Barcode}"); } } // éªè¯æ¯å¦è³å°åé äºä¸é¨å if (allocatedQuantity <= 0) { return (false, "åºååé å¤±è´¥ï¼æ æ³åé 任使°é"); } // è®°å½åé ç»æ if (allocatedQuantity < totalNeedQuantity) { _logger.LogWarning($"åºåé¨ååé ï¼éè¦{totalNeedQuantity}ï¼å®é åé {allocatedQuantity}"); } else { _logger.LogInformation($"åºåå®å ¨åé ï¼åé æ°é{allocatedQuantity}"); } return (true, "åé æå"); } private void UpdateOrderDetailStatus(List<Dt_OutboundOrderDetail> details, decimal allocatedQuantity, decimal needQuantity) { ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs
@@ -65,15 +65,17 @@ } foreach (var item in model.Details) { var issueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeQty); var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode,item.BarcodeUnit, item.BarcodeQty); item.Unit = issueoStockResult.Unit; item.OrderQuantity = issueoStockResult.Quantity; var moveissueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeMoveQty); var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty); item.MoveQty = moveissueoStockResult.Quantity; } model.OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.OutboundOrderRule)); if (model.OrderType != InOrderTypeEnum.Allocat.ObjToInt() || model.OrderType != InOrderTypeEnum.InternalAllocat.ObjToInt()) { model.OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.OutboundOrderRule)); } Db.InsertNav(model).Include(x => x.Details).ExecuteCommand(); return WebResponseContent.Instance.OK(); @@ -125,10 +127,10 @@ BarcodeQty = item.OrderQuantity, BarcodeUnit = item.Unit, }; var issueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeQty); var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode,item.BarcodeUnit, item.BarcodeQty); item.Unit = issueoStockResult.Unit; item.OrderQuantity = issueoStockResult.Quantity; var moveissueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeMoveQty); var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty); item.MoveQty = moveissueoStockResult.Quantity; outboundOrderDetails.Add(outboundOrderDetail); @@ -146,10 +148,10 @@ outboundOrderDetail.BarcodeMoveQty = item.MoveQty; outboundOrderDetail.BarcodeQty = item.OrderQuantity; outboundOrderDetail.BarcodeUnit = item.Unit; var issueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeQty); var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeQty); outboundOrderDetail.Unit = issueoStockResult.Unit; outboundOrderDetail.OrderQuantity = issueoStockResult.Quantity; var moveissueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeMoveQty); var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty); outboundOrderDetail.MoveQty = moveissueoStockResult.Quantity; ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -2168,6 +2168,8 @@ FactoryArea = originalLock.FactoryArea, lineNo = originalLock.lineNo, WarehouseCode = originalLock.WarehouseCode, BarcodeQty=originalLock.BarcodeQty, BarcodeUnit=originalLock.BarcodeUnit, }; ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj
@@ -7,6 +7,7 @@ </PropertyGroup> <ItemGroup> <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" /> <ProjectReference Include="..\WIDESEA_IAllocateService\WIDESEA_IAllocateService.csproj" /> <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" /> <ProjectReference Include="..\WIDESEA_IOutboundService\WIDESEA_IOutboundService.csproj" /> ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -51,6 +51,7 @@ using WIDESEA_IStockService; using WIDESEA_ITaskInfoService; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Outbound; namespace WIDESEA_TaskInfoService { @@ -65,6 +66,7 @@ private readonly IInboundOrderService _inboundOrderService; private readonly IInboundOrderDetailService _inboundOrderDetailService; private readonly IRepository<Dt_OutboundBatch> _OutboundBatchRepository; private readonly IOutboundOrderService _outboundOrderService; private readonly IOutboundOrderDetailService _outboundOrderDetailService; private readonly IOutStockLockInfoService _outStockLockInfoService; ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_BasicService; using WIDESEA_Common.CommonEnum; using WIDESEA_Common.LocationEnum; using WIDESEA_Common.OrderEnum; @@ -17,6 +18,8 @@ using WIDESEA_DTO.Basic; using WIDESEA_DTO.Stock; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Basic; using WIDESEA_Model.Models.Outbound; namespace WIDESEA_TaskInfoService { @@ -596,5 +599,197 @@ } #region åæ¹åé åºå /// <summary> /// åæ¹åé åºå /// </summary> public async Task<WebResponseContent> BatchAllocateStock(string orderNo, int orderDetailId, decimal batchQuantity, string outStation) { try { List<Dt_Task> tasks = new List<Dt_Task>(); List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>(); List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>(); List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>(); List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>(); (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderNo, orderDetailId, batchQuantity, outStation); if (result.Item2 != null && result.Item2.Count > 0) { stockInfos.AddRange(result.Item2); } if (result.Item3 != null && result.Item3.Count > 0) { outboundOrderDetails.AddRange(result.Item3); } if (result.Item4 != null && result.Item4.Count > 0) { outStockLockInfos.AddRange(result.Item4); } if (result.Item5 != null && result.Item5.Count > 0) { locationInfos.AddRange(result.Item5); } if (result.Item1 != null && result.Item1.Count > 0) { tasks.AddRange(result.Item1); } WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos); return content; } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¹åé åºå失败 - OrderNo: {orderNo}, OrderDetailId: {orderDetailId}, Quantity: {batchQuantity}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¹åé 失败ï¼{ex.Message}"); } } /// <summary> /// åæ¹åé åºåæ°æ®å¤ç /// </summary> public async Task<(List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?)> BatchAllocateStockDataHandle(string orderNo, int orderDetailId, decimal batchQuantity, string outStation) { List<Dt_Task> tasks = new List<Dt_Task>(); // è·å订åæç» var outboundOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() .FirstAsync(x => x.Id == orderDetailId ); if (outboundOrderDetail == null) { throw new Exception("æªæ¾å°åºåºåæç»ä¿¡æ¯"); } // éªè¯è®¢åæç»ç¶æ if (outboundOrderDetail.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() && outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) { throw new Exception("æéåºåºåæç»åå¨åºåºä¸æå·²å®æ"); } // éªè¯åé æ°é decimal allocatedQty = outboundOrderDetail.AllocatedQuantity; decimal overOutQty = outboundOrderDetail.OverOutQuantity; decimal needOutQty = outboundOrderDetail.NeedOutQuantity; decimal availableQty = needOutQty - allocatedQty - overOutQty; if (availableQty <= 0) throw new Exception("æ å¯åé æ°é"); if (batchQuantity > availableQty) throw new Exception($"åé æ°éä¸è½è¶ è¿å¯åé æ°é{availableQty}"); List<Dt_StockInfo>? stockInfos = null; List<Dt_OutboundOrderDetail>? orderDetails = null; List<Dt_OutStockLockInfo>? outStockLockInfos = null; List<Dt_LocationInfo>? locationInfos = null; // çææ¹æ¬¡å· string batchNo = await GenerateBatchNo(); // åé åºå (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) allocateResult = await _outboundOrderDetailService.AssignStockForBatch(outboundOrderDetail, batchQuantity, batchNo); if (allocateResult.Item1 != null && allocateResult.Item1.Count > 0) { // åå»ºåæ¹è®°å½ await CreateBatchRecord(orderNo, orderDetailId, batchQuantity, batchNo); Dt_OutboundOrder outboundOrder = await _outboundOrderService.Repository.QueryFirstAsync(x => x.Id == outboundOrderDetail.OrderId); TaskTypeEnum typeEnum = outboundOrder.OrderType switch { (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound, (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate, (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality, _ => TaskTypeEnum.Outbound }; tasks = GetTasks(allocateResult.Item1, typeEnum, outStation); tasks.ForEach(x => { x.OrderNo = outboundOrder.OrderNo; }); allocateResult.Item2.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); allocateResult.Item3.ForEach(x => { x.Status = OutLockStockStatusEnum.åºåºä¸.ObjToInt(); }); stockInfos = allocateResult.Item1; orderDetails = allocateResult.Item2; outStockLockInfos = allocateResult.Item3; locationInfos = allocateResult.Item4; } else { throw new Exception("æ åºå"); } return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos); } /// <summary> /// æ´æ°è®¢åæç»ç¶æ /// </summary> private void UpdateOrderDetailStatus(List<Dt_OutboundOrderDetail> details, decimal allocatedQuantity, decimal needQuantity) { foreach (var detail in details) { // æ ¹æ®åé æ 嵿´æ°ç¶æ if (allocatedQuantity >= needQuantity) { detail.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); } else { detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt(); } } } private async Task<string> GenerateBatchNo() { var batchNo = UniqueValueGenerator.Generate(); return $"Out{batchNo} "; } private async Task<Dt_OutboundBatch> CreateBatchRecord(string orderNo, int orderDetailId, decimal batchQuantity, string batchNo) { var batchRecord = new Dt_OutboundBatch { BatchNo = batchNo, OrderNo = orderNo, OrderDetailId = orderDetailId, BatchQuantity = batchQuantity, BatchStatus = (int)BatchStatusEnum.åé ä¸, Operator = App.User.UserName }; await _OutboundBatchRepository.Db.Insertable(batchRecord).ExecuteCommandAsync(); return batchRecord; } #endregion } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj
@@ -7,6 +7,7 @@ </PropertyGroup> <ItemGroup> <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" /> <ProjectReference Include="..\WIDESEA_IAllocateService\WIDESEA_IAllocateService.csproj" /> <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" /> <ProjectReference Include="..\WIDESEA_IInboundService\WIDESEA_IInboundService.csproj" /> ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs
@@ -51,14 +51,14 @@ [HttpPost, Route("Test"), AllowAnonymous, MethodParamsValidate] public async Task<WebResponseContent> Test() { Service.Db.Deleteable<Dt_InboundOrder>().Where(x=>x.UpperOrderNo== "12020251100040").ExecuteCommand(); _inboundService.InboundOrderDetailService.Db.Deleteable<Dt_InboundOrderDetail>() .Where(p => SqlFunc.Subqueryable<Dt_InboundOrder>().Where(s => s.Id == p.OrderId && s.UpperOrderNo == "12020251100040").Any()).ExecuteCommand(); // var purchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("101001-00002", 10); // Service.Db.Deleteable<Dt_InboundOrder>().Where(x=>x.UpperOrderNo== "12020251100040").ExecuteCommand(); //_inboundService.InboundOrderDetailService.Db.Deleteable<Dt_InboundOrderDetail>() // .Where(p => SqlFunc.Subqueryable<Dt_InboundOrder>().Where(s => s.Id == p.OrderId && s.UpperOrderNo == "12020251100040").Any()).ExecuteCommand(); // var pdddurchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("100513-00210", 10); var purchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("100513-00303", 1); var pdddurchaseToStockResult = await _materialUnitService.ConvertFromToStockAsync("100513-00303", "W013", 1); //var sddd = _locationInfoService.AssignLocation(); //var code = sddd.LocationCode; ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/InventoryLockJob.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ using Quartz; using SqlSugar; namespace WIDESEA_WMSServer.Jobs { [DisallowConcurrentExecution] public class InventoryLockJob : IJob { private readonly ILogger<ErpJob> _logger; private readonly ISqlSugarClient _db; public InventoryLockJob(ILogger<ErpJob> logger, ISqlSugarClient db ) { _logger = logger; _db = db; } public Task Execute(IJobExecutionContext context) { return Task.CompletedTask; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs
@@ -161,6 +161,15 @@ .ForJob(jobKey) .WithIdentity("ErpJob-trigger") .WithCronSchedule("0 0 10,14,20 * * ?")); var inventoryLockJobKey = new JobKey("InventoryLockJob"); q.AddJob<InventoryLockJob>(opts => opts.WithIdentity(inventoryLockJobKey)); q.AddTrigger(opts => opts .ForJob(inventoryLockJobKey) .WithIdentity("InventoryLockJob-trigger") .WithCronSchedule("0 0/10 * * * ?")); // æ¯10åéæ§è¡ä¸æ¬¡ });