using System.Text; using DS.Module.Core; using DS.Module.Core.Enums; using DS.Module.Core.Extensions; using DS.WMS.Core.Application.Dtos; using DS.WMS.Core.Application.Entity; using DS.WMS.Core.Code.Entity; using DS.WMS.Core.Fee.Entity; using DS.WMS.Core.Invoice.Dtos; using DS.WMS.Core.Op.Entity; using DS.WMS.Core.Settlement.Dtos; using DS.WMS.Core.Settlement.Entity; using DS.WMS.Core.Settlement.Interface; using DS.WMS.Core.Sys.Entity; using SqlSugar; namespace DS.WMS.Core.Settlement.Method { /// /// 收/付费申请结算服务 /// public class ApplicationSettlementService : SettlementService, IApplicationSettlementService { /// /// 初始化 /// /// public ApplicationSettlementService(IServiceProvider provider) : base(provider) { } /// /// 获取结算单分页列表 /// /// /// public async Task>> GetListAsync(PageRequest request) { var query = TenantDb.Queryable().Select(x => new ApplicationSettlementDto { SettlementTypeName = x.SettlementType.StlName, //结算方式 RMBAmount = SqlFunc.Subqueryable().Where(y => y.ApplicationId == x.Id && y.Currency == FeeCurrency.RMB_CODE) .Select(y => SqlFunc.AggregateSum(y.ApplyAmount)), USDAmount = SqlFunc.Subqueryable().Where(y => y.ApplicationId == x.Id && y.Currency == FeeCurrency.USD_CODE) .Select(y => SqlFunc.AggregateSum(y.ApplyAmount)), OtherAmount = SqlFunc.Subqueryable().Where(y => y.ApplicationId == x.Id && y.Currency != FeeCurrency.RMB_CODE && y.Currency != FeeCurrency.USD_CODE) .Select(y => SqlFunc.AggregateSum(y.ApplyAmount)), CustomerAccount = x.CustomerBank.AccountName + "/" + x.CustomerBank.Currency, CustomerBankName = x.CustomerBank.BankName, //未开票 UnInvoiceList = SqlFunc.Subqueryable().InnerJoin((d, f) => d.ApplicationId == x.Id && d.RecordId == f.Id) .GroupBy((d, f) => f.Currency).ToList((d, f) => new CurrencyAmount { Currency = f.Currency, Amount = f.Amount - f.InvoiceAmount - f.OrderInvoiceAmount + f.OrderInvSettlementAmount }), }, true); var whereList = request.GetConditionalModels(Db); var result = await query.Where(whereList).ToQueryPageAsync(request.PageCondition); if (result.Data.Count > 0) { //关联用户名称 var userIds = result.Data.Select(x => x.CreateBy) .Union(result.Data.Where(x => x.LockUserId.HasValue).Select(x => x.LockUserId.Value)) .Union(result.Data.Where(x => x.UnlockUserId.HasValue).Select(x => x.UnlockUserId.Value)) .Distinct(); var users = await Db.Queryable().Where(x => userIds.Contains(x.Id)).Select(x => new { x.Id, x.UserName }).ToListAsync(); var orgIds = result.Data.Select(x => x.SaleDeptId).Distinct().ToList(); var orgs = await Db.Queryable().Where(x => orgIds.Contains(x.Id)).Select(x => new { x.Id, x.OrgName }).ToListAsync(); foreach (var item in result.Data) { item.CreateByName = users.Find(x => x.Id == item.CreateBy)?.UserName; item.LockUser = users.Find(x => x.Id == item.LockUserId)?.UserName; item.UnlockUser = users.Find(x => x.Id == item.UnlockUserId)?.UserName; item.SaleDeptName = orgs.Find(x => x.Id == item.SaleDeptId)?.OrgName; } } return result; } /// /// 获取结算单详情 /// /// 结算单ID /// public async Task> GetAsync(long id) { var model = await TenantDb.Queryable().Select(x => new ApplicationSettlementDto { SettlementTypeName = x.SettlementType.StlName, //结算方式 CustomerBankName = x.CustomerBank.BankName, CustomerAccount = x.CustomerBank.Account }, true).FirstAsync(x => x.Id == id); if (model != null) { if (model.SaleDeptId.HasValue) model.SaleDeptName = await Db.Queryable().Where(x => x.Id == model.SaleDeptId.Value) .Select(x => x.OrgName).FirstAsync(); if (model.OrgBankId.HasValue) model.OrgBankName = await Db.Queryable().Where(x => x.Id == model.OrgBankId.Value) .Select(x => x.BankName + " " + x.BankAccountNo).FirstAsync(); model.SettlementDetails = await GetSettlementDetails(id); if (model.SettlementDetails.Count > 0) { //关联用户名称 var userIds = model.SettlementDetails.Select(x => x.CreateBy).Distinct(); var users = await Db.Queryable().Where(x => userIds.Contains(x.Id)).Select(x => new { x.Id, x.UserName }).ToListAsync(); foreach (var item in model.SettlementDetails) { item.CreateByName = users.Find(x => x.Id == item.CreateBy)?.UserName; } } } return DataResult.Success(model); } /// /// 获取费用申请结算明细 /// /// /// protected override async Task> GetSettlementDetails(long id) { var list = await TenantDb.Queryable() .InnerJoin((i, d1) => i.Id == d1.ApplicationId) .InnerJoin((i, d1, d2) => d1.ApplicationId == d2.RefId) .Where((i, d1, d2) => d2.ApplicationId == id && d2.Category == DetailCategory.PaidApplication && d1.Category == DetailCategory.ChargeApplication) .GroupBy((i, d1, d2) => i.Id) .Select((i, d1, d2) => new SettlementDetailGroup { SettlementAmount = SqlFunc.Subqueryable().Where(d3 => d3.ApplicationId == i.Id).Sum(d3 => d3.ApplyAmount), ApplicationNOList = SqlFunc.Subqueryable().Where(a => a.Id == d1.RefId).ToList(a => a.ApplicationNO) }, true).ToListAsync(); return list; //var list = await TenantDb.Queryable() // .InnerJoin((d, pd) => d.DetailId == pd.Id) // .InnerJoin((d, pd, pa) => pd.ApplicationId == pa.Id) // .Where(d => d.ApplicationId == id) // .Select((d, pd, pa) => new // { // d.Id, // d.ApplicationId, // d.ApplyAmount, // d.Currency, // d.OriginalCurrency, // d.OriginalAmount, // pa.ApplicationNO, // pa.Status, // pa.CreateTime, // pa.CreateBy, // pa.PaymentDate, // pa.Note // }).ToListAsync(); //var details = new List(); //if (list.Count == 0) // return details; //var gp = list.GroupBy(d => d.ApplicationId); //var uids = list.Select(x => x.CreateBy).Distinct(); //var users = await Db.Queryable().Where(x => uids.Contains(x.Id)).Select(x => new //{ // x.Id, // x.UserName //}).ToListAsync(); //foreach (var g in gp) //{ // var firstItem = g.FirstOrDefault(); // var dto = new SettlementDetailDto // { // ApplicationId = g.Key, // //Ids = g.Select(x => x.Id), // RMBApplyAmount = g.Where(x => x.OriginalCurrency == FeeCurrency.RMB_CODE).Sum(x => x.ApplyAmount), // USDApplyAmount = g.Where(x => x.OriginalCurrency == FeeCurrency.USD_CODE).Sum(x => x.ApplyAmount), // ApplicationNO = firstItem.ApplicationNO, // Status = (int)firstItem.Status, // StatusText = firstItem.Status.GetDescription(), // CreateTime = firstItem?.CreateTime, // CreateBy = firstItem?.CreateBy, // CreateByName = users.Find(x => x.Id == firstItem?.CreateBy)?.UserName, // PaymentDate = firstItem?.PaymentDate, // Note = firstItem?.Note // }; // //包含多个币别 // if (g.GroupBy(x => x.OriginalCurrency).Select(x => x.Key).Count() > 1) // { // //原始币别=不等于结算单币别的首个币别(参考DS7) // dto.OriginalCurrency = g.Select(x => x.OriginalCurrency).FirstOrDefault(); // //原始金额=当前结算单的币别合计 // dto.OriginalAmount = g.Sum(x => x.ApplyAmount); // } // else // { // dto.OriginalCurrency = firstItem?.OriginalCurrency; // dto.OriginalAmount = g.Where(x => x.OriginalCurrency == dto.OriginalCurrency).Sum(x => x.ApplyAmount); // } // //结算金额=原申请的原始币别合计 // dto.SettlementAmount = g.Where(x => x.OriginalCurrency == dto.OriginalCurrency).Sum(x => x.ApplyAmount); // details.Add(dto); //} //return details; } /// /// 获取结算单明细 /// /// /// public async Task>> GetDetailsAsync(PageRequest request) { var details = await GetSettlementDetails(request.OtherQueryCondition); return DataResult>.Success(details); } /// /// 获取结算单费用明细 /// /// 申请单明细ID /// public async Task>> GetSettlementDetailsAsync(long[] ids) { var details = await TenantDb.Queryable().Where(d => ids.Contains(d.Id) && d.Category == DetailCategory.PaidApplicationSettlement) .LeftJoin((d, f) => d.RecordId == f.Id) .LeftJoin((d, f, b) => f.BusinessId == b.BusinessId && f.BusinessType == b.BusinessType) .LeftJoin((d, f, b, p) => d.ApplicationId == p.Id) .Select((d, f, b, p) => new PaymentApplicationDetailDto { Id = d.Id, ApplicationId = d.ApplicationId, BusinessId = f.BusinessId, BusinessType = f.BusinessType, RecordId = d.RecordId, FeeName = d.FeeName, FeeType = d.FeeType, //收付 ApplyAmount = d.ApplyAmount, //结算金额 CustomerId = f.CustomerId, CustomerName = d.CustomerName, OriginalCurrency = d.OriginalCurrency, //原始币别 OriginalRate = f.ExchangeRate, //原始汇率 ExchangeRate = d.ExchangeRate, //折算汇率 OriginalAmount = d.OriginalAmount, //原始金额 InvoiceNO = p.InvoiceNO //发票号 }).ToListAsync(); await FulfillDetailsAsync(details); return DataResult>.Success(details); } /// /// 获取待结算的申请分页列表 /// /// /// public async Task>> GetApplicationListAsync(PageRequest request) { var query = TenantDb.Queryable() .InnerJoin((a, d) => a.Id == d.ApplicationId && d.ApplyAmount - d.ProcessedAmount != 0) .InnerJoin((a, d, f) => d.RecordId == f.Id) .InnerJoin((a, d, f, s) => f.BusinessId == s.Id && f.BusinessType == BusinessType.OceanShippingExport) .GroupBy((a, d) => a.Id); if (request.OtherQueryCondition != null) { if (request.OtherQueryCondition.PortId.HasValue) query = query.Where((a, d, f, s) => s.LoadPortId == request.OtherQueryCondition.PortId || s.DischargePortId == request.OtherQueryCondition.PortId); if (request.OtherQueryCondition.UnsettledOnly) query = query.Where(a => a.Status == PaymentApplicationStatus.AuditPassed || a.Status == PaymentApplicationStatus.PartialSettlement); } else { query = query.Where(a => a.Status == PaymentApplicationStatus.AuditPassed || a.Status == PaymentApplicationStatus.PartialSettlement || a.Status == PaymentApplicationStatus.SettlementCompleted); } var whereList = request.GetConditionalModels(Db); var result = await query.Select(a => new PaymentApplicationDtoV2 { AmountRMB = a.AmountRMB == null ? 0 : a.AmountRMB.Value, //RMB申请金额 AmountUSD = a.AmountUSD == null ? 0 : a.AmountUSD.Value, //USD申请金额 AmountOther = a.AmountOther == null ? 0 : a.AmountOther.Value, //RMB未结金额 UnSettledRMB = SqlFunc.Subqueryable().Where(d => d.ApplicationId == a.Id && d.Currency == FeeCurrency.RMB_CODE) .Select(d => SqlFunc.AggregateSum(d.ApplyAmount - d.ProcessedAmount)), //USD未结金额 UnSettledUSD = SqlFunc.Subqueryable().Where(d => d.ApplicationId == a.Id && d.Currency == FeeCurrency.USD_CODE) .Select(d => SqlFunc.AggregateSum(d.ApplyAmount - d.ProcessedAmount)), //USD未结其他 UnSettledOther = SqlFunc.Subqueryable().Where(d => d.ApplicationId == a.Id && d.Currency != FeeCurrency.RMB_CODE && d.Currency != FeeCurrency.USD_CODE) .Select(d => SqlFunc.AggregateSum(d.ApplyAmount - d.ProcessedAmount)), SettlementTypeName = a.SettlementType.StlName, //结算方式 }, true).MergeTable().Where(whereList).ToQueryPageAsync(request.PageCondition); if (result.Data?.Count > 0) { var userIds = result.Data.Select(x => x.CreateBy).Distinct(); var users = await Db.Queryable().Where(x => userIds.Contains(x.Id)).Select(x => new { x.Id, x.UserName }).ToListAsync(); var orgIds = result.Data.Select(x => x.SaleDeptId).Where(x => x.HasValue).Distinct().ToList(); var orgs = await Db.Queryable().Where(x => orgIds.Contains(x.Id)).Select(x => new { x.Id, x.OrgName }).ToListAsync(); foreach (var item in result.Data) { item.CreateByName = users.Find(x => x.Id == item.CreateBy)?.UserName; item.SaleDeptName = orgs.Find(x => x.Id == item.SaleDeptId)?.OrgName; item.SettlementRMB = item.UnSettledRMB; item.SettlementUSD = item.UnSettledUSD; item.SettlementOther = item.UnSettledOther; } } return result; } /// /// 获取申请单费用明细 /// /// 申请单ID /// public async Task>> GetApplicationDetailsAsync(long id) { var details = await TenantDb.Queryable().Where(d => d.ApplicationId == id && d.Category == DetailCategory.PaidApplication && (d.ApplyAmount - d.ProcessedAmount) != 0) .LeftJoin((d, f) => d.RecordId == f.Id) .Select((d, f) => new PaymentApplicationDetailDto { Id = d.Id, ApplicationId = d.ApplicationId, BusinessId = f.BusinessId, BusinessType = f.BusinessType, RecordId = d.RecordId, FeeName = d.FeeName, FeeType = d.FeeType, //收付 Amount = d.ApplyAmount, //申请金额 ApplyAmount = f.SettlementAmount, //已结算金额 RestAmount = d.ApplyAmount - d.ProcessedAmount, //剩余结算金额 CustomerId = f.CustomerId, CustomerName = d.CustomerName, OriginalCurrency = d.OriginalCurrency, //原始币别 OriginalRate = f.ExchangeRate, //原始汇率 ExchangeRate = d.ExchangeRate, //折算汇率 OriginalAmount = d.OriginalAmount //原始金额 }).ToListAsync(); await FulfillDetailsAsync(details); return DataResult>.Success(details); } /// /// 获取发票费用明细的原始币别 /// /// /// public async Task>> GetExchangesAsync(List documents) { var ids = documents.Select(x => x.Id); var list = await TenantDb.Queryable() .Where(d => ids.Contains(d.ApplicationId)) .Select(d => new { d.ApplicationId, d.OriginalCurrency }).ToListAsync(); foreach (var document in documents) { document.ExchangeRates ??= []; //获取该发票费用明细的全部币别 var items = list.FindAll(x => x.ApplicationId == document.Id); foreach (var item in items) { if (!document.ExchangeRates.Exists(x => x.Currency == item.OriginalCurrency)) document.ExchangeRates.Add(new CurrencyExchangeRate { Currency = item.OriginalCurrency }); } } return DataResult>.Success(documents); } internal async Task FulfillDetailsAsync(List details) { if (details.Count == 0) return; var gList = details.GroupBy(x => x.BusinessType); foreach (var g in gList) { var ids = g.Select(x => x.BusinessId); switch (g.Key) { case BusinessType.OceanShippingExport: var list1 = await TenantDb.Queryable().Where(s => ids.Contains(s.Id)) .LeftJoin((s, cs) => s.SourceId == cs.Id) .Select((s, cs) => new { s.Id, s.AccountDate,//会计期间 s.Vessel, //船名 s.Voyno, //航次 s.CustomerName, s.MBLNO, //主提单号 s.CustomerNo, //委托编号 s.ETD, //开船日期 s.Sale, //揽货人 cs.SourceName, //业务来源 s.Note }).ToListAsync(); foreach (var item in g) { var biz = list1.Find(x => x.Id == item.BusinessId); if (biz != null) { item.MBLNO = biz.MBLNO; item.CustomerNo = biz.CustomerNo; item.ClientName = biz.CustomerName; item.ETD = biz.ETD; item.SourceName = biz.SourceName; item.SaleName = biz.Sale; item.AccountDate = biz.AccountDate; item.Vessel = biz.Vessel; item.Voyage = biz.Voyno; item.Note = biz.Note; } } break; case BusinessType.OceanShippingImport: break; } } } protected override async Task PreSaveAsync(ApplicationSettlement settlement) { var appIds = settlement.Details.Select(x => x.RefId).Distinct(); var appList = await TenantDb.Queryable().Where(x => appIds.Contains(x.Id)) .Select(x => new { x.Id, x.Status, x.CustomerId, x.CustomerName, }).ToListAsync(); if (appList.Count == 0) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData)); if (appList.Exists(x => x.Status != PaymentApplicationStatus.AuditPassed && x.Status != PaymentApplicationStatus.PartialSettlement)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ApplicationSelectStatusError)); if (appList.GroupBy(x => x.CustomerId).Select(x => x.Key).Count() > 1) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.DetailCustomerOnlyOne)); //获取剩余待结算金额 var ids2 = settlement.Details.Select(x => x.DetailId); var appDetails = await TenantDb.Queryable().Where(x => ids2.Contains(x.Id) && x.Category == DetailCategory.PaidApplication) .Select(x => new { x.Id, //RestAmount = x.ApplyAmount - x.ProcessedAmount, OriginalRestAmount = x.OriginalAmount - x.OriginalProcessedAmount }).ToListAsync(); StringBuilder sb = new(); foreach (var detail in settlement.Details) { var item = appDetails.Find(x => x.Id == detail.DetailId); if (item == null) { sb.Append(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.EmptyData))); break; } if (detail.OriginalAmount > 0 && detail.OriginalAmount > item.OriginalRestAmount) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DetailExceedingLimit)), detail.FeeName); sb.Append(";"); continue; } else if (detail.OriginalAmount < 0 && detail.OriginalAmount < item.OriginalRestAmount) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DetailExceedingLimit)), detail.FeeName); sb.Append(";"); continue; } detail.Category = settlement.BillType == SettlementBillType.Payment ? DetailCategory.PaidApplicationSettlement : DetailCategory.ChargeApplicationSettlement; } return sb.Length > 0 ? DataResult.Failed(sb.ToString()) : DataResult.Success; } protected override async Task OnSaveAsync(ApplicationSettlement settlement) { //更新付费申请明细的已处理金额 var list = settlement.Details.Select(x => new ApplicationDetail { Id = x.DetailId.Value, Category = DetailCategory.PaidApplication, ProcessedAmount = x.ApplyAmount, OriginalProcessedAmount = x.OriginalAmount }).ToList(); await TenantDb.Updateable(list) .PublicSetColumns(x => x.ProcessedAmount, "+") .PublicSetColumns(x => x.OriginalProcessedAmount, "+") .UpdateColumns(x => new { x.ProcessedAmount, x.OriginalProcessedAmount }) .ExecuteCommandAsync(); } protected override async Task PostSaveAsync(ApplicationSettlement settlement) { //回写付费申请的状态 var ids = settlement.Details.Select(x => x.DetailId); var appIds = await TenantDb.Queryable().Where(x => ids.Contains(x.Id) && (x.Category == DetailCategory.PaidApplication || x.Category == DetailCategory.ChargeApplication)) .Select(x => x.ApplicationId).ToListAsync(); var details = await TenantDb.Queryable().Where(x => appIds.Contains(x.ApplicationId) && (x.Category == DetailCategory.PaidApplication || x.Category == DetailCategory.ChargeApplication)) .GroupBy(x => x.ApplicationId).Select(x => new { x.ApplicationId, Count = SqlFunc.AggregateCount(x.Id), ProcessedCount = SqlFunc.Subqueryable().Where(y => appIds.Contains(y.ApplicationId) && (y.Category == DetailCategory.PaidApplication || y.Category == DetailCategory.ChargeApplication) && y.OriginalAmount - y.OriginalProcessedAmount == 0).Count() }).ToListAsync(); List applications = []; foreach (var item in details) { if (item.Count == 0 || item.ProcessedCount == 0) continue; var entity = new PaymentApplication { Id = item.ApplicationId }; if (item.Count == item.ProcessedCount) { entity.Status = PaymentApplicationStatus.SettlementCompleted; } else if (item.ProcessedCount != 0) { entity.Status = PaymentApplicationStatus.PartialSettlement; } applications.Add(entity); } await TenantDb.Updateable(applications).UpdateColumns(x => new { x.Status }).ExecuteCommandAsync(); return await base.PostSaveAsync(settlement); } protected override DataResult PreDelete(List settlements) { if (settlements.Any(x => x.IsLocked)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.SettlementIsLocked)); return DataResult.Success; } protected override async Task OnDeleteDetailAsync(List settlements) { await base.OnDeleteDetailAsync(settlements); //还原收/付费申请明细 var list = settlements.FindAll(x => x.Mode == SettlementMode.Payment || x.Mode == SettlementMode.Charge); if (list.Count > 0) { var items = list.SelectMany(x => x.Details); var ids = items.Select(x => x.DetailId); var details = TenantDb.Queryable().Where(x => ids.Contains(x.Id) && x.Category == DetailCategory.PaidApplication) .Select(x => new ApplicationDetail { Id = x.Id, ApplicationId = x.ApplicationId, ProcessedAmount = x.ProcessedAmount, OriginalAmount = x.OriginalAmount, OriginalProcessedAmount = x.OriginalProcessedAmount }).ToList(); foreach (var detail in details) { var item = items.FirstOrDefault(x => x.DetailId == detail.Id); detail.ProcessedAmount -= item.ApplyAmount; detail.OriginalProcessedAmount -= item.OriginalAmount; } await TenantDb.Updateable(details) .UpdateColumns(x => new { x.ProcessedAmount, x.OriginalProcessedAmount }) .ExecuteCommandAsync(); var gpList = details.GroupBy(x => x.ApplicationId); List applications = []; foreach (var gp in gpList) { var processedCount = gp.Count(x => x.OriginalAmount - x.OriginalProcessedAmount == 0); var itemCount = gp.Count(); if (itemCount == processedCount) continue; var entity = new PaymentApplication { Id = gp.Key }; if (processedCount == 0) { entity.Status = PaymentApplicationStatus.AuditPassed; } else if (itemCount > processedCount) { entity.Status = PaymentApplicationStatus.PartialSettlement; } applications.Add(entity); } if (applications.Count > 0) await TenantDb.Updateable(applications).UpdateColumns(x => new { x.Status }).ExecuteCommandAsync(); } } } }