From cf3050083e157819b94781d0445547ffc73e21f2 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期五, 28 十一月 2025 21:17:28 +0800
Subject: [PATCH] 提交

---
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs |  397 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 394 insertions(+), 3 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_BasicService/MaterialUnitService.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_BasicService/MaterialUnitService.cs"
index cd8417b..7c48c96 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_BasicService/MaterialUnitService.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_BasicService/MaterialUnitService.cs"
@@ -1,24 +1,415 @@
-锘縰sing System;
+锘縰sing Microsoft.AspNetCore.JsonPatch.Internal;
+using Microsoft.Extensions.Logging;
+using SqlSugar;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using WIDESEA_Core.BaseRepository;
 using WIDESEA_Core.BaseServices;
+using WIDESEA_DTO.Basic;
 using WIDESEA_IBasicService;
 using WIDESEA_Model.Models;
 using WIDESEA_Model.Models.Basic;
+ 
 
 namespace WIDESEA_BasicService
 {
     public class MaterialUnitService : ServiceBase<Dt_MaterialUnit, IRepository<Dt_MaterialUnit>>, IMaterialUnitService
     {
+        private readonly ILogger<MaterialUnitService> _logger;
         public IRepository<Dt_MaterialUnit> Repository => BaseDal;
-        public MaterialUnitService(IRepository<Dt_MaterialUnit> BaseDal) : base(BaseDal)
+
+        public IRepository<Dt_MaterielInfo> _materielInfoRepository;
+        public MaterialUnitService(IRepository<Dt_MaterialUnit> BaseDal, ILogger<MaterialUnitService> logger, IRepository<Dt_MaterielInfo> materielInfoRepository) : base(BaseDal)
         {
+            _logger = logger;
+            _materielInfoRepository = materielInfoRepository;
         }
 
-     
+
+        /// <summary>
+        /// 鑾峰彇鐗╂枡鍙婂叾鎵�鏈夊崟浣嶈浆鎹㈠叧绯伙紙浣跨敤MaterialUnit宸﹁繛鎺aterial锛�
+        /// </summary>
+        private async Task<MaterialWithUnits> GetMaterialWithUnitsAsync(string materialCode)
+        {
+             
+            var unitConversions = await Repository.Db.Queryable<Dt_MaterialUnit>()
+                .LeftJoin<Dt_MaterielInfo>((u, m) => u.ItemNo == m.MaterielCode)
+                .Where((u, m) => u.ItemNo == materialCode)
+                .Select((u, m) => new
+                {
+                    MaterialCode = u.ItemNo,
+                    FromUom = u.FromUom,
+                    ToUom = u.ToUom,
+                    Ratio = u.Ratio,
+                    IssueUnit = m.usageUOM,
+                    PurchaseUnit = m.purchaseUOM,
+                    StockUnit = m.inventoryUOM
+                })
+                .ToListAsync();
+
+            if (!unitConversions.Any())
+            {
+                // 濡傛灉娌℃湁鎵惧埌鍗曚綅杞崲璁板綍锛屽皾璇曠洿鎺ヨ幏鍙栫墿鏂欎俊鎭�
+                var material = await _materielInfoRepository.Db.Queryable<Dt_MaterielInfo>()
+                    .Where(m => m.MaterielCode == materialCode)
+                    .FirstAsync();
+
+                if (material == null)
+                {
+                    throw new ArgumentException($"鏈壘鍒扮墿鏂欑紪鍙�: {materialCode}");
+                }
+
+                return new MaterialWithUnits
+                {
+                    MaterialCode = materialCode,
+                    IssueUnit = material.usageUOM,
+                    PurchaseUnit = material.purchaseUOM,
+                    StockUnit = material.inventoryUOM
+                };
+            }
+
+            // 浠庣涓�鏉¤褰曚腑鑾峰彇鐗╂枡淇℃伅锛堟墍鏈夎褰曢兘鏈夌浉鍚岀殑鐗╂枡淇℃伅锛�
+            var firstRecord = unitConversions.First();
+
+            return new MaterialWithUnits
+            {
+                MaterialCode = materialCode,
+                IssueUnit = firstRecord.IssueUnit,
+                PurchaseUnit = firstRecord.PurchaseUnit,
+                StockUnit = firstRecord.StockUnit,
+                UnitConversions = unitConversions.Select(u => new UnitConversion
+                {
+                    FromUom = u.FromUom,
+                    ToUom = u.ToUom,
+                    Ratio = u.Ratio
+                }).ToList()
+            };
+        }
+
+        /// <summary>
+        /// 鍗曚綅杞崲
+        /// </summary>
+        public async Task<MaterialWithUnitConversionResult> ConvertAsync(string materialCode, decimal quantity, string fromUnit, string toUnit)
+        {
+            if (string.IsNullOrEmpty(materialCode))
+                throw new ArgumentException("鐗╂枡缂栧彿涓嶈兘涓虹┖");
+
+            if (string.IsNullOrEmpty(fromUnit) || string.IsNullOrEmpty(toUnit))
+                throw new ArgumentException("鍗曚綅涓嶈兘涓虹┖");
+
+            // 濡傛灉鍗曚綅鐩稿悓锛岀洿鎺ヨ繑鍥烇紝涓嶈繘琛岃浆鎹�
+            if (fromUnit.Equals(toUnit, StringComparison.OrdinalIgnoreCase))
+                return new MaterialWithUnitConversionResult(quantity, toUnit, false);
+
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+            var ratio = GetConversionRatioFromData(materialData, fromUnit, toUnit);
+
+            if (ratio == null)
+            {
+                _logger.LogWarning($"鏈壘鍒扮墿鏂� {materialCode} 浠� {fromUnit} 鍒� {toUnit} 鐨勫崟浣嶈浆鎹㈠叧绯�");
+                throw new InvalidOperationException($"鏈壘鍒扮墿鏂� {materialCode} 浠� {fromUnit} 鍒� {toUnit} 鐨勫崟浣嶈浆鎹㈠叧绯�");
+            }
+
+            var convertedQuantity = quantity * ratio.Value;
+            return new MaterialWithUnitConversionResult(convertedQuantity, toUnit, true);
+        }
+
+        private  MaterialWithUnitConversionResult ConvertAsync(MaterialWithUnits materialData, decimal quantity, string fromUnit, string toUnit)
+        {
+            if(materialData==null)
+                throw new ArgumentException("鐗╂枡涓嶅瓨鍦�");
+
+            if (string.IsNullOrEmpty(fromUnit) || string.IsNullOrEmpty(toUnit))
+                throw new ArgumentException("鍗曚綅涓嶈兘涓虹┖");
+
+            // 濡傛灉鍗曚綅鐩稿悓锛岀洿鎺ヨ繑鍥烇紝涓嶈繘琛岃浆鎹�
+            if (fromUnit.Equals(toUnit, StringComparison.OrdinalIgnoreCase))
+                return new MaterialWithUnitConversionResult(quantity, toUnit, false);
+
+        
+            var ratio = GetConversionRatioFromData(materialData, fromUnit, toUnit);
+
+            if (ratio == null)
+            {
+                _logger.LogWarning($"鏈壘鍒扮墿鏂� {materialData.MaterialCode} 浠� {fromUnit} 鍒� {toUnit} 鐨勫崟浣嶈浆鎹㈠叧绯�");
+                throw new InvalidOperationException($"鏈壘鍒扮墿鏂� {materialData.MaterialCode} 浠� {fromUnit} 鍒� {toUnit} 鐨勫崟浣嶈浆鎹㈠叧绯�");
+            }
+
+            var convertedQuantity = quantity * ratio.Value;
+            return new MaterialWithUnitConversionResult(convertedQuantity, toUnit, true);
+        }
+
+        /// <summary>
+        /// 閲囪喘鍗曚綅杞簱瀛樺崟浣�
+        /// </summary>
+        public async Task<MaterialWithUnitConversionResult> ConvertPurchaseToStockAsync(string materialCode, decimal quantity)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+
+            // 濡傛灉閲囪喘鍗曚綅鍜屽簱瀛樺崟浣嶇浉鍚岋紝鐩存帴杩斿洖
+            if (materialData.PurchaseUnit.Equals(materialData.StockUnit, StringComparison.OrdinalIgnoreCase))
+                return new MaterialWithUnitConversionResult(quantity, materialData.StockUnit, false);
+
+            return ConvertAsync(materialData, quantity, materialData.PurchaseUnit, materialData.StockUnit);
+        }
+
+        public async Task<MaterialWithUnitConversionResult> ConvertFromToStockAsync(string materialCode,string  fromUom, decimal quantity)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+
+            // 濡傛灉棰嗘枡鍗曚綅鍜屽簱瀛樺崟浣嶇浉鍚岋紝鐩存帴杩斿洖
+            if (fromUom.Equals(materialData.StockUnit, StringComparison.OrdinalIgnoreCase))
+                return new MaterialWithUnitConversionResult(quantity, materialData.StockUnit, false);
+
+            return ConvertAsync(materialData, quantity, fromUom, materialData.StockUnit);
+        }
+
+
+        /// <summary>
+        /// 棰嗘枡鍗曚綅杞簱瀛樺崟浣�
+        /// </summary>
+        public async Task<MaterialWithUnitConversionResult> ConvertIssueToStockAsync(string materialCode, decimal quantity)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+
+            // 濡傛灉棰嗘枡鍗曚綅鍜屽簱瀛樺崟浣嶇浉鍚岋紝鐩存帴杩斿洖
+            if (materialData.IssueUnit.Equals(materialData.StockUnit, StringComparison.OrdinalIgnoreCase))
+                return new MaterialWithUnitConversionResult(quantity, materialData.StockUnit, false);
+
+            return   ConvertAsync(materialData, quantity, materialData.IssueUnit, materialData.StockUnit);
+        }
+
+        /// <summary>
+        /// 鑾峰彇鍗曚綅杞崲姣旂巼
+        /// </summary>
+        public async Task<decimal?> GetConversionRatioAsync(string materialCode, string fromUnit, string toUnit)
+        {
+            // 濡傛灉鍗曚綅鐩稿悓锛屾瘮鐜囦负1
+            if (fromUnit.Equals(toUnit, StringComparison.OrdinalIgnoreCase))
+                return 1m;
+
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+            return GetConversionRatioFromData(materialData, fromUnit, toUnit);
+        }
+
+        /// <summary>
+        /// 浠庡唴瀛樻暟鎹腑鑾峰彇杞崲姣旂巼
+        /// </summary>
+        private decimal? GetConversionRatioFromData(MaterialWithUnits materialData, string fromUnit, string toUnit)
+        {
+            // 鐩存帴鏌ユ壘杞崲鍏崇郴
+            var directConversion = materialData.UnitConversions
+                .FirstOrDefault(x => x.FromUom.Equals(fromUnit, StringComparison.OrdinalIgnoreCase) &&
+                                    x.ToUom.Equals(toUnit, StringComparison.OrdinalIgnoreCase));
+
+            if (directConversion != null)
+            {
+                return directConversion.Ratio;
+            }
+
+            // 鏌ユ壘鍙嶅悜杞崲鍏崇郴锛堝彇鍊掓暟锛�
+            var reverseConversion = materialData.UnitConversions
+                .FirstOrDefault(x => x.FromUom.Equals(toUnit, StringComparison.OrdinalIgnoreCase) &&
+                                    x.ToUom.Equals(fromUnit, StringComparison.OrdinalIgnoreCase));
+
+            if (reverseConversion != null)
+            {
+                return 1 / reverseConversion.Ratio;
+            }
+
+            // 閫氳繃搴撳瓨鍗曚綅杩涜闂存帴杞崲
+            var stockUnit = materialData.StockUnit;
+
+            // 鏌ユ壘 fromUnit -> stockUnit 鐨勮浆鎹�
+            var fromToStock = materialData.UnitConversions
+                .FirstOrDefault(x => x.FromUom.Equals(fromUnit, StringComparison.OrdinalIgnoreCase) &&
+                                    x.ToUom.Equals(stockUnit, StringComparison.OrdinalIgnoreCase));
+            var stockToFrom = materialData.UnitConversions
+                .FirstOrDefault(x => x.FromUom.Equals(stockUnit, StringComparison.OrdinalIgnoreCase) &&
+                                    x.ToUom.Equals(fromUnit, StringComparison.OrdinalIgnoreCase));
+
+            // 鏌ユ壘 stockUnit -> toUnit 鐨勮浆鎹�
+            var stockToTo = materialData.UnitConversions
+                .FirstOrDefault(x => x.FromUom.Equals(stockUnit, StringComparison.OrdinalIgnoreCase) &&
+                                    x.ToUom.Equals(toUnit, StringComparison.OrdinalIgnoreCase));
+            var toToStock = materialData.UnitConversions
+                .FirstOrDefault(x => x.FromUom.Equals(toUnit, StringComparison.OrdinalIgnoreCase) &&
+                                    x.ToUom.Equals(stockUnit, StringComparison.OrdinalIgnoreCase));
+
+            decimal? ratioFromToStock = null;
+            decimal? ratioStockToTo = null;
+
+            // 璁$畻 fromUnit -> stockUnit 鐨勬瘮鐜�
+            if (fromToStock != null)
+            {
+                ratioFromToStock = fromToStock.Ratio;
+            }
+            else if (stockToFrom != null)
+            {
+                ratioFromToStock = 1 / stockToFrom.Ratio;
+            }
+            else if (fromUnit.Equals(stockUnit, StringComparison.OrdinalIgnoreCase))
+            {
+                ratioFromToStock = 1; // 濡傛灉婧愬崟浣嶅氨鏄簱瀛樺崟浣嶏紝姣旂巼涓�1
+            }
+
+            // 璁$畻 stockUnit -> toUnit 鐨勬瘮鐜�
+            if (stockToTo != null)
+            {
+                ratioStockToTo = stockToTo.Ratio;
+            }
+            else if (toToStock != null)
+            {
+                ratioStockToTo = 1 / toToStock.Ratio;
+            }
+            else if (toUnit.Equals(stockUnit, StringComparison.OrdinalIgnoreCase))
+            {
+                ratioStockToTo = 1; // 濡傛灉鐩爣鍗曚綅灏辨槸搴撳瓨鍗曚綅锛屾瘮鐜囦负1
+            }
+
+            // 濡傛灉鎵惧埌浜嗕袱鏉¤矾寰勶紝杩斿洖涔樼Н
+            if (ratioFromToStock != null && ratioStockToTo != null)
+            {
+                return ratioFromToStock * ratioStockToTo;
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// 鎵归噺杞崲锛堜紭鍖栨�ц兘锛屼竴娆℃煡璇㈠鐞嗗涓墿鏂欙級
+        /// </summary>
+        public async Task<Dictionary<string, MaterialWithUnitConversionResult>> BatchConvertAsync(List<BatchConversionRequest> requests)
+        {
+            if (requests == null || !requests.Any())
+                return new Dictionary<string, MaterialWithUnitConversionResult>();
+
+            // 鎸夌墿鏂欑紪鍙峰垎缁�
+            var materialGroups = requests.GroupBy(x => x.MaterialCode);
+            var results = new Dictionary<string, MaterialWithUnitConversionResult>();
+
+            foreach (var group in materialGroups)
+            {
+                var materialCode = group.Key;
+                var materialData = await GetMaterialWithUnitsAsync(materialCode);
+
+                foreach (var request in group)
+                {
+                    try
+                    {
+                        // 濡傛灉鍗曚綅鐩稿悓锛岀洿鎺ヨ繑鍥�
+                        if (request.FromUnit.Equals(request.ToUnit, StringComparison.OrdinalIgnoreCase))
+                        {
+                            results[request.RequestId] = new MaterialWithUnitConversionResult(request.Quantity, request.ToUnit, false);
+                            continue;
+                        }
+
+                        var ratio = GetConversionRatioFromData(materialData, request.FromUnit, request.ToUnit);
+                        if (ratio != null)
+                        {
+                            var convertedQuantity = request.Quantity * ratio.Value;
+                            results[request.RequestId] = new MaterialWithUnitConversionResult(convertedQuantity, request.ToUnit, true);
+                        }
+                        else
+                        {
+                            _logger.LogWarning($"鏈壘鍒扮墿鏂� {materialCode} 浠� {request.FromUnit} 鍒� {request.ToUnit} 鐨勫崟浣嶈浆鎹㈠叧绯�");
+                            results[request.RequestId] = new MaterialWithUnitConversionResult(request.Quantity, request.ToUnit, false);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, $"鎵归噺杞崲鐗╂枡 {materialCode} 鏃跺彂鐢熼敊璇�");
+                        results[request.RequestId] = new MaterialWithUnitConversionResult(request.Quantity, request.ToUnit, false);
+                    }
+                }
+            }
+
+            return results;
+        }
+
+        /// <summary>
+        /// 鎵归噺閲囪喘鍗曚綅杞簱瀛樺崟浣�
+        /// </summary>
+        public async Task<Dictionary<string, MaterialWithUnitConversionResult>> BatchConvertPurchaseToStockAsync(List<BatchConversionRequest> requests)
+        {
+            if (requests == null || !requests.Any())
+                return new Dictionary<string, MaterialWithUnitConversionResult>();
+
+            // 鎸夌墿鏂欑紪鍙峰垎缁�
+            var materialGroups = requests.GroupBy(x => x.MaterialCode);
+            var results = new Dictionary<string, MaterialWithUnitConversionResult>();
+
+            foreach (var group in materialGroups)
+            {
+                var materialCode = group.Key;
+                var materialData = await GetMaterialWithUnitsAsync(materialCode);
+
+                foreach (var request in group)
+                {
+                    try
+                    {
+                        // 濡傛灉閲囪喘鍗曚綅鍜屽簱瀛樺崟浣嶇浉鍚岋紝鐩存帴杩斿洖
+                        if (materialData.PurchaseUnit.Equals(materialData.StockUnit, StringComparison.OrdinalIgnoreCase))
+                        {
+                            results[request.RequestId] = new MaterialWithUnitConversionResult(request.Quantity, materialData.StockUnit, false);
+                            continue;
+                        }
+
+                        var ratio = GetConversionRatioFromData(materialData, materialData.PurchaseUnit, materialData.StockUnit);
+                        if (ratio != null)
+                        {
+                            var convertedQuantity = request.Quantity * ratio.Value;
+                            results[request.RequestId] = new MaterialWithUnitConversionResult(convertedQuantity, materialData.StockUnit, true);
+                        }
+                        else
+                        {
+                            _logger.LogWarning($"鏈壘鍒扮墿鏂� {materialCode} 浠庨噰璐崟浣嶅埌搴撳瓨鍗曚綅鐨勮浆鎹㈠叧绯�");
+                            results[request.RequestId] = new MaterialWithUnitConversionResult(request.Quantity, materialData.StockUnit, false);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, $"鎵归噺杞崲鐗╂枡 {materialCode} 閲囪喘鍗曚綅鍒板簱瀛樺崟浣嶆椂鍙戠敓閿欒");
+                        results[request.RequestId] = new MaterialWithUnitConversionResult(request.Quantity, materialData.StockUnit, false);
+                    }
+                }
+            }
+
+            return results;
+        }
+
+        /// <summary>
+        /// 鑾峰彇鐗╂枡鐨勫簱瀛樺崟浣�
+        /// </summary>
+        public async Task<string> GetStockUnitAsync(string materialCode)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+            return materialData.StockUnit;
+        }
+
+        /// <summary>
+        /// 鑾峰彇鐗╂枡鐨勯噰璐崟浣�
+        /// </summary>
+        public async Task<string> GetPurchaseUnitAsync(string materialCode)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+            return materialData.PurchaseUnit;
+        }
+
+        /// <summary>
+        /// 鑾峰彇鐗╂枡鐨勯鏂欏崟浣�
+        /// </summary>
+        public async Task<string> GetIssueUnitAsync(string materialCode)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+            return materialData.IssueUnit;
+        }
+
+
+
     }
 }
 

--
Gitblit v1.9.3