| | |
| | | using System; |
| | | 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 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å·¦è¿æ¥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; |
| | | } |
| | | |
| | | |
| | | |
| | | } |
| | | } |
| | | |