Files
WMS-Api/src/WMS.Web.Domain/Services/InStockService.cs
2023-11-27 12:05:13 +08:00

502 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AutoMapper;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WMS.Web.Core.Dto;
using WMS.Web.Core.Dto.ChangeBoxRecord;
using WMS.Web.Core.Dto.Erp;
using WMS.Web.Core.Dto.InStock;
using WMS.Web.Core.Dto.InStockTask;
using WMS.Web.Core.Dto.Inventory;
using WMS.Web.Core.Dto.Login;
using WMS.Web.Core.Internal.Results;
using WMS.Web.Domain.Entitys;
using WMS.Web.Domain.Infrastructure;
using WMS.Web.Domain.IService;
using WMS.Web.Domain.IService.Public;
using WMS.Web.Domain.Mappers;
using WMS.Web.Domain.Values;
namespace WMS.Web.Domain.Services
{
/// <summary>
/// 入库单服务
/// </summary>
public class InStockService : IInStockService
{
private readonly IMapper _mapper;
private readonly IErpService _erpService;
private readonly ILoginService _loginService;
private readonly ISerialNumberService _serialNumberService;
private readonly ISerialNumbersRepositories _serialNumbersRepositories;
private readonly IChangeMoveBoxService _changeMoveBoxService;
private readonly IBoxRepositories _boxRepositories;
private readonly IBoxInventoryService _boxInventoryService;
private readonly IBasicsRepositories _basicsRepositories;
private readonly IInStockRepositories _inStockRepositories;
private readonly IInStockTaskRepositories _inStockTaskRepositories;
private readonly IErpBasicDataExtendService _erpBasicDataExtendService;
public InStockService(IMapper mapper, ISerialNumbersRepositories serialNumbersRepositories, IErpService erpService, IBoxInventoryService boxInventoryService, ISerialNumberService serialNumberService, ILoginService loginService, IBoxRepositories boxRepositories,
IBasicsRepositories basicsRepositories, IErpBasicDataExtendService erpBasicDataExtendService, IChangeMoveBoxService changeMoveBoxService,
IInStockRepositories inStockRepositories, IInStockTaskRepositories inStockTaskRepositories)
{
_mapper = mapper;
_erpService = erpService;
_loginService = loginService;
_boxInventoryService = boxInventoryService;
_serialNumberService = serialNumberService;
_boxRepositories = boxRepositories;
_changeMoveBoxService = changeMoveBoxService;
_basicsRepositories = basicsRepositories;
_inStockRepositories = inStockRepositories;
_inStockTaskRepositories = inStockTaskRepositories;
_erpBasicDataExtendService = erpBasicDataExtendService;
_serialNumbersRepositories = serialNumbersRepositories;
}
/// <summary>
/// 同步-金蝶
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<Result> Sync(OperateRequest dto)
{
return Result.ReSuccess();
}
/// <summary>
/// 收货
/// </summary>
/// <param name="dto"></param>
/// <param name="loginInfo"></param>
/// <returns></returns>
public async Task<Result> Receive(UpdateInStockTaskRequest dto, LoginInDto loginInfo)
{
IDbContextTransaction _transaction = _basicsRepositories.GetTransaction();
bool isRollback = false;
bool isTransaction = false;
var result = await this.Update(dto, loginInfo.UserInfo.StaffId, true, isTransaction);
if (!result.IsSuccess) isRollback = true;
//提交事务
var isSuccess = _basicsRepositories.CommitTransaction(isRollback, _transaction);
if (!isSuccess)
return Result.ReFailure(ResultCodes.DateWriteError);
return Result.ReSuccess();
}
/// <summary>
/// 上架-采购订单
/// </summary>
/// <param name="instock"></param>
/// <param name="loginInfo"></param>
/// <returns></returns>
public async Task<Result> Shelf(PurchaseShelfRequest instock, LoginInDto loginInfo)
{
IDbContextTransaction _transaction = _basicsRepositories.GetTransaction();
bool isRollback = false;
bool isTransaction = false;
//1.添加入库单同步金蝶在save方法里面进行
var save_result = await this.ShelfSave(instock, InstockType.Purchase, loginInfo, isTransaction);
if (!save_result.IsSuccess) isRollback = true;
//提交事务
var isSuccess = _basicsRepositories.CommitTransaction(isRollback, _transaction);
if (!isSuccess)
return Result.ReFailure(ResultCodes.DateWriteError);
return Result.ReSuccess();
}
/// <summary>
/// 非采购单上架
/// </summary>
/// <param name="dto"></param>
/// <param name="loginInDto"></param>
/// <returns></returns>
public async Task<Result> ShelfNoPurchase(NoPurchaseShelfRequest dto, LoginInDto loginInDto)
{
IDbContextTransaction _transaction = _basicsRepositories.GetTransaction();
bool isRollback = false;
bool isTransaction = false;
//保存非采购上架的数据
var shelfSave_result = await this.ShelfNoPurchaseSave(dto, loginInDto, isTransaction);
if (!shelfSave_result.IsSuccess) isRollback = true;
//提交事务
var isSuccess = _basicsRepositories.CommitTransaction(isRollback, _transaction);
if (!isSuccess)
return Result.ReFailure(ResultCodes.DateWriteError);
return Result.ReSuccess();
}
/// <summary>
/// 获取任务单:根据箱号信息
/// </summary>
/// <param name="boxBillNo"></param>
/// <returns></returns>
public async Task<Result<BoxInStockTaskDto>> GetInfoByBox(string boxBillNo)
{
//1.获取物料集合
var materials_result = await _erpService.BillQueryForMaterial();
var materials = new List<ErpMaterialDto>();
if (materials_result.IsSuccess)
materials = materials_result.Data.ToList();
//1.先判断:箱号已经绑定了入库任务单中;备注:业务说法就是箱是否收货了
var tast = await _inStockTaskRepositories.GetBy(boxBillNo);
if (tast == null)
return Result<BoxInStockTaskDto>.ReFailure(ResultCodes.Box_NoBind_Task_Data);
//2.找到箱对应的物料信息
var box = await _boxRepositories.GetByNo(boxBillNo);
if (box == null)
return Result<BoxInStockTaskDto>.ReFailure(ResultCodes.BoxNoData);
//2.1判断箱是否上架过了
var isExist = await _inStockRepositories.IsExistBy(box.Id);
if (isExist)
return Result<BoxInStockTaskDto>.ReFailure(ResultCodes.BoxIsTrueShelf);
//3.组装返回数据
var result = new BoxInStockTaskDto();
result.SourceBillNo = tast.SourceBillNo;
result.TaskId = tast.Id;
result.BoxId = box.Id;
foreach (var item in box.Details)
{
//这里找任务单的物料条件物料ID和应入库数量大于收货数量防止有同样的物料数据收货的时候 会有收货数量,只要找到没有收完的就行了
var current_task_Det = tast.Details.Where(x => x.MaterialId == item.MaterialId && x.AccruedQty > x.ReceiveQty).FirstOrDefault();
//3.2映射返回明细对象
var box_task_detail = new BoxDetailsInStockTaskDto();
box_task_detail.SupplierId = current_task_Det == null ? 0 : current_task_Det.SupplierId;
box_task_detail.OrgId = current_task_Det == null ? 0 : current_task_Det.OrgId;
box_task_detail.ReceiveQty = current_task_Det == null ? 0 : current_task_Det.ReceiveQty;
box_task_detail.AccruedQty = current_task_Det == null ? 0 : current_task_Det.AccruedQty;
box_task_detail.MaterialId = item.MaterialId;
box_task_detail.BoxMaterialQty = item.Qty;
box_task_detail.MaterialName = _erpBasicDataExtendService.GetMaterialName(materials, item.MaterialId);
box_task_detail.MaterialNumber = _erpBasicDataExtendService.GetMaterialNumber(materials, item.MaterialId);
box_task_detail.Specifications = _erpBasicDataExtendService.GetMaterialSpecifications(materials, item.MaterialId);
result.Details.Add(box_task_detail);
}
return Result<BoxInStockTaskDto>.ReSuccess(result);
}
/// <summary>
/// 采购上架-保存
/// </summary>
/// <param name="dto"></param>
/// <param name="staffId"></param>
/// <param name="isTransaction"></param>
/// <returns></returns>
private async Task<Result> ShelfSave(PurchaseShelfRequest dto, InstockType type, LoginInDto loginInfo, bool isTransaction = true)
{
var entity = new InStock();
entity.Type = type;
entity.Method = InventoryInOutMethod.Box;
entity.Details = _mapper.Map<List<InStockDetails>>(dto.Details);
entity.Create(loginInfo.UserInfo.StaffId);
//序列号集:箱里面的
var serialNumbers = await _serialNumbersRepositories.GetEntityListByBoxIds(dto.Details.GroupBy(x => x.BoxId).Select(x => x.Key).ToList());
//赋值序列号
entity.Details.ForEach(x =>
{
var current_box_serNums = serialNumbers.Where(t => t.BoxId == x.BoxId).ToList();
if (current_box_serNums != null)
{
var current_box_mat_serNums = current_box_serNums.Where(t => t.MaterialId == x.MaterialId).Select(t => t.SerialNumber).ToList();
x.SerialNumbers.AddRange(current_box_mat_serNums);
}
});
entity = await _inStockRepositories.Add(entity, isTransaction);
if (entity == null)
return Result.ReFailure(ResultCodes.DateWriteError);
//同步金蝶后,反写任务单的已交数量
var taskId = dto.Details.GroupBy(x => x.TaskId).Select(x => x.Key).FirstOrDefault();
var task = await _inStockTaskRepositories.Get(taskId);
if (task != null)
{
task.Details.ForEach(x =>
{
x.DeliveredQty = x.ReceiveQty;
});
task.Shelf(loginInfo.UserInfo.StaffId);
task = await _inStockTaskRepositories.Update(task, isTransaction);
if (task == null)
return Result.ReFailure(ResultCodes.DateWriteError);
}
//保存成功后:序列号跟踪流程添加
var serialNumber_result = await _serialNumberService.InStock(entity, loginInfo, isTransaction);
if (!serialNumber_result.IsSuccess)
return serialNumber_result;
//箱库存变动
var dtoDatas = new List<InStock>();
dtoDatas.Add(entity);
var boxInventoryResult = await _boxInventoryService.GenerateInStockBox(dtoDatas, isTransaction);
if (!boxInventoryResult.IsSuccess)
return boxInventoryResult;
return Result.ReSuccess();
}
/// <summary>
/// 非采购上架-保存
/// </summary>
/// <param name="dto"></param>
/// <param name="loginInfo"></param>
/// <param name="isTransaction"></param>
/// <returns></returns>
private async Task<Result> ShelfNoPurchaseSave(NoPurchaseShelfRequest dto, LoginInDto loginInfo, bool isTransaction = true)
{
//1.获取来源单
var task = await _inStockTaskRepositories.Get(dto.TaskId);
if (task == null)
return Result.ReFailure(ResultCodes.SourceBillNoDateError);
//2.生成:入库单
var entity = new InStock();
entity.Type = task.Type;
entity.Method = dto.ShelfMethod == (int)ShelfMethod.Box ? InventoryInOutMethod.Box : InventoryInOutMethod.Product;
entity.Details = new List<InStockDetails>();
var temps = new List<InStockDetails>();
//3.遍历box信息,并拼装入库单明细
foreach (var box in dto.Boxs)
{
//3.1先数据映射
var dets = _mapper.Map<List<InStockDetails>>(box.Details);
//3.2遍历赋值boxID和仓库和仓位和来源单和供应商和组织
dets.ForEach(item =>
{
item.TaskId = dto.TaskId;
item.SourceBillNo = task.SourceBillNo;
item.BoxId = box.BoxId;
item.SubStockId = dto.SubStockId;
item.StockCode = dto.StockCode;
item.OrgCode = dto.OrgCode;
item.OrgId = task.Details.Select(x => x.OrgId).FirstOrDefault();
});
//3.3添加到临时集合中
temps.AddRange(dets);
}
//3.4给对象明细赋值
entity.Details = temps;
//4.创建
entity.Create(loginInfo.UserInfo.StaffId);
//序列号集:箱里面的
var serialNumbers = await _serialNumbersRepositories.GetEntityListByBoxIds(dto.Boxs.GroupBy(x => x.BoxId).Select(x => x.Key).ToList());
if (dto.ShelfMethod == (int)ShelfMethod.Box)
{
//赋值序列号
entity.Details.ForEach(x =>
{
var current_box_serNums = serialNumbers.Where(t => t.BoxId == x.BoxId).ToList();
if (current_box_serNums != null)
{
var current_box_mat_serNums = current_box_serNums.Where(t => t.MaterialId == x.MaterialId).Select(t => t.SerialNumber).ToList();
x.SerialNumbers.AddRange(current_box_mat_serNums);
}
});
}
//5.1保存入库单信息
entity = await _inStockRepositories.Add(entity, isTransaction);
if (entity == null)
return Result.ReFailure(ResultCodes.DateWriteError);
//同步金蝶后,反写任务单的已交数量
if (task != null)
{
task.Details.ForEach(x =>
{
var current_entityDet_matQty = entity.Details.Where(t => t.MaterialId == x.MaterialId).Sum(t => t.Qty);
if (x.AccruedQty > current_entityDet_matQty)
{
x.ReceiveQty = x.ReceiveQty + current_entityDet_matQty;
x.DeliveredQty = x.ReceiveQty;
}
});
task.NoPurchaseShelf(loginInfo.UserInfo.StaffId);
task = await _inStockTaskRepositories.Update(task, isTransaction);
if (task == null)
return Result.ReFailure(ResultCodes.DateWriteError);
}
//6.当按产品上架:就要调用改箱的操作;
if (dto.ShelfMethod == (int)ShelfMethod.Product)
{
//通过序列号,获取序列号对应的箱
var cureent_serialNumbs = await _serialNumbersRepositories.GetEntityList(dto.Boxs.SelectMany(x => x.Details).SelectMany(x => x.SerialNumbers).ToList());
var current_boxIds= cureent_serialNumbs.GroupBy(x => x.BoxId).Select(x => x.Key).ToList();
var ganenrateChangeBoxs = new List<SaveChangeBoxRecordRequest>();
var dto_box = dto.Boxs.FirstOrDefault();
var changeBox = new SaveChangeBoxRecordRequest();
changeBox.DestBoxId = dto_box.BoxId;
foreach (var item in dto_box.Details)
{
var changeBoxRD = new SaveChangeBoxRecordDetailsRequest();
changeBoxRD.MaterialId = item.MaterialId;
changeBoxRD.Qty = item.Qty;
changeBoxRD.SerialNumbers = item.SerialNumbers;
changeBox.Details.Add(changeBoxRD);
}
ganenrateChangeBoxs.Add(changeBox);
//改箱保存操作
var changeBoxSave_Result = await _changeMoveBoxService.ChangeBoxSave(ganenrateChangeBoxs, loginInfo, isTransaction);
if (!changeBoxSave_Result.IsSuccess)
return changeBoxSave_Result;
}
//6.1序列号跟踪流程添加;备注:和上面的改箱操作后会记录序列号轨迹不冲突;
var serialNumber_result = await _serialNumberService.InStock(entity, loginInfo, isTransaction);
if (!serialNumber_result.IsSuccess)
return serialNumber_result;
//箱库存变动
var dtoDatas = new List<InStock>();
dtoDatas.Add(entity);
var boxInventoryResult = await _boxInventoryService.GenerateInStockBox(dtoDatas, isTransaction);
if (!boxInventoryResult.IsSuccess)
return boxInventoryResult;
return Result.ReSuccess();
}
/// <summary>
/// 修改-入库任务信息
/// </summary>
/// <param name="dto"></param>
/// <param name="staffId"></param>
/// <param name="isReceive"></param>
/// <param name="isTransaction"></param>
/// <returns></returns>
public async Task<Result<InStockTask>> Update(UpdateInStockTaskRequest dto, int staffId, bool? isReceive, bool isTransaction = true, bool isNoPurchaseShelf = false)
{
var entity = await _inStockTaskRepositories.Get(dto.Id);
if (entity == null)
return Result<InStockTask>.ReFailure(ResultCodes.DateWriteError);
entity = _mapper.Map(dto, entity);
////子集单独映射:这个弃用这个不好的地方就是前端必须拿到全部的boxs集合一起给到后端处理
//entity.Boxs = _mapper.ToMapList(dto.Boxs, entity.Boxs);
//子集单独映射box子集单独组装获取这个好处就是前端就只传新增的box过来
foreach (var item in dto.Boxs)
{
var box = _mapper.Map<InStockTaskBox>(item);
entity.Boxs.Add(box);
}
//子集单独映射
entity.Details = _mapper.ToMapList(dto.Details, entity.Details);
if (isNoPurchaseShelf)
entity.NoPurchaseShelf(staffId);
else
{
if (isReceive.HasValue && isReceive.Value)
entity.Receive(staffId);
else
entity.Shelf(staffId);
}
var result = await _inStockTaskRepositories.Update(entity, isTransaction);
if (result != null)
return Result<InStockTask>.ReSuccess(entity);
else
return Result<InStockTask>.ReFailure(ResultCodes.DateWriteError);
}
/// <summary>
/// 批量修改-入库任务信息
/// </summary>
/// <param name="ids"></param>
/// <param name="staffId"></param>
/// <param name="isReceive"></param>
/// <param name="isTransaction"></param>
/// <returns></returns>
public async Task<Result> UpdateRange(List<int> ids, int staffId, bool isReceive, bool isTransaction = true)
{
var entitys = await _inStockTaskRepositories.GetList(ids);
if (entitys == null || entitys.Count == 0)
return Result.ReFailure(ResultCodes.DateWriteError);
foreach (var item in entitys)
{
if (isReceive)
item.Receive(staffId);
else
item.Shelf(staffId);
}
var isSuccess = await _inStockTaskRepositories.UpdateRange(entitys, isTransaction);
if (isSuccess)
return Result.ReSuccess();
else
return Result.ReFailure(ResultCodes.DateWriteError);
}
/// <summary>
/// 采购订单物料明细和箱物料明细-对比
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<Result<ContrastMaterialsResponse>> Contrast(ContrastMaterialsRequest dto)
{
//1.找到任务单的明细信息
var task = await _inStockTaskRepositories.Get(dto.TaskId);
if (task == null)
return Result<ContrastMaterialsResponse>.ReFailure(ResultCodes.OrderNoData);
//1.1筛选出对应仓库的明细
var task_details = task.Details.Where(x => x.StockCode == dto.StockCode).ToList();
//2.找到箱对应的物料信息:多个箱
var boxs = await _boxRepositories.GetEntityListByNos(dto.BoxBillNos);
if (boxs == null || boxs.Count == 0)
return Result<ContrastMaterialsResponse>.ReFailure(ResultCodes.BoxNoData);
//合并多个箱明细的数据:相同物料数量合并
var boxDetails = boxs.SelectMany(x => x.Details).GroupBy(x => x.MaterialId).Select(x => new { MaterialId = x.Key, Qty = x.Sum(t => t.Qty) }).ToList();
//3.比对false为比对失败;
bool isRight = boxDetails.All(x => task_details.Any(t => t.MaterialId == x.MaterialId && t.AccruedQty >= x.Qty)) && boxDetails.Count <= task_details.Count;
if (!isRight)
return Result<ContrastMaterialsResponse>.ReFailure(ResultCodes.ContrastError);
//4.是否任务单存在绑定箱号判断:存在的话,就不能收货或者非采购上架
bool isHave = task.Boxs.Where(x => dto.BoxBillNos.Contains(x.BoxBillNo)).Any();
if (isHave)
return Result<ContrastMaterialsResponse>.ReFailure(ResultCodes.BoxHaveError);
//4.返回对比结果true为比对成功并把箱ID和箱号返回
var response = new ContrastMaterialsResponse();
foreach (var box in boxs)
{
var r_box = new ContrastBoxResponse();
r_box.BoxBillNo = box.BoxBillNo;
r_box.BoxId = box.Id;
r_box.TotalCount = box.Details.Sum(x => x.Qty);
r_box.Details = _mapper.Map<List<ContrastBoxDetailsResponse>>(box.Details);
response.Boxs.Add(r_box);
}
return Result<ContrastMaterialsResponse>.ReSuccess(response);
}
}
}