Files
WMS-Api/src/WMS.Web.Domain/Services/OutStockService.cs
18942506660 bd4fcdb904 修复bug
2023-12-08 15:32:48 +08:00

262 lines
13 KiB
C#

using AutoMapper;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
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.Erp;
using WMS.Web.Core.Dto.Inventory;
using WMS.Web.Core.Dto.Login;
using WMS.Web.Core.Dto.OutStock;
using WMS.Web.Core.Dto.TakeStock;
using WMS.Web.Core.Help;
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.Values;
using WMS.Web.Domain.Values.Erp;
using WMS.Web.Domain.Values.Single;
namespace WMS.Web.Domain.Services
{
/// <summary>
/// 出库服务
/// </summary>
public class OutStockService : IOutStockService
{
private readonly IMapper _mapper;
private readonly ILoginService _loginService;
public readonly IBasicsRepositories _transactionRepositories;
private readonly IOutStockRepositories _outStockRepositories;
private readonly IOutStockTaskRepositories _outStockTaskRepositories;
private readonly IOutStockTaskService _outStockTaskService;
private readonly IErpService _erpService;
private readonly ISerialNumberService _serialNumberService;
private readonly IBoxInventoryService _boxInventoryService;
private readonly IInventoryInOutDetailsService _inventoryInOutDetailsService;
private readonly IBoxInventoryRepositories _boxInventoryRepositories;
private readonly ILogger<OutStockService> _logger;
private readonly IBoxRepositories _boxRepositories;
private readonly ISingleDataService _singleDataService;
public OutStockService(IMapper mapper, ILoginService loginService,
IBasicsRepositories transactionRepositories,
IOutStockRepositories outStockRepositories, IOutStockTaskRepositories outStockTaskRepositories,
IOutStockTaskService outStockTaskService, IErpService erpService, ISerialNumberService serialNumberService,
IBoxInventoryService boxInventoryService, IInventoryInOutDetailsService inventoryInOutDetailsService,
IBoxInventoryRepositories boxInventoryRepositories, ILogger<OutStockService> logger, IBoxRepositories boxRepositories,
ISingleDataService singleDataService)
{
_mapper = mapper;
_loginService = loginService;
_transactionRepositories = transactionRepositories;
_outStockRepositories = outStockRepositories;
_outStockTaskRepositories = outStockTaskRepositories;
_outStockTaskService = outStockTaskService;
_erpService = erpService;
_serialNumberService = serialNumberService;
_boxInventoryService = boxInventoryService;
_inventoryInOutDetailsService = inventoryInOutDetailsService;
_boxInventoryRepositories = boxInventoryRepositories;
_logger = logger;
_boxRepositories = boxRepositories;
_singleDataService = singleDataService;
}
/// <summary>
/// 出库单
/// </summary>
/// <param name="dto"></param>
/// <param name="loginInfo"></param>
/// <returns></returns>
public async Task<Result> Save(SaveOutStockRequest dto, LoginInDto loginInfo)
{
_logger.LogInformation($"出库:{JsonConvert.SerializeObject(dto)} 操作人:{loginInfo.UserInfo.StaffId}");
var outStockTask = await _outStockTaskRepositories.Get(dto.TaskId);
if (outStockTask == null) return Result.ReFailure(ResultCodes.OutStockTaskNoData);
if (outStockTask.Status == OutStockStatus.Already)
return Result.ReFailure(ResultCodes.OutStockTaskAlready);
if (outStockTask.Status == OutStockStatus.Repeal)
return Result.ReFailure(ResultCodes.OutStockTaskRepeal);
//上传的物料在任务单里没有找到不能出库
var ex = dto.Details.Select(s => s.MaterialId).Except(outStockTask.Details.Select(s => s.MaterialId)).ToList();
if (ex.Count() > 0 && dto.Method == 2)
return Result.ReFailure(ResultCodes.OutStockMaterialError);
var boxIds = dto.Details.Select(s => s.BoxId).ToList();
var boxInventoryList = await _boxInventoryRepositories.GetList(boxIds);
foreach (var boxid in boxIds)
{
var inventory = boxInventoryList.FirstOrDefault(f => f.BoxId == boxid);
if (inventory == null)
{
var box = await _boxRepositories.Get(boxid);
return Result.ReFailure($"箱号:{box?.BoxBillNo} 不存在库存", 70008);
}
if (!inventory.StockCode.Equals(outStockTask.StockCode))
{
var box = await _boxRepositories.Get(boxid);
var stockCodeName = _singleDataService.GetSingleData(SingleAction.StocksJoinOrgCode, loginInfo.UserInfo.CompanyId, outStockTask.StockCode + outStockTask.OrgCode);
return Result.ReFailure($"箱号:{box?.BoxBillNo}在仓库:{stockCodeName} 不存在库存", 70009);
}
}
//1.需要验证物料对应箱和序列号是否存在库存
//如果是按箱出库从库存拿取数据
if (dto.Method == 1)
{
dto.Details.Clear();
//获取箱子对应的所有库存
//过滤掉不同仓库和不同组织的
boxInventoryList = boxInventoryList.Where(w => w.StockCode == outStockTask.StockCode && w.OrgCode == outStockTask.OrgCode).ToList();
//组装dto
var boxInventoryDetails = boxInventoryList.SelectMany(s => s.Details).ToList();
foreach (var b in boxInventoryDetails)
{
var num = boxInventoryDetails.Where(w => w.MaterialId == b.MaterialId).Sum(s => s.Qty);
var taskDetail = outStockTask.Details.FirstOrDefault(f => f.MaterialId == b.MaterialId);
if (taskDetail == null) return Result.ReFailure(ResultCodes.BoxOutStockTaskMaterialError);
//箱子里该物料的总数量大于出库单(应出库数量-已出库数量) 不能出库
if (num > (taskDetail.AccruedQty - taskDetail.RealityQty))
return Result.ReFailure(ResultCodes.BoxNumberError);
var box = boxInventoryList.FirstOrDefault(f => f.Id == b.Fid);
if (box == null) return Result.ReFailure(ResultCodes.BoxNoData);
var dtod = _mapper.Map<SaveOutStockDetailsRequest>(b);
dtod.BoxId = box.BoxId;
dtod.SubStockId = box.SubStockId;
dto.Details.Add(dtod);
}
}
var mIds = dto.Details.GroupBy(g => g.MaterialId).Select(s => s.Key).ToList();
OutStock entity = new OutStock();
foreach (var mid in mIds)
{
//任务单明细
var tDetail = outStockTask.Details.FirstOrDefault(f => f.MaterialId == mid);
var dtoDetails = dto.Details.Where(w => w.MaterialId == mid);
var qty = dtoDetails.Sum(s => s.Qty);
if (tDetail == null) continue;
var res = outStockTask.OutStock(mid, qty, loginInfo.UserInfo.StaffId);
if (!res.IsSuccess) return res;
var outd = _mapper.Map<OutStockDetails>(tDetail);
//循环添加erp同步明细
foreach (var c in res.Data)
{
var tErpDetail = tDetail.ErpDetails.FirstOrDefault(f => f.Erp_DetailId == c.erpDetailId);
var oErpDetail = _mapper.Map<OutStockErpDetails>(tErpDetail);
oErpDetail.Qty = c.qty;
outd.ErpDetails.Add(oErpDetail);
}
var boxs = dto.Details.Where(w => w.MaterialId == mid).ToList();
outd.BoxsDetails = _mapper.Map<List<OutStockBoxsDetails>>(boxs);
outd.Qty = qty;
outd.SerialNumbers.AddRange(dtoDetails.SelectMany(s => s.SerialNumbers));
entity.Details.Add(outd);
}
entity.Create(loginInfo.UserInfo.StaffId, outStockTask, dto.Method);
IDbContextTransaction _transaction = _transactionRepositories.GetTransaction();
Result res_Rollback = Result.ReSuccess();
bool isSuccess = true;
//修改库存
if (res_Rollback.IsSuccess)
{
entity = await _outStockRepositories.Add(entity, false);
if (entity == null) res_Rollback = Result.ReFailure(ResultCodes.DateWriteError);
}
if (res_Rollback.IsSuccess)
{
outStockTask = await _outStockTaskRepositories.Edit(outStockTask, false);
if (outStockTask == null) res_Rollback = Result.ReFailure(ResultCodes.DateWriteError);
}
if (res_Rollback.IsSuccess)
{
var res_change = await _serialNumberService.OutStock(entity, loginInfo, false);
if (!res_change.IsSuccess) res_Rollback = res_change;
}
if (res_Rollback.IsSuccess)
{
var res_Inventory = await _boxInventoryService.GenerateOutStockBox(entity, false);
if (!res_Inventory.IsSuccess) res_Rollback = res_Inventory;
}
//提交事务
isSuccess = _transactionRepositories.CommitTransaction(res_Rollback.IsSuccess ? false : true, _transaction);
if (!res_Rollback.IsSuccess) return res_Rollback;
if (!isSuccess)
return Result.ReFailure(ResultCodes.DateWriteError);
return Result.ReSuccess();
}
/// <summary>
/// 同步金蝶
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<Result> Sync(OperateRequest dto, LoginInDto loginInfo)
{
var list = await _outStockRepositories.GetEntityList(dto.Ids);
foreach (var entity in list)
{
await SalOutStock(entity, loginInfo);
}
return Result.ReSuccess();
}
/// <summary>
/// 同步金蝶销售出库
/// </summary>
/// <returns></returns>
private async Task<Result> SalOutStock(OutStock entity, LoginInDto loginInfo)
{
if (entity.Type != OutStockType.Sal) return Result.ReSuccess();
if (entity.SuccessSync != SyncStatus.Fail) return Result.ReSuccess();
List<string> failList = entity.Details.SelectMany(s => s.ErpDetails)
.Where(w => w.SuccessSync == SyncStatus.Fail).GroupBy(g => g.SourceBillNo)
.Select(s => s.Key).ToList();//同步失败的来源单号
//找到单据里需要同步的单据
var sourcNos = entity.Details.SelectMany(s => s.ErpDetails).GroupBy(s => s.SourceBillNo).Select(s => s.Key);
foreach (var s in failList)
{
var erp_details = entity.Details.SelectMany(s => s.ErpDetails).Select(s => s.Erp_DetailId).ToList();
var erpDto = new ErpPushDto()
{
FormId = FormIdParam.SAL_DELIVERYNOTICE.ToString(),
Numbers = new List<string>() { s },
TargetFormId = FormIdParam.SAL_OUTSTOCK.ToString(),
DetailsId = string.Join(",", erp_details)
};
//下推金蝶
var res = await this.Push(erpDto);
if (res.IsSuccess)
entity.SyncSuccess(s, loginInfo.UserInfo.StaffId);
else
entity.SyncFail(res.Message, loginInfo.UserInfo.StaffId, SyncStatus.Fail);
}
//entity.SuccessSync = entity.SuccessSyncFail.Count() > 0 ? false : true;
//最好一条一条执行,否则执行失败 但是金蝶那边又同步成功 就会造成数据比价乱
var isSuccess = await _outStockRepositories.Edit(entity, true);
if (entity == null) return Result.ReFailure(ResultCodes.DateWriteError);
return Result.ReSuccess();
}
/// <summary>
/// 下推
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
private async Task<Result> Push(ErpPushDto dto)
{
var res = await _erpService.Push(dto);
return Result.ReSuccess();
}
}
}