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.Application.Interface; using DS.WMS.Core.Fee.Dtos; using DS.WMS.Core.Fee.Entity; using DS.WMS.Core.Info.Entity; using DS.WMS.Core.Op.Entity; using DS.WMS.Core.Sys.Entity; using SqlSugar; namespace DS.WMS.Core.Application.Method { /// /// 发票申请服务 /// public class InvoiceApplicationService : ApplicationService, IInvoiceApplicationService { /// /// 初始化 /// /// public InvoiceApplicationService(IServiceProvider serviceProvider) : base(serviceProvider) { } /// /// 获取分页列表 /// /// /// public async Task>> GetListAsync(PageRequest request) { List whereList = request.GetConditionalModels(Db); var result = await TenantDb.Queryable().Where(whereList) .Select((x) => new InvoiceApplicationDto { Id = x.Id, ApplicationNO = x.ApplicationNO, Status = x.Status, CustomerName = x.CustomerName, InvoiceHeader = x.InvoiceHeader, Currency = x.Currency, ApplyAmount = x.ApplyAmount, InvoiceAmount = x.InvoiceAmount, CreateTime = x.CreateTime, //申请时间 CreateBy = x.CreateBy, //申请人ID InvoiceNO = x.InvoiceNO, InvoiceBillNO = x.InvoiceBillNO, //实际开出票号 Note = x.Note, Reason = x.Reason, InvoiceDate = x.InvoiceDate, Category = x.Category, //申请类型 TaxRate = x.TaxRate, PushMode = x.PushMode, Email = x.Email, CellPhoneNO = x.CellPhoneNO, InvoiceRemark = x.InvoiceRemark, //开票要求 //原币金额 OriginalAmountList = SqlFunc.Subqueryable().Where(y => x.Id == y.ApplicationId) .GroupBy(y => y.OriginalCurrency).ToList(y => new CurrencyAmount { Currency = y.OriginalCurrency, Amount = y.OriginalAmount }), //未结算 UnsettledList = SqlFunc.Subqueryable().InnerJoin((d, f) => d.RecordId == f.Id && (f.Amount - f.SettlementAmount - f.OrderSettlementAmount) != 0) .GroupBy((d, f) => f.Currency).ToList((d, f) => new CurrencyAmount { Currency = f.Currency, Amount = f.Amount - f.SettlementAmount - f.OrderSettlementAmount }) }).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(); foreach (var item in result.Data) { item.CreateByName = users.Find(x => x.Id == item.CreateBy)?.UserName; } result.AdditionalData = new Dictionary { { "ApplyAmountSum", result.Data.Sum(x => x.ApplyAmount) }, { "InvoiceAmountSum", result.Data.Sum(x => x.InvoiceAmount.GetValueOrDefault()) } }; } return result; } /// /// 获取待开票的业务列表(编辑用) /// /// /// public async Task>> GetBizListAsync(PageRequest request) { var query = CreateBizQuery(); if (!request.QueryCondition.IsNullOrEmpty()) { var whereList = Db.ConfigQuery.Context.Utilities.JsonToConditionalModels(request.QueryCondition); query = query.Where(whereList); } if (request.OtherQueryCondition.HasValue) { FeeRange feeRange = request.OtherQueryCondition.Value; switch (feeRange) { case FeeRange.Unsettled: query = query.Where(x => x.SettlementAmount == 0); break; case FeeRange.Settled: query = query.Where(x => x.SettlementAmount > 0); break; case FeeRange.PaidNotReceived: query = query.Where(x => x.UnSettlementPaid == 0 && x.UnSettlementCharged > 0); break; case FeeRange.ReceivedNotPaid: query = query.Where(x => x.UnSettlementPaid > 0 && x.UnSettlementCharged == 0); break; case FeeRange.NotAppliedSettled: query = query.Where(x => x.OrderAmount == 0 && x.SettlementAmount == 0); break; case FeeRange.UnreconciledSettled: query = query.Where(x => x.DebitNo == null && x.SettlementAmount == 0); break; case FeeRange.NotIssuedSettled: query = query.Where(x => x.InvoiceAmount == 0 && x.SettlementAmount == 0); break; case FeeRange.ReconciledNotSettled: query = query.Where(x => x.DebitNo != null && x.SettlementAmount == 0); break; case FeeRange.NotReceivedPaid: query = query.Where(x => x.UnSettlementPaid > 0 && x.UnSettlementCharged > 0); break; case FeeRange.SettledNotIssued: query = query.Where(x => x.InvoiceAmount == 0 && x.SettlementAmount > 0); break; } } var result = await query.ToQueryPageAsync(request.PageCondition); if (result.Data.Count > 0) { //关联用户名称 var userIds = result.Data.Where(x => x.OperatorId.HasValue).Select(x => x.OperatorId.Value) .Union(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).Distinct(); var orgs = await Db.Queryable().Where(x => userIds.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; if (item.OperatorId.HasValue) item.Operator = users.Find(x => x.Id == item.OperatorId.Value)?.UserName; item.SaleDeptName = orgs.Find(x => x.Id == item.SaleDeptId)?.OrgName; } } return result; } //创建各项业务数据的查询并集 internal ISugarQueryable CreateBizQuery() { //海运出口 var query1 = TenantDb.Queryable() .InnerJoin((s, f) => s.Id == f.BusinessId && f.BusinessType == BusinessType.OceanShippingExport) .Where((s, f) => f.FeeStatus == FeeStatus.AuditPassed) .GroupBy((s, f) => s.Id) .Select((s, f) => new BizInvoiceApplication { Id = s.Id, AccountDate = s.AccountDate, BusinessType = BusinessType.OceanShippingExport, CntrTotal = s.CntrTotal, CreateBy = s.CreateBy, CustomerId = f.CustomerId,//费用对象 CustomerName = f.CustomerName, CustomerNo = s.CustomerNo, ClientName = s.CustomerName, //委托单位 DischargePort = s.DischargePort, ETD = s.ETD, HBLNO = s.HBLNO, LoadPort = s.LoadPort, MBLNO = s.MBLNO, OperatorId = s.OperatorId, SaleDeptId = s.SaleDeptId, SaleId = s.SaleId, SaleName = s.Sale,//揽货人 Vessel = s.Vessel,//船名 Voyage = s.Voyno,//航次 BookingNO = s.BookingNo, StlName = s.StlName, DebitNo = f.DebitNo, OrderAmount = f.OrderAmount, InvoiceAmount = f.InvoiceAmount, SettlementAmount = f.SettlementAmount, //未申请开票金额=金额-开票金额-申请开票金额+申请开票金额已开票 UnBilledRMB = SqlFunc.Subqueryable().Where(f => f.BusinessId == s.Id && f.FeeStatus == FeeStatus.AuditPassed && f.Currency == FeeCurrency.RMB_CODE).Select(f => SqlFunc.AggregateSum(f.Amount - f.InvoiceAmount - f.OrderInvoiceAmount + f.OrderInvSettlementAmount)), UnBilledUSD = SqlFunc.Subqueryable().Where(f => f.BusinessId == s.Id && f.FeeStatus == FeeStatus.AuditPassed && f.Currency == FeeCurrency.USD_CODE).Select(f => SqlFunc.AggregateSum(f.Amount - f.InvoiceAmount - f.OrderInvoiceAmount + f.OrderInvSettlementAmount)), UnBilledOther = SqlFunc.Subqueryable().Where(f => f.BusinessId == s.Id && f.FeeStatus == FeeStatus.AuditPassed && f.Currency != FeeCurrency.RMB_CODE && f.Currency != FeeCurrency.USD_CODE).Select(f => SqlFunc.AggregateSum(f.Amount - f.InvoiceAmount - f.OrderInvoiceAmount + f.OrderInvSettlementAmount)), //未结算金额=金额-结算金额-申请金额+申请金额已结算 UnSettlementPaid = SqlFunc.Subqueryable().Where(f => f.BusinessId == s.Id && f.FeeStatus == FeeStatus.AuditPassed && f.FeeType == FeeType.Payable) .Select(f => SqlFunc.AggregateSum(f.Amount - f.SettlementAmount - f.OrderAmount + f.OrderSettlementAmount)), UnSettlementCharged = SqlFunc.Subqueryable().Where(f => f.BusinessId == s.Id && f.FeeStatus == FeeStatus.AuditPassed && f.FeeType == FeeType.Receivable) .Select(f => SqlFunc.AggregateSum(f.Amount - f.SettlementAmount - f.OrderAmount + f.OrderSettlementAmount)), }); //海运进口 return TenantDb.UnionAll(new List> { query1 }); } /// /// 根据业务编号及类型获取关联费用记录 /// /// 业务ID与业务类型 /// public async Task> GetFeesAsync(params BizItem[] items) { var bizIds = items.Select(x => x.Id).ToList(); var types = items.Select(x => x.BusinessType).ToList(); var list = await TenantDb.Queryable() .Where(f => bizIds.Contains(f.BusinessId) && types.Contains(f.BusinessType) && f.FeeStatus == FeeStatus.AuditPassed) .Select(f => new FeeInvoiceDto { RecordId = f.Id, BusinessId = f.BusinessId, BusinessType = f.BusinessType, CustomerId = f.CustomerId, CustomerName = f.CustomerName, FeeId = f.FeeId, FeeName = f.FeeName, FeeType = f.FeeType, Amount = f.Amount, Currency = f.Currency, RestAmount = f.Amount - f.InvoiceAmount - f.OrderInvoiceAmount + f.OrderInvSettlementAmount, Remark = f.Remark, CreateBy = f.CreateBy }).ToListAsync(); //移除开票剩余金额为0的项 list.RemoveAll(f => f.RestAmount == 0); if (list.Count > 0) { //关联用户名称 var userIds = list.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 list) { item.CreateByName = users.Find(x => x.Id == item.CreateBy)?.UserName; } } return DataResult.Success(new InvoiceApplicaitonBiz(list)); } /// /// 获取发票申请详情 /// /// 申请单ID /// public async Task> GetAsync(long id) { var dto = await TenantDb.Queryable().Where(a => a.Id == id) .LeftJoin((a, b1) => a.CustomerBankId == b1.Id) .LeftJoin((a, b1, b2) => a.USDCustomerBankId == b2.Id) .Select((a, b1, b2) => new InvoiceApplicationDto { Id = a.Id, ApplicationNO = a.ApplicationNO, AutualCustomerName = a.AutualCustomerName, Currency = a.Currency, CustomerId = a.CustomerId, CustomerName = a.CustomerName, Status = a.Status, CreateTime = a.CreateTime, CreateBy = a.CreateBy, InvoiceDate = a.InvoiceDate, InvoiceAmount = a.InvoiceAmount, InvoiceHeader = a.InvoiceHeader, InvoiceNO = a.InvoiceNO, InvoiceBillNO = a.InvoiceBillNO, InvoiceRemark = a.InvoiceRemark, SaleDeptId = a.SaleDeptId, TaxID = a.TaxID, Note = a.Note, ApplyAmount = a.ApplyAmount, OtherCurrencyAmount = a.OtherCurrencyAmount, Category = a.Category, CustomerAddTel = a.CustomerAddTel, CustomerBankId = a.CustomerBankId, CustomerBankName = b1.BankName + " " + b1.Account, USDCustomerBankId = a.USDCustomerBankId, USDCustomerBankName = b2.BankName + " " + b2.Account }).FirstAsync(); if (dto != null) { dto.CreateByName = await Db.Queryable().Where(x => x.Id == dto.CreateBy).Select(x => x.UserName).FirstAsync(); dto.InvoiceDetails = await TenantDb.Queryable().Where(x => x.ApplicationId == dto.Id && x.Category == DetailCategory.InvoiceApplication).ToListAsync(); dto.Details = await TenantDb.Queryable().LeftJoin((d, f) => d.RecordId == f.Id) .Where(d => d.ApplicationId == id).Select((d, f) => new InvoiceApplicationDetailDto { Id = d.Id, RecordId = f.Id, FeeType = d.FeeType, FeeId = d.FeeId, FeeName = d.FeeName, Amount = d.ApplyAmount, OriginalAmount = d.OriginalAmount, //未申请金额=(金额-结算金额-申请金额+申请金额已结算) RestAmount = f.Amount - f.SettlementAmount - f.OrderAmount + f.OrderSettlementAmount, Currency = d.Currency, OriginalCurrency = d.OriginalCurrency, OriginalRate = f.ExchangeRate, ExchangeRate = d.ExchangeRate, AccTaxRate = f.AccTaxRate, BusinessId = f.BusinessId, BusinessType = f.BusinessType }).ToListAsync(); var gList = dto.Details.GroupBy(x => x.BusinessType).ToList(); foreach (var g in gList) { var ids = g.Select(x => x.BusinessId).ToList(); switch (g.Key) { case BusinessType.OceanShippingExport: var list1 = await TenantDb.Queryable().Where(x => ids.Contains(x.Id)).Select(x => new { x.Id, x.MBLNO, x.CustomerNo, x.CustomerName, x.ETD, x.CntrTotal, x.AccountDate, x.OperatorCode, x.Vessel, x.Voyno, x.Carrier, x.Forwarder }).ToListAsync(); foreach (var item in g) { var biz = list1.Find(x => x.Id == item.BusinessId); if (biz != null) { item.MBLNO = biz.MBLNO; item.CustomerName = biz.CustomerName; item.CustomerNo = biz.CustomerNo; item.ETD = biz.ETD; item.CntrTotal = biz.CntrTotal; item.AccountDate = biz.AccountDate; item.Operator = biz.OperatorCode; item.Vessel = biz.Vessel; item.Voyage = biz.Voyno; item.Carrier = biz.Carrier; item.Forwarder = biz.Forwarder; } } break; case BusinessType.OceanShippingImport: break; } } dto.SummaryItems = dto.Details.GroupBy(x => new { x.FeeType, x.Currency }).Select(x => new SummaryItem { FeeType = x.Key.FeeType, Currency = x.Key.Currency, Amount = x.Sum(y => y.Amount), OriginalAmount = x.Sum(y => y.OriginalAmount) }).ToList(); } return DataResult.Success(dto); } /// /// 删除发票明细 /// /// 发票明细ID /// public async Task DeleteInvoiceDetailAsync(params long[] ids) { int rows = await TenantDb.Deleteable().Where(x => ids.Contains(x.Id)).ExecuteCommandAsync(); return rows > 0 ? DataResult.Success : DataResult.FailedWithDesc(nameof(MultiLanguageConst.Operation_Failed)); } protected override async Task> GetDetailsAsync(IEnumerable items, List? conditions) { var ids1 = items.Select(x => x.Id); var ids2 = items.Select(x => x.BusinessType); var list = await TenantDb.Queryable().Where(x => ids1.Contains(x.BusinessId) && ids2.Contains(x.BusinessType) && x.FeeStatus == FeeStatus.AuditPassed) .Where(conditions) .Select(x => new ApplicationDetail { Id = x.Id, ApplyAmount = x.Amount - x.InvoiceAmount - x.OrderInvoiceAmount + x.OrderInvSettlementAmount, ExchangeRate = x.ExchangeRate, OriginalCurrency = x.Currency }).ToListAsync(); list.RemoveAll(x => x.ApplyAmount == 0); foreach (var item in list) item.OriginalAmount = item.ApplyAmount; return list; } protected override DataResult EnsureApplication(InvoiceApplication application, InvoiceApplication? dbValue) { if (dbValue != null && dbValue.Status != InvoiceApplicationStatus.Pending && dbValue.Status != InvoiceApplicationStatus.AuditRejected) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ApplicationSaveStatusError)); return DataResult.Success; } protected override DataResult CalculateAmount(InvoiceApplication application, List fees) { if (fees == null) return DataResult.Success; //if (fees.Any(x => x.CustomerId != application.CustomerId)) // return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ApplicationCustomerDetail)); //原始金额为0,则禁止提交 if (application.Details.Any(x => x.OriginalAmount == 0)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.OriginalAmountCannotBeZero)); StringBuilder sb = new(); foreach (var detail in application.Details) { var fee = fees.Find(x => x.Id == detail.RecordId); if (fee == null) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.ApplicationCannotRelateFee)), detail.FeeName); sb.Append(";"); continue; } detail.Record = fee; //检查税率是否一致 if (application.TaxRate != fee.TaxRate) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.InvoiceRateFee)), detail.FeeName); sb.Append(";"); continue; } //未申请开票金额=(金额-已开票金额-申请开票金额+申请开票金额已开票 var restAmount = fee.Amount - fee.InvoiceAmount - fee.OrderInvoiceAmount + fee.OrderInvSettlementAmount; if (restAmount == 0) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.FeeNobalance)), fee.FeeName); sb.Append(";"); continue; } if (detail.OriginalAmount > 0 && detail.OriginalAmount > restAmount) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DetailExceedingLimit)), fee.FeeName); sb.Append(";"); continue; } if (detail.OriginalAmount < 0 && detail.OriginalAmount < restAmount) { sb.AppendFormat(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DetailExceedingLimit)), fee.FeeName); sb.Append(";"); continue; } //更新费用记录的申请开票金额 fee.OrderInvoiceAmount += detail.OriginalAmount; //类别固定为发票申请 detail.Category = DetailCategory.InvoiceApplication; } if (sb.Length > 0) return DataResult.Failed(sb.ToString()); return DataResult.Success; } protected override async Task OnSaveAsync(InvoiceApplication application, List? fees) { //先清空发票明细 if (application.Id > 0) await TenantDb.Deleteable().Where(x => x.ApplicationId == application.Id).ExecuteCommandAsync(); //然后根据申请单明细重新生成 await CreateInvoiceDetailsAsync([application]); if (application.InvoiceDetails?.Count > 0) { await TenantDb.Insertable(application.InvoiceDetails).ExecuteCommandAsync(); } //更新申请开票金额 if (fees != null && fees.Count > 0) await TenantDb.Updateable(fees) .PublicSetColumns(x => x.OrderInvoiceAmount, "+") .UpdateColumns(x => new { x.OrderInvoiceAmount }) .ExecuteCommandAsync(); await base.OnSaveAsync(application, fees); } //生成发票明细 async Task CreateInvoiceDetailsAsync(IEnumerable applications) { foreach (var application in applications) { application.ApplyAmount = application.Details.Sum(x => x.ApplyAmount); application.AmountUppercase = new Money(application.ApplyAmount).ToString(); if (application.Details.Count > 0 && (application.InvoiceDetails == null || application.InvoiceDetails.Count == 0)) { var ids = application.Details.Select(x => x.RecordId).ToList(); var feesCodes = await TenantDb.Queryable().InnerJoin((f, c) => f.FeeId == c.Id && ids.Contains(f.Id)) .Select((f, c) => new { FeeId = c.Id, c.GoodName }).ToListAsync(); var list = feesCodes.GroupBy(x => x.GoodName).Select(x => new { Name = x.Key, FeeIds = x.Select(y => y.FeeId) }).ToList(); application.InvoiceDetails = new List(list.Count); foreach (var item in list) { var invDetail = new InvoiceDetail { ApplicationId = application.Id, Name = item.Name, TaxRate = application.TaxRate, Quantity = 1, TaxUnitPrice = application.Details.FindAll(x => item.FeeIds.Contains(x.FeeId)).Sum(x => x.ApplyAmount), Category = DetailCategory.InvoiceApplication }; invDetail.TaxAmount = invDetail.TaxUnitPrice * application.TaxRate; invDetail.UnitPrice = invDetail.TaxUnitPrice - invDetail.TaxAmount; invDetail.Amount = invDetail.TaxUnitPrice * invDetail.Quantity; application.InvoiceDetails.Add(invDetail); } } } } protected override DataResult PreDelete(List applications) { if (applications.Any(x => x.Status != InvoiceApplicationStatus.Pending && x.Status != InvoiceApplicationStatus.AuditRejected)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ApplicationDeleteStatusError)); return DataResult.Success; } protected override async Task OnDeleteDetailAsync(List applications, DeleteOption deleteOption) { //还原费用表的申请开票金额 var fees = applications.SelectMany(x => x.Details).Select(x => new FeeRecord { Id = x.RecordId, OrderInvoiceAmount = x.OriginalAmount }).ToList(); await TenantDb.Updateable(fees) .PublicSetColumns(it => it.OrderInvoiceAmount, "-") .UpdateColumns(x => new { x.OrderInvoiceAmount }) .ExecuteCommandAsync(); if (deleteOption == DeleteOption.DetailOnly) { //删除明细需要同时变更发票明细 var appIds = applications.Select(x => x.Id).ToList(); var excludeIds = applications.SelectMany(x => x.Details).Select(x => x.Id).ToList(); var details = await TenantDb.Queryable().Where(x => appIds.Contains(x.ApplicationId) && !excludeIds.Contains(x.Id)) .Select(x => new ApplicationDetail { Id = x.Id, ApplicationId = x.ApplicationId, RecordId = x.RecordId, FeeId = x.FeeId, ApplyAmount = x.ApplyAmount }).ToListAsync(); foreach (var item in applications) { //重新设置申请明细 item.Details = details.FindAll(x => x.ApplicationId == item.Id); } await TenantDb.Deleteable().Where(x => appIds.Contains(x.ApplicationId)).ExecuteCommandAsync(); await CreateInvoiceDetailsAsync(applications); var invDetails = applications.SelectMany(x => x.InvoiceDetails).ToList(); if (invDetails.Count > 0) await TenantDb.Insertable(invDetails).ExecuteCommandAsync(); await TenantDb.Updateable(applications).UpdateColumns(x => new { x.ApplyAmount, x.AmountUppercase }).ExecuteCommandAsync(); } } protected override DataResult PreSubmitApproval(List applications) { if (applications.Exists(x => x.Status == InvoiceApplicationStatus.AuditSubmittd || x.Status == InvoiceApplicationStatus.AuditPassed)) return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ApplicationIsAuditing)); return DataResult.Success; } protected override void OnSubmitApproval(InvoiceApplication application) { application.Status = InvoiceApplicationStatus.AuditSubmittd; } protected override void OnWithdraw(InvoiceApplication application) { application.Status = InvoiceApplicationStatus.Pending; } } }