402 lines
21 KiB
C#
402 lines
21 KiB
C#
using AutoMapper;
|
|
using Microsoft.EntityFrameworkCore.Storage;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
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.Erp.OutStock;
|
|
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;
|
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
|
private readonly IInStockTaskBoxService _inStockTaskService;
|
|
private readonly IChangeMoveBoxService _changeMoveBoxService;
|
|
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, IServiceScopeFactory serviceScopeFactory, IInStockTaskBoxService inStockTaskService,
|
|
IChangeMoveBoxService changeMoveBoxService)
|
|
{
|
|
_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;
|
|
_serviceScopeFactory = serviceScopeFactory;
|
|
_inStockTaskService = inStockTaskService;
|
|
_changeMoveBoxService = changeMoveBoxService;
|
|
}
|
|
/// <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 mIds = dto.Details.GroupBy(g => g.MaterialNumber).Select(s => s.Key).ToList();
|
|
foreach (var m in mIds)
|
|
{
|
|
var d_boxIds = dto.Details.Where(w => w.MaterialNumber == m).Select(s => s.BoxId);
|
|
if (d_boxIds.Distinct().Count() != d_boxIds.Count())
|
|
return Result.ReFailure(ResultCodes.BoxOutStockTaskBoxError);
|
|
}
|
|
//上传的物料在任务单里没有找到不能出库
|
|
var ex = dto.Details.Select(s => s.MaterialNumber).Except(outStockTask.Details.Where(w => w.IsRepeal != true).Select(s => s.MaterialNumber)).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)
|
|
{
|
|
var oldDetails = dto.Details.Clone();
|
|
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)
|
|
{
|
|
if (b.Qty <= 0) continue;
|
|
var num = boxInventoryDetails.Where(w => w.MaterialNumber == b.MaterialNumber).Sum(s => s.Qty);
|
|
var taskDetail = outStockTask.Details.FirstOrDefault(f => f.MaterialNumber == b.MaterialNumber && f.IsRepeal != true);
|
|
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.SubStockCode = box.SubStockCode;
|
|
var oldd = oldDetails.FirstOrDefault(f=>f.BoxId==box.BoxId&&f.MaterialNumber.Equals(dtod.MaterialNumber));
|
|
if(oldd==null)
|
|
return Result.ReFailure(ResultCodes.BoxMateriaNoData);
|
|
dtod.BoxLength = oldd.BoxLength;
|
|
dtod.BoxWide = oldd.BoxWide;
|
|
dtod.BoxHigh = oldd.BoxHigh;
|
|
dtod.BoxWeight = oldd.BoxWeight;
|
|
dto.Details.Add(dtod);
|
|
}
|
|
}
|
|
|
|
|
|
OutStock entity = new OutStock();
|
|
foreach (var mid in mIds)
|
|
{
|
|
//任务单明细
|
|
var tDetail = outStockTask.Details.FirstOrDefault(f => f.MaterialNumber == mid && f.IsRepeal != true);
|
|
var dtoDetails = dto.Details.Where(w => w.MaterialNumber == mid);
|
|
var qty = dtoDetails.Sum(s => s.Qty);
|
|
|
|
if (tDetail == null) continue;
|
|
_logger.LogInformation($"出库前任务单信息:{JsonConvert.SerializeObject(outStockTask)}");
|
|
var res = outStockTask.OutStock(mid, qty, loginInfo.UserInfo.StaffId);
|
|
_logger.LogInformation($"出库后任务单信息:{JsonConvert.SerializeObject(outStockTask)}--结果:{JsonConvert.SerializeObject(res)} --{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.MaterialNumber == mid).ToList();
|
|
foreach (var box in boxs)
|
|
{
|
|
box.SerialNumbers.AddRange(box.TwoSerialNumbers);
|
|
box.SerialNumbers.Sort();
|
|
}
|
|
outd.BoxsDetails = _mapper.Map<List<OutStockBoxsDetails>>(boxs);
|
|
|
|
outd.Qty = qty;
|
|
outd.SerialNumbers.AddRange(dtoDetails.SelectMany(s => s.SerialNumbers));
|
|
outd.SerialNumbers.AddRange(dtoDetails.SelectMany(s => s.TwoSerialNumbers));
|
|
outd.SerialNumbers.Sort();
|
|
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;
|
|
}
|
|
//if (res_Rollback.IsSuccess)
|
|
//{
|
|
// var res_changeMoveBox = await _changeMoveBoxService.ChangeBox_OutStock(entity, false);
|
|
// if (!res_changeMoveBox.IsSuccess) res_Rollback = res_changeMoveBox;
|
|
//}
|
|
if (res_Rollback.IsSuccess)
|
|
{
|
|
boxIds = entity.Details.SelectMany(s => s.BoxsDetails).Select(s => s.BoxId).ToList();
|
|
var res_InStockTask = await _inStockTaskService.UnBind(boxIds, false);
|
|
if (!res_InStockTask.IsSuccess) res_Rollback = res_InStockTask;
|
|
}
|
|
|
|
//提交事务
|
|
isSuccess = _transactionRepositories.CommitTransaction(res_Rollback.IsSuccess ? false : true, _transaction);
|
|
if (!res_Rollback.IsSuccess) return res_Rollback;
|
|
if (!isSuccess)
|
|
return Result.ReFailure(ResultCodes.DateWriteError);
|
|
//取消同步金蝶
|
|
//if (entity.Type == OutStockType.Sal)
|
|
//{
|
|
// OperateRequest oRequest = new OperateRequest();
|
|
// oRequest.Ids.Add(entity.Id);
|
|
// await Sync(oRequest, loginInfo, false);
|
|
//}
|
|
|
|
return Result.ReSuccess();
|
|
}
|
|
/// <summary>
|
|
/// 同步金蝶
|
|
/// </summary>
|
|
/// <param name="dto"></param>
|
|
/// <returns></returns>
|
|
public Task<Result> Sync(OperateRequest dto, LoginInDto loginInfo, bool isRepeatSync = true)
|
|
{
|
|
var list = _outStockRepositories.GetEntityList(dto.Ids).GetAwaiter().GetResult();
|
|
list = list.Where(w => w.Type == OutStockType.Sal).ToList();
|
|
var isSuccess = true;
|
|
if (isRepeatSync)
|
|
{
|
|
list = list.Where(w => w.SuccessSync == SyncStatus.Fail).ToList();
|
|
list.ForEach(f => f.RepeatSync());
|
|
isSuccess = _outStockRepositories.EditEntityList(list, true).GetAwaiter().GetResult();
|
|
}
|
|
Task.Run(async () =>
|
|
{
|
|
foreach (var entity in list)
|
|
{
|
|
var res = await SalOutStock(entity, loginInfo);
|
|
if (!res.IsSuccess)
|
|
_logger.LogError($"出库单同步失败:{res.Message}");
|
|
}
|
|
});
|
|
return Task.FromResult(Result.ReSuccess());
|
|
}
|
|
|
|
/// <summary>
|
|
/// 同步金蝶销售出库
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private async Task<Result> SalOutStock(OutStock entity, LoginInDto loginInfo)
|
|
{
|
|
var scope = _serviceScopeFactory.CreateScope();
|
|
var sc_outStockRepositories = scope.ServiceProvider.GetRequiredService<IOutStockRepositories>();
|
|
|
|
if (entity.Type != OutStockType.Sal) return Result.ReSuccess();
|
|
if (entity.SuccessSync == SyncStatus.Success) 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);
|
|
var erpDetails = entity.Details.SelectMany(s => s.ErpDetails).Where(w => w.SuccessSync == SyncStatus.SyncIng).ToList();
|
|
foreach (var s in erpDetails)
|
|
{
|
|
var erp_details = entity.Details
|
|
.SelectMany(s => s.ErpDetails)
|
|
.Where(w => w.SourceBillNo.Equals(s)).Select(s => s.Erp_DetailId).ToList();
|
|
var erpDto = new ErpPushDto()
|
|
{
|
|
RuleId = "DeliveryNotice-OutStock",//转换规则内码 发货通知单下推销售出库单
|
|
FormId = FormIdParam.SAL_DELIVERYNOTICE.ToString(),
|
|
//Numbers = new List<string>() { s },
|
|
TargetFormId = FormIdParam.SAL_OUTSTOCK.ToString(),
|
|
DetailsId = s.Erp_DetailId.ToString()
|
|
//DetailsId = string.Join(",", erp_details)
|
|
};
|
|
//下推金蝶
|
|
var res = await this.Push(erpDto, s, entity.BillNo, scope);
|
|
if (res.result.IsSuccess)
|
|
entity.SyncSuccess(s.Erp_DetailId, loginInfo?.UserInfo?.StaffId ?? 0, res.erpBillNo);
|
|
else
|
|
entity.SyncFail(res.result.Message, s.Erp_DetailId, loginInfo?.UserInfo?.StaffId ?? 0, res.syncStatus);
|
|
}
|
|
//entity.SuccessSync = entity.SuccessSyncFail.Count() > 0 ? false : true;
|
|
//最好一条一条执行,否则执行失败 但是金蝶那边又同步成功 就会造成数据比价乱
|
|
_logger.LogInformation($"同步金蝶后写入数据库--开始:{JsonConvert.SerializeObject(entity)}");
|
|
var isSuccess = await sc_outStockRepositories.Edit(entity, true);
|
|
_logger.LogInformation($"同步金蝶后写入数据库--结束:{JsonConvert.SerializeObject(entity)} 结果:{isSuccess}");
|
|
if (entity == null) return Result.ReFailure(ResultCodes.DateWriteError);
|
|
return Result.ReSuccess();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 下推
|
|
/// </summary>
|
|
/// <param name="dto"></param>
|
|
/// <returns></returns>
|
|
private async Task<(Result result, SyncStatus syncStatus, string erpBillNo)> Push(ErpPushDto dto, OutStockErpDetails erpDetail, string billNo, IServiceScope scope)
|
|
{
|
|
var sc_erpService = scope.ServiceProvider.GetRequiredService<IErpService>();
|
|
|
|
var res = await sc_erpService.Push(dto);
|
|
if (!res.IsSuccess)
|
|
{
|
|
_logger.LogInformation($"出库单->下推失败 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId} 错误:{res.Message}");
|
|
return (Result.ReFailure(res.Message, res.Status), SyncStatus.Fail, "");
|
|
}
|
|
string id = res.Data;
|
|
var resSalOutStock = await sc_erpService.BillQueryForSalOutStock(id);
|
|
var salOutStock = resSalOutStock.Data;
|
|
salOutStock.Details[0].Qty = erpDetail.Qty;
|
|
//{"Id":12709885,"Number":"XSCKD10629570","DIndex":0}
|
|
|
|
string formId = dto.TargetFormId.ToString();
|
|
_logger.LogInformation($"出库单->开始同步金蝶 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId} 数据: {JsonConvert.SerializeObject(dto)}");
|
|
var res_s = await sc_erpService.Save<ErpSalOutStockSaveDto>(salOutStock, formId);
|
|
if (!res_s.IsSuccess)
|
|
{
|
|
_logger.LogInformation($"出库单->修改数量失败 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId} 错误:{res_s.Message}");
|
|
return (Result.ReFailure(res_s.Message, res_s.Status), SyncStatus.SubmitFail, id);
|
|
}
|
|
//提交
|
|
_logger.LogInformation($"出库单->保存成功 开始提交 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId}");
|
|
ErpOperateDto o_dto = new ErpOperateDto(formId, res_s.Data);//res_s.Data
|
|
var resSubmit = await sc_erpService.Submit(o_dto, formId);
|
|
if (!resSubmit.IsSuccess)
|
|
{
|
|
_logger.LogInformation($"出库单->提交失败 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId} 错误:{resSubmit.Message}");
|
|
return (resSubmit, SyncStatus.SubmitFail, o_dto.Numbers.First());
|
|
}
|
|
//审核
|
|
_logger.LogInformation($"出库单->提交成功 开始审核 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId}");
|
|
resSubmit = await sc_erpService.Audit(o_dto, formId);
|
|
if (!resSubmit.IsSuccess)
|
|
{
|
|
_logger.LogInformation($"出库单->审核失败 单号:{billNo} erp明细Id:{erpDetail.Erp_DetailId} 错误:{resSubmit.Message}");
|
|
return (resSubmit, SyncStatus.CheckFail, o_dto.Numbers.First());
|
|
}
|
|
_logger.LogInformation($"同步金蝶成功");
|
|
return (Result.ReSuccess(), SyncStatus.Success, o_dto.Numbers.First());
|
|
}
|
|
/// <summary>
|
|
/// 修改出库箱信息
|
|
/// </summary>
|
|
/// <param name="dto"></param>
|
|
/// <param name="loginInfo"></param>
|
|
/// <returns></returns>
|
|
/// <exception cref="NotImplementedException"></exception>
|
|
public async Task<Result> EditBoxInfo(List<EditBoxInfoRequest> dto, LoginInDto loginInfo)
|
|
{
|
|
var ids = dto.Select(s => s.OutStockId).ToList();
|
|
var entityList = await _outStockRepositories.GetEntityList(ids);
|
|
foreach (var d in dto)
|
|
{
|
|
var entity = entityList.FirstOrDefault(f => f.Id == d.OutStockId);
|
|
if (entity == null) return Result.ReFailure(ResultCodes.NoDateError);
|
|
var res = entity.EditBoxInfo(d.OutStockBoxDetailsId, d.BoxLength, d.BoxWide, d.BoxHigh, d.BoxWeight, loginInfo.UserInfo.StaffId);
|
|
if (!res.IsSuccess) return res;
|
|
}
|
|
var isSuccess = await _outStockRepositories.EditEntityList(entityList);
|
|
if (!isSuccess) return Result.ReFailure(ResultCodes.DateWriteError);
|
|
return Result.ReSuccess();
|
|
}
|
|
}
|
|
}
|