From a83781bc4e66b8b3bf19fb371d7da7bede0a5b25 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期一, 01 十二月 2025 13:55:45 +0800
Subject: [PATCH] 提交
---
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs | 3721 ++++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 3,043 insertions(+), 678 deletions(-)
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs"
index 93f7d84..08aaba5 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs"
@@ -1,13 +1,16 @@
锘縰sing Dm.filter;
using MailKit.Search;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.Json;
using System.Threading.Tasks;
+using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.OrderEnum;
using WIDESEA_Common.StockEnum;
@@ -15,13 +18,20 @@
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
+using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
+using WIDESEA_Core.Utilities;
+using WIDESEA_DTO.Allocate;
using WIDESEA_DTO.Basic;
+using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Outbound;
+using WIDESEA_IAllocateService;
using WIDESEA_IBasicService;
+using WIDESEA_IInboundService;
using WIDESEA_IOutboundService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
+using WIDESEA_Model.Models.Basic;
namespace WIDESEA_OutboundService
{
@@ -43,7 +53,12 @@
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_InboundOrder> _inboundOrderRepository;
+ private readonly IInboundOrderDetailService _inboundOrderDetailService;
+ private readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository;
private readonly ILogger<OutboundPickingService> _logger;
@@ -61,7 +76,10 @@
};
- public OutboundPickingService(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) : base(BaseDal)
+ public OutboundPickingService(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, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository) : base(BaseDal)
{
_unitOfWorkManage = unitOfWorkManage;
_stockInfoService = stockInfoService;
@@ -75,529 +93,16 @@
_taskRepository = taskRepository;
_eSSApiService = eSSApiService;
_logger = logger;
+ _invokeMESService = invokeMESService;
+ _dailySequenceService = dailySequenceService;
+ _allocateService = allocateService;
+ _inboundOrderRepository = inboundOrderRepository;
+ _inboundOrderDetailService = inboundOrderDetailService;
+ _warehouseAreaRepository = warehouseAreaRepository;
}
- #region 鏌ヨ鍑哄簱璇︽儏鍒楄〃
- public async Task<List<OutStockLockListResp>> GetOutStockLockListAsync(string orderNo)
- {
- var locks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(t => t.OrderNo == orderNo)
- .ToListAsync();
-
- return locks.Select(t => new OutStockLockListResp
- {
- Id = t.Id,
- // TaskNum = t.TaskNum,
- PalletCode = t.PalletCode,
- CurrentBarcode = t.CurrentBarcode,
- AssignQuantity = t.AssignQuantity,
- PickedQty = t.PickedQty,
- Status = t.Status,
- // IsSplitted = t.IsSplitted
- }).ToList();
- }
- #endregion
- public async Task<WebResponseContent> ValidateBarcode(string barcode)
- {
- try
- {
- if (string.IsNullOrEmpty(barcode))
- {
- return WebResponseContent.Instance.Error("鏉$爜涓嶈兘涓虹┖");
- }
-
- // 鏍规嵁鏉$爜鏌ヨ搴撳瓨鏄庣粏
- var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Includes(x => x.StockInfo)
- .Where(x => x.Barcode == barcode)
- .FirstAsync();
-
- if (stockDetail == null)
- {
- return WebResponseContent.Instance.Error("鏉$爜涓嶅瓨鍦�");
- }
-
-
-
- var result = new
- {
- Barcode = barcode,
- MaterielCode = stockDetail.MaterielCode,
-
- BatchNo = stockDetail.BatchNo,
- AvailableQuantity = stockDetail.StockQuantity - stockDetail.OutboundQuantity,
- LocationCode = stockDetail.StockInfo?.LocationCode,
- PalletCode = stockDetail.StockInfo?.PalletCode
- };
-
- return WebResponseContent.Instance.OK(null, result);
- }
- catch (Exception ex)
- {
- return WebResponseContent.Instance.Error($"鏉$爜楠岃瘉澶辫触: {ex.Message}");
- }
- }
-
-
- public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
- {
- try
- {
- _unitOfWorkManage.BeginTran();
- var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(it => it.OrderNo == orderNo &&
- it.Status == (int)OutLockStockStatusEnum.鍑哄簱涓� &&
- it.PalletCode == palletCode &&
- it.CurrentBarcode == barcode)
- .FirstAsync();
-
- if (lockInfo == null)
- {
- var splitBarcode = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
- .Where(it => it.NewBarcode == barcode && it.Status == 1)
- .FirstAsync();
-
- if (splitBarcode != null)
- {
- // 閫氳繃鎷嗗寘鏉$爜璁板綍鎵惧埌瀵瑰簲鐨勫嚭搴撻攣瀹氳褰�
- lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(it => it.ParentLockId == splitBarcode.OutStockLockInfoId)
- .FirstAsync();
-
- if (lockInfo == null)
- throw new Exception($"鏈壘鍒版媶鍖呮潯鐮亄barcode}瀵瑰簲鐨勫嚭搴撻攣瀹氳褰�");
- }
- else
- {
- throw new Exception($"鏉$爜{barcode}涓嶅睘浜庢墭鐩榹palletCode}鎴栦笉瀛樺湪寰呭垎鎷h褰�");
- }
-
- }
- if (lockInfo.PalletCode != palletCode)
- throw new Exception($"鏉$爜{barcode}涓嶅睘浜庢墭鐩榹palletCode}");
-
- var outorderdetail = _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().First(x => x.Id == lockInfo.OrderDetailId);
- if (outorderdetail != null && lockInfo.AssignQuantity > outorderdetail.OrderQuantity)
- {
- throw new Exception($"鏉$爜{barcode}鐨勫嚭搴撴暟閲忓ぇ浜庤鍗曠殑鏁伴噺");
- }
-
- var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
- .FirstAsync();
-
- if (stockDetail == null)
- return WebResponseContent.Instance.Error("鏃犳晥鐨勬潯鐮佹垨鐗╂枡缂栫爜");
-
-
- decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
-
- // 4. 鏇存柊搴撳瓨
- stockDetail.StockQuantity -= actualQty;
- stockDetail.OutboundQuantity -= actualQty;
- await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
-
- lockInfo.PickedQty += actualQty;
- lockInfo.Status = (int)OutLockStockStatusEnum.鎷i�夊畬鎴�;
- await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
-
- var splitBarcodeRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
- .Where(it => it.NewBarcode == barcode)
- .FirstAsync();
-
- if (splitBarcodeRecord != null)
- {
- splitBarcodeRecord.Status = 2;
- await _splitPackageService.Db.Updateable(splitBarcodeRecord).ExecuteCommandAsync();
- }
-
- await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
- .SetColumns(it => it.PickedQty == it.PickedQty + actualQty)
- .Where(it => it.Id == lockInfo.OrderDetailId)
- .ExecuteCommandAsync();
-
-
- await CheckAndUpdateOrderStatus(orderNo);
-
- //鏌ヨ浠诲姟琛�
- var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
- // 9. 璁板綍鎷i�夊巻鍙�
- var pickingHistory = new Dt_PickingRecord
- {
- FactoryArea = lockInfo.FactoryArea,
- TaskNo = task?.TaskNum ?? 0,
- LocationCode = task?.SourceAddress ?? "",
- StockId = stockDetail.Id,
- OrderNo = orderNo,
- OrderDetailId = lockInfo.OrderDetailId,
- PalletCode = palletCode,
- Barcode = barcode,
- MaterielCode = lockInfo.MaterielCode,
- PickQuantity = lockInfo.AssignQuantity,
- PickTime = DateTime.Now,
- Operator = App.User.UserName,
- OutStockLockId = lockInfo.Id
- };
- await Db.Insertable(pickingHistory).ExecuteCommandAsync();
-
- _unitOfWorkManage.CommitTran();
- return WebResponseContent.Instance.OK("鎷i�夌‘璁ゆ垚鍔�");
-
- }
- catch (Exception ex)
- {
- return WebResponseContent.Instance.Error($"鎷i�夌‘璁ゅけ璐ワ細{ex.Message}");
- }
- }
- // 妫�鏌ュ苟鏇存柊璁㈠崟鐘舵��
- private async Task CheckAndUpdateOrderStatus(string orderNo)
- {
-
- var orderDetails = _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
- .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) // 鍏宠仈鏉′欢锛氱埗琛� Id = 瀛愯〃 OrderId
- .Where((o, item) => item.OrderNo == orderNo) // 杩囨护鐖惰〃 OrderNo
- .Select((o, item) => o) // 鍙繑鍥炲瓙琛ㄦ暟鎹�
- .ToList();
-
- //var orderDetails = await _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
- // .Where(x => x.OrderId == orderNo.ObjToInt())
- // .ToListAsync();
-
- bool allCompleted = true;
- foreach (var detail in orderDetails)
- {
- if (detail.OverOutQuantity < detail.NeedOutQuantity)
- {
- allCompleted = false;
- break;
- }
- }
-
- if (allCompleted)
- {
- await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
- .SetColumns(x => x.OrderStatus == 2) // 宸插畬鎴�
- .Where(x => x.OrderNo == orderNo)
- .ExecuteCommandAsync();
-
- //鍥炰紶MES
- }
- }
-
-
- /// <summary>
- /// 鍥炲簱鎿嶄綔
- /// </summary>
-
- public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
- {
- try
- {
- // 鑾峰彇鎵�鏈夋湭鍒嗘嫞鐨勫嚭搴撻攣瀹氳褰曪紝鍖呮嫭鎷嗗寘浜х敓鐨勮褰�
- var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(it => it.OrderNo == orderNo && it.Status == 1)
- .ToListAsync();
-
- var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
-
-
- var tasks = new List<Dt_Task>();
-
- // 鏌ヨ浠诲姟琛�
- var task = remainingLocks.Any()
- ? _taskRepository.QueryData(x => x.TaskNum == remainingLocks.First().TaskNum).FirstOrDefault()
- : _taskRepository.QueryData(x => x.PalletCode == palletCode).FirstOrDefault();
-
- if (task == null)
- {
- return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟淇℃伅");
- }
-
- // 妫�鏌ユ墭鐩樹笂鏄惁鏈夊叾浠栭潪鍑哄簱璐х墿锛堝簱瀛樿揣鐗╋級
- var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Where(it => it.StockId == stockinfo.Id &&( it.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() || it.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt()))
- .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) // 鏈畬鍏ㄥ嚭搴撶殑
- .ToListAsync();
-
- // 濡傛灉娌℃湁闇�瑕佸洖搴撶殑璐х墿锛堟棦鏃犳湭鍒嗘嫞鍑哄簱璐х墿锛屼篃鏃犲叾浠栧簱瀛樿揣鐗╋級
- if (!remainingLocks.Any() && !palletStockGoods.Any())
- {
- //鏄惁鑷姩鍥炲簱锛屾妸涔嬪墠鍑哄簱鐨勪换鍔″垹闄わ紝鐒跺悗缁勪釜绌虹洏鍏ュ簱銆�
- return WebResponseContent.Instance.Error("娌℃湁闇�瑕佸洖搴撶殑鍓╀綑璐х墿");
- }
-
-
- var firstlocation = _locationInfoService.Db.Queryable<Dt_LocationInfo>().First(x => x.LocationCode == task.SourceAddress);
- decimal totalReturnQty = 0;
- var hasRemainingLocks = remainingLocks.Any(x => x.PalletCode == palletCode);
-
- // 鎯呭喌1锛氬鐞嗘湭鍒嗘嫞鐨勫嚭搴撻攣瀹氳褰�
- if (hasRemainingLocks)
- {
- var palletLocks = remainingLocks.Where(x => x.PalletCode == palletCode).ToList();
- totalReturnQty = palletLocks.Sum(x => x.AssignQuantity - x.PickedQty);
-
- if (totalReturnQty > 0)
- {
- // 鍒嗛厤鏂拌揣浣�
- var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
-
- // 鏇存柊鍑哄簱閿佸畾璁板綍鐘舵��
- var lockIds = palletLocks.Select(x => x.Id).ToList();
- await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
- .SetColumns(it => new Dt_OutStockLockInfo { Status = OutLockStockStatusEnum.鍥炲簱涓�.ObjToInt() })
- .Where(it => lockIds.Contains(it.Id))
- .ExecuteCommandAsync();
-
- // 鏇存柊鎷嗗寘鏉$爜璁板綍鐘舵��
- var splitBarcodes = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
- .Where(it => lockIds.Contains(it.OutStockLockInfoId))
- .ToListAsync();
-
- foreach (var splitBarcode in splitBarcodes)
- {
- splitBarcode.Status = 3;
- await _splitPackageService.Db.Updateable(splitBarcode).ExecuteCommandAsync();
- }
-
- // 澶勭悊搴撳瓨璁板綍
- foreach (var lockInfo in palletLocks)
- {
- decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
-
- // 妫�鏌ュ簱瀛樿褰曟槸鍚﹀瓨鍦�
- var existingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
- .FirstAsync();
-
- if (existingStock != null)
- {
- // 搴撳瓨璁板綍瀛樺湪锛屾仮澶嶉攣瀹氭暟閲�
- await _stockInfoDetailService.Db.Updateable<Dt_StockInfoDetail>()
- .SetColumns(it => new Dt_StockInfoDetail
- {
- OutboundQuantity = it.OutboundQuantity - returnQty
- })
- .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
- .ExecuteCommandAsync();
- }
- else
- {
- // 搴撳瓨璁板綍涓嶅瓨鍦紙鍙兘鏄媶鍖呬骇鐢熺殑鏂版潯鐮侊級锛屽垱寤烘柊鐨勫簱瀛樿褰�
- var newStockDetail = new Dt_StockInfoDetail
- {
- StockId = lockInfo.StockId,
- MaterielCode = lockInfo.MaterielCode,
- OrderNo = lockInfo.OrderNo,
- BatchNo = lockInfo.BatchNo,
- StockQuantity = returnQty,
- OutboundQuantity = 0,
- Barcode = lockInfo.CurrentBarcode,
- InboundOrderRowNo = "0",
- Status = StockStatusEmun.鍏ュ簱纭.ObjToInt(),
-
- };
- await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
- }
- }
-
- // 鍒涘缓鍥炲簱浠诲姟
- CreateReturnTask(tasks, task, palletCode, newLocation);
- }
- }
-
- // 鎯呭喌2锛氬嚭搴撹揣鐗╁凡鍒嗘嫞瀹岋紝浣嗘墭鐩樹笂杩樻湁鍏朵粬搴撳瓨璐х墿闇�瑕佸洖搴�
- if (!hasRemainingLocks && palletStockGoods.Any())
- {
- // 鍒嗛厤鏂拌揣浣�
- var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
-
- // 鍒涘缓鍥炲簱浠诲姟
- CreateReturnTask(tasks, task, palletCode, newLocation);
-
- totalReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity);
- }
-
- // 淇濆瓨浠诲姟
- if (tasks.Any())
- {
- try
- {
- await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync();
- var targetAddress = task.TargetAddress;
- _taskRepository.DeleteData(task);
- // 缁� ESS 娴佸姩淇″彿鍜屽垱寤轰换鍔�
- try
- {
- var result = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
- {
- slotCode = movestations[targetAddress],
- containerCode = palletCode
- });
- if (result)
- {
- TaskModel esstask = new TaskModel()
- {
- taskType = "putaway",
- taskGroupCode = "",
- groupPriority = 0,
- tasks = new List<TasksType>
- {
- new()
- {
- taskCode = tasks.First().TaskNum.ToString(),
- taskPriority = 0,
- taskDescribe = new TaskDescribeType {
- containerCode = palletCode,
- containerType = "CT_KUBOT_STANDARD",
- fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
- toStationCode = "",
- toLocationCode = tasks.First().TargetAddress,
- deadline = 0, storageTag = ""
- }
- }
- }
- };
-
-
- var resulttask = await _eSSApiService.CreateTaskAsync(esstask);
-
- _logger.LogInformation("ReturnRemaining 鍒涘缓浠诲姟杩斿洖: " + resulttask);
- }
- }
- catch (Exception ex)
- {
- _logger.LogInformation("ReturnRemaining 鍒涘缓浠诲姟杩斿洖 catch err: " + ex.Message);
-
- }
-
-
- return WebResponseContent.Instance.OK($"鍥炲簱鎿嶄綔鎴愬姛锛屽叡鍥炲簱鏁伴噺锛歿totalReturnQty}");
- }
- catch (Exception ex)
- {
- return WebResponseContent.Instance.Error($"鍒涘缓鍥炲簱浠诲姟澶辫触: {ex.Message}");
- }
- }
-
- return WebResponseContent.Instance.Error("鏈垱寤轰换浣曞洖搴撲换鍔�");
- }
- catch (Exception ex)
- {
- return WebResponseContent.Instance.Error($"鍥炲簱鎿嶄綔澶辫触: {ex.Message}");
- }
- }
-
- /// <summary>
- /// 鍒涘缓鍥炲簱浠诲姟
- /// </summary>
- private void CreateReturnTask(List<Dt_Task> tasks, Dt_Task originalTask, string palletCode, Dt_LocationInfo newLocation)
- {
- Dt_Task newTask = new()
- {
- CurrentAddress = stations[originalTask.TargetAddress],
- Grade = 0,
- PalletCode = palletCode,
- NextAddress = "",
- OrderNo = originalTask.OrderNo,
- Roadway = newLocation.RoadwayNo,
- SourceAddress = stations[originalTask.TargetAddress],
- TargetAddress = newLocation.LocationCode,
- TaskStatus = TaskStatusEnum.New.ObjToInt(),
- TaskType = TaskTypeEnum.InPick.ObjToInt(),
- PalletType = originalTask.PalletType,
- WarehouseId = originalTask.WarehouseId,
-
- };
- tasks.Add(newTask);
- }
-
- /// <summary>
- /// 妫�鏌ユ墭鐩樻槸鍚﹂渶瑕佸洖搴撶殑杈呭姪鏂规硶
- /// </summary>
- public async Task<bool> CheckPalletNeedReturn(string orderNo, string palletCode)
- {
- // 1. 妫�鏌ユ槸鍚︽湁鏈垎鎷g殑鍑哄簱璁板綍
- var hasUnpickedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && it.Status == 1)
- .AnyAsync();
-
- if (hasUnpickedLocks)
- return true;
-
- // 2. 妫�鏌ュ嚭搴撴槸鍚﹀凡瀹屾垚浣嗘墭鐩樿繕鏈夊簱瀛樿揣鐗�
- var outboundFinished = !await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(it => it.PalletCode == palletCode && it.Status == 1)
- .AnyAsync();
-
- var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
-
-
- var hasRemainingGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Where(it => it.StockId == stockinfo.Id && it.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt())
- .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity)
- .AnyAsync();
-
- return outboundFinished && hasRemainingGoods;
- }
-
- // 鍙栨秷鎷i�夊姛鑳�
- public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode)
- {
- try
- {
- _unitOfWorkManage.BeginTran();
- // 鏌ユ壘鎷i�夎褰�
- var outStockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(x => x.OrderNo == orderNo &&
- x.PalletCode == palletCode &&
- x.CurrentBarcode == barcode &&
- x.Status == 6)
- .FirstAsync();
-
- if (outStockInfo == null)
- return WebResponseContent.Instance.Error("鏈壘鍒板凡鎷i�夎褰�");
-
- // 杩樺師鍑哄簱璇︽儏鐘舵��
- outStockInfo.PickedQty = 0;
- outStockInfo.Status = 1;
- await _outStockLockInfoService.Db.Updateable(outStockInfo).ExecuteCommandAsync();
-
-
- var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Where(x => x.Barcode == barcode && x.StockId == outStockInfo.StockId)
- .FirstAsync();
-
- stockDetail.StockQuantity += outStockInfo.AssignQuantity;
- stockDetail.OutboundQuantity += outStockInfo.AssignQuantity;
- await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
-
-
- // 杩樺師鍑哄簱鍗曟槑缁�
- var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
- .Where(x => x.Id == outStockInfo.OrderDetailId)
- .FirstAsync();
-
- orderDetail.OverOutQuantity -= outStockInfo.AssignQuantity;
- orderDetail.PickedQty = 0;
- await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
-
- // 鍒犻櫎鎷i�夊巻鍙�
- await Db.Deleteable<Dt_PickingRecord>()
- .Where(x => x.OutStockLockId == outStockInfo.Id)
- .ExecuteCommandAsync();
-
- _unitOfWorkManage.CommitTran();
- return WebResponseContent.Instance.OK("鍙栨秷鎷i�夋垚鍔�");
-
- }
- catch (Exception ex)
- {
- return WebResponseContent.Instance.Error($"鍙栨秷鎷i�夊け璐ワ細{ex.Message}");
- }
- }
-
+ #region 鏌ヨ鏂规硶
// 鑾峰彇鏈嫞閫夊垪琛�
public async Task<List<Dt_OutStockLockInfo>> GetUnpickedList(string orderNo, string palletCode)
{
@@ -659,186 +164,3046 @@
return summary;
}
+
+ #endregion
+
+ #region 鏍稿績涓氬姟娴佺▼
/// <summary>
- /// 鑾峰彇鎷i�夊巻鍙�
+ /// 鎷i��
/// </summary>
- public async Task<List<Dt_PickingRecord>> GetPickingHistory(int orderId)
- {
- // 閫氳繃鍑哄簱鍗旾D鏌ヨ鐩稿叧鐨勬嫞閫夊巻鍙�
- // 娉ㄦ剰锛欴t_PickingRecord 涓病鏈夌洿鎺ュ瓨鍌∣rderId锛岄渶瑕侀�氳繃鍑哄簱鍗曟槑缁嗗叧鑱�
- var detailIds = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
- .Where(d => d.OrderId == orderId)
- .Select(d => d.Id)
- .ToListAsync();
-
- return await Db.Queryable<Dt_PickingRecord>()
- .Where(p => detailIds.Contains(p.OrderDetailId))
- .OrderByDescending(p => p.PickTime)
- .ToListAsync();
- }
-
-
- /// <summary>
- /// 鑾峰彇鎵樼洏鐨勫嚭搴撶姸鎬佷俊鎭�
- /// </summary>
- public async Task<WebResponseContent> GetPalletOutboundStatus(string palletCode)
- {
- // 鑾峰彇鎵樼洏鐨勯攣瀹氫俊鎭�
- var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(x => x.PalletCode == palletCode)
- .ToListAsync();
-
- // 鑾峰彇鎵樼洏搴撳瓨淇℃伅
- var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
- .Includes(x => x.Details)
- .Where(x => x.PalletCode == palletCode)
- .FirstAsync();
-
- if (stockInfo == null)
- return WebResponseContent.Instance.Error("鏈壘鍒版墭鐩樹俊鎭�");
-
- // 璁$畻鍚勭鏁伴噺
- var totalStockQuantity = stockInfo.Details.Sum(x => x.StockQuantity);
- var totalOutboundQuantity = stockInfo.Details.Sum(x => x.OutboundQuantity);
- var totalLockedQuantity = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
- .Sum(x => x.AssignQuantity - x.PickedQty);
- var totalPickedQuantity = lockInfos.Sum(x => x.PickedQty);
-
- var result = new
- {
- PalletCode = palletCode,
- LocationCode = stockInfo.LocationCode,
- StockStatus = stockInfo.StockStatus,
- TotalStockQuantity = totalStockQuantity,
- TotalOutboundQuantity = totalOutboundQuantity,
- TotalLockedQuantity = totalLockedQuantity,
- TotalPickedQuantity = totalPickedQuantity,
- AvailableQuantity = totalStockQuantity - totalOutboundQuantity,
- LockInfos = lockInfos.Select(x => new
- {
- x.Id,
- x.MaterielCode,
- x.OrderDetailId,
- x.AssignQuantity,
- x.PickedQty,
- x.Status,
- x.CurrentBarcode,
- x.IsSplitted
- }).ToList(),
- StockDetails = stockInfo.Details.Select(x => new
- {
- x.Barcode,
- x.MaterielCode,
- StockQuantity = x.StockQuantity,
- OutboundQuantity = x.OutboundQuantity,
- AvailableQuantity = x.StockQuantity - x.OutboundQuantity
- }).ToList()
- };
-
- return WebResponseContent.Instance.OK(null, result);
- }
-
- /// <summary>
- /// 鐩存帴鍑哄簱 - 鏁翠釜鎵樼洏鍑哄簱锛屾竻绌哄簱瀛�
- /// </summary>
- public async Task<WebResponseContent> DirectOutbound(DirectOutboundRequest request)
+ /// <param name="orderNo"></param>
+ /// <param name="palletCode"></param>
+ /// <param name="barcode"></param>
+ /// <returns></returns>
+ public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
{
try
{
_unitOfWorkManage.BeginTran();
- var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
- .Includes(x => x.Details)
- .Where(x => x.PalletCode == request.PalletCode).FirstAsync();
+ var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode);
+ if (!validationResult.IsValid)
+ return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
- if (stockInfo == null)
- return WebResponseContent.Instance.Error("鏈壘鍒版墭鐩樺簱瀛樹俊鎭�");
+ var (lockInfo, orderDetail, stockDetail) = validationResult.Data;
+ // 璁$畻瀹為檯鎷i�夋暟閲�
+ var quantityResult = await CalculateActualPickingQuantity(lockInfo, orderDetail, stockDetail);
+ if (!quantityResult.IsValid)
+ return WebResponseContent.Instance.Error(quantityResult.ErrorMessage);
- var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
- .Where(x => x.OrderNo == request.OrderNo && x.PalletCode == request.PalletCode)
- .ToListAsync();
+ var (actualQty, adjustedReason) = quantityResult.Data;
-
- foreach (var lockInfo in lockInfos)
+ var overPickingValidation = await ValidateOverPicking(orderDetail.Id, actualQty);
+ if (!overPickingValidation.IsValid)
{
- if (lockInfo.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
- {
- lockInfo.PickedQty = lockInfo.AssignQuantity;
- }
- lockInfo.Status = (int)OutLockStockStatusEnum.宸插嚭搴�;
- await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
-
- var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
- .Where(x => x.Id == lockInfo.OrderDetailId)
- .FirstAsync();
- if (orderDetail != null)
- {
- orderDetail.OverOutQuantity += lockInfo.PickedQty;
- orderDetail.LockQuantity -= lockInfo.PickedQty;
- orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
- orderDetail.LockQuantity = 0;
- await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
- }
- }
- var groupDetails = lockInfos.GroupBy(x => x.OrderDetailId).Select(x => new
- {
- OrderDetailId = x.Key,
- TotalQuantity = x.Sum(o => o.PickedQty)
- }).ToList();
- foreach (var item in groupDetails)
- {
- var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().Where(x => x.Id == item.OrderDetailId).FirstAsync();
- if (orderDetail != null)
- {
- orderDetail.OverOutQuantity = item.TotalQuantity;
- orderDetail.LockQuantity = 0;
- orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
- await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
- }
+ return WebResponseContent.Instance.Error(overPickingValidation.ErrorMessage);
}
- await CheckAndUpdateOrderStatus(request.OrderNo);
+ // 鎵ц鍒嗘嫞閫昏緫锛堝彧澶勭悊搴撳瓨鍜岄攣瀹氾紝涓嶅鐞嗚鍗曪級
+ var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, orderNo, palletCode, barcode, actualQty);
- var lockInfoIds = lockInfos.Select(x => x.Id).ToList();
- var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
- .Where(x => lockInfoIds.Contains(x.OutStockLockInfoId) &&
- x.Status == (int)SplitPackageStatusEnum.宸叉媶鍖�)
- .ToListAsync();
+ // 缁熶竴鏇存柊璁㈠崟鏁版嵁锛堟墍鏈夊垎鏀兘鍦ㄨ繖閲屾洿鏂帮級
+ await UpdateOrderRelatedData(orderDetail.Id, pickingResult.ActualPickedQty, orderNo);
- foreach (var record in splitRecords)
- {
- record.Status = (int)SplitPackageStatusEnum.宸叉嫞閫�;
- await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
- }
-
-
- var location = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
- .Where(x => x.LocationCode == stockInfo.LocationCode)
- .FirstAsync();
- if (location != null)
- {
- location.LocationStatus = (int)LocationStatusEnum.Free;
- await _locationInfoService.Db.Updateable(location).ExecuteCommandAsync();
- }
-
- foreach (var detail in stockInfo.Details)
- {
- await _stockInfoDetailService.Db.Deleteable(detail).ExecuteCommandAsync();
- }
- await _stockInfoService.Db.Deleteable(stockInfo).ExecuteCommandAsync();
-
-
+ // 璁板綍鎿嶄綔鍘嗗彶
+ await RecordPickingHistory(pickingResult, orderNo, palletCode);
_unitOfWorkManage.CommitTran();
- return WebResponseContent.Instance.OK("鐩存帴鍑哄簱鎴愬姛");
+
+ return CreatePickingResponse(pickingResult, adjustedReason);
}
catch (Exception ex)
{
_unitOfWorkManage.RollbackTran();
- return WebResponseContent.Instance.Error($"鐩存帴鍑哄簱澶辫触: {ex.Message}");
+ _logger.LogError($"ConfirmPicking澶辫触 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
+ return WebResponseContent.Instance.Error($"鎷i�夌‘璁ゅけ璐ワ細{ex.Message}");
}
+ }
+
+ /// <summary>
+ /// 鍙栨秷鎷i��
+ /// </summary>
+ /// <param name="orderNo"></param>
+ /// <param name="palletCode"></param>
+ /// <param name="barcode"></param>
+ /// <returns></returns>
+ public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode)
+ {
+ try
+ {
+ if (await IsPalletReturned(palletCode))
+ {
+ return WebResponseContent.Instance.Error($"鎵樼洏{palletCode}宸茬粡鍥炲簱锛屼笉鑳藉彇娑堝垎鎷�");
+ }
+ _unitOfWorkManage.BeginTran();
+
+ // 鍓嶇疆楠岃瘉
+ var validationResult = await ValidateCancelRequest(orderNo, palletCode, barcode);
+ if (!validationResult.IsValid)
+ return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
+
+ var (pickingRecord, lockInfo, orderDetail) = validationResult.Data;
+
+ //鎵ц鍙栨秷閫昏緫
+ await ExecuteCancelLogic(lockInfo, pickingRecord, orderDetail, orderNo);
+
+ _unitOfWorkManage.CommitTran();
+
+ return WebResponseContent.Instance.OK($"鍙栨秷鍒嗘嫞鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ _logger.LogError($"CancelPicking澶辫触 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
+ return WebResponseContent.Instance.Error($"鍙栨秷鍒嗘嫞澶辫触锛歿ex.Message}");
+ }
+ }
+ /// <summary>
+ /// 鍥炲簱
+ /// </summary>
+ /// <param name="orderNo"></param>
+ /// <param name="palletCode"></param>
+ /// <param name="reason"></param>
+ /// <returns></returns>
+ public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
+ {
+ try
+ {
+ _unitOfWorkManage.BeginTran();
+
+
+ if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode))
+ return WebResponseContent.Instance.Error("璁㈠崟鍙峰拰鎵樼洏鐮佷笉鑳戒负绌�");
+
+ // 鑾峰彇搴撳瓨鍜屼换鍔′俊鎭�
+ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode);
+
+ if (stockInfo == null)
+ return WebResponseContent.Instance.Error($"鏈壘鍒版墭鐩� {palletCode} 瀵瑰簲鐨勫簱瀛樹俊鎭�");
+
+ var task = await GetCurrentTask(orderNo, palletCode);
+ if (task == null)
+ return WebResponseContent.Instance.Error("鏈壘鍒板搴旂殑浠诲姟淇℃伅");
+
+ //鍒嗘瀽闇�瑕佸洖搴撶殑璐х墿
+ //var returnAnalysis = await AnalyzeReturnItems(orderNo, palletCode, stockInfo.Id);
+ //if (!returnAnalysis.HasItemsToReturn)
+ // return await HandleNoReturnItems(orderNo, palletCode, task);
+
+ var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
+ if (!statusAnalysis.HasItemsToReturn)
+ return await HandleNoReturnItems(orderNo, palletCode, task, stockInfo.Id);
+
+ // 4. 妫�鏌ユ槸鍚︽湁杩涜涓殑浠诲姟
+ if (statusAnalysis.HasActiveTasks)
+ {
+ return WebResponseContent.Instance.Error($"鎵樼洏 {palletCode} 鏈夎繘琛屼腑鐨勪换鍔★紝涓嶈兘鎵ц鍥炲簱鎿嶄綔");
+ }
+
+ //鎵ц鍥炲簱鎿嶄綔
+ await ExecuteReturnOperations(orderNo, palletCode, stockInfo, task, statusAnalysis);
+
+ await ReleaseAllLocksForReallocation(orderNo, palletCode, statusAnalysis);
+
+ _unitOfWorkManage.CommitTran();
+
+
+ // 鍒涘缓鍥炲簱浠诲姟
+ await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, TaskTypeEnum.InPick, task.PalletType);
+
+ // 鏇存柊璁㈠崟鐘舵�侊紙涓嶈Е鍙慚ES鍥炰紶锛�
+ await UpdateOrderStatusForReturn(orderNo);
+
+ return WebResponseContent.Instance.OK($"鍥炲簱鎿嶄綔鎴愬姛");
+ //return WebResponseContent.Instance.OK($"鍥炲簱鎿嶄綔鎴愬姛锛屽叡鍥炲簱鏁伴噺锛歿statusAnalysis.TotalReturnQty}");
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ _logger.LogError($"ReturnRemaining澶辫触 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
+ return WebResponseContent.Instance.Error($"鍥炲簱鎿嶄綔澶辫触: {ex.Message}");
+ }
+ }
+
+ /// <summary>
+ /// 绌烘墭鐩樺彇璧版帴鍙o紙甯﹁鍗曞彿锛�
+ /// 楠岃瘉鎵樼洏鏄惁鐪熺殑涓虹┖锛屾竻鐞嗘暟鎹紝鏇存柊璁㈠崟鐘舵�侊紝鍒涘缓鍙栨墭鐩樹换鍔�
+ /// </summary>
+ public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode)
+ {
+ try
+ {
+ _unitOfWorkManage.BeginTran();
+
+ if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode))
+ return WebResponseContent.Instance.Error("璁㈠崟鍙峰拰鎵樼洏鐮佷笉鑳戒负绌�");
+
+ // 妫�鏌ヨ鍗曟槸鍚﹀瓨鍦�
+ var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+ .Where(x => x.OrderNo == orderNo)
+ .FirstAsync();
+
+ if (order == null)
+ return WebResponseContent.Instance.Error($"鏈壘鍒拌鍗� {orderNo}");
+
+ //妫�鏌ユ墭鐩樻槸鍚﹀瓨鍦ㄤ笖灞炰簬璇ヨ鍗�
+ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ return WebResponseContent.Instance.Error($"鏈壘鍒版墭鐩� {palletCode} 瀵瑰簲鐨勫簱瀛樹俊鎭�");
+
+ var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
+
+ if (!statusAnalysis.CanRemove)
+ {
+ if (!statusAnalysis.IsEmptyPallet)
+ {
+ return WebResponseContent.Instance.Error($"鎵樼洏 {palletCode} 涓婅繕鏈夎揣鐗╋紝涓嶈兘鍙栬蛋");
+ }
+ if (statusAnalysis.HasActiveTasks)
+ {
+ return WebResponseContent.Instance.Error($"鎵樼洏 {palletCode} 杩樻湁杩涜涓殑浠诲姟锛屼笉鑳藉彇璧�");
+ }
+ }
+ // 娓呯悊闆跺簱瀛樻暟鎹�
+ await CleanupZeroStockData(stockInfo.Id);
+
+ // 鍒犻櫎鎴栧彇娑堢浉鍏充换鍔�
+ await HandleTaskCleanup(orderNo, palletCode);
+
+ // 鏇存柊璁㈠崟鐩稿叧鏁版嵁
+ await UpdateOrderData(orderNo, palletCode);
+
+
+ _unitOfWorkManage.CommitTran();
+
+ _logger.LogInformation($"绌烘墭鐩樺彇璧版搷浣滄垚鍔� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}, 鎿嶄綔浜�: {App.User.UserName}");
+
+ return WebResponseContent.Instance.OK("绌烘墭鐩樺彇璧版搷浣滄垚鍔�");
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ _logger.LogError($"RemoveEmptyPallet澶辫触 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
+ return WebResponseContent.Instance.Error($"绌烘墭鐩樺彇璧板け璐�: {ex.Message}");
+ }
+ }
+ #endregion
+
+ #region 鍒嗘嫞纭绉佹湁鏂规硶
+
+ private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>> ValidatePickingRequest(string orderNo, string palletCode, string barcode)
+ {
+ // 1. 鍩虹鍙傛暟楠岃瘉
+ if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode) || string.IsNullOrEmpty(barcode))
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("璁㈠崟鍙枫�佹墭鐩樼爜鍜屾潯鐮佷笉鑳戒负绌�");
+
+ // 2. 鏌ユ壘鏈夋晥鐨勯攣瀹氫俊鎭�
+ var lockInfo = await FindValidLockInfo(orderNo, palletCode, barcode);
+ if (lockInfo == null)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"鏈壘鍒版湁鏁堢殑閿佸畾淇℃伅");
+
+ // 3. 妫�鏌ヨ鍗曠姸鎬�
+ var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+ .Where(x => x.OrderNo == orderNo)
+ .FirstAsync();
+
+ if (order?.OrderStatus == (int)OutOrderStatusEnum.鍑哄簱瀹屾垚)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"璁㈠崟{orderNo}宸插畬鎴愶紝涓嶈兘缁х画鍒嗘嫞");
+
+ // 4. 鑾峰彇璁㈠崟鏄庣粏
+ var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
+
+ if (orderDetail == null)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"鏈壘鍒拌鍗曟槑缁�");
+
+ // 5. 妫�鏌ヨ鍗曟槑缁嗘暟閲�
+ if (orderDetail.OverOutQuantity >= orderDetail.NeedOutQuantity)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"璁㈠崟鏄庣粏闇�姹傛暟閲忓凡婊¤冻");
+
+ // 6. 鑾峰彇搴撳瓨鏄庣粏
+ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId &&
+ x.Status != StockStatusEmun.鍏ュ簱纭.ObjToInt())
+ .FirstAsync();
+
+ if (stockDetail == null)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"鏃犳晥鐨勬潯鐮佹垨鐗╂枡缂栫爜");
+
+ // 7. 妫�鏌ュ簱瀛樼姸鎬佸拰鏁伴噺
+ if (stockDetail.StockQuantity <= 0)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"鏉$爜{barcode}搴撳瓨涓嶈冻");
+
+ if (stockDetail.Status != StockStatusEmun.鍑哄簱閿佸畾.ObjToInt())
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"鏉$爜{barcode}鐘舵�佷笉姝g‘锛屾棤娉曞垎鎷�");
+
+ // 8. 妫�鏌ユ槸鍚﹂噸澶嶅垎鎷�
+ var existingPicking = await Db.Queryable<Dt_PickingRecord>()
+ .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && x.PalletCode == palletCode && x.OutStockLockId == lockInfo.Id)
+ .FirstAsync();
+
+ if (existingPicking != null)
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"鏉$爜{barcode}宸茬粡鍒嗘嫞杩�");
+
+ return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Success((lockInfo, orderDetail, stockDetail));
+ }
+
+ private async Task<Dt_OutStockLockInfo> FindValidLockInfo(string orderNo, string palletCode, string barcode)
+ {
+ // 浼樺厛鏌ユ壘绮剧‘鍖归厤鐨勮褰�
+ var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(it => it.OrderNo == orderNo &&
+ it.Status == (int)OutLockStockStatusEnum.鍑哄簱涓� &&
+ it.PalletCode == palletCode &&
+ it.CurrentBarcode == barcode &&
+ it.AssignQuantity > it.PickedQty).FirstAsync();
+
+ if (lockInfo == null)
+ {
+ // 鏌ユ壘鍚屼竴璁㈠崟涓嬬殑璁板綍
+ lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(it => it.OrderNo == orderNo && it.CurrentBarcode == barcode && it.Status == (int)OutLockStockStatusEnum.鍑哄簱涓� && it.AssignQuantity > it.PickedQty).FirstAsync();
+
+ if (lockInfo == null)
+ {
+ // 妫�鏌ユ槸鍚﹀凡缁忓畬鎴愬垎鎷�
+ var completedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(it => it.CurrentBarcode == barcode &&
+ (it.Status == (int)OutLockStockStatusEnum.鎷i�夊畬鎴� ||
+ it.PickedQty >= it.AssignQuantity)).FirstAsync();
+
+ if (completedLockInfo != null)
+ throw new Exception($"鏉$爜{barcode}宸茬粡瀹屾垚鍒嗘嫞锛屼笉鑳介噸澶嶅垎鎷�");
+ else
+ return null;
+ }
+ }
+
+ return lockInfo;
+ }
+
+ private async Task<ValidationResult<(decimal, string)>> CalculateActualPickingQuantity(
+ Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail)
+ {
+ decimal plannedQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
+ decimal remainingOrderQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity;
+ decimal stockQuantity = stockDetail.StockQuantity;
+
+ if (plannedQty <= 0)
+ {
+ return ValidationResult<(decimal, string)>.Error($"璁″垝鎷i�夋暟閲忓繀椤诲ぇ浜�0锛屽綋鍓�: {plannedQty}");
+ }
+
+ if (remainingOrderQty <= 0)
+ {
+ return ValidationResult<(decimal, string)>.Error($"璁㈠崟鍓╀綑闇�姹傛暟閲忓繀椤诲ぇ浜�0锛屽綋鍓�: {remainingOrderQty}");
+ }
+
+ if (stockQuantity <= 0)
+ {
+ return ValidationResult<(decimal, string)>.Error($"搴撳瓨鏁伴噺蹇呴』澶т簬0锛屽綋鍓�: {stockQuantity}");
+ }
+ // 涓夐噸妫�鏌ワ細鍙栨渶灏忓��
+ decimal actualQty = plannedQty;
+ string adjustedReason = null;
+
+ if (plannedQty > remainingOrderQty)
+ {
+ actualQty = remainingOrderQty;
+ adjustedReason = $"璁㈠崟鏁伴噺闄愬埗锛氫粠{plannedQty}璋冩暣涓簕actualQty}";
+ }
+ if (actualQty > stockQuantity)
+ {
+ actualQty = stockQuantity;
+ adjustedReason = adjustedReason != null
+ ? $"{adjustedReason}锛屽簱瀛樻暟閲忛檺鍒讹細杩涗竴姝ヨ皟鏁翠负{actualQty}"
+ : $"搴撳瓨鏁伴噺闄愬埗锛氫粠{plannedQty}璋冩暣涓簕actualQty}";
+ }
+ if (actualQty <= 0)
+ {
+ return ValidationResult<(decimal, string)>.Error($"鏃犳硶鍒嗘嫞锛氳绠楀悗鐨勫疄闄呮暟閲忎负{actualQty}");
+ }
+ decimal projectedOverOut = orderDetail.OverOutQuantity + actualQty;
+ if (projectedOverOut > orderDetail.NeedOutQuantity)
+ {
+ // 濡傛灉浼氳秴鎷o紝璋冩暣涓哄垰濂芥弧瓒抽渶姹傜殑鏁伴噺
+ actualQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity;
+ adjustedReason = adjustedReason != null
+ ? $"{adjustedReason}锛岄槻瓒呮嫞闄愬埗锛氭渶缁堣皟鏁翠负{actualQty}"
+ : $"闃茶秴鎷i檺鍒讹細浠巤plannedQty}璋冩暣涓簕actualQty}";
+ }
+
+ if (adjustedReason != null)
+ {
+ _logger.LogWarning($"鍒嗘嫞鏁伴噺璋冩暣锛歿adjustedReason}锛岃鍗晎orderDetail.NeedOutQuantity}锛屽凡鍑哄簱{orderDetail.OverOutQuantity}锛屽簱瀛榹stockQuantity}");
+ }
+
+ return ValidationResult<(decimal, string)>.Success((actualQty, adjustedReason));
+ }
+
+ /// <summary>
+ /// 涓撻棬楠岃瘉鏄惁浼氬彂鐢熻秴鎷�
+ /// </summary>
+ private async Task<ValidationResult<bool>> ValidateOverPicking(int orderDetailId, decimal pickingQty)
+ {
+ var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == orderDetailId);
+
+ if (orderDetail == null)
+ return ValidationResult<bool>.Error("鏈壘鍒拌鍗曟槑缁�");
+
+ decimal projectedOverOut = orderDetail.OverOutQuantity + pickingQty;
+
+ if (projectedOverOut > orderDetail.NeedOutQuantity)
+ {
+ return ValidationResult<bool>.Error(
+ $"鍒嗘嫞鍚庡皢瀵艰嚧瓒呮嫞锛氬綋鍓嶅凡鍑哄簱{orderDetail.OverOutQuantity}锛屾湰娆″垎鎷pickingQty}锛屽悎璁projectedOverOut}锛岃秴杩囬渶姹倇orderDetail.NeedOutQuantity}");
+ }
+
+ return ValidationResult<bool>.Success(true);
+ }
+
+
+ private async Task<PickingResult> ExecutePickingLogic(
+ Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail,
+ string orderNo, string palletCode, string barcode, decimal actualQty)
+ {
+ decimal stockQuantity = stockDetail.StockQuantity;
+ var result = new PickingResult
+ {
+ FinalLockInfo = lockInfo,
+ FinalBarcode = barcode,
+ FinalStockId = stockDetail.Id,
+ ActualPickedQty = actualQty
+ };
+
+ if (actualQty < stockQuantity)
+ {
+ await HandleSplitPacking(lockInfo, stockDetail, actualQty, stockQuantity, result);
+ // 鎷嗗寘鍦烘櫙杩斿洖瀹為檯鎷i�夋暟閲�
+ result.ActualPickedQty = actualQty;
+ }
+ else if (actualQty == stockQuantity)
+ {
+ await HandleFullPicking(lockInfo, stockDetail, actualQty, result);
+ // 鏁村寘鎷i�夎繑鍥炲疄闄呮嫞閫夋暟閲�
+ result.ActualPickedQty = actualQty;
+ }
+ else
+ {
+ await HandlePartialPicking(lockInfo, stockDetail, actualQty, stockQuantity, result);
+ // 閮ㄥ垎鎷i�夎繑鍥炶皟鏁村悗鐨勬暟閲�
+ result.ActualPickedQty = result.ActualPickedQty; // 宸茬粡鍦ㄦ柟娉曞唴璋冩暣
+ }
+
+ return result;
+ }
+ private async Task HandleSplitPacking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
+ decimal actualQty, decimal stockQuantity, PickingResult result)
+ {
+ decimal remainingStockQty = stockQuantity - actualQty;
+
+ // 鏇存柊鍘熸潯鐮佸簱瀛�
+ stockDetail.StockQuantity = remainingStockQty;
+ stockDetail.OutboundQuantity = remainingStockQty;
+ await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+
+ // 鐢熸垚鏂版潯鐮�
+ string newBarcode = await GenerateNewBarcode();
+
+ // 鍒涘缓鏂伴攣瀹氫俊鎭�
+ var newLockInfo = await CreateSplitLockInfo(lockInfo, actualQty, newBarcode);
+
+ // 璁板綍鎷嗗寘鍘嗗彶
+ await RecordSplitHistory(lockInfo, stockDetail, actualQty, remainingStockQty, newBarcode);
+
+ // 鏇存柊鍘熼攣瀹氫俊鎭�
+ lockInfo.AssignQuantity = remainingStockQty;
+ lockInfo.PickedQty = 0;
+ lockInfo.Operator = App.User.UserName;
+ await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+
+ // 璁剧疆缁撴灉
+ result.FinalLockInfo = newLockInfo;
+ result.FinalBarcode = newBarcode;
+ result.SplitResults.AddRange(CreateSplitResults(lockInfo, actualQty, remainingStockQty, newBarcode, stockDetail.Barcode));
+
+
+
+ _logger.LogInformation($"鎷嗗寘鍒嗘嫞瀹屾垚 - OrderDetailId: {lockInfo.OrderDetailId}, 鍒嗘嫞鏁伴噺: {actualQty}");
+ }
+ private async Task HandleFullPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
+ decimal actualQty, PickingResult result)
+ {
+ // 1. 鏇存柊搴撳瓨
+ stockDetail.StockQuantity = 0;
+ stockDetail.OutboundQuantity = 0;
+ stockDetail.Status = StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt();
+ await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+
+ // 2. 鏇存柊閿佸畾淇℃伅
+ lockInfo.PickedQty += actualQty;
+ lockInfo.Status = (int)OutLockStockStatusEnum.鎷i�夊畬鎴�;
+ lockInfo.Operator = App.User.UserName;
+ await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+ }
+
+ private async Task HandlePartialPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
+ decimal actualQty, decimal stockQuantity, PickingResult result)
+ {
+ decimal stockOutQty = stockQuantity;
+ decimal remainingAssignQty = actualQty - stockQuantity;
+
+ // 1. 鏇存柊搴撳瓨
+ stockDetail.StockQuantity = 0;
+ stockDetail.OutboundQuantity = 0;
+ stockDetail.Status = StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt();
+ await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+
+ // 2. 鏇存柊閿佸畾淇℃伅
+ lockInfo.PickedQty += stockOutQty;
+ lockInfo.AssignQuantity = remainingAssignQty;
+ lockInfo.Operator = App.User.UserName;
+ await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+
+ // 3. 鏇存柊鎷嗗寘璁板綍鐘舵��
+ await UpdateSplitRecordsStatus(stockDetail.Barcode);
+
+ result.ActualPickedQty = stockOutQty;
+ }
+
+ private async Task UpdateOrderRelatedData(int orderDetailId, decimal pickedQty, string orderNo)
+ {
+ // 鑾峰彇鏈�鏂扮殑璁㈠崟鏄庣粏鏁版嵁
+ var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == orderDetailId);
+
+ decimal newOverOutQuantity = currentOrderDetail.OverOutQuantity + pickedQty;
+ decimal newPickedQty = currentOrderDetail.PickedQty + pickedQty;
+
+ if (newOverOutQuantity > currentOrderDetail.NeedOutQuantity)
+ {
+ _logger.LogError($"闃茶秴鎷f鏌ュけ璐� - OrderDetailId: {orderDetailId}, 宸插嚭搴�: {newOverOutQuantity}, 闇�姹�: {currentOrderDetail.NeedOutQuantity}, 鏈鍒嗘嫞: {pickedQty}");
+
+ decimal adjustedQty = currentOrderDetail.NeedOutQuantity - currentOrderDetail.OverOutQuantity;
+
+ if (adjustedQty > 0)
+ {
+ _logger.LogWarning($"鑷姩璋冩暣鍒嗘嫞鏁伴噺闃叉瓒呮嫞锛氫粠{pickedQty}璋冩暣涓簕adjustedQty}");
+ newOverOutQuantity = currentOrderDetail.NeedOutQuantity;
+ newPickedQty = currentOrderDetail.PickedQty + adjustedQty;
+ pickedQty = adjustedQty; // 鏇存柊瀹為檯鎷i�夋暟閲�
+ }
+ else
+ {
+ throw new Exception($"鍒嗘嫞鍚庡皢瀵艰嚧宸插嚭搴撴暟閲�({newOverOutQuantity})瓒呰繃璁㈠崟闇�姹傛暟閲�({currentOrderDetail.NeedOutQuantity})锛屼笖鏃犳硶鑷姩璋冩暣");
+ }
+ }
+
+ // 鏇存柊璁㈠崟鏄庣粏鏁伴噺鍜岀姸鎬�
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ PickedQty = newPickedQty,
+ OverOutQuantity = newOverOutQuantity,
+ OrderDetailStatus = newOverOutQuantity >= currentOrderDetail.NeedOutQuantity ?
+ OrderDetailStatusEnum.Over.ObjToInt() :
+ OrderDetailStatusEnum.Outbound.ObjToInt()
+ })
+ .Where(it => it.Id == orderDetailId)
+ .ExecuteCommandAsync();
+
+ // 鏇存柊閿佸畾鏁伴噺
+ await UpdateOrderDetailLockQuantity(orderDetailId);
+
+ // 妫�鏌ュ苟鏇存柊璁㈠崟鐘舵��
+ await CheckAndUpdateOrderStatus(orderNo);
+ }
+
+ private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode)
+ {
+ var task = await _taskRepository.Db.Queryable<Dt_Task>()
+ .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (result.FinalLockInfo.Id <= 0)
+ {
+ throw new Exception($"閿佸畾淇℃伅ID鏃犳晥: {result.FinalLockInfo.Id}锛屾棤娉曡褰曟嫞閫夊巻鍙�");
+ }
+
+ var pickingHistory = new Dt_PickingRecord
+ {
+ FactoryArea = result.FinalLockInfo.FactoryArea,
+ TaskNo = task?.TaskNum ?? 0,
+ LocationCode = task?.SourceAddress ?? "",
+ StockId = result.FinalStockId,
+ OrderNo = orderNo,
+ OrderDetailId = result.FinalLockInfo.OrderDetailId,
+ PalletCode = palletCode,
+ Barcode = result.FinalBarcode,
+ MaterielCode = result.FinalLockInfo.MaterielCode,
+ PickQuantity = result.ActualPickedQty,
+ PickTime = DateTime.Now,
+ Operator = App.User.UserName,
+ OutStockLockId = result.FinalLockInfo.Id,
+ BarcodeUnit = result.FinalLockInfo.BarcodeUnit,
+ BarcodeQty = result.FinalLockInfo.BarcodeQty,
+ BatchNo = result.FinalLockInfo.BatchNo,
+ lineNo = result.FinalLockInfo.lineNo,
+ SupplyCode = result.FinalLockInfo.SupplyCode,
+ WarehouseCode = result.FinalLockInfo.WarehouseCode,
+
+
+ };
+
+ await Db.Insertable(pickingHistory).ExecuteCommandAsync();
+ }
+
+ #endregion
+
+ #region 鍙栨秷鍒嗘嫞绉佹湁鏂规硶
+ private async Task<ValidationResult<bool>> ValidateDataConsistencyBeforeCancel(CancelPickingContext context)
+ {
+ try
+ {
+ // 楠岃瘉璁㈠崟鏄庣粏鏁版嵁
+ var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == context.OrderDetail.Id);
+
+ if (currentOrderDetail.OverOutQuantity < context.PickingRecord.PickQuantity)
+ return ValidationResult<bool>.Error($"璁㈠崟鏄庣粏宸插嚭搴撴暟閲�({currentOrderDetail.OverOutQuantity})灏忎簬鍙栨秷鏁伴噺({context.PickingRecord.PickQuantity})");
+
+ if (currentOrderDetail.PickedQty < context.PickingRecord.PickQuantity)
+ return ValidationResult<bool>.Error($"璁㈠崟鏄庣粏宸叉嫞閫夋暟閲�({currentOrderDetail.PickedQty})灏忎簬鍙栨秷鏁伴噺({context.PickingRecord.PickQuantity})");
+
+ // 楠岃瘉閿佸畾淇℃伅鏁版嵁
+ var currentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .FirstAsync(x => x.Id == context.LockInfo.Id);
+
+ if (currentLockInfo.PickedQty < context.PickingRecord.PickQuantity)
+ return ValidationResult<bool>.Error($"閿佸畾淇℃伅宸叉嫞閫夋暟閲�({currentLockInfo.PickedQty})灏忎簬鍙栨秷鏁伴噺({context.PickingRecord.PickQuantity})");
+
+ ////// 楠岃瘉搴撳瓨鏁版嵁
+ ////var currentStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ //// .FirstAsync(x => x.Barcode == context.PickingRecord.Barcode && x.StockId == context.PickingRecord.StockId);
+
+ ////if (currentStockDetail == null)
+ //// return ValidationResult<bool>.Error($"鏈壘鍒板搴旂殑搴撳瓨鏄庣粏璁板綍");
+
+ ////if (currentStockDetail.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
+ //// currentStockDetail.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+ //// return ValidationResult<bool>.Error($"鏉$爜{context.PickingRecord.Barcode}宸茬粡鍥炲簱锛屾棤娉曞彇娑堝垎鎷�");
+
+ // 楠岃瘉鐘舵�佹祦杞殑鍚堟硶鎬�
+ if (!await CanCancelPicking(currentLockInfo, null))
+ return ValidationResult<bool>.Error($"褰撳墠鐘舵�佷笉鍏佽鍙栨秷鍒嗘嫞");
+
+ return ValidationResult<bool>.Success(true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"鍙栨秷鍒嗘嫞鏁版嵁涓�鑷存�ч獙璇佸け璐�: {ex.Message}");
+ return ValidationResult<bool>.Error($"鏁版嵁楠岃瘉澶辫触: {ex.Message}");
+ }
+ }
+
+ private async Task<bool> CanCancelPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail)
+ {
+ // 閿佸畾淇℃伅鐘舵�佹鏌�
+ if (lockInfo.Status != (int)OutLockStockStatusEnum.鎷i�夊畬鎴�)
+ return false;
+
+ ////// 搴撳瓨鐘舵�佹鏌�
+ ////if (stockDetail.Status == StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt())
+ //// return false;
+
+ // 濡傛灉鏄媶鍖呰褰曪紝杩橀渶瑕佹鏌ョ埗閿佸畾淇℃伅鐘舵��
+ if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
+ {
+ var parentLock = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .FirstAsync(x => x.Id == lockInfo.ParentLockId.Value);
+
+ if (parentLock == null || parentLock.Status == (int)OutLockStockStatusEnum.鍥炲簱涓�)
+ return false;
+ }
+
+ return true;
+ }
+ private async Task<ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>> ValidateCancelRequest(string orderNo, string palletCode, string barcode)
+ {
+ // 鍩虹鍙傛暟楠岃瘉
+ if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode) || string.IsNullOrEmpty(barcode))
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("璁㈠崟鍙枫�佹墭鐩樼爜鍜屾潯鐮佷笉鑳戒负绌�");
+
+ // 鏌ユ壘鎷i�夎褰�
+ var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
+ .Where(it => it.OrderNo == orderNo &&
+ it.PalletCode == palletCode &&
+ it.Barcode == barcode)
+ .OrderByDescending(it => it.PickTime)
+ .FirstAsync();
+
+ if (pickingRecord == null)
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("鏈壘鍒板搴旂殑鎷i�夎褰�");
+
+ if (pickingRecord.PickQuantity <= 0)
+ {
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"鎷i�夎褰曟暟閲忔棤鏁�: {pickingRecord.PickQuantity}");
+ }
+ // 鏌ユ壘閿佸畾淇℃伅
+ var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(it => it.Id == pickingRecord.OutStockLockId)
+ .FirstAsync();
+
+ if (lockInfo == null)
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("鏈壘鍒板搴旂殑鍑哄簱閿佸畾淇℃伅");
+
+ if (lockInfo.PickedQty < pickingRecord.PickQuantity)
+ {
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error(
+ $"鍙栨秷鏁伴噺({pickingRecord.PickQuantity})瓒呰繃閿佸畾淇℃伅鐨勫凡鎷i�夋暟閲�({lockInfo.PickedQty})");
+ }
+ // 妫�鏌ョ姸鎬佹槸鍚﹀厑璁稿彇娑�
+ if (lockInfo.Status != (int)OutLockStockStatusEnum.鎷i�夊畬鎴�)
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("褰撳墠鐘舵�佷笉鍏佽鍙栨秷鍒嗘嫞");
+
+ var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
+
+ if (order?.OrderStatus == (int)OutOrderStatusEnum.鍑哄簱瀹屾垚)
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("璁㈠崟宸插嚭搴撳畬鎴愶紝涓嶅厑璁稿彇娑堝垎鎷�");
+
+ var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().FirstAsync(x => x.Id == pickingRecord.OrderDetailId);
+
+ if (orderDetail == null)
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"鏈壘鍒拌鍗曟槑缁嗭紝ID: {pickingRecord.OrderDetailId}");
+
+ // 妫�鏌ヨ鍗曟槑缁嗙殑宸叉嫞閫夋暟閲忔槸鍚﹁冻澶熷彇娑�
+ if (orderDetail.PickedQty < pickingRecord.PickQuantity)
+ {
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"鍙栨秷鏁伴噺({pickingRecord.PickQuantity})瓒呰繃璁㈠崟鏄庣粏鐨勫凡鎷i�夋暟閲�({orderDetail.PickedQty})");
+ }
+
+ // 妫�鏌ヨ鍗曟槑缁嗙殑宸插嚭搴撴暟閲忔槸鍚﹁冻澶熷彇娑�
+ if (orderDetail.OverOutQuantity < pickingRecord.PickQuantity)
+ {
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"鍙栨秷鏁伴噺({pickingRecord.PickQuantity})瓒呰繃璁㈠崟鏄庣粏鐨勫凡鍑哄簱鏁伴噺({orderDetail.OverOutQuantity})");
+ }
+
+ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>().FirstAsync(it => it.Barcode == barcode && it.StockId == pickingRecord.StockId);
+
+ if (stockDetail != null)
+ {
+ // 妫�鏌ュ簱瀛樼姸鎬� - 濡傛灉鐘舵�佹槸鍏ュ簱纭鎴栧叆搴撳畬鎴愶紝璇存槑宸茬粡鍥炲簱
+ if (stockDetail.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
+ stockDetail.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+ {
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"鏉$爜{barcode}宸茬粡鍥炲簱锛屼笉鑳藉彇娑堝垎鎷�");
+ }
+ }
+
+ return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Success((pickingRecord, lockInfo, orderDetail));
+ }
+ /// <summary>
+ /// 妫�鏌ユ潯鐮佹槸鍚﹀凡缁忓洖搴�
+ /// </summary>
+ private async Task<bool> IsBarcodeReturned(string barcode, int stockId)
+ {
+ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(it => it.Barcode == barcode && it.StockId == stockId)
+ .FirstAsync();
+
+ if (stockDetail == null)
+ return false;
+
+ // 濡傛灉鐘舵�佹槸鍏ュ簱纭鎴栧叆搴撳畬鎴愶紝璇存槑宸茬粡鍥炲簱
+ return stockDetail.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
+ stockDetail.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt();
+ }
+
+ /// <summary>
+ /// 妫�鏌ラ攣瀹氫俊鎭搴旂殑鏉$爜鏄惁宸茬粡鍥炲簱
+ /// </summary>
+ private async Task<bool> IsLockInfoReturned(Dt_OutStockLockInfo lockInfo)
+ {
+ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
+ .FirstAsync();
+
+ if (stockDetail == null)
+ return false;
+
+ return stockDetail.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
+ stockDetail.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt();
+ }
+ private async Task ExecuteCancelLogic(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord,
+ Dt_OutboundOrderDetail orderDetail, string orderNo)
+ {
+ decimal cancelQty = pickingRecord.PickQuantity;
+
+ // 鏁版嵁涓�鑷存�ч獙璇�
+ var context = new CancelPickingContext
+ {
+ LockInfo = lockInfo,
+ PickingRecord = pickingRecord,
+ OrderDetail = orderDetail,
+ OrderNo = orderNo,
+ CancelQuantity = cancelQty
+ };
+
+ var validationResult = await ValidateDataConsistencyBeforeCancel(context);
+ if (!validationResult.IsValid)
+ throw new Exception(validationResult.ErrorMessage);
+
+ // 澶勭悊涓嶅悓绫诲瀷鐨勫彇娑�
+ if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
+ {
+ await HandleSplitBarcodeCancel(lockInfo, pickingRecord, cancelQty);
+ }
+ else
+ {
+ await HandleNormalBarcodeCancel(lockInfo, pickingRecord, cancelQty);
+ }
+
+ // 鏇存柊璁㈠崟鏄庣粏
+ await UpdateOrderDetailOnCancel(pickingRecord.OrderDetailId, cancelQty);
+
+ // 鍒犻櫎鎷i�夎褰�
+ var deleteResult = await Db.Deleteable<Dt_PickingRecord>()
+ .Where(x => x.Id == pickingRecord.Id)
+ .ExecuteCommandAsync();
+
+ if (deleteResult <= 0)
+ throw new Exception("鍒犻櫎鎷i�夎褰曞け璐�");
+
+ _logger.LogInformation($"鍒犻櫎鎷i�夎褰� - 璁板綍ID: {pickingRecord.Id}, 鏉$爜: {pickingRecord.Barcode}");
+
+ // 閲嶆柊妫�鏌ヨ鍗曠姸鎬�
+ await UpdateOrderStatusForReturn(orderNo);
+
+
+ }
+
+ private async Task HandleSplitBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
+ {
+ // 鏌ユ壘鐖堕攣瀹氫俊鎭�
+ var parentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(x => x.Id == lockInfo.ParentLockId.Value)
+ .FirstAsync();
+
+ if (parentLockInfo == null)
+ throw new Exception("鏈壘鍒扮埗閿佸畾淇℃伅锛屾棤娉曞彇娑堟媶鍖呭垎鎷�");
+
+ // 妫�鏌ョ埗鏉$爜鍜屾媶鍖呮潯鐮佺殑鐘舵��
+ if (await IsLockInfoReturned(parentLockInfo))
+ throw new Exception($"鐖舵潯鐮亄parentLockInfo.CurrentBarcode}宸茬粡鍥炲簱锛屾棤娉曞彇娑堟媶鍖呭垎鎷�");
+
+ if (await IsLockInfoReturned(lockInfo))
+ throw new Exception($"鎷嗗寘鏉$爜{lockInfo.CurrentBarcode}宸茬粡鍥炲簱锛屾棤娉曞彇娑堟媶鍖呭垎鎷�");
+
+ // 鎭㈠鐖堕攣瀹氫俊鎭殑鍒嗛厤鏁伴噺
+ parentLockInfo.AssignQuantity += cancelQty;
+ parentLockInfo.Status = (int)OutLockStockStatusEnum.鍑哄簱涓�; // 鎭㈠涓哄嚭搴撲腑鐘舵��
+ await _outStockLockInfoService.Db.Updateable(parentLockInfo).ExecuteCommandAsync();
+
+ // 鎭㈠鐖舵潯鐮佸簱瀛�
+ var parentStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(x => x.Barcode == parentLockInfo.CurrentBarcode && x.StockId == parentLockInfo.StockId)
+ .FirstAsync();
+
+ if (parentStockDetail != null)
+ {
+ parentStockDetail.StockQuantity += cancelQty;
+ parentStockDetail.OutboundQuantity = parentStockDetail.StockQuantity;
+ parentStockDetail.Status = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+ await _stockInfoDetailService.Db.Updateable(parentStockDetail).ExecuteCommandAsync();
+
+ _logger.LogInformation($"鎭㈠鐖舵潯鐮佸簱瀛� - 鏉$爜: {parentStockDetail.Barcode}, 鎭㈠鏁伴噺: {cancelQty}, 鏂板簱瀛�: {parentStockDetail.StockQuantity}");
+ }
+
+ // 澶勭悊鎷嗗寘浜х敓鐨勬柊鏉$爜搴撳瓨
+ var splitStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId)
+ .FirstAsync();
+
+ if (splitStockDetail != null)
+ {
+ // 鍒犻櫎鎷嗗寘浜х敓鐨勬柊鏉$爜搴撳瓨璁板綍
+ await _stockInfoDetailService.Db.Deleteable(splitStockDetail).ExecuteCommandAsync();
+ _logger.LogInformation($"鍒犻櫎鎷嗗寘鏂版潯鐮佸簱瀛� - 鏉$爜: {splitStockDetail.Barcode}");
+ }
+
+ // 鏇存柊鎷嗗寘璁板綍鐘舵��
+ var updateCount = await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
+ .SetColumns(x => new Dt_SplitPackageRecord
+ {
+ Status = (int)SplitPackageStatusEnum.宸叉挙閿�,
+ IsReverted = true,
+ Operator = App.User.UserName,
+ RevertTime = DateTime.Now
+ })
+ .Where(x => x.NewBarcode == lockInfo.CurrentBarcode && !x.IsReverted)
+ .ExecuteCommandAsync();
+
+ _logger.LogInformation($"鏇存柊鎷嗗寘璁板綍鐘舵�� - 鏇存柊璁板綍鏁�: {updateCount}");
+
+ // 鍒犻櫎鎷嗗寘浜х敓鐨勯攣瀹氫俊鎭�
+ await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
+ .Where(x => x.Id == lockInfo.Id)
+ .ExecuteCommandAsync();
+
+ _logger.LogInformation($"鍒犻櫎鎷嗗寘閿佸畾淇℃伅 - 閿佸畾ID: {lockInfo.Id}, 鏉$爜: {lockInfo.CurrentBarcode}");
+ }
+ private async Task HandleNormalBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
+ {
+ if (await IsLockInfoReturned(lockInfo))
+ throw new Exception($"鏉$爜{lockInfo.CurrentBarcode}宸茬粡鍥炲簱锛屾棤娉曞彇娑堝垎鎷�");
+
+ // 鎭㈠閿佸畾淇℃伅
+ lockInfo.PickedQty -= cancelQty;
+ if (lockInfo.PickedQty < 0) lockInfo.PickedQty = 0;
+
+ // 鍙湁褰撴嫞閫夋暟閲忓畬鍏ㄥ彇娑堟椂鎵嶆仮澶嶇姸鎬�
+ if (lockInfo.PickedQty == 0)
+ {
+ lockInfo.Status = (int)OutLockStockStatusEnum.鍑哄簱涓�;
+ }
+
+ lockInfo.Operator = App.User.UserName;
+ await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+
+ _logger.LogInformation($"鎭㈠閿佸畾淇℃伅 - 閿佸畾ID: {lockInfo.Id}, 鎵e噺鎷i�夋暟閲�: {cancelQty}, 鏂板凡鎷i�夋暟閲�: {lockInfo.PickedQty}");
+
+ // 鎭㈠搴撳瓨
+ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(x => x.Barcode == pickingRecord.Barcode && x.StockId == pickingRecord.StockId)
+ .FirstAsync();
+
+ if (stockDetail != null)
+ {
+ stockDetail.StockQuantity += cancelQty;
+ stockDetail.OutboundQuantity = stockDetail.StockQuantity;
+
+ // 鎭㈠搴撳瓨鐘舵��
+ if (stockDetail.Status == StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt())
+ {
+ stockDetail.Status = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+ }
+
+ await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+
+ _logger.LogInformation($"鎭㈠搴撳瓨 - 鏉$爜: {stockDetail.Barcode}, 鎭㈠鏁伴噺: {cancelQty}, " +
+ $"鏂板簱瀛�: {stockDetail.StockQuantity}, 鏂扮姸鎬�: {stockDetail.Status}");
+ }
+ else
+ {
+ _logger.LogWarning($"鏈壘鍒板簱瀛樿褰� - 鏉$爜: {pickingRecord.Barcode}, 搴撳瓨ID: {pickingRecord.StockId}");
+ }
+ }
+ private async Task UpdateOrderDetailOnCancel(int orderDetailId, decimal cancelQty)
+ {
+ // 鑾峰彇鏈�鏂扮殑璁㈠崟鏄庣粏鏁版嵁锛堝甫閿侊級
+ var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .With(SqlWith.RowLock)
+ .FirstAsync(x => x.Id == orderDetailId);
+
+ decimal newOverOutQuantity = currentOrderDetail.OverOutQuantity - cancelQty;
+ decimal newPickedQty = currentOrderDetail.PickedQty - cancelQty;
+
+ // 涓ユ牸妫�鏌ュ彇娑堝悗鏁伴噺涓嶄細涓鸿礋鏁�
+ if (newOverOutQuantity < 0)
+ throw new Exception($"鍙栨秷鍒嗘嫞灏嗗鑷村凡鍑哄簱鏁伴噺({newOverOutQuantity})涓鸿礋鏁�");
+
+ if (newPickedQty < 0)
+ throw new Exception($"鍙栨秷鍒嗘嫞灏嗗鑷村凡鎷i�夋暟閲�({newPickedQty})涓鸿礋鏁�");
+
+ // 纭畾鏂扮殑鐘舵��
+ int newStatus;
+ if (newOverOutQuantity >= currentOrderDetail.NeedOutQuantity)
+ {
+ newStatus = OrderDetailStatusEnum.Over.ObjToInt();
+ }
+ else if (newOverOutQuantity > 0)
+ {
+ newStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+ }
+ else
+ {
+ newStatus = OrderDetailStatusEnum.New.ObjToInt();
+ }
+
+ // 鏇存柊璁㈠崟鏄庣粏
+ var updateResult = await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ PickedQty = newPickedQty,
+ OverOutQuantity = newOverOutQuantity,
+ OrderDetailStatus = newStatus
+ })
+ .Where(it => it.Id == orderDetailId)
+ .ExecuteCommandAsync();
+
+ if (updateResult <= 0)
+ throw new Exception("鏇存柊璁㈠崟鏄庣粏澶辫触");
+
+ // 鏇存柊閿佸畾鏁伴噺
+ await UpdateOrderDetailLockQuantity(orderDetailId);
+
+ _logger.LogInformation($"鏇存柊璁㈠崟鏄庣粏 - OrderDetailId: {orderDetailId}, " +
+ $"鎵e噺宸插嚭搴�: {cancelQty}, 鏂板凡鍑哄簱: {newOverOutQuantity}, " +
+ $"鎵e噺宸叉嫞閫�: {cancelQty}, 鏂板凡鎷i��: {newPickedQty}, " +
+ $"鏂扮姸鎬�: {newStatus}");
+ }
+ #endregion
+
+ #region 鍥炲簱鎿嶄綔绉佹湁鏂规硶
+
+ private async Task<Dt_StockInfo> GetStockInfo(string palletCode)
+ {
+ return await _stockInfoService.Db.Queryable<Dt_StockInfo>()
+ .FirstAsync(x => x.PalletCode == palletCode);
+ }
+ /// <summary>
+ /// 妫�鏌ユ暣涓墭鐩樻槸鍚﹀凡缁忓洖搴�
+ /// </summary>
+ private async Task<bool> IsPalletReturned(string palletCode)
+ {
+ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ return false;
+
+ // 濡傛灉鎵樼洏鐘舵�佹槸鍏ュ簱纭鎴栧叆搴撳畬鎴愶紝璇存槑宸茬粡鍥炲簱
+ return stockInfo.StockStatus == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
+ stockInfo.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt();
+ }
+ private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode)
+ {
+ // 鍏堝皾璇曢�氳繃璁㈠崟鍙峰拰鎵樼洏鍙锋煡鎵句换鍔�
+ var task = await _taskRepository.Db.Queryable<Dt_Task>()
+ .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (task == null)
+ {
+ // 濡傛灉鎵句笉鍒帮紝鍐嶉�氳繃鎵樼洏鍙锋煡鎵�
+ task = await _taskRepository.Db.Queryable<Dt_Task>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+ }
+
+ return task;
+ }
+
+ private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId)
+ {
+ decimal totalQty = 0;
+ var processedBarcodes = new HashSet<string>();
+
+ foreach (var splitRecord in splitRecords)
+ {
+ if (splitRecord.Status != (int)SplitPackageStatusEnum.宸叉挙閿�)
+ continue;
+ // 妫�鏌ュ師鏉$爜
+ if (!processedBarcodes.Contains(splitRecord.OriginalBarcode))
+ {
+ var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId &&
+ it.Status != StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt())
+ .FirstAsync();
+
+ if (originalStock != null && originalStock.StockQuantity > 0)
+ {
+ totalQty += originalStock.StockQuantity;
+ processedBarcodes.Add(splitRecord.OriginalBarcode);
+ }
+ }
+
+ // 妫�鏌ユ柊鏉$爜
+ if (!processedBarcodes.Contains(splitRecord.NewBarcode))
+ {
+ var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId && it.Status != StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt())
+ .FirstAsync();
+
+ if (newStock != null && newStock.StockQuantity > 0)
+ {
+ totalQty += newStock.StockQuantity;
+ processedBarcodes.Add(splitRecord.NewBarcode);
+ }
+ }
+ }
+
+ return totalQty;
+ }
+
+ private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode, Dt_Task originalTask, int stockInfoId)
+ {
+ // 妫�鏌ユ槸鍚︽墍鏈夎揣鐗╅兘宸叉嫞閫夊畬鎴�
+ //var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ // .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
+ // .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.鎷i�夊畬鎴�);
+
+ //if (allPicked)
+ //{
+ // // 鍒犻櫎鍘熷鍑哄簱浠诲姟 缁勭┖鐩� 绌虹洏鍥炲簱
+ // //await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
+ // return WebResponseContent.Instance.OK("鎵�鏈夎揣鐗╁凡鎷i�夊畬鎴愶紝鎵樼洏涓虹┖");
+ //}
+ //else
+ //{
+ // // 鍒犻櫎鍘熷鍑哄簱浠诲姟
+ // //await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
+ // return WebResponseContent.Instance.Error("娌℃湁闇�瑕佸洖搴撶殑鍓╀綑璐х墿");
+ //}
+ try
+ {
+ var locationtype = 0;
+ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ {
+ var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>().FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
+ locationtype = firstLocation?.LocationType ?? 1;
+ }
+ else
+ {
+ locationtype = stockInfo.LocationType;
+ _stockInfoService.DeleteData(stockInfo);
+ }
+
+ var targetAddress = originalTask.TargetAddress;
+
+ await CleanupZeroStockData(stockInfoId);
+
+
+ var emptystockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.缁勭洏鏆傚瓨.ObjToInt(), PalletCode = palletCode, LocationType = locationtype };
+ emptystockInfo.Details = new List<Dt_StockInfoDetail>();
+ _stockInfoService.AddMaterielGroup(emptystockInfo);
+ //绌烘墭鐩樺浣曞鐞� 杩樻湁涓�涓嚭搴撲换鍔¤澶勭悊銆�
+ originalTask.PalletType = PalletTypeEnum.Empty.ObjToInt();
+
+ await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty, PalletTypeEnum.Empty.ObjToInt());
+
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($" HandleNoReturnItems 澶辫触: {ex.Message}");
+ return WebResponseContent.Instance.Error($" 鍥炲簱绌烘墭鐩樺け璐ワ紒");
+ }
+ return WebResponseContent.Instance.OK("绌烘墭鐩樺洖搴撲换鍔″垱寤烘垚鍔�");
+
+ }
+
+ private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo,
+ Dt_Task task, PalletStatusAnalysis analysis)
+ {
+ // 鎯呭喌1锛氬鐞嗘湭鍒嗘嫞鐨勫嚭搴撻攣瀹氳褰�
+ if (analysis.HasRemainingLocks)
+ {
+ await HandleRemainingLocksReturn(analysis.RemainingLocks, stockInfo.Id);
+
+ // await UpdateOrderDetailsOnReturn(analysis.RemainingLocks);
+ }
+
+ // 澶勭悊鎵樼洏涓婂叾浠栧簱瀛樿揣鐗�
+ if (analysis.HasPalletStockGoods)
+ {
+ var validStockGoods = analysis.PalletStockGoods
+ .Where(x => x.Status != StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt())
+ .ToList();
+
+ if (validStockGoods.Any())
+ {
+ await HandlePalletStockGoodsReturn(analysis.PalletStockGoods);
+ }
+ else
+ {
+ _logger.LogInformation("娌℃湁鏈夋晥鐨勫簱瀛樿揣鐗╅渶瑕佸洖搴�");
+ }
+ }
+
+ // 澶勭悊鎷嗗寘璁板綍
+ if (analysis.HasSplitRecords)
+ {
+ var validSplitRecords = analysis.SplitRecords
+ .Where(x => x.Status != (int)SplitPackageStatusEnum.宸叉嫞閫�)
+ .ToList();
+
+ if (validSplitRecords.Any())
+ {
+ await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode);
+ }
+ else
+ {
+ _logger.LogInformation("娌℃湁鏈夋晥鐨勬媶鍖呰褰曢渶瑕佸鐞�");
+ }
+ }
+
+ // 鏇存柊搴撳瓨涓昏〃鐘舵��
+ await UpdateStockInfoStatus(stockInfo);
+ }
+
+ /// <summary>
+ /// 瀹屽叏閲婃斁閿佸畾锛屽厑璁搁噸鏂板垎閰嶅簱瀛�
+ /// </summary>
+ private async Task ReleaseAllLocksForReallocation(string orderNo, string palletCode, PalletStatusAnalysis analysis)
+ {
+ _logger.LogInformation($"寮�濮嬮噴鏀鹃攣瀹氫互渚块噸鏂板垎閰� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}");
+
+ // 1. 澶勭悊鏈垎鎷g殑鍑哄簱閿佸畾璁板綍 - 瀹屽叏閲婃斁
+ if (analysis.HasRemainingLocks)
+ {
+ await ReleaseRemainingLocks(analysis.RemainingLocks);
+ }
+
+ // 2. 澶勭悊宸插洖搴撶殑閿佸畾璁板綍 - 鍒犻櫎鎴栨爣璁颁负鏃犳晥
+ await CleanupReturnedLocks(orderNo, palletCode);
+
+ // 3. 閲嶇疆璁㈠崟鏄庣粏鐨勯攣瀹氭暟閲�
+ await ResetOrderDetailLockQuantities(analysis);
+
+ _logger.LogInformation($"閿佸畾閲婃斁瀹屾垚 - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}");
+ }
+
+ /// <summary>
+ /// 閲婃斁鏈垎鎷g殑閿佸畾璁板綍
+ /// </summary>
+ private async Task ReleaseRemainingLocks(List<Dt_OutStockLockInfo> remainingLocks)
+ {
+ var lockIds = remainingLocks.Select(x => x.Id).ToList();
+
+ // 灏嗛攣瀹氳褰曠姸鎬佹敼涓�"宸查噴鏀�"锛屾垨鑰呯洿鎺ュ垹闄�
+ // 鏍囪涓哄凡閲婃斁
+ await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
+ .SetColumns(it => new Dt_OutStockLockInfo
+ {
+ Status = (int)OutLockStockStatusEnum.宸查噴鏀�, // 闇�瑕佹柊澧炶繖涓姸鎬�
+ // ReleaseTime = DateTime.Now,
+ Operator = App.User.UserName
+ })
+ .Where(it => lockIds.Contains(it.Id))
+ .ExecuteCommandAsync();
+
+ // 鐩存帴鍒犻櫎锛堟洿褰诲簳锛�
+ // await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
+ // .Where(it => lockIds.Contains(it.Id))
+ // .ExecuteCommandAsync();
+
+ _logger.LogInformation($"閲婃斁{remainingLocks.Count}鏉℃湭鍒嗘嫞閿佸畾璁板綍");
+ }
+
+ /// <summary>
+ /// 娓呯悊宸插洖搴撶殑閿佸畾璁板綍
+ /// </summary>
+ private async Task CleanupReturnedLocks(string orderNo, string palletCode)
+ {
+ // 鏌ユ壘鎵�鏈夌姸鎬佷负鍥炲簱涓殑閿佸畾璁板綍骞堕噴鏀�
+ var returnedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(it => it.OrderNo == orderNo &&
+ it.PalletCode == palletCode &&
+ it.Status == (int)OutLockStockStatusEnum.鍥炲簱涓�)
+ .ToListAsync();
+
+ if (returnedLocks.Any())
+ {
+ var returnedLockIds = returnedLocks.Select(x => x.Id).ToList();
+
+ await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
+ .SetColumns(it => new Dt_OutStockLockInfo
+ {
+ Status = (int)OutLockStockStatusEnum.宸查噴鏀�,
+ //ReleaseTime = DateTime.Now,
+ Operator = App.User.UserName
+ })
+ .Where(it => returnedLockIds.Contains(it.Id))
+ .ExecuteCommandAsync();
+
+ _logger.LogInformation($"娓呯悊{returnedLocks.Count}鏉″洖搴撲腑閿佸畾璁板綍");
+ }
+ }
+
+ /// <summary>
+ /// 閲嶇疆璁㈠崟鏄庣粏鐨勯攣瀹氭暟閲�
+ /// </summary>
+ private async Task ResetOrderDetailLockQuantities(PalletStatusAnalysis analysis)
+ {
+ // 鏀堕泦鎵�鏈夊彈褰卞搷鐨勮鍗曟槑缁咺D
+ var affectedOrderDetailIds = new HashSet<int>();
+
+ if (analysis.HasRemainingLocks)
+ {
+ foreach (var lockInfo in analysis.RemainingLocks)
+ {
+ affectedOrderDetailIds.Add(lockInfo.OrderDetailId);
+ }
+ }
+
+ // 閲嶇疆杩欎簺璁㈠崟鏄庣粏鐨勯攣瀹氭暟閲�
+ foreach (var orderDetailId in affectedOrderDetailIds)
+ {
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ LockQuantity = 0, // 閲嶇疆閿佸畾鏁伴噺
+ OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt() // 閲嶇疆鐘舵�佷负鏂板缓
+ })
+ .Where(it => it.Id == orderDetailId)
+ .ExecuteCommandAsync();
+ }
+
+ _logger.LogInformation($"閲嶇疆{affectedOrderDetailIds.Count}涓鍗曟槑缁嗙殑閿佸畾鏁伴噺");
+ }
+ private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks, int stockId)
+ {
+ var lockIds = remainingLocks.Select(x => x.Id).ToList();
+
+ // 鏇存柊鍑哄簱閿佸畾璁板綍鐘舵�佷负鍥炲簱涓�
+ await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
+ .SetColumns(it => new Dt_OutStockLockInfo
+ {
+ Status = (int)OutLockStockStatusEnum.鍥炲簱涓�,
+ })
+ .Where(it => lockIds.Contains(it.Id))
+ .ExecuteCommandAsync();
+
+ // 澶勭悊搴撳瓨璁板綍
+ foreach (var lockInfo in remainingLocks)
+ {
+ decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
+ if (returnQty <= 0)
+ {
+ _logger.LogWarning($"閿佸畾璁板綍{lockInfo.Id}鏃犻渶鍥炲簱锛屽垎閰嶆暟閲�: {lockInfo.AssignQuantity}, 宸叉嫞閫�: {lockInfo.PickedQty}");
+ continue;
+ }
+
+ _logger.LogInformation($"澶勭悊閿佸畾璁板綍鍥炲簱 - 閿佸畾ID: {lockInfo.Id}, 鏉$爜: {lockInfo.CurrentBarcode}, 鍥炲簱鏁伴噺: {returnQty}");
+ // 鏌ユ壘瀵瑰簲鐨勫簱瀛樻槑缁�
+ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
+ .FirstAsync();
+
+ if (stockDetail != null)
+ {
+ stockDetail.StockQuantity += returnQty;
+ // 鎭㈠搴撳瓨鐘舵��
+ stockDetail.OutboundQuantity = Math.Max(0, stockDetail.OutboundQuantity - returnQty);
+ stockDetail.Status = StockStatusEmun.鍏ュ簱纭.ObjToInt();
+ await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+ }
+ else
+ {
+ _logger.LogWarning($"鏈壘鍒板搴旂殑搴撳瓨鏄庣粏 - 鏉$爜: {lockInfo.CurrentBarcode}, 搴撳瓨ID: {lockInfo.StockId}");
+ // 鍒涘缓鏂扮殑搴撳瓨璁板綍
+ //var newStockDetail = new Dt_StockInfoDetail
+ //{
+ // StockId = lockInfo.StockId,
+ // MaterielCode = lockInfo.MaterielCode,
+ // MaterielName = lockInfo.MaterielName,
+ // OrderNo = lockInfo.OrderNo,
+ // BatchNo = lockInfo.BatchNo,
+ // StockQuantity = returnQty,
+ // OutboundQuantity = 0,
+ // Barcode = lockInfo.CurrentBarcode,
+ // InboundOrderRowNo = "",
+ // Status = StockStatusEmun.鍏ュ簱纭.ObjToInt(),
+ // SupplyCode = lockInfo.SupplyCode,
+ // WarehouseCode = lockInfo.WarehouseCode,
+ // Unit = lockInfo.Unit,
+ //};
+ //await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
+ }
+ try
+ {
+ var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
+
+ if (orderDetail != null)
+ {
+ decimal newLockQuantity = Math.Max(0, orderDetail.LockQuantity - returnQty);
+
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ LockQuantity = newLockQuantity
+ })
+ .Where(it => it.Id == lockInfo.OrderDetailId)
+ .ExecuteCommandAsync();
+
+ _logger.LogInformation($"鏇存柊璁㈠崟鏄庣粏閿佸畾鏁伴噺 - OrderDetailId: {lockInfo.OrderDetailId}, " +
+ $"鎵e噺閿佸畾: {returnQty}, 鏂伴攣瀹氭暟閲�: {newLockQuantity}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"鏇存柊璁㈠崟鏄庣粏閿佸畾鏁伴噺澶辫触 - OrderDetailId: {lockInfo.OrderDetailId}, Error: {ex.Message}");
+ }
+ }
+
+
+ }
+
+ private async Task UpdateOrderDetailsOnReturn(List<Dt_OutStockLockInfo> remainingLocks)
+ {
+ // 鎸夎鍗曟槑缁嗗垎缁�
+ var orderDetailGroups = remainingLocks.GroupBy(x => x.OrderDetailId);
+
+ foreach (var group in orderDetailGroups)
+ {
+ var orderDetailId = group.Key;
+ var totalReturnQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
+
+ // 鑾峰彇褰撳墠璁㈠崟鏄庣粏
+ var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == orderDetailId);
+
+ if (orderDetail != null)
+ {
+ // 璋冩暣宸叉嫞閫夋暟閲忓拰宸插嚭搴撴暟閲�
+ decimal newPickedQty = Math.Max(0, orderDetail.PickedQty - totalReturnQty);
+ decimal newOverOutQuantity = Math.Max(0, orderDetail.OverOutQuantity - totalReturnQty);
+
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ PickedQty = newPickedQty,
+ OverOutQuantity = newOverOutQuantity,
+ })
+ .Where(it => it.Id == orderDetailId)
+ .ExecuteCommandAsync();
+ }
+ }
+ }
+
+ private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods)
+ {
+ _logger.LogInformation($"鍥炲簱鎿嶄綔锛氬彂鐜皗palletStockGoods.Count}涓簱瀛樻槑缁嗛渶瑕佸洖搴擄紝绛夊緟AGV鎼繍");
+ foreach (var stockGood in palletStockGoods)
+ {
+ _logger.LogInformation($"寰呭洖搴撹揣鐗� - 鏉$爜: {stockGood.Barcode}, 鏁伴噺: {stockGood.StockQuantity}, 褰撳墠鐘舵��: {stockGood.Status}");
+
+ if (stockGood.Status != StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt())
+ {
+ stockGood.OutboundQuantity = 0;
+ stockGood.Status = StockStatusEmun.鍏ュ簱纭.ObjToInt();
+ await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
+
+ _logger.LogInformation($"搴撳瓨璐х墿鍥炲簱瀹屾垚 - 鏉$爜: {stockGood.Barcode}, 鏂扮姸鎬�: {stockGood.Status}");
+ }
+ else
+ {
+ _logger.LogWarning($"璺宠繃宸插嚭搴撳畬鎴愮殑璐х墿 - 鏉$爜: {stockGood.Barcode}");
+ }
+ }
+ }
+
+ private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, string orderNo, string palletCode)
+ {
+ var validRecords = splitRecords.Where(x => x.Status != (int)SplitPackageStatusEnum.宸叉嫞閫�).ToList();
+
+ if (!validRecords.Any())
+ {
+ _logger.LogInformation("娌℃湁闇�瑕佸洖搴撶殑鎷嗗寘璁板綍");
+ return;
+ }
+
+ _logger.LogInformation($"鏇存柊{validRecords.Count}鏉℃媶鍖呰褰曠姸鎬佷负宸插洖搴�");
+
+ // 鏇存柊鎷嗗寘璁板綍鐘舵��
+ await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
+ .SetColumns(x => new Dt_SplitPackageRecord
+ {
+ Status = (int)SplitPackageStatusEnum.宸插洖搴�,
+ Operator = App.User.UserName
+ })
+ .Where(x => validRecords.Select(r => r.Id).Contains(x.Id))
+ .ExecuteCommandAsync();
+ }
+
+ private async Task UpdateStockInfoStatus(Dt_StockInfo stockInfo)
+ {
+ _logger.LogInformation($"鍥炲簱鎿嶄綔锛氭墭鐩榹stockInfo.PalletCode}绛夊緟AGV鍥炲簱鎼繍");
+ // 鏇存柊搴撳瓨涓昏〃鐘舵��
+ stockInfo.StockStatus = StockStatusEmun.鍏ュ簱纭.ObjToInt();
+ await _stockInfoService.Db.Updateable(stockInfo).ExecuteCommandAsync();
+ }
+ /// <summary>
+ /// 鍒涘缓鍥炲簱浠诲姟
+ /// </summary>
+ /// <param name="orderNo"></param>
+ /// <param name="palletCode"></param>
+ /// <param name="originalTask"></param>
+ /// <param name="analysis"></param>
+ /// <returns></returns>
+ private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum, int palletType)
+ {
+ var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
+ .FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
+
+ // 鍒嗛厤鏂拌揣浣�
+ var newLocation = _locationInfoService.AssignLocation(firstLocation.LocationType);
+
+ Dt_Task returnTask = new()
+ {
+ CurrentAddress = stations[originalTask.TargetAddress],
+ Grade = 0,
+ PalletCode = palletCode,
+ NextAddress = "",
+ // OrderNo = originalTask.OrderNo,
+ OrderNo = orderNo,
+ Roadway = newLocation.RoadwayNo,
+ SourceAddress = stations[originalTask.TargetAddress],
+ TargetAddress = newLocation.LocationCode,
+ TaskStatus = TaskStatusEnum.New.ObjToInt(),
+ TaskType = taskTypeEnum.ObjToInt(),
+ PalletType = palletType,
+ WarehouseId = originalTask.WarehouseId
+
+ };
+ // 淇濆瓨鍥炲簱浠诲姟
+ await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
+ var targetAddress = originalTask.TargetAddress;
+
+ // 鍒犻櫎鍘熷鍑哄簱浠诲姟
+ _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.鑷姩瀹屾垚);
+ // await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
+
+
+
+ // 缁� ESS 鍙戦�佹祦鍔ㄤ俊鍙峰拰鍒涘缓浠诲姟
+ await SendESSCommands(palletCode, targetAddress, returnTask);
+ }
+ /// <summary>
+ /// 缁橢SS涓嬩换鍔�
+ /// </summary>
+ /// <param name="palletCode"></param>
+ /// <param name="targetAddress"></param>
+ /// <param name="returnTask"></param>
+ /// <returns></returns>
+ /// <exception cref="Exception"></exception>
+ private async Task SendESSCommands(string palletCode, string targetAddress, Dt_Task returnTask)
+ {
+ try
+ {
+ // 1. 鍙戦�佹祦鍔ㄤ俊鍙�
+ var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
+ {
+ slotCode = movestations[targetAddress],
+ containerCode = palletCode
+ });
+
+ //if (moveResult)
+ //{
+ // 2. 鍒涘缓鍥炲簱浠诲姟
+ var essTask = new TaskModel()
+ {
+ taskType = "putaway",
+ taskGroupCode = "",
+ groupPriority = 0,
+ tasks = new List<TasksType>{ new() {
+ taskCode = returnTask.TaskNum.ToString(),
+ taskPriority = 0,
+ taskDescribe = new TaskDescribeType
+ {
+ containerCode = palletCode,
+ containerType = "CT_KUBOT_STANDARD",
+ fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
+ toStationCode = "",
+ toLocationCode = returnTask.TargetAddress,
+ deadline = 0,
+ storageTag = ""
+ }
+ } }
+ };
+
+ var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
+ _logger.LogInformation($"ReturnRemaining 鍒涘缓浠诲姟鎴愬姛: {resultTask}");
+ //}
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"ReturnRemaining ESS鍛戒护鍙戦�佸け璐�: {ex.Message}");
+ throw new Exception($"ESS绯荤粺閫氫俊澶辫触: {ex.Message}");
+ }
+ }
+
+ #endregion
+
+ #region 璁㈠崟鐘舵�佺鐞�
+
+ private async Task CheckAndUpdateOrderStatus(string orderNo)
+ {
+ try
+ {
+ 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 = true;
+ bool hasPartial = false;
+ bool hasLocked = false;
+
+ foreach (var detail in orderDetails)
+ {
+ if (detail.OverOutQuantity < detail.NeedOutQuantity)
+ {
+ allCompleted = false;
+ }
+
+ if (detail.OrderDetailStatus == OrderDetailStatusEnum.Outbound.ObjToInt())
+ {
+ hasPartial = true;
+ }
+
+ //if (detail.OrderDetailStatus == OrderDetailStatusEnum.Locked.ObjToInt())
+ //{
+ // hasLocked = true;
+ //}
+ }
+
+ var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+ .FirstAsync(x => x.OrderNo == orderNo);
+
+ if (outboundOrder == null) return;
+
+ int newStatus;
+ if (allCompleted)
+ {
+ newStatus = (int)OutOrderStatusEnum.鍑哄簱瀹屾垚;
+ }
+ else if (hasPartial)
+ {
+ newStatus = (int)OutOrderStatusEnum.鍑哄簱涓�;
+ }
+ else
+ {
+ newStatus = (int)OutOrderStatusEnum.鏈紑濮�;
+ }
+
+ if (outboundOrder.OrderStatus != newStatus)
+ {
+ await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
+ .SetColumns(x => new Dt_OutboundOrder
+ {
+ OrderStatus = newStatus,
+ Operator = App.User.UserName,
+ })
+ .Where(x => x.OrderNo == orderNo)
+ .ExecuteCommandAsync();
+
+ _logger.LogInformation($"璁㈠崟鐘舵�佹洿鏂� - OrderNo: {orderNo}, 鏃х姸鎬�: {outboundOrder.OrderStatus}, 鏂扮姸鎬�: {newStatus}");
+
+ // 鍙湁姝e父鍒嗘嫞瀹屾垚鏃舵墠鍚慚ES鍙嶉
+ if (allCompleted && newStatus == (int)OutOrderStatusEnum.鍑哄簱瀹屾垚)
+ {
+ await HandleOrderCompletion(outboundOrder, orderNo);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"CheckAndUpdateOrderStatus澶辫触 - OrderNo: {orderNo}, Error: {ex.Message}");
+ }
+ }
+
+ /// <summary>
+ /// 鏇存柊璁㈠崟鏄庣粏鐘舵�侊紙鍩轰簬宸插嚭搴撴暟閲忥級
+ /// </summary>
+ private async Task UpdateOrderDetailStatus(int orderDetailId)
+ {
+ var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+ .FirstAsync(x => x.Id == orderDetailId);
+
+ if (orderDetail == null) return;
+
+ int newStatus = orderDetail.OrderDetailStatus;
+
+ if (orderDetail.OverOutQuantity >= orderDetail.NeedOutQuantity)
+ {
+ // 宸插嚭搴撴暟閲� >= 闇�姹傛暟閲忥紝鏍囪涓哄畬鎴�
+ newStatus = OrderDetailStatusEnum.Over.ObjToInt();
+ }
+ else if (orderDetail.OverOutQuantity > 0)
+ {
+ // 鏈夐儴鍒嗗嚭搴擄紝浣嗘湭瀹屾垚
+ newStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+ }
+ else if (orderDetail.LockQuantity > 0)
+ {
+ // 鏈夐攣瀹氭暟閲忥紝浣嗘湭鍑哄簱
+ //newStatus = OrderDetailStatusEnum.Locked.ObjToInt();
+ }
+ else
+ {
+ // 鏂板缓鐘舵��
+ newStatus = OrderDetailStatusEnum.New.ObjToInt();
+ }
+
+ // 鍙湁鐘舵�佸彂鐢熷彉鍖栨椂鎵嶆洿鏂�
+ if (orderDetail.OrderDetailStatus != newStatus)
+ {
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ OrderDetailStatus = newStatus,
+
+ })
+ .Where(it => it.Id == orderDetailId)
+ .ExecuteCommandAsync();
+ }
+ }
+ /// <summary>
+ /// 鏇存柊璁㈠崟鏄庣粏閿佸畾鏁伴噺
+ /// </summary>
+ private async Task UpdateOrderDetailLockQuantity(int orderDetailId)
+ {
+ var totalLockedQty = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(x => x.OrderDetailId == orderDetailId &&
+ x.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
+ .SumAsync(x => x.AssignQuantity - x.PickedQty);
+
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(it => new Dt_OutboundOrderDetail
+ {
+ LockQuantity = totalLockedQty
+ })
+ .Where(it => it.Id == orderDetailId)
+ .ExecuteCommandAsync();
+ }
+ private async Task UpdateOrderStatusForReturn(string orderNo)
+ {
+ try
+ {
+ 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 = true;
+ foreach (var detail in orderDetails)
+ {
+ if (detail.OverOutQuantity < detail.NeedOutQuantity)
+ {
+ allCompleted = false;
+ break;
+ }
+ }
+
+ var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+ .FirstAsync(x => x.OrderNo == orderNo);
+
+ if (outboundOrder == null) return;
+
+ int newStatus = allCompleted ? (int)OutOrderStatusEnum.鍑哄簱瀹屾垚 : (int)OutOrderStatusEnum.鍑哄簱涓�;
+
+ if (outboundOrder.OrderStatus != newStatus)
+ {
+ await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
+ .SetColumns(x => new Dt_OutboundOrder
+ {
+ OrderStatus = newStatus,
+ Operator = App.User.UserName,
+ })
+ .Where(x => x.OrderNo == orderNo)
+ .ExecuteCommandAsync();
+
+ _logger.LogInformation($"鍥炲簱鎿嶄綔鏇存柊璁㈠崟鐘舵�� - OrderNo: {orderNo}, 鏂扮姸鎬�: {newStatus}");
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"UpdateOrderStatusForReturn澶辫触 - OrderNo: {orderNo}, Error: {ex.Message}");
+ }
+ }
+
+ private async Task HandleOrderCompletion(Dt_OutboundOrder outboundOrder, string orderNo)
+ {
+ // 璋冩嫧鍑哄簱鍜岄噸妫�鍑哄簱涓嶉渶瑕佸弽棣圡ES
+ if (outboundOrder.OrderType == OutOrderTypeEnum.Allocate.ObjToInt())
+ {
+ var allocate = _allocateService.Repository.QueryData(x => x.UpperOrderNo == outboundOrder.UpperOrderNo).First();
+ var allocatefeedmodel = new AllocateDto
+ {
+ ReqCode = Guid.NewGuid().ToString(),
+ ReqTime = DateTime.Now.ToString(),
+ BusinessType = "3",
+
+ FactoryArea = outboundOrder.FactoryArea,
+ OperationType = 1,
+ Operator = App.User.UserName,
+ OrderNo = outboundOrder.UpperOrderNo,
+ // documentsNO = outboundOrder.OrderNo,
+ // status = outboundOrder.OrderStatus,
+ fromWarehouse = allocate?.FromWarehouse ?? "",
+ toWarehouse = allocate?.ToWarehouse ?? "",
+ Details = new List<AllocateDtoDetail>()
+
+ };
+ // 鍙幏鍙栧凡鎷i�夊畬鎴愮殑閿佸畾璁板綍
+ var lists = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(x => x.OrderNo == orderNo && x.Status == (int)OutLockStockStatusEnum.鎷i�夊畬鎴�)
+ .ToListAsync();
+
+ var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.lineNo, item.Unit, item.WarehouseCode })
+ .Select(group => new AllocateDtoDetail
+ {
+ MaterialCode = group.Key.MaterielCode,
+ LineNo = group.Key.lineNo,
+ WarehouseCode = group.Key.WarehouseCode,
+ Qty = group.Sum(x => x.PickedQty),
+
+ Unit = group.Key.Unit,
+ Barcodes = group.Select(row => new BarcodeInfo
+ {
+ Barcode = row.CurrentBarcode,
+ SupplyCode = row.SupplyCode,
+ BatchNo = row.BatchNo,
+ Unit = row.Unit,
+ Qty = row.PickedQty
+ }).ToList()
+
+
+ }).ToList();
+ allocatefeedmodel.Details = groupedData;
+
+ var result = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
+ if (result != null && result.code == 200)
+ {
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(x => x.ReturnToMESStatus == 1)
+ .Where(x => x.OrderId == outboundOrder.Id).ExecuteCommandAsync();
+
+ await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
+ .SetColumns(x => new Dt_OutboundOrder
+ {
+ ReturnToMESStatus = 1,
+ Operator = App.User.UserName,
+ }).Where(x => x.OrderNo == orderNo).ExecuteCommandAsync();
+ }
+ }
+ else if (outboundOrder.OrderType == OutOrderTypeEnum.ReCheck.ObjToInt())
+ {
+
+ }
+ else
+ {
+ try
+ {
+ var feedmodel = new FeedbackOutboundRequestModel
+ {
+ reqCode = Guid.NewGuid().ToString(),
+ reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+ business_type = outboundOrder.BusinessType,
+ factoryArea = outboundOrder.FactoryArea,
+ operationType = 1,
+ Operator = App.User.UserName,
+ orderNo = outboundOrder.UpperOrderNo,
+ documentsNO = outboundOrder.OrderNo,
+ status = outboundOrder.OrderStatus,
+ details = new List<FeedbackOutboundDetailsModel>()
+ };
+
+ // 鍙幏鍙栧凡鎷i�夊畬鎴愮殑閿佸畾璁板綍
+ var lists = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(x => x.OrderNo == orderNo && x.Status == (int)OutLockStockStatusEnum.鎷i�夊畬鎴�)
+ .ToListAsync();
+
+ var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.lineNo, item.Unit, item.WarehouseCode })
+ .Select(group => new FeedbackOutboundDetailsModel
+ {
+ materialCode = group.Key.MaterielCode,
+ lineNo = group.Key.lineNo,
+ warehouseCode = group.Key.WarehouseCode,
+ qty = group.Sum(x => x.BarcodeQty),
+ currentDeliveryQty = group.Sum(x => x.BarcodeQty),
+ unit = group.Key.Unit,
+ barcodes = group.Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
+ {
+ barcode = row.CurrentBarcode,
+ supplyCode = row.SupplyCode,
+ batchNo = row.BatchNo,
+ unit = row.BarcodeUnit,
+ qty = row.BarcodeQty
+ }).ToList()
+ }).ToList();
+
+ feedmodel.details = groupedData;
+
+ var result = await _invokeMESService.FeedbackOutbound(feedmodel);
+ if (result != null && result.code == 200)
+ {
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(x => x.ReturnToMESStatus == 1)
+ .Where(x => x.OrderId == outboundOrder.Id)
+ .ExecuteCommandAsync();
+
+ await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
+ .SetColumns(x => new Dt_OutboundOrder
+ {
+ ReturnToMESStatus = 1,
+ Operator = App.User.UserName,
+ })
+
+ .Where(x => x.OrderNo == orderNo)
+ .ExecuteCommandAsync();
+ }
+
+ _logger.LogError($"FeedbackOutbound鎴愬姛 - OrderNo: {orderNo}, {JsonSerializer.Serialize(result)}");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"FeedbackOutbound澶辫触 - OrderNo: {orderNo}, Error: {ex.Message}");
+ }
+ }
+ }
+
+ #endregion
+
+ #region 绌烘墭鐩�
+
+ /// <summary>
+ /// 娓呯悊闆跺簱瀛樻暟鎹�
+ /// </summary>
+ private async Task CleanupZeroStockData(int stockId)
+ {
+ try
+ {
+ // 1. 鍒犻櫎搴撳瓨鏁伴噺涓�0鐨勬槑缁嗚褰�
+ var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
+ .Where(x => x.StockId == stockId && x.StockQuantity == 0 && (x.Status == StockStatusEmun.鍑哄簱瀹屾垚.ObjToInt() || x.Status ==
+ StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt()))
+ .ExecuteCommandAsync();
+
+ await _stockInfoService.Db.Deleteable<Dt_StockInfo>()
+ .Where(x => x.Id == stockId).ExecuteCommandAsync();
+
+ _logger.LogInformation($"娓呯悊闆跺簱瀛樻槑缁嗚褰� - StockId: {stockId}, 鍒犻櫎璁板綍鏁�: {deleteDetailCount}");
+
+
+
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"娓呯悊闆跺簱瀛樻暟鎹け璐� - StockId: {stockId}, Error: {ex.Message}");
+ // 娉ㄦ剰锛氭竻鐞嗗け璐ヤ笉搴旇褰卞搷涓绘祦绋�
+ }
+ }
+ /// <summary>
+ /// 澶勭悊浠诲姟娓呯悊锛堟寜璁㈠崟鍜屾墭鐩橈級
+ /// </summary>
+ private async Task HandleTaskCleanup(string orderNo, string palletCode)
+ {
+ try
+ {
+ // 1. 鏌ユ壘鎵�鏈変笌璇ヨ鍗曞拰鎵樼洏鐩稿叧鐨勪换鍔�
+ var tasks = await _taskRepository.Db.Queryable<Dt_Task>().Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode).ToListAsync();
+
+ if (tasks.Any())
+ {
+ foreach (var task in tasks)
+ {
+ task.TaskStatus = (int)TaskStatusEnum.Finish;
+ }
+ // await _taskRepository.Db.Updateable(tasks).ExecuteCommandAsync();
+
+ _taskRepository.DeleteAndMoveIntoHty(tasks, OperateTypeEnum.鑷姩瀹屾垚);
+ _logger.LogInformation($"瀹屾垚{tasks.Count}涓墭鐩樹换鍔� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}");
+ }
+
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"澶勭悊浠诲姟娓呯悊澶辫触 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
+ throw new Exception($"浠诲姟娓呯悊澶辫触: {ex.Message}");
+ }
+ }
+ /// <summary>
+ /// 鏇存柊璁㈠崟鐩稿叧鏁版嵁
+ /// </summary>
+ private async Task UpdateOrderData(string orderNo, string palletCode)
+ {
+ try
+ {
+ // 妫�鏌ヨ鍗曟槸鍚﹁繕鏈夊叾浠栨墭鐩樺湪澶勭悊涓�
+ var otherActivePallets = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(x => x.OrderNo == orderNo &&
+ x.PalletCode != palletCode &&
+ (x.Status == (int)OutLockStockStatusEnum.鍑哄簱涓� || x.Status == (int)OutLockStockStatusEnum.鍥炲簱涓�))
+ .AnyAsync();
+
+ var otherActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>()
+ .Where(x => x.OrderNo == orderNo &&
+ x.PalletCode != palletCode
+ // && x.TaskStatus.In((int)TaskStatusEnum.寰呮墽琛�, (int)TaskStatusEnum.鎵ц涓�)
+ )
+ .AnyAsync();
+
+ // 濡傛灉娌℃湁鍏朵粬鎵樼洏鍦ㄥ鐞嗭紝妫�鏌ヨ鍗曟槸鍚﹀簲璇ュ畬鎴�
+ if (!otherActivePallets && !otherActiveTasks)
+ {
+ await CheckAndUpdateOrderCompletion(orderNo);
+ }
+ else
+ {
+ _logger.LogInformation($"璁㈠崟 {orderNo} 杩樻湁鍏朵粬鎵樼洏鍦ㄥ鐞嗭紝涓嶆洿鏂拌鍗曠姸鎬�");
+ }
+
+ // 3. 鏇存柊鎷i�夎褰曠姸鎬侊紙鍙�夛級
+ await UpdatePickingRecordsStatus(orderNo, palletCode);
+
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"鏇存柊璁㈠崟鏁版嵁澶辫触 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
+ throw new Exception($"鏇存柊璁㈠崟鏁版嵁澶辫触: {ex.Message}");
+ }
+ }
+
+ /// <summary>
+ /// 妫�鏌ュ苟鏇存柊璁㈠崟瀹屾垚鐘舵��
+ /// </summary>
+ private async Task CheckAndUpdateOrderCompletion(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 = true;
+ foreach (var detail in orderDetails)
+ {
+ if (detail.OverOutQuantity < detail.NeedOutQuantity)
+ {
+ allCompleted = false;
+ break;
+ }
+ }
+
+ var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+ .FirstAsync(x => x.OrderNo == orderNo);
+
+ if (outboundOrder != null && allCompleted && outboundOrder.OrderStatus != (int)OutOrderStatusEnum.鍑哄簱瀹屾垚)
+ {
+ outboundOrder.OrderStatus = (int)OutOrderStatusEnum.鍑哄簱瀹屾垚;
+ outboundOrder.Operator = App.User.UserName;
+ await _outboundOrderService.Db.Updateable(outboundOrder).ExecuteCommandAsync();
+
+ _logger.LogInformation($"璁㈠崟 {orderNo} 宸叉爣璁颁负鍑哄簱瀹屾垚");
+
+ // 鍚慚ES鍙嶉璁㈠崟瀹屾垚锛堝鏋滈渶瑕侊級
+ await HandleOrderCompletion(outboundOrder, orderNo);
+ }
+ }
+
+ /// <summary>
+ /// 鏇存柊鎷i�夎褰曠姸鎬�
+ /// </summary>
+ private async Task UpdatePickingRecordsStatus(string orderNo, string palletCode)
+ {
+ try
+ {
+ // 鍙互灏嗙浉鍏崇殑鎷i�夎褰曟爣璁颁负宸插畬鎴�
+ var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
+ .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
+ .ToListAsync();
+
+ // 杩欓噷鍙互鏍规嵁闇�瑕佹洿鏂版嫞閫夎褰曠殑鐘舵�佸瓧娈�
+ // 渚嬪锛歱ickingRecord.Status = (int)PickingStatusEnum.宸插畬鎴�;
+
+ _logger.LogInformation($"鎵惧埌{pickingRecords.Count}鏉℃嫞閫夎褰� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}");
+
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"鏇存柊鎷i�夎褰曠姸鎬佸け璐�: {ex.Message}");
+ }
+ }
+ #endregion
+
+
+
+ #region 杈呭姪鏂规硶
+ /// <summary>
+ /// 缁熶竴鍒嗘瀽鎵樼洏鐘舵�� - 杩斿洖鎵樼洏鐨勫畬鏁寸姸鎬佷俊鎭�
+ /// </summary>
+ private async Task<PalletStatusAnalysis> AnalyzePalletStatus(string orderNo, string palletCode, int stockId)
+ {
+ var result = new PalletStatusAnalysis
+ {
+ OrderNo = orderNo,
+ PalletCode = palletCode,
+ StockId = stockId
+ };
+
+ // 鍒嗘瀽鏈垎鎷g殑鍑哄簱閿佸畾璁板綍
+ var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(it => it.OrderNo == orderNo &&
+ it.PalletCode == palletCode &&
+ it.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
+ .ToListAsync();
+
+ if (remainingLocks.Any())
+ {
+ result.HasRemainingLocks = true;
+ result.RemainingLocks = remainingLocks;
+ result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty);
+ _logger.LogInformation($"鍙戠幇{remainingLocks.Count}鏉℃湭鍒嗘嫞閿佸畾璁板綍锛屾�绘暟閲�: {result.RemainingLocksReturnQty}");
+ }
+
+ // 鍒嗘瀽鎵樼洏涓婄殑搴撳瓨璐х墿
+ var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ .Where(it => it.StockId == stockId &&
+ (it.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
+ it.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt() ||
+ it.Status == StockStatusEmun.鍑哄簱閿佸畾.ObjToInt()))
+ .Where(it => it.StockQuantity > 0)
+ .ToListAsync();
+
+ if (palletStockGoods.Any())
+ {
+ result.HasPalletStockGoods = true;
+ result.PalletStockGoods = palletStockGoods;
+ result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity);
+ _logger.LogInformation($"鍙戠幇{palletStockGoods.Count}涓簱瀛樿揣鐗╋紝鎬绘暟閲�: {result.PalletStockReturnQty}");
+
+ // 璁板綍璇︾粏鐘舵�佸垎甯�
+ var statusGroups = palletStockGoods.GroupBy(x => x.Status);
+ foreach (var group in statusGroups)
+ {
+ _logger.LogInformation($"搴撳瓨鐘舵�亄group.Key}: {group.Count()}涓揣鐗╋紝鏁伴噺: {group.Sum(x => x.StockQuantity)}");
+ }
+ }
+
+ //鍒嗘瀽鎷嗗寘璁板綍
+ var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
+ .Where(it => it.OrderNo == orderNo &&
+ it.PalletCode == palletCode &&
+ !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.宸叉嫞閫� &&
+ it.Status != (int)SplitPackageStatusEnum.宸插洖搴�)
+ .ToListAsync();
+
+ if (splitRecords.Any())
+ {
+ result.HasSplitRecords = true;
+ result.SplitRecords = splitRecords;
+ result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId);
+
+ _logger.LogInformation($"鍙戠幇{splitRecords.Count}鏉℃湭鎷i�夋媶鍖呰褰曪紝鎬绘暟閲�: {result.SplitReturnQty}");
+ }
+
+ // 4. 璁$畻鎬诲洖搴撴暟閲忓拰绌烘墭鐩樼姸鎬�
+ result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty;
+ result.HasItemsToReturn = result.TotalReturnQty > 0;
+ result.IsEmptyPallet = !result.HasItemsToReturn;
+
+ // 5. 妫�鏌ユ槸鍚︽湁杩涜涓殑浠诲姟
+ result.HasActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>()
+ .Where(x => x.OrderNo == orderNo && x.TaskType == TaskTypeEnum.InPick.ObjToInt() &&
+ x.PalletCode == palletCode &&
+ x.TaskStatus == (int)TaskStatusEnum.New)
+ .AnyAsync();
+
+ _logger.LogInformation($"鎵樼洏鐘舵�佸垎鏋愬畬鎴� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}, " +
+ $"鎬诲洖搴撴暟閲�: {result.TotalReturnQty}, 鏄惁绌烘墭鐩�: {result.IsEmptyPallet}, " +
+ $"鏈夎繘琛屼腑浠诲姟: {result.HasActiveTasks}");
+
+ return result;
+ }
+
+ /// <summary>
+ /// 妫�鏌ユ墭鐩樻槸鍚︿负绌�
+ /// </summary>
+ private async Task<bool> IsPalletEmpty(string orderNo, string palletCode)
+ {
+ try
+ {
+ // 鑾峰彇搴撳瓨淇℃伅
+ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ return false;
+
+ // 浣跨敤缁熶竴鐨勭姸鎬佸垎鏋�
+ var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
+ return statusAnalysis.IsEmptyPallet;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning($"妫�鏌ユ墭鐩樻槸鍚︿负绌哄け璐� - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
+ return false;
+ }
+ }
+ /// <summary>
+ /// 妫�鏌ュ苟澶勭悊绌烘墭鐩�
+ /// </summary>
+ private async Task<bool> CheckAndHandleEmptyPallet(string orderNo, string palletCode)
+ {
+ try
+ {
+ // 1. 鑾峰彇搴撳瓨淇℃伅
+ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ {
+ _logger.LogWarning($"鏈壘鍒版墭鐩� {palletCode} 鐨勫簱瀛樹俊鎭�");
+ return false;
+ }
+
+ // 2. 浣跨敤缁熶竴鐨勭姸鎬佸垎鏋�
+ var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
+
+ // 3. 妫�鏌ユ槸鍚︿负绌烘墭鐩樹笖娌℃湁杩涜涓殑浠诲姟
+ if (!statusAnalysis.IsEmptyPallet || statusAnalysis.HasActiveTasks)
+ {
+ return false;
+ }
+
+ _logger.LogInformation($"妫�娴嬪埌绌烘墭鐩橈紝寮�濮嬭嚜鍔ㄥ鐞� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}");
+
+ //// 娓呯悊闆跺簱瀛樻暟鎹�
+ //await CleanupZeroStockData(stockInfo.Id);
+
+ //// 鏇存柊搴撳瓨涓昏〃鐘舵�佷负绌烘墭鐩�
+ //await UpdateStockInfoAsEmpty(stockInfo);
+
+ //// 澶勭悊鍑哄簱閿佸畾璁板綍
+ //await HandleOutStockLockRecords(orderNo, palletCode);
+
+ //// 澶勭悊浠诲姟鐘舵��
+ //await HandleTaskStatusForEmptyPallet(orderNo, palletCode);
+
+ //// 鏇存柊璁㈠崟鏁版嵁
+ //await UpdateOrderDataForEmptyPallet(orderNo, palletCode);
+
+ ////璁板綍鎿嶄綔鍘嗗彶
+ //await RecordAutoEmptyPalletOperation(orderNo, palletCode);
+
+ _logger.LogInformation($"绌烘墭鐩樿嚜鍔ㄥ鐞嗗畬鎴� - 璁㈠崟: {orderNo}, 鎵樼洏: {palletCode}");
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"鑷姩澶勭悊绌烘墭鐩樺け璐� - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
+ return false;
+ }
+ }
+
+ private async Task<string> GenerateNewBarcode()
+ {
+ var seq = await _dailySequenceService.GetNextSequenceAsync();
+ return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
+ }
+
+ private async Task<Dt_OutStockLockInfo> CreateSplitLockInfo(Dt_OutStockLockInfo originalLock, decimal quantity, string newBarcode)
+ {
+ var newLockInfo = new Dt_OutStockLockInfo
+ {
+ OrderNo = originalLock.OrderNo,
+ OrderDetailId = originalLock.OrderDetailId,
+ BatchNo = originalLock.BatchNo,
+ MaterielCode = originalLock.MaterielCode,
+ MaterielName = originalLock.MaterielName,
+ StockId = originalLock.StockId,
+ OrderQuantity = quantity,
+ OriginalQuantity = quantity,
+ AssignQuantity = quantity,
+ PickedQty = quantity,
+ LocationCode = originalLock.LocationCode,
+ PalletCode = originalLock.PalletCode,
+ TaskNum = originalLock.TaskNum,
+ Status = (int)OutLockStockStatusEnum.鎷i�夊畬鎴�,
+ Unit = originalLock.Unit,
+ SupplyCode = originalLock.SupplyCode,
+ OrderType = originalLock.OrderType,
+ CurrentBarcode = newBarcode,
+ OriginalLockQuantity = quantity,
+ IsSplitted = 1,
+ ParentLockId = originalLock.Id,
+ Operator = App.User.UserName,
+ FactoryArea = originalLock.FactoryArea,
+ lineNo = originalLock.lineNo,
+ WarehouseCode = originalLock.WarehouseCode,
+ BarcodeQty = originalLock.BarcodeQty,
+ BarcodeUnit = originalLock.BarcodeUnit,
+
+ };
+
+ var newLockId = await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteReturnIdentityAsync();
+ newLockInfo.Id = newLockId;
+ return newLockInfo;
+ }
+
+ private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
+ decimal splitQty, decimal remainQty, string newBarcode)
+ {
+ var splitHistory = new Dt_SplitPackageRecord
+ {
+ FactoryArea = lockInfo.FactoryArea,
+ TaskNum = lockInfo.TaskNum,
+ OutStockLockInfoId = lockInfo.Id,
+ StockId = stockDetail.StockId,
+ Operator = App.User.UserName,
+ IsReverted = false,
+ OriginalBarcode = stockDetail.Barcode,
+ NewBarcode = newBarcode,
+ SplitQty = splitQty,
+ RemainQuantity = remainQty,
+ MaterielCode = lockInfo.MaterielCode,
+ SplitTime = DateTime.Now,
+ OrderNo = lockInfo.OrderNo,
+ PalletCode = lockInfo.PalletCode,
+ Status = (int)SplitPackageStatusEnum.宸叉嫞閫�
+ };
+
+ await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
+ }
+
+ private List<SplitResult> CreateSplitResults(Dt_OutStockLockInfo lockInfo, decimal splitQty, decimal remainQty, string newBarcode, string originalBarcode)
+ {
+ return new List<SplitResult>
+ {
+ new SplitResult
+ {
+ materialCode = lockInfo.MaterielCode,
+ supplierCode = lockInfo.SupplyCode,
+ quantityTotal = splitQty.ToString("F2"),
+ batchNumber = newBarcode,
+ batch = lockInfo.BatchNo,
+ factory = lockInfo.FactoryArea,
+ date = DateTime.Now.ToString("yyyy-MM-dd"),
+ },
+ new SplitResult
+ {
+ materialCode = lockInfo.MaterielCode,
+ supplierCode = lockInfo.SupplyCode,
+ quantityTotal = remainQty.ToString("F2"),
+ batchNumber = originalBarcode,
+ batch = lockInfo.BatchNo,
+ factory = lockInfo.FactoryArea,
+ date = DateTime.Now.ToString("yyyy-MM-dd"),
+ }
+ };
+ }
+
+ private async Task UpdateSplitRecordsStatus(string barcode)
+ {
+ var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
+ .Where(it => it.OriginalBarcode == barcode || it.NewBarcode == barcode)
+ .Where(it => !it.IsReverted)
+ .ToListAsync();
+
+ foreach (var record in relatedSplitRecords)
+ {
+ record.Status = (int)SplitPackageStatusEnum.宸叉嫞閫�;
+ await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
+ }
+ }
+
+ private async Task<int> GenerateTaskNumber()
+ {
+ return await _dailySequenceService.GetNextSequenceAsync();
+ }
+
+ private WebResponseContent CreatePickingResponse(PickingResult result, string adjustedReason)
+ {
+ //if (result.SplitResults.Any())
+ //{
+ // var responseData = new { SplitResults = result.SplitResults, AdjustedReason = "" };
+ // if (!string.IsNullOrEmpty(adjustedReason))
+ // {
+ // responseData = new { SplitResults = result.SplitResults, AdjustedReason = adjustedReason };
+ // }
+ // return WebResponseContent.Instance.OK("鎷i�夌‘璁ゆ垚鍔燂紝宸茶嚜鍔ㄦ媶鍖�", responseData);
+ //}
+
+ //if (!string.IsNullOrEmpty(adjustedReason))
+ //{
+ // return WebResponseContent.Instance.OK($"鎷i�夌‘璁ゆ垚鍔燂紙{adjustedReason}锛�");
+ //}
+
+ //return WebResponseContent.Instance.OK("鎷i�夌‘璁ゆ垚鍔�");
+
+ if (result.SplitResults.Any())
+ {
+ return WebResponseContent.Instance.OK("鎷i�夌‘璁ゆ垚鍔燂紝宸茶嚜鍔ㄦ媶鍖�", new { SplitResults = result.SplitResults });
+ }
+ return WebResponseContent.Instance.OK("鎷i�夌‘璁ゆ垚鍔�", new { SplitResults = new List<SplitResult>() });
+ }
+
+
+
+ #region 铏氭嫙鍑哄叆搴�
+ public WebResponseContent GetAvailablePurchaseOrders()
+ {
+ List<Dt_InboundOrder> InOders = _inboundOrderRepository.QueryData().Where(x => x.OrderStatus != InOrderStatusEnum.鍏ュ簱瀹屾垚.ObjToInt()).ToList();
+ List<string> InOderCodes = InOders.Select(x => x.UpperOrderNo).ToList();
+ return WebResponseContent.Instance.OK("鎴愬姛", data: InOderCodes);
+ }
+
+ public WebResponseContent GetAvailablePickingOrders()
+ {
+ List<Dt_OutboundOrder> outOders = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderStatus != OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt()).ToList();
+
+ List<string> outOderCodes = outOders.Select(x => x.UpperOrderNo).ToList();
+ return WebResponseContent.Instance.OK("鎴愬姛", data: outOderCodes);
+
+ }
+ public WebResponseContent BarcodeValidate(NoStockOutModel noStockOut)
+ {
+ try
+ {
+ Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.鍏ュ簱瀹屾垚.ObjToInt()).Includes(x => x.Details).First();
+ if (inboundOrder == null)
+ {
+ return WebResponseContent.Instance.Error($"鏈壘鍒伴噰璐崟锛歿noStockOut.inOder}");
+ }
+ var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
+
+ if (matchedDetail == null)
+ {
+ return WebResponseContent.Instance.Error($"鍦ㄩ噰璐崟 {noStockOut.inOder} 涓湭鎵惧埌鏉$爜涓� {noStockOut.barCode} 鐨勬槑缁嗐��");
+ }
+ matchedDetail.NoStockOutQty = 0;
+
+ Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt()).Includes(x => x.Details).First();
+ if (outboundOrder == null)
+ {
+ return WebResponseContent.Instance.Error($"鏈壘鍒板嚭搴撳崟锛歿noStockOut.inOder}");
+ }
+ var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
+
+ if (matchedCode == null)
+ {
+ return WebResponseContent.Instance.Error($"鍦ㄥ嚭搴撳崟鐨勭墿鏂欑紪鐮佷腑鏈壘鍒颁笌閲囪喘鍗曚腑鐨剓matchedDetail.MaterielCode} 瀵瑰簲鐨勭墿鏂欍��");
+ }
+ matchedCode.NoStockOutQty = 0;
+
+ //鍓╀綑鍏ュ簱鏁伴噺鍗宠櫄鎷熷嚭鍏ュ簱鍓╀綑鍙嚭鏁伴噺
+ decimal outQuantity = matchedDetail.OrderQuantity - matchedDetail.ReceiptQuantity;
+ if (outQuantity == 0)
+ {
+ return WebResponseContent.Instance.Error($"璇ラ噰璐崟涓殑鏉$爜瀵瑰簲鐨勫彲鍑烘暟閲忎负0");
+ }
+ if (matchedCode.OrderQuantity < outQuantity)
+ {
+ return WebResponseContent.Instance.Error($"璇ラ噰璐崟涓殑鏉$爜瀵瑰簲鐨勫彲鍑烘暟閲忚秴鍑哄嚭搴撳崟鍑哄簱鏁伴噺{matchedDetail.OrderQuantity - matchedCode.OrderQuantity},涓嶆弧瓒虫暣鍖呭嚭搴�");
+ }
+ //鍗曟嵁鍑哄簱閿佸畾鏁伴噺
+ matchedDetail.NoStockOutQty += outQuantity;
+ matchedCode.NoStockOutQty += outQuantity;
+
+ if ((matchedCode.LockQuantity + matchedCode.NoStockOutQty) > matchedCode.OrderQuantity)
+ {
+ return WebResponseContent.Instance.Error($"鍑哄簱鍗曟槑缁嗘暟閲忔孩鍑簕matchedCode.LockQuantity - matchedCode.OrderQuantity}");
+ }
+ matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.Inbounding.ObjToInt();
+ matchedCode.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
+
+ _unitOfWorkManage.BeginTran();
+ _inboundOrderDetailService.UpdateData(matchedDetail);
+ _outboundOrderDetailService.UpdateData(matchedCode);
+ _unitOfWorkManage.CommitTran();
+ return WebResponseContent.Instance.OK();
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+ }
+
+
+ public WebResponseContent DeleteBarcode(NoStockOutModel noStockOut)
+ {
+ try
+ {
+ Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.鍏ュ簱瀹屾垚.ObjToInt()).Includes(x => x.Details).First();
+ if (inboundOrder == null)
+ {
+ return WebResponseContent.Instance.Error($"鏈壘鍒伴噰璐崟锛歿noStockOut.inOder}");
+ }
+ var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
+
+ if (matchedDetail == null)
+ {
+ return WebResponseContent.Instance.Error($"鍦ㄩ噰璐崟 {noStockOut.inOder} 涓湭鎵惧埌鏉$爜涓� {noStockOut.barCode} 鐨勬槑缁嗐��");
+ }
+ matchedDetail.NoStockOutQty = 0;
+
+ if (matchedDetail.ReceiptQuantity == 0 && matchedDetail.OverInQuantity == 0)
+ {
+ matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
+ }
+
+ Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt()).Includes(x => x.Details).First();
+ if (outboundOrder == null)
+ {
+ return WebResponseContent.Instance.Error($"鏈壘鍒板嚭搴撳崟锛歿noStockOut.inOder}");
+ }
+ var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
+
+ if (matchedCode == null)
+ {
+ return WebResponseContent.Instance.Error($"鍦ㄥ嚭搴撳崟鐨勭墿鏂欑紪鐮佷腑鏈壘鍒颁笌閲囪喘鍗曚腑鐨剓matchedDetail.MaterielCode} 瀵瑰簲鐨勭墿鏂欍��");
+ }
+ matchedCode.NoStockOutQty = 0;
+ if (matchedCode.LockQuantity == 0 && matchedCode.OverOutQuantity == 0)
+ {
+ matchedCode.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
+ }
+ _unitOfWorkManage.BeginTran();
+ _inboundOrderDetailService.UpdateData(matchedDetail);
+ _outboundOrderDetailService.UpdateData(matchedCode);
+ _unitOfWorkManage.CommitTran();
+ return WebResponseContent.Instance.OK();
+
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+ }
+
+ public async Task<WebResponseContent> NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit)
+ {
+ try
+ {
+ Dt_InboundOrder inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.InOderSubmit && x.OrderStatus != InOrderStatusEnum.鍏ュ簱瀹屾垚.ObjToInt()).Includes(x => x.Details).First();
+ if (inboundOrder == null)
+ {
+ return WebResponseContent.Instance.Error($"鏈壘鍒伴噰璐崟锛歿noStockOutSubmit.InOderSubmit}");
+ }
+ Dt_OutboundOrder outboundOrder = _inboundOrderRepository.Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.OutOderSubmit && x.OrderStatus != OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt()).Includes(x => x.Details).First();
+ if (outboundOrder == null)
+ {
+ return WebResponseContent.Instance.Error($"鏈壘鍒板嚭搴撳崟锛歿noStockOutSubmit.OutOderSubmit}");
+ }
+ List<Dt_InboundOrderDetail> inboundOrderDetails = new List<Dt_InboundOrderDetail>();
+ List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
+ foreach (var BarCode in noStockOutSubmit.BarCodeSubmit)
+ {
+ var inboundOrderDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == BarCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
+
+ if (inboundOrderDetail == null)
+ {
+ return WebResponseContent.Instance.Error($"鍦ㄩ噰璐崟 {noStockOutSubmit.InOderSubmit} 涓湭鎵惧埌鏉$爜涓� {BarCode} 鐨勬槑缁嗐��");
+ }
+ var outboundOrderDetail = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == inboundOrderDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
+
+ if (outboundOrderDetail == null)
+ {
+ return WebResponseContent.Instance.Error($"鍦ㄥ嚭搴撳崟鐨勭墿鏂欑紪鐮佷腑鏈壘鍒颁笌閲囪喘鍗曚腑鐨剓inboundOrderDetail.MaterielCode} 瀵瑰簲鐨勭墿鏂欍��");
+ }
+ inboundOrderDetail.ReceiptQuantity += inboundOrderDetail.NoStockOutQty;
+ inboundOrderDetail.OverInQuantity = inboundOrderDetail.ReceiptQuantity;
+ inboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
+ inboundOrderDetails.Add(inboundOrderDetail);
+
+ outboundOrderDetail.LockQuantity += outboundOrderDetail.NoStockOutQty;
+ outboundOrderDetail.OverOutQuantity = outboundOrderDetail.LockQuantity;
+ if (outboundOrderDetail.OrderQuantity == outboundOrderDetail.OverOutQuantity)
+ {
+ outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
+ }
+ outboundOrderDetails.Add(outboundOrderDetail);
+
+ }
+ //鍒ゆ柇鍏ュ簱鍗曟嵁鏄庣粏鏄惁鍏ㄩ儴鏄畬鎴愮姸鎬�
+ int e = inboundOrder.Details.Count();
+ int w = inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
+ bool inoderOver = inboundOrder.Details.Count() == inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
+ if (inoderOver)
+ {
+ inboundOrder.OrderStatus = InOrderStatusEnum.鍏ュ簱瀹屾垚.ObjToInt();
+ }
+ else
+ {
+ inboundOrder.OrderStatus = InOrderStatusEnum.鍏ュ簱涓�.ObjToInt();
+ }
+ //鍒ゆ柇鍑哄簱鍗曟嵁鏄庣粏鏄惁鍏ㄩ儴鏄畬鎴愮姸鎬�
+ bool outOderOver = outboundOrder.Details.Count() == outboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
+ if (outOderOver)
+ {
+ outboundOrder.OrderStatus = OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt();
+ }
+ else
+ {
+ outboundOrder.OrderStatus = OutOrderStatusEnum.鍑哄簱涓�.ObjToInt();
+ }
+ //鏁版嵁澶勭悊
+ _unitOfWorkManage.BeginTran();
+ _inboundOrderDetailService.UpdateData(inboundOrderDetails);
+ _outboundOrderDetailService.UpdateData(outboundOrderDetails);
+ _inboundOrderRepository.UpdateData(inboundOrder);
+ _outboundOrderService.UpdateData(outboundOrder);
+ _unitOfWorkManage.CommitTran();
+
+ if (inboundOrder.OrderStatus == InOrderStatusEnum.鍏ュ簱瀹屾垚.ObjToInt())
+ {
+ var feedmodel = new FeedbackInboundRequestModel
+ {
+ reqCode = Guid.NewGuid().ToString(),
+ reqTime = DateTime.Now.ToString(),
+ business_type = inboundOrder.BusinessType,
+ factoryArea = inboundOrder.FactoryArea,
+ operationType = 1,
+ Operator = inboundOrder.Operator,
+ orderNo = inboundOrder.UpperOrderNo,
+ status = inboundOrder.OrderStatus,
+ details = new List<FeedbackInboundDetailsModel>()
+
+ };
+
+ var groupedData = inboundOrder.Details.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
+ .Select(group => new FeedbackInboundDetailsModel
+ {
+ materialCode = group.Key.MaterielCode,
+ supplyCode = group.Key.SupplyCode,
+ batchNo = group.Key.BatchNo,
+ lineNo = group.Key.lineNo,
+ warehouseCode = group.Key.WarehouseCode,
+ qty = group.Sum(x => x.BarcodeQty),
+ // warehouseCode= "1072",
+ unit = group.Key.BarcodeUnit,
+ barcodes = group.Select(row => new FeedbackBarcodesModel
+ {
+ barcode = row.Barcode,
+ qty = row.BarcodeQty
+ }).ToList()
+ }).ToList();
+ feedmodel.details = groupedData;
+
+ var result = await _invokeMESService.FeedbackInbound(feedmodel);
+ if (result != null && result.code == 200)
+ {
+ _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
+ .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
+ _inboundOrderDetailService.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
+ .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
+ }
+ }
+ if (outboundOrder.OrderStatus == OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt())
+ {
+ var feedmodel = new FeedbackOutboundRequestModel
+ {
+ reqCode = Guid.NewGuid().ToString(),
+ reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+ business_type = outboundOrder.BusinessType,
+ factoryArea = outboundOrder.FactoryArea,
+ operationType = 1,
+ Operator = outboundOrder.Operator,
+ orderNo = outboundOrder.UpperOrderNo,
+ documentsNO = outboundOrder.OrderNo,
+ status = outboundOrder.OrderStatus,
+ details = new List<FeedbackOutboundDetailsModel>()
+ };
+
+
+ foreach (var detail in outboundOrder.Details)
+ {
+ // 鑾峰彇璇ユ槑缁嗗搴旂殑鏉$爜淇℃伅锛堜粠閿佸畾璁板綍锛�
+ var detailLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+ .Where(x => x.OrderNo == outboundOrder.OrderNo &&
+ x.OrderDetailId == detail.Id &&
+ x.Status == (int)OutLockStockStatusEnum.鎷i�夊畬鎴�)
+ .ToListAsync();
+
+ var detailModel = new FeedbackOutboundDetailsModel
+ {
+ materialCode = detail.MaterielCode,
+ lineNo = detail.lineNo,
+ warehouseCode = detail.WarehouseCode,
+ qty = detail.OverOutQuantity, // 浣跨敤璁㈠崟鏄庣粏鐨勫凡鍑哄簱鏁伴噺
+ currentDeliveryQty = detail.OverOutQuantity,
+ unit = detail.Unit,
+ barcodes = detailLocks.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel
+ {
+ barcode = lockInfo.CurrentBarcode,
+ supplyCode = lockInfo.SupplyCode,
+ batchNo = lockInfo.BatchNo,
+ unit = lockInfo.BarcodeUnit,
+ qty = lockInfo.BarcodeQty //
+ }).ToList()
+ };
+
+ feedmodel.details.Add(detailModel);
+ }
+ var result = await _invokeMESService.FeedbackOutbound(feedmodel);
+ if (result != null && result.code == 200)
+ {
+ await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ .SetColumns(x => x.ReturnToMESStatus == 1)
+ .Where(x => x.OrderId == outboundOrder.Id)
+ .ExecuteCommandAsync();
+
+ await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
+ .SetColumns(x => x.ReturnToMESStatus == 1)
+ .Where(x => x.Id == outboundOrder.Id)
+ .ExecuteCommandAsync();
+ }
+ }
+
+
+ return WebResponseContent.Instance.OK();
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+ }
+ #endregion
+
+ public WebResponseContent UnPalletQuantity(string orderNo)
+ {
+ // 鍒濆鍖栬繑鍥濪TO锛堥粯璁ゅ�奸兘涓�0锛岄伩鍏峮ull锛�
+ var resultDTO = new PalletSumQuantityDTO
+ {
+ StockSumQuantity = 0,
+ StockCount = 0,
+ UniqueUnit = ""
+ };
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ if (string.IsNullOrWhiteSpace(orderNo))
+ {
+ return content.Error("浼犲叆鐨勮鍗曞彿orderNo涓虹┖鎴栫┖鐧�");
+ }
+ var orderDetail = Db.Queryable<Dt_PickingRecord>().Where(s => s.OrderNo == orderNo).ToList();
+ if (orderDetail == null)
+ {
+ return content.Error("鏈壘鍒板崟鎹�");
+ }
+ var unitGroups = orderDetail.GroupBy(d => d.BarcodeUnit).ToList();
+ if (unitGroups.Count == 1)
+ {
+ resultDTO.UniqueUnit = unitGroups.First().Key;
+ }
+ else
+ {
+ resultDTO.UniqueUnit = "";
+ }
+ var validDetails = _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>().Where(s => s.OrderNo == orderNo).ToList();
+ resultDTO.StockSumQuantity = orderDetail.Sum(d => d.PickQuantity);
+ resultDTO.StockCount = orderDetail.Count;
+ if (validDetails.Any())
+ {
+ resultDTO.StockSumQuantity -= validDetails.Sum(d => d.StockQuantity);
+ // 鏄庣粏璁板綍鏁帮細绗﹀悎鏉′欢鐨勬湁鏁堣褰曟潯鏁�
+ resultDTO.StockCount -= validDetails.Count;
+ }
+ return content.OK("", resultDTO);
+ }
+ catch (Exception ex)
+ {
+ return content.Error("SumQuantity 缁熻搴撳瓨鏁伴噺澶辫触锛岃鍗曞彿锛歿OrderNo}");
+ }
+ }
+
+ public WebResponseContent BarcodeMaterielGroup(BarcodeMaterielGroupDTO materielGroupDTO)
+ {
+
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ (bool, string, object?) result2 = ModelValidate.ValidateModelData(materielGroupDTO);
+ 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))
+ {
+ return content = WebResponseContent.Instance.Error($"浠撳簱涓病鏈夎{materielGroupDTO.WarehouseType}缂栧彿銆�");
+ }
+
+
+ // Dt_InboundOrder inboundOrder = GetInboundOrder(materielGroupDTO.OrderNo);
+
+ var dbinboundOrderDetails = Db.Queryable<Dt_PickingRecord>().Where(x => x.OrderNo == materielGroupDTO.OrderNo && !x.IsCancelled && x.Barcode == materielGroupDTO.Barcodes).ToList();
+
+ if (dbinboundOrderDetails != null && !dbinboundOrderDetails.Any())
+ {
+ return content = WebResponseContent.Instance.Error($"鍗曟嵁涓病鏈夎{materielGroupDTO.Barcodes}鏉$爜鏁版嵁銆�");
+ }
+
+ List<string?> materielCodes = dbinboundOrderDetails.GroupBy(x => x.Barcode).Select(x => x.Key).ToList();
+
+ Dt_StockInfo? stockInfo = _stockService.StockInfoService.GetStockByPalletCode(materielGroupDTO.PalletCode);
+
+
+
+ (bool, string, object?) result = CheckMaterielGroupParam(materielGroupDTO, materielCodes, stockInfo);
+ if (!result.Item1) return content = WebResponseContent.Instance.Error(result.Item2);
+
+ if (stockInfo == null)
+ {
+ stockInfo = new Dt_StockInfo() { PalletType = (int)PalletTypeEnum.None, LocationType = materielGroupDTO.locationType.ObjToInt() };
+ stockInfo.Details = new List<Dt_StockInfoDetail>();
+ }
+ var inboindId = 0; Dt_InboundOrder dt_InboundOrder = null;
+ var dbinbound = _inboundOrderRepository.QueryData(x => x.InboundOrderNo == dbinboundOrderDetails.First().OrderNo).First();
+ if (dbinbound == null)
+ {
+ dt_InboundOrder = new Dt_InboundOrder
+ {
+ WarehouseId = 0,
+ InboundOrderNo = dbinboundOrderDetails.First()?.OrderNo,
+ UpperOrderNo = dbinboundOrderDetails.First()?.OrderNo,
+ SupplierId = dbinboundOrderDetails.First()?.SupplyCode,
+ OrderType = materielGroupDTO.orderTypes,
+
+ BusinessType = materielGroupDTO.orderTypes.ToString(),
+
+ FactoryArea = dbinboundOrderDetails.First()?.FactoryArea,
+ Remark = "",
+ Details = new List<Dt_InboundOrderDetail>()
+ };
+ inboindId = _inboundOrderRepository.AddData(dt_InboundOrder);
+ }
+ else
+ {
+ dt_InboundOrder = new Dt_InboundOrder { Details = new List<Dt_InboundOrderDetail>() };
+ inboindId = dbinbound.Id;
+ }
+
+ foreach (var item in dbinboundOrderDetails)
+ {
+ stockInfo.Details.Add(new Dt_StockInfoDetail
+ {
+ StockId = stockInfo == null ? 0 : stockInfo.Id,
+ Barcode = item.Barcode,
+ MaterielCode = item.MaterielCode,
+ BatchNo = item.BatchNo,
+ Unit = item.BarcodeUnit,
+ InboundOrderRowNo = item.lineNo,
+ SupplyCode = item.SupplyCode,
+ WarehouseCode = materielGroupDTO.WarehouseType,
+ StockQuantity = item.PickQuantity,
+ BarcodeQty = item.BarcodeQty,
+ BarcodeUnit = item.BarcodeUnit,
+ FactoryArea = item.FactoryArea,
+ Status = 0,
+ OrderNo = item.OrderNo,
+ BusinessType = InOrderTypeEnum.InternalAllocat.ObjToInt().ToString()
+
+ });
+ item.WarehouseCode = item.WarehouseCode;
+
+ dt_InboundOrder.Details.Add(new Dt_InboundOrderDetail
+ {
+ OrderId = inboindId,
+ MaterielCode = item.MaterielCode,
+ MaterielName = "",
+ BatchNo = item.BatchNo,
+ OrderQuantity = item.PickQuantity,
+ ReceiptQuantity = 0,
+ OverInQuantity = 0,
+ Unit = item.BarcodeUnit,
+ RowNo = 0,
+ lineNo = item.lineNo,
+ SupplyCode = item.SupplyCode,
+ WarehouseCode = item.WarehouseCode,
+ Barcode = item.Barcode,
+ OutBoxbarcodes = "",
+ BarcodeQty = (decimal)item.BarcodeQty,
+ BarcodeUnit = item.BarcodeUnit
+ });
+ }
+
+ _inboundOrderDetailService.Db.Insertable<Dt_InboundOrderDetail>(dt_InboundOrder.Details);
+ if (stockInfo.Id == 0)
+ {
+ stockInfo.PalletCode = materielGroupDTO.PalletCode;
+ stockInfo.StockStatus = StockStatusEmun.缁勭洏鏆傚瓨.ObjToInt();
+ }
+ stockInfo.PalletType = (int)PalletTypeEnum.None;
+
+ List<int> updateDetailIds = dbinboundOrderDetails.Select(x => x.Id).ToList();
+
+ try
+ {
+ _unitOfWorkManage.BeginTran();
+
+ _stockService.StockInfoService.AddMaterielGroup(stockInfo);
+ _unitOfWorkManage.CommitTran();
+ return WebResponseContent.Instance.OK();
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+ }
+ catch (Exception ex)
+ {
+ content = WebResponseContent.Instance.Error(ex.Message);
+ }
+ finally
+ {
+
+ }
+ return content;
+
+ }
+
+
+ public (bool, string, object?) CheckMaterielGroupParam(BarcodeMaterielGroupDTO materielGroupDTO, List<string> barcodeCodes, Dt_StockInfo stockInfo)
+ {
+ (bool, string, object?) result = ModelValidate.ValidateModelData(materielGroupDTO);
+ if (!result.Item1) return result;
+
+ if (_taskRepository.QueryFirst(x => x.PalletCode == materielGroupDTO.PalletCode) != null)
+ {
+ return (false, "璇ユ墭鐩樺彿宸叉湁浠诲姟", materielGroupDTO);
+ }
+
+ if (stockInfo != null && !string.IsNullOrEmpty(stockInfo.LocationCode) && stockInfo.StockStatus != StockStatusEmun.缁勭洏鏆傚瓨.ObjToInt())
+ {
+ return (false, "宸蹭笂鏋剁殑鎵樼洏涓嶈兘鍐嶆缁勭洏", materielGroupDTO);
+ }
+
+ if (_stockService.StockInfoDetailService.ExistBarcodes(barcodeCodes))
+ {
+ return (false, $"{barcodeCodes[0]} 鏉$爜鍦ㄥ簱瀛樹腑宸插瓨鍦�", materielGroupDTO);
+ }
+
+
+
+ return (true, "鎴愬姛", materielGroupDTO);
+ }
+
+
+ #endregion
+ }
+
+
+
+ #region 鏀寔绫诲畾涔�
+
+ public class ValidationResult<T>
+ {
+ public bool IsValid { get; set; }
+ public T Data { get; set; }
+ public string ErrorMessage { get; set; }
+
+ public static ValidationResult<T> Success(T data)
+ {
+ return new ValidationResult<T>
+ {
+ IsValid = true,
+ Data = data
+ };
+ }
+
+ public static ValidationResult<T> Error(string message)
+ {
+ return new ValidationResult<T>
+ {
+ IsValid = false,
+ ErrorMessage = message
+ };
}
}
+ public class PickingResult
+ {
+ public Dt_OutStockLockInfo FinalLockInfo { get; set; }
+ public string FinalBarcode { get; set; }
+ public int FinalStockId { get; set; }
+ public decimal ActualPickedQty { get; set; }
+ public List<SplitResult> SplitResults { get; set; } = new List<SplitResult>();
+ }
+
+ public class ReturnAnalysisResult
+ {
+ public bool HasItemsToReturn { get; set; }
+ public bool HasRemainingLocks { get; set; }
+ public bool HasPalletStockGoods { get; set; }
+ public bool HasSplitRecords { get; set; }
+ public decimal RemainingLocksReturnQty { get; set; }
+ public decimal PalletStockReturnQty { get; set; }
+ public decimal SplitReturnQty { get; set; }
+ public decimal TotalReturnQty { get; set; }
+ public List<Dt_OutStockLockInfo> RemainingLocks { get; set; } = new List<Dt_OutStockLockInfo>();
+ public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>();
+ public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>();
+ }
+ public class PalletStatusAnalysis
+ {
+ public string OrderNo { get; set; }
+ public string PalletCode { get; set; }
+ public int StockId { get; set; }
+
+ // 鍥炲簱鐩稿叧灞炴��
+ public bool HasItemsToReturn { get; set; }
+ public bool HasRemainingLocks { get; set; }
+ public bool HasPalletStockGoods { get; set; }
+ public bool HasSplitRecords { get; set; }
+ public decimal RemainingLocksReturnQty { get; set; }
+ public decimal PalletStockReturnQty { get; set; }
+
+ public decimal SplitReturnQty { get; set; }
+ public decimal TotalReturnQty { get; set; }
+
+ // 鏈垎閰嶇殑閿佸畾璁板綍锛堝鑷姩鎷嗗寘浜х敓鐨勶級
+ public bool HasUnallocatedLocks { get; set; }
+ public List<Dt_OutStockLockInfo> UnallocatedLocks { get; set; } = new List<Dt_OutStockLockInfo>();
+ public decimal UnallocatedLocksReturnQty { get; set; }
+
+ public List<Dt_OutStockLockInfo> RemainingLocks { get; set; } = new List<Dt_OutStockLockInfo>();
+ public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>();
+ public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>();
+
+ public List<string> AllBarcodes { get; set; } = new List<string>();
+ // 绌烘墭鐩樼浉鍏冲睘鎬�
+ public bool IsEmptyPallet { get; set; }
+ public bool HasActiveTasks { get; set; }
+
+ // 渚垮埄鏂规硶
+ public bool CanReturn => HasItemsToReturn && !HasActiveTasks;
+ public bool CanRemove => IsEmptyPallet && !HasActiveTasks;
+ }
+ public class PickingContext
+ {
+ public string OrderNo { get; set; }
+ public string PalletCode { get; set; }
+ public string Barcode { get; set; }
+ public string Operator { get; set; }
+ public Dt_OutStockLockInfo LockInfo { get; set; }
+ public Dt_OutboundOrderDetail OrderDetail { get; set; }
+ public Dt_StockInfoDetail StockDetail { get; set; }
+ public decimal ActualQuantity { get; set; }
+ public string AdjustedReason { get; set; }
+ }
+ public class CancelPickingContext
+ {
+ public string OrderNo { get; set; }
+ public string PalletCode { get; set; }
+ public string Barcode { get; set; }
+ public string Operator { get; set; }
+
+ public decimal CancelQuantity { get; set; }
+ public Dt_PickingRecord PickingRecord { get; set; }
+ public Dt_OutStockLockInfo LockInfo { get; set; }
+ public Dt_OutboundOrderDetail OrderDetail { get; set; }
+ }
+ #endregion
}
--
Gitblit v1.9.3