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 { /// /// 出库服务 /// 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 _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 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; } /// /// 出库单 /// /// /// /// public async Task 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) { 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(b); dtod.BoxId = box.BoxId; dtod.SubStockCode = box.SubStockCode; 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(tDetail); //循环添加erp同步明细 foreach (var c in res.Data) { var tErpDetail = tDetail.ErpDetails.FirstOrDefault(f => f.Erp_DetailId == c.erpDetailId); var oErpDetail = _mapper.Map(tErpDetail); oErpDetail.Qty = c.qty; outd.ErpDetails.Add(oErpDetail); } var boxs = dto.Details.Where(w => w.MaterialNumber == mid).ToList(); outd.BoxsDetails = _mapper.Map>(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; } //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(); } /// /// 同步金蝶 /// /// /// public Task 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()); } /// /// 同步金蝶销售出库 /// /// private async Task SalOutStock(OutStock entity, LoginInDto loginInfo) { var scope = _serviceScopeFactory.CreateScope(); var sc_outStockRepositories = scope.ServiceProvider.GetRequiredService(); if (entity.Type != OutStockType.Sal) return Result.ReSuccess(); if (entity.SuccessSync == SyncStatus.Success) return Result.ReSuccess(); //List 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() { 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(); } /// /// 下推 /// /// /// private async Task<(Result result, SyncStatus syncStatus, string erpBillNo)> Push(ErpPushDto dto, OutStockErpDetails erpDetail, string billNo, IServiceScope scope) { var sc_erpService = scope.ServiceProvider.GetRequiredService(); 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(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()); } /// /// 修改出库箱信息 /// /// /// /// /// public async Task EditBoxInfo(List 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(); } } }