zs
2025-05-12 7baa0d978e41f395891753b208d805bc02edee38
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
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.CommonDto;
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsMaterialStocks;
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsPlaces;
using CMS.Plugin.HIAWms.Application.Contracts.Services;
using CMS.Plugin.HIAWms.Domain.WmsMaterials;
using CMS.Plugin.HIAWms.Domain.WmsMaterialStocks;
using CMS.Plugin.HIAWms.Domain.WmsPlaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
 
namespace CMS.Plugin.HIAWms.Application.Implements
{
    /// <summary>
    /// 公共操作服务
    /// </summary>
    public class WmsCommonAppService : CMSPluginAppService,IWmsCommonAppService
    {
        private readonly IWmsMaterialRepository _wmsMaterialRepository;
        private readonly IWmsPlaceRepository _wmsPlaceRepository;
        private readonly IWmsMaterialStockRepository _wmsMaterialStockRepository;
 
        public WmsCommonAppService(IWmsMaterialRepository wmsMaterialRepository,
            IWmsPlaceRepository wmsPlaceRepository,
            IWmsMaterialStockRepository wmsMaterialStockRepository
            )
        {
            _wmsMaterialRepository = wmsMaterialRepository;
            _wmsPlaceRepository = wmsPlaceRepository;
            _wmsMaterialStockRepository = wmsMaterialStockRepository;
        }
 
 
        /// <summary>
        /// 查找空库位
        /// </summary>
        /// <param name="materialModel"></param>
        /// <param name="materialNo"></param>
        /// <param name="requiredNum"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<Dictionary<WmsPlaceDto, int>> FindAvailablePlacesAsync(string materialModel,int requiredNum, string materialNo="" )
        {
            // 1. 获取所有库存和库位信息
            var stockList = await _wmsMaterialStockRepository.GetListAsync(new WmsMaterialStock());
 
            var allPlaceList = ObjectMapper.Map<List<WmsPlace>, List<WmsPlaceDto>>(await _wmsPlaceRepository.GetListAsync(new WmsPlace()));
 
            // 2. 查找相同物料型号和编号的库存(按库存量降序)
            var sameModelStocks = stockList
                .Where(x => x.MaterialModel == materialModel)
                .WhereIf(!string.IsNullOrEmpty(materialNo), x => x.MaterialNo == materialNo)
                .OrderByDescending(x => x.StockNumber)
                .ToList();
 
            var availablePlaces = new Dictionary<WmsPlaceDto, int>();
            int remainingNum = requiredNum;
 
            // 3. 优先检查已有库存的库位是否能存放(相同 MaterialNo)
            foreach (var stock in sameModelStocks)
            {
                if (remainingNum <= 0) break; // 数量已分配完
 
                var placeInfo = allPlaceList.FirstOrDefault(x => x.PlaceNo == stock.PlaceNo);
                if (placeInfo == null) continue;
 
                int availableSpace = placeInfo.MaxStockNumber - stock.StockNumber;
                if (availableSpace <= 0) continue;
 
                int allocatedNum = Math.Min(availableSpace, remainingNum);
                availablePlaces.Add(placeInfo, allocatedNum);
                remainingNum -= allocatedNum;
            }
 
            // 4. 如果仍有剩余,查找空库位
            if (remainingNum > 0)
            {
                var usedPlaceNos = stockList.Select(x => x.PlaceNo).Distinct().ToList();
                var emptyPlaces = allPlaceList
                    .Where(x => !usedPlaceNos.Contains(x.PlaceNo))
                    .ToList();
 
                foreach (var place in emptyPlaces)
                {
                    if (remainingNum <= 0) break;
 
                    int allocatedNum = Math.Min(place.MaxStockNumber, remainingNum);
                    availablePlaces.Add(place, allocatedNum);
                    remainingNum -= allocatedNum;
                }
            }
 
            // 5. 如果仍有剩余,说明库位不足
            if (remainingNum > 0)
            {
                throw new UserFriendlyException($"库位不足,还差 {remainingNum} 个无法存放!");
            }
 
            return availablePlaces;
        }
 
        /// <summary>
        /// 查找库存
        /// </summary>
        /// <param name="materialModel"></param>
        /// <param name="requiredNum"></param>
        /// <param name="materialNo"></param>
        /// <returns></returns>
        public async Task<Dictionary<WmsMaterialStockDto, int>> FindStockAsync(string materialModel, int requiredNum, string materialNo = "")
        {
            // 1. 获取所有库存(排除锁定库存)
            var stockList = (await _wmsMaterialStockRepository.GetListAsync(new WmsMaterialStock()))
                .Where(x => x.IsLock == Domain.Shared.Enums.YesNoEnum.N)
                .ToList();
 
            // 2. 筛选匹配物料
            var availableStocklist = stockList
                .Where(x => x.MaterialModel == materialModel)
                .WhereIf(!string.IsNullOrEmpty(materialNo), x => x.MaterialNo == materialNo)
                .OrderBy(x => x.StockNumber) // 优先从库存少的库位出
                .ToList();
 
            var availableStocks = ObjectMapper.Map<List<WmsMaterialStock>, List<WmsMaterialStockDto>>(availableStocklist);
 
            // 3. 检查总库存是否足够
            int totalAvailable = availableStocks.Sum(x => x.StockNumber);
            if (totalAvailable < requiredNum)
            {
                throw new UserFriendlyException(
                    $"库存不足!需求: {requiredNum}, 可用: {totalAvailable}, 缺: {requiredNum - totalAvailable}");
            }
 
            // 4. 计算各库位出库数量
            var allocation = new Dictionary<WmsMaterialStockDto, int>(); // <库位号, 出库数>
            int remaining = requiredNum;
 
            foreach (var stock in availableStocks)
            {
                if (remaining <= 0) break;
 
                int deductAmount = Math.Min(stock.StockNumber, remaining);
                allocation.Add(stock, deductAmount);
                remaining -= deductAmount;
            }
 
            return allocation;
        }
    }
}