From f333dad1d1e8ea095a0708a90b561e9376cd4b43 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期二, 25 十一月 2025 09:32:12 +0800
Subject: [PATCH] 提交
---
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs | 239 +++++++++++++++++++++++++++++++++++++----------
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs | 2
2 files changed, 189 insertions(+), 52 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 01a0644..a18f867 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"
@@ -738,7 +738,73 @@
#endregion
#region 鍙栨秷鍒嗘嫞绉佹湁鏂规硶
+ private async Task<ValidationResult<bool>> ValidateDataConsistencyBeforeCancel(CancelPickingContext context)
+ {
+ try
+ {
+ // 1. 楠岃瘉璁㈠崟鏄庣粏鏁版嵁
+ 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})");
+
+ // 2. 楠岃瘉閿佸畾淇℃伅鏁版嵁
+ 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})");
+
+ // 3. 楠岃瘉搴撳瓨鏁版嵁
+ 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}宸茬粡鍥炲簱锛屾棤娉曞彇娑堝垎鎷�");
+
+ // 4. 楠岃瘉鐘舵�佹祦杞殑鍚堟硶鎬�
+ if (!await CanCancelPicking(currentLockInfo, currentStockDetail))
+ 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)
{
// 鍩虹鍙傛暟楠岃瘉
@@ -846,30 +912,25 @@
stockDetail.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt();
}
private async Task ExecuteCancelLogic(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord,
- Dt_OutboundOrderDetail orderDetail, string orderNo)
+ Dt_OutboundOrderDetail orderDetail, string orderNo)
{
decimal cancelQty = pickingRecord.PickQuantity;
- var currentStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
- .Where(it => it.Barcode == pickingRecord.Barcode && it.StockId == pickingRecord.StockId)
- .FirstAsync();
-
- if (currentStockDetail != null &&
- (currentStockDetail.Status == StockStatusEmun.鍏ュ簱纭.ObjToInt() ||
- currentStockDetail.Status == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt()))
+ // 1. 鏁版嵁涓�鑷存�ч獙璇�
+ var context = new CancelPickingContext
{
- throw new Exception($"鏉$爜{pickingRecord.Barcode}宸茬粡鍥炲簱锛屾棤娉曞彇娑堝垎鎷�");
- }
- // 妫�鏌ュ彇娑堝悗鏁伴噺涓嶄細涓鸿礋鏁�
- decimal newOverOutQuantity = orderDetail.OverOutQuantity - cancelQty;
- decimal newPickedQty = orderDetail.PickedQty - cancelQty;
+ LockInfo = lockInfo,
+ PickingRecord = pickingRecord,
+ OrderDetail = orderDetail,
+ OrderNo = orderNo,
+ CancelQuantity = cancelQty
+ };
- if (newOverOutQuantity < 0 || newPickedQty < 0)
- {
- throw new Exception($"鍙栨秷鍒嗘嫞灏嗗鑷存暟鎹紓甯革細宸插嚭搴搟newOverOutQuantity}锛屽凡鎷i�墈newPickedQty}");
- }
+ var validationResult = await ValidateDataConsistencyBeforeCancel(context);
+ if (!validationResult.IsValid)
+ throw new Exception(validationResult.ErrorMessage);
- // 澶勭悊涓嶅悓绫诲瀷鐨勫彇娑�
+ // 2. 澶勭悊涓嶅悓绫诲瀷鐨勫彇娑�
if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
{
await HandleSplitBarcodeCancel(lockInfo, pickingRecord, cancelQty);
@@ -879,18 +940,25 @@
await HandleNormalBarcodeCancel(lockInfo, pickingRecord, cancelQty);
}
- // 鏇存柊璁㈠崟鏄庣粏
+ // 鏇存柊璁㈠崟鏄庣粏
await UpdateOrderDetailOnCancel(pickingRecord.OrderDetailId, cancelQty);
// 鍒犻櫎鎷i�夎褰�
- await Db.Deleteable<Dt_PickingRecord>()
+ var deleteResult = await Db.Deleteable<Dt_PickingRecord>()
.Where(x => x.Id == pickingRecord.Id)
.ExecuteCommandAsync();
- // 閲嶆柊妫�鏌ヨ鍗曠姸鎬�
- await UpdateOrderStatusForReturn(orderNo);
- }
+ 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)
{
// 鏌ユ壘鐖堕攣瀹氫俊鎭�
@@ -901,61 +969,85 @@
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 stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+ // 鎭㈠鐖舵潯鐮佸簱瀛�
+ var parentStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
.Where(x => x.Barcode == parentLockInfo.CurrentBarcode && x.StockId == parentLockInfo.StockId)
.FirstAsync();
- if (stockDetail != null)
+ if (parentStockDetail != null)
{
- stockDetail.StockQuantity += cancelQty;
- stockDetail.OutboundQuantity = stockDetail.StockQuantity;
- stockDetail.Status = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
- await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+ 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}");
}
// 鏇存柊鎷嗗寘璁板綍鐘舵��
- await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
+ 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();
- await UpdateOrderDetailOnCancel(pickingRecord.OrderDetailId, cancelQty);
+ _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;
- lockInfo.Status = (int)OutLockStockStatusEnum.鍑哄簱涓�;
+ // 鍙湁褰撴嫞閫夋暟閲忓畬鍏ㄥ彇娑堟椂鎵嶆仮澶嶇姸鎬�
+ 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>()
@@ -966,36 +1058,57 @@
{
stockDetail.StockQuantity += cancelQty;
stockDetail.OutboundQuantity = stockDetail.StockQuantity;
- stockDetail.Status = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+
+ // 鎭㈠搴撳瓨鐘舵��
+ 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 || newPickedQty < 0)
- {
- throw new Exception($"鍙栨秷鍒嗘嫞灏嗗鑷村凡鍑哄簱鏁伴噺({newOverOutQuantity})鎴栧凡鎷i�夋暟閲�({newPickedQty})涓鸿礋鏁�");
- }
+ // 涓ユ牸妫�鏌ュ彇娑堝悗鏁伴噺涓嶄細涓鸿礋鏁�
+ if (newOverOutQuantity < 0)
+ throw new Exception($"鍙栨秷鍒嗘嫞灏嗗鑷村凡鍑哄簱鏁伴噺({newOverOutQuantity})涓鸿礋鏁�");
- await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+ if (newPickedQty < 0)
+ throw new Exception($"鍙栨秷鍒嗘嫞灏嗗鑷村凡鎷i�夋暟閲�({newPickedQty})涓鸿礋鏁�");
+
+ // 鏇存柊璁㈠崟鏄庣粏
+ var updateResult = await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
.SetColumns(it => new Dt_OutboundOrderDetail
{
PickedQty = newPickedQty,
- OverOutQuantity = newOverOutQuantity,
+ OverOutQuantity = newOverOutQuantity
})
.Where(it => it.Id == orderDetailId)
.ExecuteCommandAsync();
- }
+ if (updateResult <= 0)
+ throw new Exception("鏇存柊璁㈠崟鏄庣粏澶辫触");
+
+ _logger.LogInformation($"鏇存柊璁㈠崟鏄庣粏 - OrderDetailId: {orderDetailId}, " +
+ $"鎵e噺宸插嚭搴�: {cancelQty}, 鏂板凡鍑哄簱: {newOverOutQuantity}, " +
+ $"鎵e噺宸叉嫞閫�: {cancelQty}, 鏂板凡鎷i��: {newPickedQty}");
+ }
#endregion
#region 鍥炲簱鎿嶄綔绉佹湁鏂规硶
@@ -2159,5 +2272,29 @@
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
}
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_TaskInfoService/TaskService.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_TaskInfoService/TaskService.cs"
index e8b0bd9..4c2e541 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_TaskInfoService/TaskService.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_TaskInfoService/TaskService.cs"
@@ -616,7 +616,7 @@
details = new List<FeedbackOutboundDetailsModel>()
};
- // 浣跨敤璁㈠崟鏄庣粏鐨凮verOutQuantity浣滀负鍥炰紶鏁伴噺
+
foreach (var detail in orderDetails)
{
// 鑾峰彇璇ユ槑缁嗗搴旂殑鏉$爜淇℃伅锛堜粠閿佸畾璁板綍锛�
--
Gitblit v1.9.3