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 TenantDb.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();
}
}
}
}