using 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>, IMaterialUnitService { private readonly ILogger _logger; public IRepository Repository => BaseDal; public IRepository _materielInfoRepository; public MaterialUnitService(IRepository BaseDal, ILogger logger, IRepository materielInfoRepository) : base(BaseDal) { _logger = logger; _materielInfoRepository = materielInfoRepository; } /// /// 获取物料及其所有单位转换关系(使用MaterialUnit左连接Material) /// private async Task GetMaterialWithUnitsAsync(string materialCode) { var unitConversions = await Repository.Db.Queryable() .LeftJoin((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() .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() }; } /// /// 单位转换 /// public async Task 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); } /// /// 采购单位转库存单位 /// public async Task 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 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); } /// /// 领料单位转库存单位 /// public async Task 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); } /// /// 获取单位转换比率 /// public async Task 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); } /// /// 从内存数据中获取转换比率 /// 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; } /// /// 批量转换(优化性能,一次查询处理多个物料) /// public async Task> BatchConvertAsync(List requests) { if (requests == null || !requests.Any()) return new Dictionary(); // 按物料编号分组 var materialGroups = requests.GroupBy(x => x.MaterialCode); var results = new Dictionary(); 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; } /// /// 批量采购单位转库存单位 /// public async Task> BatchConvertPurchaseToStockAsync(List requests) { if (requests == null || !requests.Any()) return new Dictionary(); // 按物料编号分组 var materialGroups = requests.GroupBy(x => x.MaterialCode); var results = new Dictionary(); 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; } /// /// 获取物料的库存单位 /// public async Task GetStockUnitAsync(string materialCode) { var materialData = await GetMaterialWithUnitsAsync(materialCode); return materialData.StockUnit; } /// /// 获取物料的采购单位 /// public async Task GetPurchaseUnitAsync(string materialCode) { var materialData = await GetMaterialWithUnitsAsync(materialCode); return materialData.PurchaseUnit; } /// /// 获取物料的领料单位 /// public async Task GetIssueUnitAsync(string materialCode) { var materialData = await GetMaterialWithUnitsAsync(materialCode); return materialData.IssueUnit; } } }