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<Dt_MaterialUnit, IRepository<Dt_MaterialUnit>>, IMaterialUnitService
|
{
|
private readonly ILogger<MaterialUnitService> _logger;
|
public IRepository<Dt_MaterialUnit> Repository => 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左连接Material)
|
/// </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;
|
}
|
|
|
|
}
|
}
|