| | |
| | | using HslCommunication.WebSocket; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; |
| | | using SqlSugar; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using Mapster; |
| | | using MapsterMapper; |
| | | using WIDESEA_Common.CommonEnum; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_Core.Const; |
| | | using WIDESEA_Core.DB; |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_Core.Seed; |
| | | using WIDESEA_Core.Utilities; |
| | | using WIDESEA_DTO.Basic; |
| | | using WIDESEA_DTO.Task; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IRecordService; |
| | | using WIDESEA_Model.Models; |
| | | |
| | | namespace WIDESEA_BasicService |
| | | { |
| | | /// <summary> |
| | | /// 货位信息服务实现类 |
| | | /// </summary> |
| | | public partial class LocationInfoService : ServiceBase<Dt_LocationInfo, IRepository<Dt_LocationInfo>>, ILocationInfoService |
| | | { |
| | | private readonly IUnitOfWorkManage _unitOfWorkManage; |
| | | private readonly IMapper _mapper; |
| | | private readonly IRepository<Dt_Task> _taskRepository; |
| | | private readonly IRepository<Dt_StockInfo> _stockInfoRepository; |
| | | public IRepository<Dt_LocationInfo> Repository => BaseDal; |
| | | private readonly IRecordService _recordService; |
| | | private readonly IRepository<Dt_Warehouse> _warehouseRepository; |
| | | |
| | | public LocationInfoService(IRepository<Dt_LocationInfo> BaseDal, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockInfoRepository) : base(BaseDal) |
| | | /// <summary> |
| | | /// 构造函数 |
| | | /// </summary> |
| | | /// <param name="baseDal">基础数据访问对象</param> |
| | | /// <param name="taskRepository">任务仓储</param> |
| | | /// <param name="stockInfoRepository">库存信息仓储</param> |
| | | public LocationInfoService( |
| | | IRepository<Dt_LocationInfo> baseDal, |
| | | IRepository<Dt_Task> taskRepository, |
| | | IRepository<Dt_StockInfo> stockInfoRepository, |
| | | IRepository<Dt_Warehouse> warehouseRepository, |
| | | IMapper mapper, |
| | | IRecordService recordService) : base(baseDal) |
| | | { |
| | | _unitOfWorkManage = unitOfWorkManage; |
| | | _taskRepository = taskRepository; |
| | | _stockInfoRepository = stockInfoRepository; |
| | | _mapper = mapper; |
| | | _recordService = recordService; |
| | | _warehouseRepository = warehouseRepository; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取货位信息仓储 |
| | | /// </summary> |
| | | public IRepository<Dt_LocationInfo> Repository => BaseDal; |
| | | |
| | | /// <summary> |
| | | /// 批量启用货位 |
| | | /// </summary> |
| | | /// <param name="keys">货位主键数组</param> |
| | | /// <returns></returns> |
| | | /// <returns>操作结果</returns> |
| | | public WebResponseContent LocationEnableStatus(int[] keys) |
| | | { |
| | | List<Dt_LocationInfo> locationInfos = Repository.QueryData(x => keys.Contains(x.Id)); |
| | | locationInfos.ForEach(x => |
| | | { |
| | | x.EnableStatus = EnableStatusEnum.Normal.ObjToInt(); |
| | | }); |
| | | var locationInfos = Repository.QueryData(x => keys.Contains(x.Id)); |
| | | locationInfos.ForEach(x => x.EnableStatus = EnableStatusEnum.Normal.GetHashCode()); |
| | | Repository.UpdateData(locationInfos); |
| | | |
| | | return WebResponseContent.Instance.OK(); |
| | | } |
| | | |
| | |
| | | /// 批量禁用货位 |
| | | /// </summary> |
| | | /// <param name="keys">货位主键数组</param> |
| | | /// <returns></returns> |
| | | /// <returns>操作结果</returns> |
| | | public WebResponseContent LocationDisableStatus(int[] keys) |
| | | { |
| | | List<Dt_LocationInfo> locationInfos = Repository.QueryData(x => keys.Contains(x.Id)); |
| | | locationInfos.ForEach(x => |
| | | { |
| | | x.EnableStatus = EnableStatusEnum.Disable.ObjToInt(); |
| | | }); |
| | | var locationInfos = Repository.QueryData(x => keys.Contains(x.Id)); |
| | | locationInfos.ForEach(x => x.EnableStatus = EnableStatusEnum.Disable.GetHashCode()); |
| | | Repository.UpdateData(locationInfos); |
| | | |
| | | return WebResponseContent.Instance.OK(); |
| | | } |
| | | |
| | |
| | | /// 单个启用货位 |
| | | /// </summary> |
| | | /// <param name="key">货位主键</param> |
| | | /// <returns></returns> |
| | | public WebResponseContent LocationEnableStatus(int key) |
| | | { |
| | | return LocationEnableStatus(new int[] { key }); |
| | | } |
| | | /// <returns>操作结果</returns> |
| | | public WebResponseContent LocationEnableStatus(int key) => LocationEnableStatus(new[] { key }); |
| | | |
| | | /// <summary> |
| | | /// 单个禁用货位 |
| | | /// </summary> |
| | | /// <param name="key">货位主键</param> |
| | | /// <returns></returns> |
| | | public WebResponseContent LocationDisableStatus(int key) |
| | | { |
| | | return LocationDisableStatus(new int[] { key }); |
| | | } |
| | | /// <returns>操作结果</returns> |
| | | public WebResponseContent LocationDisableStatus(int key) => LocationDisableStatus(new[] { key }); |
| | | |
| | | /// <summary> |
| | | /// 初始化货位 |
| | | /// </summary> |
| | | /// <param name="initializationLocationDTO"></param> |
| | | /// <returns></returns> |
| | | public WebResponseContent InitializationLocation(InitializationLocationDTO initializationLocationDTO) |
| | | /// <param name="dto">初始化货位数据传输对象</param> |
| | | /// <returns>操作结果</returns> |
| | | public WebResponseContent InitializationLocation(InitializationLocationDTO dto) |
| | | { |
| | | try |
| | | { |
| | | (bool, string, object?) result = ModelValidate.ValidateModelData(initializationLocationDTO); |
| | | if (!result.Item1) return WebResponseContent.Instance.Error(result.Item2); |
| | | var (isValid, errorMsg, _) = ModelValidate.ValidateModelData(dto); |
| | | if (!isValid) return WebResponseContent.Instance.Error(errorMsg); |
| | | |
| | | int depth = initializationLocationDTO.Depth; |
| | | List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>(); |
| | | for (int i = 0; i < initializationLocationDTO.MaxRow; i++) |
| | | var locationInfos = new List<Dt_LocationInfo>(); |
| | | int depth = dto.Depth; |
| | | |
| | | for (int row = 1; row <= dto.MaxRow; row++) |
| | | { |
| | | if ((i + 1) % initializationLocationDTO.MaxRow == 1) |
| | | depth = CalculateDepth(row, dto.MaxRow, dto.Depth, depth); |
| | | |
| | | for (int col = 1; col <= dto.MaxColumn; col++) |
| | | { |
| | | depth = initializationLocationDTO.Depth; |
| | | } |
| | | else if ((i + 1) % initializationLocationDTO.MaxRow == initializationLocationDTO.Depth + 1) |
| | | { |
| | | depth = 1; |
| | | } |
| | | else if ((i + 1) % initializationLocationDTO.MaxRow > 1 && (i + 1) % initializationLocationDTO.MaxRow <= initializationLocationDTO.Depth) |
| | | { |
| | | depth -= 1; |
| | | } |
| | | else |
| | | { |
| | | depth += 1; |
| | | } |
| | | for (int j = 0; j < initializationLocationDTO.MaxColumn; j++) |
| | | { |
| | | for (int k = 0; k < initializationLocationDTO.MaxLayer; k++) |
| | | for (int layer = 1; layer <= dto.MaxLayer; layer++) |
| | | { |
| | | Dt_LocationInfo locationInfo = new Dt_LocationInfo() |
| | | { |
| | | WarehouseId = 0, |
| | | Column = j + 1, |
| | | EnableStatus = EnableStatusEnum.Normal.ObjToInt(), |
| | | Layer = k + 1, |
| | | LocationStatus = LocationStatusEnum.Free.ObjToInt(), |
| | | LocationType = LocationTypeEnum.Undefined.ObjToInt(), |
| | | RoadwayNo = $"{initializationLocationDTO.Roadway.ToString()}", |
| | | Row = i + 1, |
| | | Depth = depth, |
| | | }; |
| | | locationInfo.LocationCode = $"{locationInfo.RoadwayNo}-{locationInfo.Row.ToString().PadLeft(3, '0')}-{locationInfo.Column.ToString().PadLeft(3, '0')}-{locationInfo.Layer.ToString().PadLeft(3, '0')}-{locationInfo.Depth.ToString().PadLeft(2, '0')}"; |
| | | locationInfo.LocationName = $"{locationInfo.RoadwayNo}巷道{locationInfo.Row.ToString().PadLeft(3, '0')}行{locationInfo.Column.ToString().PadLeft(3, '0')}列{locationInfo.Layer.ToString().PadLeft(3, '0')}层{locationInfo.Depth.ToString().PadLeft(2, '0')}深"; |
| | | locationInfos.Add(locationInfo); |
| | | var location = CreateLocationInfo(dto.Roadway, row, col, layer, depth); |
| | | locationInfos.Add(location); |
| | | } |
| | | } |
| | | } |
| | | |
| | | BaseDal.AddData(locationInfos); |
| | | return WebResponseContent.Instance.OK(); |
| | | } |
| | |
| | | return WebResponseContent.Instance.Error(ex.Message); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据巷道获取空闲货位信息 |
| | | /// </summary> |
| | | /// <param name="roadwayNo">巷道编号</param> |
| | | /// <returns>空闲货位信息,如果未找到则返回null</returns> |
| | | public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo) |
| | | { |
| | | var locations = await BaseDal.QueryDataAsync(x => |
| | | x.EnableStatus == EnableStatusEnum.Normal.GetHashCode() && |
| | | x.RoadwayNo == roadwayNo && |
| | | x.LocationStatus == LocationStatusEnum.Free.GetHashCode()); |
| | | |
| | | return locations? |
| | | .OrderByDescending(x => x.Depth) // 1. 深度优先(从大到小) |
| | | .ThenBy(x => x.Layer) // 2. 层数 |
| | | .ThenBy(x => x.Column) // 3. 列 |
| | | .ThenBy(x => x.Row) // 4. 行 |
| | | .FirstOrDefault(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据巷道和货位编码获取空闲货位信息 |
| | | /// </summary> |
| | | /// <param name="roadwayNo">巷道编号</param> |
| | | /// <param name="locationCode">货位编码</param> |
| | | /// <returns>货位信息,如果未找到则返回null</returns> |
| | | public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo, string locationCode) |
| | | { |
| | | return await BaseDal.QueryFirstAsync(x => x.RoadwayNo == roadwayNo && x.LocationCode == locationCode); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据货位编码获取货位信息 |
| | | /// </summary> |
| | | /// <param name="locationCode">货位编码</param> |
| | | /// <returns>货位信息</returns> |
| | | public async Task<Dt_LocationInfo> GetLocationInfoAsync(string locationCode) |
| | | { |
| | | return await BaseDal.QueryFirstAsync(x => x.LocationCode == locationCode); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据货位ID获取货位信息 |
| | | /// </summary> |
| | | /// <param name="id">货位id</param> |
| | | /// <returns>货位信息</returns> |
| | | public async Task<Dt_LocationInfo> GetLocationInfoAsync(int id) |
| | | { |
| | | return await BaseDal.QueryFirstAsync(x => x.Id == id); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新货位信息 |
| | | /// </summary> |
| | | /// <param name="locationInfo">货位信息对象</param> |
| | | /// <returns>更新是否成功</returns> |
| | | public async Task<bool> UpdateLocationInfoAsync(Dt_LocationInfo locationInfo) |
| | | { |
| | | var beforeLocation = await BaseDal.QueryFirstAsync(x => x.Id == locationInfo.Id); |
| | | var result = await BaseDal.UpdateDataAsync(locationInfo); |
| | | if (!result) |
| | | return false; |
| | | |
| | | return beforeLocation == null |
| | | || await _recordService.AddLocationChangeRecordAsync(beforeLocation, locationInfo, LocationChangeType.HandUpdate, remark: "货位更新"); |
| | | } |
| | | |
| | | public override WebResponseContent UpdateData(Dt_LocationInfo entity) |
| | | { |
| | | var beforeLocation = BaseDal.QueryFirst(x => x.Id == entity.Id); |
| | | var result = base.UpdateData(entity); |
| | | if (!result.Status || beforeLocation == null) |
| | | return result; |
| | | |
| | | var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "货位更新").GetAwaiter().GetResult(); |
| | | return saveRecordResult ? result : WebResponseContent.Instance.Error("货位状态变更记录保存失败"); |
| | | } |
| | | |
| | | public override WebResponseContent UpdateData(List<Dt_LocationInfo> entities) |
| | | { |
| | | var beforeLocations = entities |
| | | .Select(entity => BaseDal.QueryFirst(x => x.Id == entity.Id)) |
| | | .Where(location => location != null) |
| | | .ToDictionary(location => location!.Id, location => location!); |
| | | |
| | | var result = base.UpdateData(entities); |
| | | if (!result.Status) |
| | | return result; |
| | | |
| | | foreach (var entity in entities) |
| | | { |
| | | if (!beforeLocations.TryGetValue(entity.Id, out var beforeLocation)) |
| | | continue; |
| | | |
| | | var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "批量货位更新").GetAwaiter().GetResult(); |
| | | if (!saveRecordResult) |
| | | return WebResponseContent.Instance.Error("货位状态变更记录保存失败"); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 检查并生成移库任务或返回出库任务 |
| | | /// </summary> |
| | | /// <param name="taskNum">任务号</param> |
| | | /// <returns>任务信息</returns> |
| | | public async Task<WebResponseContent> TransferCheckAsync(int taskNum) |
| | | { |
| | | var content = new WebResponseContent(); |
| | | try |
| | | { |
| | | // 根据任务号获取任务 |
| | | var outboundTask = await _taskRepository.QueryFirstAsync(x => x.TaskNum == taskNum); |
| | | if (outboundTask == null) |
| | | return content.Error("任务不存在"); |
| | | |
| | | var location = await BaseDal.QueryFirstAsync(x => x.LocationCode == outboundTask.SourceAddress && x.RoadwayNo == outboundTask.Roadway); |
| | | |
| | | // 检查是否需要进行移库 |
| | | if (CheckForInternalTransfer(location)) |
| | | { |
| | | // 计算对应位置的相对库位(奇数行的下一行或者偶数行的上一行) |
| | | var newLocationID = GetRelativeLocationID(location); |
| | | |
| | | // 获取新的库位的任务 |
| | | var internalTransferTask = await _taskRepository.QueryFirstAsync(x => x.SourceAddress == newLocationID && x.Roadway == outboundTask.Roadway); |
| | | |
| | | // 如果新的库位没有找到对应的任务 |
| | | if (internalTransferTask == null) |
| | | { |
| | | return content.OK("获取到移库任务", await HandleNoTaskAtLocation(newLocationID, outboundTask)); |
| | | } |
| | | |
| | | // 直接返回一深位出库任务 |
| | | return content.OK("获取到一深位出库任务", internalTransferTask); |
| | | } |
| | | |
| | | // 返回当前库位的出库任务 |
| | | return content.OK("当前出库任务", outboundTask); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | return content.Error($"发生错误:{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #region 私有方法 |
| | | |
| | | /// <summary> |
| | | /// 计算相对的库位ID |
| | | /// </summary> |
| | | /// <param name="locationInfo">货位信息</param> |
| | | /// <returns>相对的库位ID</returns> |
| | | private static string GetRelativeLocationID(Dt_LocationInfo locationInfo) |
| | | { |
| | | int line = locationInfo.Row; |
| | | |
| | | // 计算相对的货位行值,奇数行的下一行或者偶数行的上一行 |
| | | int relativeLine = line % 2 == 1 ? line + 1 : line - 1; |
| | | |
| | | // 构建新的库位ID |
| | | string[] newLocationParts = new string[] { relativeLine.ToString().PadLeft(3, '0'), locationInfo.Column.ToString().PadLeft(3, '0'), locationInfo.Layer.ToString().PadLeft(3, '0') }; |
| | | return string.Join("-", newLocationParts); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 处理没有任务的库位情况 |
| | | /// </summary> |
| | | /// <param name="newLocationID">新的库位ID</param> |
| | | /// <param name="outboundTask">出库任务</param> |
| | | /// <returns>生成的移库任务或原始出库任务</returns> |
| | | private async Task<Dt_Task> HandleNoTaskAtLocation(string newLocationID, Dt_Task outboundTask) |
| | | { |
| | | // 判断该位置是否有库存 |
| | | var stockInfo = await _stockInfoRepository.QueryDataNavFirstAsync(x => |
| | | x.LocationCode == newLocationID && |
| | | x.StockStatus == StockStatusEmun.入库完成.GetHashCode() && |
| | | x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode()); |
| | | if (stockInfo == null) |
| | | { |
| | | // 如果没有库存,直接返回当前出库任务 |
| | | return outboundTask; |
| | | } |
| | | |
| | | // 如果有库存,生成移库任务 |
| | | var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway); |
| | | var taskNo = await _taskRepository.GetTaskNo(); |
| | | |
| | | var newTransferTask = new Dt_Task |
| | | { |
| | | CreateDate = DateTime.Now, |
| | | Creater = App.User.UserName ?? "system", |
| | | CurrentAddress = newLocationID, |
| | | Grade = 99, |
| | | NextAddress = emptyLocation.LocationCode, |
| | | PalletCode = stockInfo.PalletCode, |
| | | Remark = "移库", |
| | | Roadway = stockInfo.LocationDetails.RoadwayNo, |
| | | SourceAddress = newLocationID, |
| | | TaskNum = taskNo, |
| | | TargetAddress = emptyLocation.LocationCode, |
| | | TaskType = TaskRelocationTypeEnum.Relocation.GetHashCode(), |
| | | TaskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(), |
| | | WarehouseId = stockInfo.WarehouseId |
| | | }; |
| | | |
| | | var createdTask = await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync(); |
| | | var beforeStock = CloneStockSnapshot(stockInfo); |
| | | var beforeSourceLocation = stockInfo.LocationDetails == null ? null : CloneLocationSnapshot(stockInfo.LocationDetails); |
| | | var beforeTargetLocation = CloneLocationSnapshot(emptyLocation); |
| | | |
| | | // 创建移库任务后,立即锁定库存和相关货位,避免并发重复分配 |
| | | stockInfo.StockStatus = StockStatusEmun.移库锁定.GetHashCode(); |
| | | var updateStockResult = await _stockInfoRepository.UpdateDataAsync(stockInfo); |
| | | |
| | | var locationsToUpdate = new List<Dt_LocationInfo>(); |
| | | if (stockInfo.LocationDetails != null) |
| | | { |
| | | stockInfo.LocationDetails.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode(); |
| | | locationsToUpdate.Add(stockInfo.LocationDetails); |
| | | } |
| | | |
| | | emptyLocation.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode(); |
| | | locationsToUpdate.Add(emptyLocation); |
| | | |
| | | var updateLocationResult = await BaseDal.UpdateDataAsync(locationsToUpdate); |
| | | if (!updateStockResult || !updateLocationResult) |
| | | { |
| | | throw new Exception("创建移库任务后更新库存状态或货位状态失败"); |
| | | } |
| | | |
| | | var saveStockRecordResult = await _recordService.AddStockChangeRecordAsync( |
| | | beforeStock, |
| | | stockInfo, |
| | | StockChangeTypeEnum.Relocation, |
| | | createdTask.TaskNum, |
| | | createdTask.OrderNo, |
| | | "移库任务预占库存"); |
| | | if (!saveStockRecordResult) |
| | | { |
| | | throw new Exception("创建移库任务后记录库存变更失败"); |
| | | } |
| | | |
| | | if (beforeSourceLocation != null && stockInfo.LocationDetails != null) |
| | | { |
| | | var saveSourceLocationRecordResult = await _recordService.AddLocationChangeRecordAsync( |
| | | beforeSourceLocation, |
| | | stockInfo.LocationDetails, |
| | | LocationChangeType.RelocationAssignLocation, |
| | | createdTask.TaskNum, |
| | | createdTask.OrderNo, |
| | | null, |
| | | "移库任务锁定源货位"); |
| | | if (!saveSourceLocationRecordResult) |
| | | { |
| | | throw new Exception("创建移库任务后记录源货位变更失败"); |
| | | } |
| | | } |
| | | |
| | | var saveTargetLocationRecordResult = await _recordService.AddLocationChangeRecordAsync( |
| | | beforeTargetLocation, |
| | | emptyLocation, |
| | | LocationChangeType.RelocationAssignLocation, |
| | | createdTask.TaskNum, |
| | | createdTask.OrderNo, |
| | | null, |
| | | "移库任务锁定目标货位"); |
| | | if (!saveTargetLocationRecordResult) |
| | | { |
| | | throw new Exception("创建移库任务后记录目标货位变更失败"); |
| | | } |
| | | |
| | | return createdTask; |
| | | } |
| | | |
| | | private static Dt_LocationInfo CloneLocationSnapshot(Dt_LocationInfo location) |
| | | { |
| | | return new Dt_LocationInfo |
| | | { |
| | | Id = location.Id, |
| | | WarehouseId = location.WarehouseId, |
| | | LocationCode = location.LocationCode, |
| | | LocationName = location.LocationName, |
| | | RoadwayNo = location.RoadwayNo, |
| | | Row = location.Row, |
| | | Column = location.Column, |
| | | Layer = location.Layer, |
| | | Depth = location.Depth, |
| | | LocationType = location.LocationType, |
| | | LocationStatus = location.LocationStatus, |
| | | EnableStatus = location.EnableStatus, |
| | | Remark = location.Remark |
| | | }; |
| | | } |
| | | |
| | | private static Dt_StockInfo CloneStockSnapshot(Dt_StockInfo stockInfo) |
| | | { |
| | | return new Dt_StockInfo |
| | | { |
| | | Id = stockInfo.Id, |
| | | PalletCode = stockInfo.PalletCode, |
| | | PalletType = stockInfo.PalletType, |
| | | LocationId = stockInfo.LocationId, |
| | | LocationCode = stockInfo.LocationCode, |
| | | WarehouseId = stockInfo.WarehouseId, |
| | | StockStatus = stockInfo.StockStatus, |
| | | Remark = stockInfo.Remark, |
| | | OutboundDate = stockInfo.OutboundDate, |
| | | Details = stockInfo.Details?.Select(detail => new Dt_StockInfoDetail |
| | | { |
| | | Id = detail.Id, |
| | | StockId = detail.StockId, |
| | | MaterielCode = detail.MaterielCode, |
| | | MaterielName = detail.MaterielName, |
| | | OrderNo = detail.OrderNo, |
| | | BatchNo = detail.BatchNo, |
| | | ProductionDate = detail.ProductionDate, |
| | | EffectiveDate = detail.EffectiveDate, |
| | | SerialNumber = detail.SerialNumber, |
| | | StockQuantity = detail.StockQuantity, |
| | | OutboundQuantity = detail.OutboundQuantity, |
| | | Status = detail.Status, |
| | | Unit = detail.Unit, |
| | | InboundOrderRowNo = detail.InboundOrderRowNo, |
| | | Remark = detail.Remark |
| | | }).ToList() |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 检查货位是否需要移库 |
| | | /// </summary> |
| | | /// <param name="location">货位信息</param> |
| | | /// <returns>是否需要移库</returns> |
| | | private static bool CheckForInternalTransfer(Dt_LocationInfo location) |
| | | { |
| | | return location.Depth == 2; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 根据巷道获取二深位的空库位 |
| | | /// </summary> |
| | | /// <param name="roadway">巷道编号</param> |
| | | /// <returns>货位对象</returns> |
| | | private async Task<Dt_LocationInfo> GetTransferLocationEmptyAsync(string roadway) |
| | | { |
| | | return await BaseDal.QueryFirstAsync(x => x.Depth == 2 && x.LocationStatus == LocationStatusEnum.Free.GetHashCode() && x.RoadwayNo == roadway); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 计算深度 |
| | | /// </summary> |
| | | /// <param name="row">行号</param> |
| | | /// <param name="maxRow">最大行数</param> |
| | | /// <param name="maxDepth">最大深度</param> |
| | | /// <param name="currentDepth">当前深度</param> |
| | | /// <returns>计算后的深度</returns> |
| | | private static int CalculateDepth(int row, int maxRow, int maxDepth, int currentDepth) |
| | | { |
| | | int mod = row % maxRow; |
| | | if (mod == 1) return maxDepth; |
| | | if (mod == maxDepth + 1) return 1; |
| | | if (mod > 1 && mod <= maxDepth) return currentDepth - 1; |
| | | return currentDepth + 1; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 创建货位信息 |
| | | /// </summary> |
| | | /// <param name="roadwayNo">巷道编号</param> |
| | | /// <param name="row">行号</param> |
| | | /// <param name="col">列号</param> |
| | | /// <param name="layer">层数</param> |
| | | /// <param name="depth">深度</param> |
| | | /// <returns>货位信息对象</returns> |
| | | private Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth) |
| | | { |
| | | var warehouse = _warehouseRepository.QueryData(x => x.WarehouseCode == roadwayNo).FirstOrDefault(); |
| | | |
| | | if (warehouse == null) |
| | | { |
| | | throw new InvalidOperationException($"未找到巷道编号为 {roadwayNo} 的仓库信息"); |
| | | } |
| | | |
| | | return new Dt_LocationInfo |
| | | { |
| | | WarehouseId = warehouse.WarehouseId, |
| | | Row = row, |
| | | Column = col, |
| | | Layer = layer, |
| | | Depth = depth, |
| | | RoadwayNo = roadwayNo, |
| | | EnableStatus = EnableStatusEnum.Normal.GetHashCode(), |
| | | LocationStatus = LocationStatusEnum.Free.GetHashCode(), |
| | | LocationType = LocationTypeEnum.Undefined.GetHashCode(), |
| | | LocationCode = $"{roadwayNo}-{row:D3}-{col:D3}-{layer:D3}", |
| | | LocationName = $"{roadwayNo}巷道{row:D3}行{col:D3}列{layer:D3}层{depth:D2}深" |
| | | }; |
| | | } |
| | | |
| | | #endregion 私有方法 |
| | | } |
| | | } |