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.Security.Cryptography; using System.Text; using System.Threading.Tasks; using WMS.Web.Core.Dto; using WMS.Web.Core.Dto.Erp; using WMS.Web.Core.Dto.Erp.TakeStock; using WMS.Web.Core.Dto.Inventory; using WMS.Web.Core.Dto.Login; using WMS.Web.Core.Dto.TakeStock; 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 TakeStockService : ITakeStockService { private readonly IMapper _mapper; private readonly ILoginService _loginService; public readonly IBasicsRepositories _transactionRepositories; private readonly ITakeStockRepositories _takeStockRepositories; private readonly ILoginRepositories _loginRepositories; private readonly ISingleDataService _singleDataService; private readonly IErpService _erpService; private readonly ILogger _logger; private readonly IBoxInventoryService _boxInventoryService; private readonly IBoxInventoryRepositories _boxInventoryRepositories; private readonly IErpBasicDataExtendService _erpBasicDataExtendService; private readonly ISerialNumberService _serialNumberService; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly ISerialNumbersRepositories _serialNumberRepositories; private readonly IBoxRepositories _boxRepositories; public TakeStockService(IMapper mapper, ILoginService loginService, IBasicsRepositories transactionRepositories, ITakeStockRepositories takeStockRepositories, ILoginRepositories loginRepositories, ISingleDataService singleDataService, IErpService erpService, ILogger logger, IBoxInventoryService boxInventoryService, IErpBasicDataExtendService erpBasicDataExtendService, ISerialNumberService serialNumberService, IServiceScopeFactory serviceScopeFactory, ISerialNumbersRepositories serialNumberRepositories, IBoxRepositories boxRepositories, IBoxInventoryRepositories boxInventoryRepositories) { _mapper = mapper; _loginService = loginService; _transactionRepositories = transactionRepositories; _takeStockRepositories = takeStockRepositories; _loginRepositories = loginRepositories; _singleDataService = singleDataService; _erpService = erpService; _logger = logger; _boxInventoryService = boxInventoryService; _erpBasicDataExtendService = erpBasicDataExtendService; _serialNumberService = serialNumberService; _serviceScopeFactory = serviceScopeFactory; _serialNumberRepositories = serialNumberRepositories; _boxRepositories = boxRepositories; _boxInventoryRepositories = boxInventoryRepositories; } /// /// 保存 /// /// /// /// public async Task Save(List dto, LoginInDto loginInfo) { _logger.LogInformation($"盘点保存:{JsonConvert.SerializeObject(dto)} 盘点人:{loginInfo.UserInfo.StaffId}"); //dto = dto.Where(w => w.AfterQty != w.BeforeQty).ToList(); if (dto.Count() == 0) return Result.ReSuccess(); if (dto.GroupBy(g => g.BoxId).Count() > 1) return Result.ReFailure(ResultCodes.TakeStockBoxError); //v1.0.6 如果箱子不存在则添加箱信息 和 可以更换仓位 var dto_f = dto.First(); bool isUpdateSubStock = false; BoxInventory boxInventory = null; if (isUpdateSubStock) { boxInventory = await _boxInventoryRepositories.Get(dto_f.BoxId); if (boxInventory == null) return Result.ReFailure(ResultCodes.BoxInventoryNoDataError); } if (dto_f.BoxId == 0) { var dBox = await _boxRepositories.GetByNo(dto_f.BoxBillNo); if (dBox != null) return Result.ReFailure(ResultCodes.TakeStockBoxIdError); dBox = new Box() { BoxBillNo = dto_f.BoxBillNo, CompleteCartonTime = DateTime.Now, CreateUser = loginInfo.UserInfo.Nickname, CompleteCartonUser = loginInfo.UserInfo.Nickname, CreateTime = DateTime.Now }; dBox = await _boxRepositories.Add(dBox); if (dBox == null) return Result.ReFailure(ResultCodes.DateWriteError); dto.ForEach(f => f.BoxId = dBox.Id); } var serialNumbersBoxInventoryList = await GetSerialNumbersBoxInventory(dto); List list = new List(); var profitList = dto.Where(w => w.AfterQty >= w.BeforeQty).ToList();//盘盈 var lossList = dto.Where(w => w.AfterQty < w.BeforeQty).ToList();//盘亏 if (profitList.Count() > 0) { TakeStock takeStock = new TakeStock(); takeStock.Create(loginInfo.UserInfo.StaffId, TakeStockType.Profit); takeStock.Details = _mapper.Map>(profitList); var subIds = takeStock.Details.Select(s => s.SubStockCode).ToList(); var subStocks = await _transactionRepositories.GetSubUcStockAsync(subIds, loginInfo.UserInfo.CompanyId); foreach (var d in takeStock.Details) { var subStock = subStocks.FirstOrDefault(f => f.Code == d.SubStockCode); d.OrgCode = subStock?.ErpOrgCode; //d.StockCode = subStock?.StockCode; //d.SubStockCode = subStock?.Code; if ((d.StockCode.Equals("HD") || d.StockCode.Equals("GD")) && string.IsNullOrEmpty(d.Erp_SubStockCode)) return Result.ReFailure(ResultCodes.TakeStockErpSubStockError); } if (takeStock.Details.GroupBy(g => g.StockCode).Count() > 1) return Result.ReFailure(ResultCodes.TakeStockStockError); list.Add(takeStock); } if (lossList.Count() > 0) { TakeStock takeStock = new TakeStock(); takeStock.Create(loginInfo.UserInfo.StaffId, TakeStockType.Loss); takeStock.Details = _mapper.Map>(lossList); var subIds = takeStock.Details.Select(s => s.SubStockCode).ToList(); var subStocks = await _transactionRepositories.GetSubUcStockAsync(subIds, loginInfo.UserInfo.CompanyId); foreach (var d in takeStock.Details) { var subStock = subStocks.FirstOrDefault(f => f.Code == d.SubStockCode); d.OrgCode = subStock?.ErpOrgCode; //d.StockCode = subStock?.StockCode; //d.SubStockCode = subStock?.Code; if ((d.StockCode.Equals("HD") || d.StockCode.Equals("GD")) && string.IsNullOrEmpty(d.Erp_SubStockCode)) return Result.ReFailure(ResultCodes.TakeStockErpSubStockError); } if (takeStock.Details.GroupBy(g => g.StockCode).Count() > 1) return Result.ReFailure(ResultCodes.TakeStockStockError); list.Add(takeStock); } IDbContextTransaction _transaction = _transactionRepositories.GetTransaction(); Result res_Rollback = Result.ReSuccess(); bool isSuccess = true; if (res_Rollback.IsSuccess) { isSuccess = await _takeStockRepositories.AddRange(list, false); if (!isSuccess) res_Rollback = Result.ReFailure(ResultCodes.DateWriteError); } if (res_Rollback.IsSuccess) { var res_change = await _serialNumberService.TakeStock(list, loginInfo, false); if (!res_change.IsSuccess) res_Rollback = res_change; } if (res_Rollback.IsSuccess) { List takes = list; if (isUpdateSubStock) { takes = new List(); takes = GetUpdateSubStockTakeStock(dto, loginInfo, boxInventory); } var res_Inventory = await _boxInventoryService.GenerateTakeBox(list, serialNumbersBoxInventoryList, isUpdateSubStock, 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); ////同步金蝶 //OperateRequest oRequest = new OperateRequest(); //oRequest.Ids = list.Select(s => s.Id).ToList(); //await Sync(oRequest, false); return Result.ReSuccess(); } /// /// 同步金蝶 /// /// /// public Task Sync(OperateRequest dto, bool isRepeatSync = true) { return Task.FromResult(Result.ReSuccess()); //var list = _takeStockRepositories.GetEntityList(dto.Ids).GetAwaiter().GetResult(); //var isSuccess = true; //if (isRepeatSync) //{ // list = list.Where(w => w.SuccessSync == SyncStatus.Fail).ToList(); // list.ForEach(f => f.RepeatSync()); // isSuccess = _takeStockRepositories.EditEntityList(list, true).GetAwaiter().GetResult(); //} //Task.Run(async () => // { // foreach (var entity in list) // { // var res = await Loss_Profit(entity); // if (!res.IsSuccess) // _logger.LogError($"盘点同步失败:{res.Message}"); // } // }); //if (isSuccess) // return Task.FromResult(Result.ReSuccess()); //else // return Task.FromResult(Result.ReFailure(ResultCodes.DateWriteError)); } /// /// 盘盈盘亏同步金蝶 /// /// /// public async Task Loss_Profit(TakeStock entity) { _logger.LogInformation($"盘点同步金蝶: {JsonConvert.SerializeObject(entity)}"); try { var scope = _serviceScopeFactory.CreateScope(); var sc_erpService = scope.ServiceProvider.GetRequiredService(); var sc_erpBasicDataExtendService = scope.ServiceProvider.GetRequiredService(); var sc_takeStockRepositories = scope.ServiceProvider.GetRequiredService(); var details = entity.Details.Where(w => w.FinalQty > 0).ToList(); if (details.Count() == 0) { entity.Sync(true, "", SyncStatus.Success, ""); await sc_takeStockRepositories.Edit(entity, true); return Result.ReSuccess(); } var materials_result = await sc_erpService.BillQueryForMaterial(); if (!materials_result.IsSuccess) return Result.ReFailure(ResultCodes.ErpMaterialError); var materials = materials_result.Data.ToList(); var subs = await _erpService.BillQueryForSubStock(); List<(string materialNumber, string orgCode, string stockCode, int subStockId)> requestInventory = new List<(string materialNumber, string orgCode, string stockCode, int subStockId)>(); foreach (var d in details) { int subStockId = subs.Data.FirstOrDefault(w => w.OrgCode.Equals(d.OrgCode) && w.StockCode.Equals(d.Erp_SubStockCode))?.Id ?? 0; requestInventory.Add((d.MaterialNumber, d.OrgCode, d.StockCode, subStockId)); }; //var res_s = await sc_erpService.BillQueryForInventory(requestInventory); //if (!res_s.IsSuccess) //{ // entity.Sync(false, res_s.Message, SyncStatus.Fail, ""); // await sc_takeStockRepositories.Edit(entity, true); // _logger.LogInformation($"及时库存获取异常->错误:{res_s.Message} 数据: {JsonConvert.SerializeObject(entity)}"); // return Result.ReFailure(res_s.Message, res_s.Status); //} //组装dto #region 组装dto //子仓库 ORICO_JD:1000008 GD:1000005 HD:1000007 AD:1000004 List detils = new List(); foreach (var d in details) { int subStockId = subs.Data.FirstOrDefault(w => w.OrgCode.Equals(d.OrgCode) && w.StockCode.Equals(d.Erp_SubStockCode))?.Id ?? 0; var number = d.MaterialNumber; //var erpInventory = res_s.Data.FirstOrDefault(f => f.MaterialNumber == number && f.StockCode == d.StockCode //&& f.OrgCode == d.OrgCode && f.Erp_SubStockId == subStockId); //decimal qty = erpInventory?.Qty ?? 0; decimal qty = 0; var unitNumber = _erpBasicDataExtendService.GetMaterialUnitNumber(materials, d.MaterialNumber); detils.Add(new ErpTakeStockDetailsSaveDto() { FOwnerid = new ErpNumberDto(d.OrgCode), FKeeperId = new ErpNumberDto(d.OrgCode), MaterialId = new ErpNumberDto(d.MaterialNumber), UnitId = new ErpNumberDto(unitNumber), //物料带出来 StockId = new ErpNumberDto(d.StockCode), SubStockId = new ErpSubStockDto(d.StockCode, d.Erp_SubStockCode), BeforeQty = qty, AfterQty = entity.ResultType == TakeStockType.Profit ? (qty + d.FinalQty) : ((qty - d.FinalQty) > 0 ? (qty - d.FinalQty) : 0), FinalQty = entity.ResultType == TakeStockType.Profit ? d.FinalQty : 0, LossQty = entity.ResultType == TakeStockType.Loss ? d.FinalQty : 0, Fnote = d.Remark }); } ErpTakeStockSaveDto dto = new ErpTakeStockSaveDto() { BillNo = entity.BillNo, StockOrgId = new ErpNumberDto(entity.Details[0].OrgCode), Type = new ErpNumberDto(entity.ResultType == TakeStockType.Loss ? "PK01_SYS" : "PY01_SYS"), Date = entity.Date, Details = detils }; #endregion //判断盘盈盘亏 FormIdParam type = entity.ResultType == TakeStockType.Loss ? FormIdParam.STK_StockCountLoss : FormIdParam.STK_StockCountGain; //操作金蝶 var resSync = await ErpOperate(dto, type, scope); entity.Sync(resSync.result.IsSuccess, resSync.result.Message, resSync.syncStatus, resSync.erpBillNo); await sc_takeStockRepositories.Edit(entity, true); return Result.ReSuccess(); } catch (Exception ex) { _logger.LogError($"盘点同步金蝶: {ex.ToString()}"); return Result.ReFailure(ResultCodes.NoDateError); } } /// /// 同步金蝶操作 /// /// /// /// private async Task<(Result result, SyncStatus syncStatus, string erpBillNo)> ErpOperate(ErpTakeStockSaveDto dto, FormIdParam type, IServiceScope scope) { var sc_erpService = scope.ServiceProvider.GetRequiredService(); string formId = type.ToString(); _logger.LogInformation($"盘点单->开始同步金蝶 单号:{dto.BillNo} 数据: {JsonConvert.SerializeObject(dto)}"); var res_s = await sc_erpService.Save(dto, formId); if (!res_s.IsSuccess) { _logger.LogInformation($"盘点单->保存失败 单号:{dto.BillNo} 错误:{res_s.Message}"); return (Result.ReFailure(res_s.Message, res_s.Status), SyncStatus.Fail, ""); } //提交 _logger.LogInformation($"盘点单->保存成功 开始提交 单号:{dto.BillNo}"); ErpOperateDto o_dto = new ErpOperateDto(formId, res_s.Data);//res_s.Data var res = await sc_erpService.Submit(o_dto, formId); if (!res.IsSuccess) { //如果提交失败 //1.则调删单接口 //var del_res = await _erpService.Delete(o_dto, formId); //if (!del_res.IsSuccess) // _logger.LogError($"盘盈盘亏同步金蝶 单号:{dto.BillNo} 提交失败原因: {res.Message} 删单失败原因:{del_res.Message}"); _logger.LogInformation($"盘点单->提交失败 单号:{dto.BillNo} 错误:{res.Message}"); return (res, SyncStatus.SubmitFail, o_dto.Numbers.First()); } //审核 _logger.LogInformation($"盘点单->提交成功 开始审核 单号:{dto.BillNo}"); res = await sc_erpService.Audit(o_dto, formId); if (!res.IsSuccess) { //如果审核失败 //1.调反审核接口 //2.调删除接口 //var noAudit_res = await _erpService.NoAudit(o_dto, formId); //if (!noAudit_res.IsSuccess) //{ // _logger.LogError($"盘盈盘亏同步金蝶 单号:{dto.BillNo} 审核失败原因: {res.Message} 反审核失败原因:{noAudit_res.Message}"); // return res; //} //var del_res = await _erpService.Delete(o_dto, formId); //if (!del_res.IsSuccess) // _logger.LogError($"盘盈盘亏同步金蝶 单号:{dto.BillNo} 审核失败原因: {res.Message} 删单失败原因:{del_res.Message}"); _logger.LogInformation($"盘点单->审核失败 单号:{dto.BillNo} 错误:{res.Message}"); return (res, SyncStatus.CheckFail, o_dto.Numbers.First()); } _logger.LogInformation($"同步金蝶成功"); return (Result.ReSuccess(), SyncStatus.Success, o_dto.Numbers.First()); } /// /// 获取改变了箱的序列号 /// /// /// private async Task> GetSerialNumbersBoxInventory(List dto) { List list = new List(); var sNs = dto.SelectMany(s => s.SerialNumbers).ToList(); //var TwosNs = dto.SelectMany(s => s.TwoSerialNumbers).ToList(); //sNs.AddRange(TwosNs); var entityList = await _serialNumberRepositories.GetEntityList(sNs); foreach (var s in sNs) { var entity = entityList.FirstOrDefault(f => f.SerialNumber.Equals(s)); if (entity == null) continue; var d = dto.FirstOrDefault(f => f.SerialNumbers.Contains(s)); if (d.BoxId != entity.BoxId) { list.Add(new SerialNumbersBoxInventoryDto() { BoxId = entity.BoxId, MaterialNumber = d.MaterialNumber, SerialNumber = s }); } } var TwosNs = dto.SelectMany(s => s.TwoSerialNumbers).ToList(); var TwoentityList = await _serialNumberRepositories.GetEntityList(TwosNs); foreach (var s in TwosNs) { var entity = entityList.FirstOrDefault(f => f.TwoSerialNumber.Contains(s)); if (entity == null) continue; var d = dto.FirstOrDefault(f => f.TwoSerialNumbers.Contains(s)); if (d.BoxId != entity.BoxId) { list.Add(new SerialNumbersBoxInventoryDto() { BoxId = entity.BoxId, MaterialNumber = d.MaterialNumber, SerialNumber = s }); } } return list; } /// /// 盘点更换仓位组装盘点单结构,用来调用即时库存 /// /// /// private List GetUpdateSubStockTakeStock(List dto, LoginInDto loginInfo, BoxInventory boxInventory) { List list = new List(); var fist = dto.First(); //先用箱库存 组装盘亏单 把数据都盘出去 TakeStock loss = new TakeStock(); loss.Create(loginInfo.UserInfo.StaffId, TakeStockType.Loss); foreach (var d in boxInventory.Details) { loss.Details.Add(new TakeStockDetails() { BoxId = fist.BoxId, MaterialNumber = d.MaterialNumber, OrgCode = boxInventory.OrgCode, StockCode = boxInventory.StockCode, SubStockCode = boxInventory.SubStockCode, Old_SubStockCode = boxInventory.SubStockCode, AfterQty = 0, BeforeQty = d.Qty, SerialNumbers = d.SerialNumbers, FinalQty = d.Qty }); } list.Add(loss); //组装盘盈单 TakeStock profit = new TakeStock(); profit.Create(loginInfo.UserInfo.StaffId, TakeStockType.Profit); profit.Details = _mapper.Map>(dto.Where(w => w.AfterQty > 0)); foreach (var d in profit.Details) { d.OrgCode = boxInventory.OrgCode; d.BeforeQty = 0; d.FinalQty = d.AfterQty; } list.Add(profit); return list; } } }