pan
2025-11-05 b2c6ce78c7c95c37f9c3e9ea11e86a7af7b0d3fd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
using Microsoft.Extensions.Logging;
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_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>
        /// 单位转换
        /// </summary>
        public async Task<decimal> 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 quantity;
 
            var ratio = await GetConversionRatioAsync(materialCode, fromUnit, toUnit);
 
            if (ratio == null)
            {
                _logger.LogWarning($"未找到物料 {materialCode} 从 {fromUnit} 到 {toUnit} 的单位转换关系");
                throw new InvalidOperationException($"未找到物料 {materialCode} 从 {fromUnit} 到 {toUnit} 的单位转换关系");
            }
 
            return quantity * ratio.Value;
        }
 
        /// <summary>
        /// 采购单位转库存单位
        /// </summary>
        public async Task<decimal> ConvertPurchaseToStockAsync(string materialCode, decimal quantity)
        {
            // 获取物料信息
            var material = await _materielInfoRepository.Db.Queryable<Dt_MaterielInfo>()
                .Where(x => x.MaterielCode == materialCode)
                .FirstAsync();
 
            if (material == null)
            {
                throw new ArgumentException($"未找到物料编号: {materialCode}");
            }
 
            return await ConvertAsync(materialCode, quantity, material.purchaseUOM, material.inventoryUOM);
        }
 
        /// <summary>
        /// 领料单位转库存单位
        /// </summary>
        public async Task<decimal> ConvertIssueToStockAsync(string materialCode, decimal quantity)
        {
            // 获取物料信息
            var material = await _materielInfoRepository.Db.Queryable<Dt_MaterielInfo>()
                .Where(x => x.MaterielCode == materialCode)
                .FirstAsync();
 
            if (material == null)
            {
                throw new ArgumentException($"未找到物料编号: {materialCode}");
            }
 
            return await ConvertAsync(materialCode, quantity, material.usageUOM, material.inventoryUOM);
        }
        /// <summary>
        /// 获取单位转换比率
        /// </summary>
        public async Task<decimal?> GetConversionRatioAsync(string materialCode, string fromUnit, string toUnit)
        {
            // 尝试直接查找转换关系
            var conversion = await Repository.Db.Queryable<Dt_MaterialUnit>()
                .Where(x => x.ItemNo == materialCode &&
                           x.FromUom == fromUnit &&
                           x.ToUom == toUnit)
                .FirstAsync();
 
            if (conversion != null)
            {
                return conversion.Ratio;
            }
 
            // 尝试查找反向转换关系(取倒数)
            var reverseConversion = await Repository.Db.Queryable<Dt_MaterialUnit>()
                .Where(x => x.ItemNo == materialCode &&
                           x.FromUom == toUnit &&
                           x.ToUom == fromUnit)
                .FirstAsync();
 
            if (reverseConversion != null)
            {
                return 1 / reverseConversion.Ratio;
            }
 
            // 尝试通过中间单位(库存单位)进行转换
            var material = await _materielInfoRepository.Db.Queryable<Dt_MaterielInfo>()
                .Where(x => x.MaterielCode == materialCode)
                .FirstAsync();
 
            if (material != null)
            {
                var stockUnit = material.inventoryUOM;
 
                // 如果目标单位已经是库存单位,直接查找源单位到库存单位的转换
                if (toUnit.Equals(stockUnit, StringComparison.OrdinalIgnoreCase))
                {
                    var toStockRatio = await GetDirectRatioAsync(materialCode, fromUnit, stockUnit);
                    if (toStockRatio != null) return toStockRatio;
                }
 
                // 如果源单位是库存单位,直接查找库存单位到目标单位的转换
                if (fromUnit.Equals(stockUnit, StringComparison.OrdinalIgnoreCase))
                {
                    var fromStockRatio = await GetDirectRatioAsync(materialCode, stockUnit, toUnit);
                    if (fromStockRatio != null) return fromStockRatio;
                }
 
                // 通过库存单位进行间接转换:fromUnit -> stockUnit -> toUnit
                var ratio1 = await GetDirectRatioAsync(materialCode, fromUnit, stockUnit);
                var ratio2 = await GetDirectRatioAsync(materialCode, stockUnit, toUnit);
 
                if (ratio1 != null && ratio2 != null)
                {
                    return ratio1 * ratio2;
                }
            }
 
            return null;
        }
 
        /// <summary>
        /// 获取直接转换比率(包含正向和反向查找)
        /// </summary>
        private async Task<decimal?> GetDirectRatioAsync(string materialCode, string fromUnit, string toUnit)
        {
            // 正向查找
            var conversion = await Repository.Db.Queryable<Dt_MaterialUnit>()
                .Where(x => x.ItemNo == materialCode &&
                           x.FromUom == fromUnit &&
                           x.ToUom == toUnit)
                .FirstAsync();
 
            if (conversion != null)
            {
                return conversion.Ratio;
            }
 
            // 反向查找
            var reverseConversion = await Repository.Db.Queryable<Dt_MaterialUnit>()
                .Where(x => x.ItemNo == materialCode &&
                           x.FromUom == toUnit &&
                           x.ToUom == fromUnit)
                .FirstAsync();
 
            if (reverseConversion != null)
            {
                return 1 / reverseConversion.Ratio;
            }
 
            return null;
        }
 
    }
}