|
|
using System.Text;
|
|
|
using DS.Module.Core;
|
|
|
using DS.Module.Core.Data;
|
|
|
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.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 Masuit.Tools.Systems;
|
|
|
using SqlSugar;
|
|
|
|
|
|
namespace DS.WMS.Core.Settlement.Method
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 收/付费申请结算服务
|
|
|
/// </summary>
|
|
|
public class ApplicationSettlementService : SettlementService<ApplicationSettlement>, IApplicationSettlementService
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 初始化
|
|
|
/// </summary>
|
|
|
/// <param name="provider"></param>
|
|
|
public ApplicationSettlementService(IServiceProvider provider) : base(provider)
|
|
|
{
|
|
|
TenantDb.QueryFilter.Clear<IOrgId>();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取结算单分页列表
|
|
|
/// </summary>
|
|
|
/// <param name="request"></param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<DataResult<List<ApplicationSettlementDto>>> GetListAsync(PageRequest<NumberQuery> request)
|
|
|
{
|
|
|
var query = TenantDb.Queryable<ApplicationSettlement>().WhereIF(!string.IsNullOrEmpty(request.OtherQueryCondition?.Number), x =>
|
|
|
SqlFunc.Subqueryable<ApplicationDetail>().InnerJoin<FeeRecord>((d, f) => d.RecordId == f.Id && f.BusinessType == BusinessType.OceanShippingExport)
|
|
|
.InnerJoin<SeaExport>((d, f, s) => f.BusinessId == s.Id).Where((d, f, s) =>
|
|
|
s.CustomerNo.Contains(request.OtherQueryCondition.Number) || s.BookingNo.Contains(request.OtherQueryCondition.Number) ||
|
|
|
s.MBLNO.Contains(request.OtherQueryCondition.Number) || s.CustomerNum.Contains(request.OtherQueryCondition.Number)).Any())
|
|
|
.Select(x => new ApplicationSettlementDto
|
|
|
{
|
|
|
ApplicationNO = x.ApplicationNO,
|
|
|
SettlementNO = x.SettlementNO,
|
|
|
SettlementTypeName = x.SettlementType.StlName, //结算方式
|
|
|
//-----结算金额统计-----
|
|
|
RMBAmount = SqlFunc.Subqueryable<ApplicationDetail>().Where(y => y.ApplicationId == x.Id && y.Currency == FeeCurrency.RMB_CODE)
|
|
|
.Select(y => SqlFunc.AggregateSum(y.ApplyAmount)),
|
|
|
USDAmount = SqlFunc.Subqueryable<ApplicationDetail>().Where(y => y.ApplicationId == x.Id && y.Currency == FeeCurrency.USD_CODE)
|
|
|
.Select(y => SqlFunc.AggregateSum(y.ApplyAmount)),
|
|
|
OtherAmount = SqlFunc.Subqueryable<ApplicationDetail>().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<ApplicationDetail>().InnerJoin<FeeRecord>((d, f) => d.ApplicationId == x.Id && d.RecordId == f.Id)
|
|
|
.GroupBy((d, f) => new { f.BusinessId, f.BusinessType, f.Currency }).ToList((d, f) => new CurrencyAmount { Currency = f.Currency, Amount = SqlFunc.AggregateSum(f.Amount - f.InvoiceAmount) }),
|
|
|
}, 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<SysUser>().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<SysOrg>().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;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取结算单详情
|
|
|
/// </summary>
|
|
|
/// <param name="id">结算单ID</param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<DataResult<ApplicationSettlementDto>> GetAsync(long id)
|
|
|
{
|
|
|
var model = await TenantDb.Queryable<ApplicationSettlement>().Select(x => new ApplicationSettlementDto
|
|
|
{
|
|
|
SettlementTypeName = x.SettlementType.StlName, //结算方式
|
|
|
CustomerBankName = x.CustomerBank.BankName,
|
|
|
CustomerAccount = x.CustomerBank.Account,
|
|
|
CreateByName = x.CreateUserName
|
|
|
}, true).FirstAsync(x => x.Id == id);
|
|
|
|
|
|
if (model != null)
|
|
|
{
|
|
|
if (model.SaleDeptId.HasValue)
|
|
|
model.SaleDeptName = await Db.Queryable<SysOrg>().Where(x => x.Id == model.SaleDeptId.Value)
|
|
|
.Select(x => x.OrgName).FirstAsync();
|
|
|
|
|
|
if (model.OrgBankId.HasValue)
|
|
|
model.OrgBankName = await Db.Queryable<SysBank>().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 ids = model.SettlementDetails.Select(x => x.Id);
|
|
|
var appList = await TenantDb.Queryable<PaymentApplication>().Where(x => ids.Contains(x.Id))
|
|
|
.Select(x => new PaymentApplication { Id = x.Id, ApplicationNO = x.ApplicationNO }).ToListAsync();
|
|
|
|
|
|
//关联用户名称
|
|
|
var userIds = model.SettlementDetails.Select(x => x.CreateBy).Distinct();
|
|
|
var users = await Db.Queryable<SysUser>().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;
|
|
|
item.ApplicationNOList = appList.Where(x => x.Id == item.Id).Select(x => x.ApplicationNO).ToList();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return DataResult<ApplicationSettlementDto>.Success(model);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取费用申请结算明细
|
|
|
/// </summary>
|
|
|
/// <param name="id">结算单ID</param>
|
|
|
/// <returns></returns>
|
|
|
protected override async Task<List<SettlementDetailGroup>> GetSettlementDetails(long id)
|
|
|
{
|
|
|
var appIds = await TenantDb.Queryable<ApplicationDetail>().Where(x => x.ApplicationId == id)
|
|
|
.Select(x => x.DetailId).ToListAsync();
|
|
|
|
|
|
var list = await TenantDb.Queryable<PaymentApplication>()
|
|
|
.InnerJoin<ApplicationDetail>((pa, d1) => pa.Id == d1.ApplicationId) //d1=申请明细
|
|
|
.Where((pa, d1) => SqlFunc.Subqueryable<ApplicationDetail>().Where(d2 => d2.DetailId == d1.Id && d2.ApplicationId == id).Any())
|
|
|
.GroupBy((pa, d1) => pa.Id)
|
|
|
.Select((pa, d1) => new SettlementDetailGroup
|
|
|
{
|
|
|
Id = pa.Id,
|
|
|
Status = (int)pa.Status,
|
|
|
BillNO = pa.ApplicationNO,
|
|
|
PaymentDate = pa.PaymentDate
|
|
|
}, true).ToListAsync();
|
|
|
|
|
|
var ids = list.Select(x => x.Id);
|
|
|
//获取原申请明细
|
|
|
var appDetails = await TenantDb.Queryable<ApplicationDetail>().Where(x => ids.Contains(x.ApplicationId))
|
|
|
.Select(x => new
|
|
|
{
|
|
|
x.Id,
|
|
|
x.ApplicationId,
|
|
|
x.Currency,
|
|
|
x.OriginalCurrency,
|
|
|
x.ApplyAmount,
|
|
|
x.OriginalAmount,
|
|
|
x.ProcessedAmount,
|
|
|
x.OriginalProcessedAmount
|
|
|
}).ToListAsync();
|
|
|
|
|
|
//获取结算单明细
|
|
|
var stlDetails = await TenantDb.Queryable<ApplicationDetail>().Where(x => x.ApplicationId == id)
|
|
|
.Select(x => new
|
|
|
{
|
|
|
x.ApplicationId,
|
|
|
x.DetailId,
|
|
|
x.RefId,
|
|
|
x.Currency,
|
|
|
x.OriginalCurrency,
|
|
|
x.ApplyAmount,
|
|
|
x.OriginalAmount,
|
|
|
x.ProcessedAmount,
|
|
|
x.OriginalProcessedAmount
|
|
|
}).ToListAsync();
|
|
|
|
|
|
foreach (var item in list)
|
|
|
{
|
|
|
PaymentApplicationStatus status = (PaymentApplicationStatus)item.Status;
|
|
|
item.StatusText = status.GetDescription();
|
|
|
|
|
|
//申请金额
|
|
|
var details = appDetails.Where(x => x.ApplicationId == item.Id);
|
|
|
item.RMBApplyAmount = details.Where(x => x.Currency == FeeCurrency.RMB_CODE).Sum(x => x.ApplyAmount);
|
|
|
item.USDApplyAmount = details.Where(x => x.Currency == FeeCurrency.USD_CODE).Sum(x => x.ApplyAmount);
|
|
|
//未结金额
|
|
|
item.RMBStlRestAmount = details.Where(x => appIds.Contains(x.Id) && x.OriginalCurrency == FeeCurrency.RMB_CODE).Sum(x => x.OriginalAmount - x.OriginalProcessedAmount);
|
|
|
item.USDStlRestAmount = details.Where(x => appIds.Contains(x.Id) && x.OriginalCurrency == FeeCurrency.USD_CODE).Sum(x => x.OriginalAmount - x.OriginalProcessedAmount);
|
|
|
//本次结算金额
|
|
|
item.RMBStlAmount = stlDetails.Where(x => x.RefId == item.Id && x.OriginalCurrency == FeeCurrency.RMB_CODE).Sum(x => x.OriginalAmount);
|
|
|
item.USDStlAmount = stlDetails.Where(x => x.RefId == item.Id && x.OriginalCurrency == FeeCurrency.USD_CODE).Sum(x => x.OriginalAmount);
|
|
|
}
|
|
|
|
|
|
return list;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取结算单明细
|
|
|
/// </summary>
|
|
|
/// <param name="request"></param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<DataResult<List<SettlementDetailGroup>>> GetDetailsAsync(PageRequest<long> request)
|
|
|
{
|
|
|
var details = await GetSettlementDetails(request.OtherQueryCondition);
|
|
|
return DataResult<List<SettlementDetailGroup>>.Success(details);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取待结算的申请分页列表
|
|
|
/// </summary>
|
|
|
/// <param name="request"></param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<DataResult<List<PaymentApplicationDtoV2>>> GetApplicationListAsync(PageRequest<ApplicationListQuery> request)
|
|
|
{
|
|
|
var query = TenantDb.Queryable<PaymentApplication>()
|
|
|
.InnerJoin<ApplicationDetail>((a, d) => a.Id == d.ApplicationId && d.ApplyAmount - d.ProcessedAmount != 0)
|
|
|
.InnerJoin<FeeRecord>((a, d, f) => d.RecordId == f.Id)
|
|
|
.InnerJoin<SeaExport>((a, d, f, s) => f.BusinessId == s.Id && f.BusinessType == BusinessType.OceanShippingExport)
|
|
|
.Where(request.GetConditionalModels(Db))
|
|
|
.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 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<ApplicationDetail>().Where(d => d.ApplicationId == a.Id && d.Currency == FeeCurrency.RMB_CODE)
|
|
|
.Select(d => SqlFunc.AggregateSum(d.ApplyAmount - d.ProcessedAmount)),
|
|
|
//USD未结金额
|
|
|
UnSettledUSD = SqlFunc.Subqueryable<ApplicationDetail>().Where(d => d.ApplicationId == a.Id && d.Currency == FeeCurrency.USD_CODE)
|
|
|
.Select(d => SqlFunc.AggregateSum(d.ApplyAmount - d.ProcessedAmount)),
|
|
|
//USD未结其他
|
|
|
UnSettledOther = SqlFunc.Subqueryable<ApplicationDetail>().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().ToQueryPageAsync(request.PageCondition);
|
|
|
|
|
|
if (result.Data?.Count > 0)
|
|
|
{
|
|
|
var userIds = result.Data.Select(x => x.CreateBy).Distinct();
|
|
|
var users = await Db.Queryable<SysUser>().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<SysOrg>().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;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取申请单费用明细
|
|
|
/// </summary>
|
|
|
/// <param name="id">申请单ID</param>
|
|
|
/// <param name="currency">结算币别</param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<DataResult<List<PaymentApplicationDetailDto>>> GetApplicationDetailsAsync(long id, string? currency = null)
|
|
|
{
|
|
|
var details = await CreateApplicationDetailQuery((d, f, s) => d.ApplicationId == id)
|
|
|
.LeftJoin<PaymentApplication>((d, pa) => d.ApplicationId == pa.Id)
|
|
|
.WhereIF(!string.IsNullOrEmpty(currency), d => d.Currency == currency)
|
|
|
.Select((d, pa) => new PaymentApplicationDetailDto
|
|
|
{
|
|
|
Id = d.Id,
|
|
|
ApplicationId = d.ApplicationId,
|
|
|
BusinessId = d.BusinessId,
|
|
|
BusinessType = d.BusinessType,
|
|
|
RecordId = d.RecordId,
|
|
|
FeeName = d.FeeName,
|
|
|
FeeType = d.FeeType,
|
|
|
Amount = d.Amount, //总金额
|
|
|
Currency = d.Currency,
|
|
|
ApplyAmount = d.ApplyAmount, //申请金额
|
|
|
SettlementAmount = d.ProcessedAmount,
|
|
|
RestAmount = d.ApplyAmount - d.ProcessedAmount, //剩余结算金额
|
|
|
CustomerId = d.CustomerId,
|
|
|
CustomerName = d.CustomerName,
|
|
|
OriginalCurrency = d.OriginalCurrency, //原始币别
|
|
|
OriginalRate = d.ExchangeRate, //原始汇率
|
|
|
ExchangeRate = d.ExchangeRate, //折算汇率
|
|
|
OriginalAmount = d.OriginalAmount, //原始金额
|
|
|
CustomerNo = d.CustomerNo,
|
|
|
MBLNO = d.MBLNO,
|
|
|
HBLNO = d.HBLNO,
|
|
|
ETD = d.ETD,
|
|
|
SourceName = d.SourceName,
|
|
|
SaleName = d.SaleName,
|
|
|
AccountDate = d.AccountDate,
|
|
|
Vessel = d.Vessel,
|
|
|
Voyage = d.Voyage,
|
|
|
InvoiceNO = pa.InvoiceNO
|
|
|
}).ToListAsync();
|
|
|
|
|
|
return DataResult<List<PaymentApplicationDetailDto>>.Success(details);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取申请单费用明细的币别
|
|
|
/// </summary>
|
|
|
/// <param name="documents"></param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<DataResult<List<SettlementDocument>>> GetExchangesAsync(List<SettlementDocument> documents)
|
|
|
{
|
|
|
var ids = documents.Select(x => x.Id);
|
|
|
var list = await TenantDb.Queryable<ApplicationDetail>()
|
|
|
.Where(d => ids.Contains(d.ApplicationId))
|
|
|
.Select(d => new
|
|
|
{
|
|
|
d.ApplicationId,
|
|
|
d.Currency
|
|
|
}).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.Currency))
|
|
|
document.ExchangeRates.Add(new CurrencyExchangeRate { Currency = item.Currency });
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return DataResult<List<SettlementDocument>>.Success(documents);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 保存申请结算
|
|
|
/// </summary>
|
|
|
/// <param name="request"></param>
|
|
|
/// <returns></returns>
|
|
|
public override async Task<DataResult<ApplicationSettlement>> SaveAsync(SettlementRequest<ApplicationSettlement> request)
|
|
|
{
|
|
|
var settlement = request.Settlement;
|
|
|
if (settlement.SettlementDate == default)
|
|
|
settlement.SettlementDate = DateTime.Now;
|
|
|
|
|
|
if (settlement.BillType == 0)
|
|
|
return DataResult<ApplicationSettlement>.FailedWithDesc(nameof(MultiLanguageConst.UnknownSettlementType));
|
|
|
|
|
|
settlement.Mode = settlement.BillType == SettlementBillType.Payment ? SettlementMode.Payment : SettlementMode.Charge;
|
|
|
|
|
|
ApplicationSettlement? dbValue = default;
|
|
|
if (request.Settlement.Id > 0)
|
|
|
{
|
|
|
dbValue = await TenantDb.Queryable<ApplicationSettlement>().Select(x => new ApplicationSettlement
|
|
|
{
|
|
|
Id = x.Id,
|
|
|
IsLocked = x.IsLocked,
|
|
|
Mode = x.Mode
|
|
|
}).FirstAsync(x => x.Id == request.Settlement.Id);
|
|
|
|
|
|
if (dbValue != null && dbValue.IsLocked)
|
|
|
return DataResult<ApplicationSettlement>.FailedWithDesc(nameof(MultiLanguageConst.SettlementIsLocked));
|
|
|
}
|
|
|
var result = EnsureSettlement(request.Settlement, dbValue);
|
|
|
if (!result.Succeeded)
|
|
|
return DataResult<ApplicationSettlement>.Failed(result.Message, result.MultiCode);
|
|
|
|
|
|
List<ApplicationDetail>? details1 = null;
|
|
|
if (request.Documents != null && (settlement.Mode == SettlementMode.Payment || settlement.Mode == SettlementMode.Charge))
|
|
|
request.Documents = request.Documents.FindAll(x => x.SettlementUSD.HasValue || x.SettlementRMB.HasValue || x.SettlementOther.HasValue);
|
|
|
|
|
|
//按申请结算
|
|
|
if (request.Documents?.Count > 0)
|
|
|
{
|
|
|
if (settlement.Id == 0)
|
|
|
{
|
|
|
var first = request.Documents[0];
|
|
|
settlement.CustomerId = first.CustomerId;
|
|
|
settlement.CustomerName = first.CustomerName;
|
|
|
}
|
|
|
var ids = request.Documents.Select(x => x.Id);
|
|
|
Expressionable<ApplicationDetail>? expr = null;
|
|
|
if (request.Documents.Any(x => x.SettlementRMB.GetValueOrDefault() == 0) || request.Documents.Any(x => x.SettlementUSD.GetValueOrDefault() == 0) ||
|
|
|
request.Documents.Any(x => x.SettlementOther.GetValueOrDefault() == 0)) //按币别结算
|
|
|
{
|
|
|
expr = Expressionable.Create<ApplicationDetail>();
|
|
|
|
|
|
if (request.Documents.Any(x => x.SettlementRMB.GetValueOrDefault() != 0))
|
|
|
expr = expr.Or(x => x.Currency == FeeCurrency.RMB_CODE);
|
|
|
|
|
|
if (request.Documents.Any(x => x.SettlementUSD.GetValueOrDefault() != 0))
|
|
|
expr = expr.Or(x => x.Currency == FeeCurrency.USD_CODE);
|
|
|
}
|
|
|
var detailCategory = settlement.Mode == SettlementMode.Payment ? DetailCategory.PaidApplication : DetailCategory.ChargeApplication;
|
|
|
details1 = await TenantDb.Queryable<ApplicationDetail>()
|
|
|
.Where(x => ids.Contains(x.ApplicationId) && x.Category == detailCategory)
|
|
|
.WhereIF(expr != null, expr.ToExpression())
|
|
|
.Select(x => new ApplicationDetail
|
|
|
{
|
|
|
ApplicationId = settlement.Id,
|
|
|
RefId = x.ApplicationId,
|
|
|
DetailId = x.Id,
|
|
|
RecordId = x.RecordId,
|
|
|
Category = settlement.Mode == SettlementMode.Payment ? DetailCategory.PaidApplicationSettlement : DetailCategory.ChargeApplicationSettlement,
|
|
|
CustomerName = x.CustomerName ?? settlement.CustomerName,
|
|
|
FeeId = x.FeeId,
|
|
|
FeeName = x.FeeName,
|
|
|
FeeType = x.FeeType,
|
|
|
|
|
|
ApplyAmount = x.ApplyAmount - x.ProcessedAmount,
|
|
|
Currency = x.Currency,
|
|
|
OriginalAmount = x.OriginalAmount - x.OriginalProcessedAmount,
|
|
|
OriginalCurrency = x.OriginalCurrency,
|
|
|
ExchangeRate = x.ExchangeRate
|
|
|
}).ToListAsync();
|
|
|
|
|
|
if (!string.IsNullOrEmpty(settlement.Currency)) //指定结算币别
|
|
|
{
|
|
|
var details2 = details1.FindAll(x => x.Currency != settlement.Currency);
|
|
|
foreach (var detail in details2)
|
|
|
{
|
|
|
var doc = request.Documents.Find(x => x.Id == detail.RefId);
|
|
|
if (doc == null)
|
|
|
return DataResult<ApplicationSettlement>.Failed("结算单据与费用明细不一致");
|
|
|
|
|
|
var exchange = doc.ExchangeRates?.Find(x => x.Currency == detail.Currency);
|
|
|
if (exchange == null)
|
|
|
return DataResult<ApplicationSettlement>.Failed($"未传入结算币别 {settlement.Currency} 与费用原币别 {detail.OriginalCurrency} 之间的汇率信息");
|
|
|
|
|
|
detail.ExchangeRate = exchange.ExchangeRate;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//执行结算费用分配
|
|
|
foreach (var doc in request.Documents)
|
|
|
{
|
|
|
var details2 = settlement.Mode != SettlementMode.FreeSettlement ?
|
|
|
details1.Where(x => x.RefId == doc.Id).OrderBy(x => x.ApplyAmount) :
|
|
|
details1.Where(x => x.BusinessId == doc.Id && x.BusinessType == doc.BusinessType && x.CustomerName == doc.CustomerName).OrderBy(x => x.ApplyAmount);
|
|
|
|
|
|
if (doc.SettlementRMB.HasValue)
|
|
|
{
|
|
|
var rmbDetails = details2.Where(x => x.Currency == FeeCurrency.RMB_CODE).ToList();
|
|
|
result = AssignAmount(rmbDetails, doc.SettlementRMB.Value, settlement.Currency);
|
|
|
if (!result.Succeeded)
|
|
|
return DataResult<ApplicationSettlement>.Failed(result.Message, result.MultiCode);
|
|
|
}
|
|
|
if (doc.SettlementUSD.HasValue)
|
|
|
{
|
|
|
var usdDetails = details2.Where(x => x.Currency == FeeCurrency.USD_CODE).ToList();
|
|
|
result = AssignAmount(usdDetails, doc.SettlementUSD.Value, settlement.Currency);
|
|
|
if (!result.Succeeded)
|
|
|
return DataResult<ApplicationSettlement>.Failed(result.Message, result.MultiCode);
|
|
|
}
|
|
|
if (doc.SettlementOther.HasValue)
|
|
|
{
|
|
|
var otherDetails = details2.Where(x => x.Currency != FeeCurrency.RMB_CODE && x.Currency != FeeCurrency.USD_CODE).ToList();
|
|
|
result = AssignAmount(otherDetails, doc.SettlementOther.Value, settlement.Currency);
|
|
|
if (!result.Succeeded)
|
|
|
return DataResult<ApplicationSettlement>.Failed(result.Message, result.MultiCode);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (details1?.Count > 0)
|
|
|
settlement.Details.AddRange(details1.FindAll(x => x.Assigned));
|
|
|
|
|
|
return await base.SaveAsync(request);
|
|
|
}
|
|
|
|
|
|
protected override async Task<DataResult> PreSaveAsync(ApplicationSettlement settlement)
|
|
|
{
|
|
|
if (settlement.Details.Count == 0)
|
|
|
return DataResult.Success;
|
|
|
|
|
|
var appIds = settlement.Details.Select(x => x.RefId).Distinct();
|
|
|
var appList = await TenantDb.Queryable<PaymentApplication>().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<ApplicationDetail>().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,
|
|
|
ProcessedAmount = x.Currency == x.OriginalCurrency ?
|
|
|
x.ApplyAmount : x.ApplyAmount / (x.ExchangeRate ?? 1),
|
|
|
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<ApplicationSettlement> PostSaveAsync(ApplicationSettlement settlement)
|
|
|
{
|
|
|
//回写付费申请的状态
|
|
|
if (settlement.BillType == SettlementBillType.Payment)
|
|
|
{
|
|
|
var ids = settlement.Details.Select(x => x.DetailId);
|
|
|
var appIds = await TenantDb.Queryable<ApplicationDetail>().Where(x => ids.Contains(x.Id) && x.Category == DetailCategory.PaidApplication)
|
|
|
.Select(x => x.ApplicationId).ToListAsync();
|
|
|
|
|
|
var details = await TenantDb.Queryable<ApplicationDetail>().Where(x => appIds.Contains(x.ApplicationId) && x.Category == DetailCategory.PaidApplication)
|
|
|
.GroupBy(x => x.ApplicationId).Select(x => new
|
|
|
{
|
|
|
x.ApplicationId,
|
|
|
Count = SqlFunc.AggregateCount(x.Id),
|
|
|
ProcessingCount = SqlFunc.Subqueryable<ApplicationDetail>().Where(y => appIds.Contains(y.ApplicationId) &&
|
|
|
y.Category == DetailCategory.PaidApplication && y.OriginalProcessedAmount != 0 && y.OriginalAmount != y.OriginalProcessedAmount).Count(),
|
|
|
ProcessedCount = SqlFunc.Subqueryable<ApplicationDetail>().Where(y => appIds.Contains(y.ApplicationId) &&
|
|
|
y.Category == DetailCategory.PaidApplication && y.OriginalAmount == y.OriginalProcessedAmount).Count()
|
|
|
}).ToListAsync();
|
|
|
|
|
|
List<PaymentApplication> 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.ProcessingCount != 0 || item.ProcessedCount != 0)
|
|
|
{
|
|
|
entity.Status = PaymentApplicationStatus.PartialSettlement;
|
|
|
}
|
|
|
else if (item.ProcessingCount == 0 && item.ProcessedCount == 0)
|
|
|
{
|
|
|
entity.Status = PaymentApplicationStatus.AuditPassed;
|
|
|
}
|
|
|
|
|
|
applications.Add(entity);
|
|
|
}
|
|
|
await TenantDb.Updateable(applications).UpdateColumns(x => new { x.Status }).ExecuteCommandAsync();
|
|
|
}
|
|
|
|
|
|
|
|
|
return await base.PostSaveAsync(settlement);
|
|
|
}
|
|
|
|
|
|
protected override DataResult PreDelete(List<ApplicationSettlement> settlements)
|
|
|
{
|
|
|
if (settlements.Any(x => x.IsLocked))
|
|
|
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.SettlementIsLocked));
|
|
|
|
|
|
return DataResult.Success;
|
|
|
}
|
|
|
|
|
|
//还原收/付费申请明细
|
|
|
protected override async Task OnDeleteDetailAsync(List<ApplicationSettlement> 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 = await TenantDb.Queryable<ApplicationDetail>().Where(x => ids.Contains(x.Id))
|
|
|
.Select(x => new ApplicationDetail
|
|
|
{
|
|
|
Id = x.Id,
|
|
|
Currency = x.Currency,
|
|
|
ApplicationId = x.ApplicationId,
|
|
|
ProcessedAmount = x.ProcessedAmount,
|
|
|
OriginalAmount = x.OriginalAmount,
|
|
|
OriginalProcessedAmount = x.OriginalProcessedAmount
|
|
|
}).ToListAsync();
|
|
|
|
|
|
foreach (var detail in details)
|
|
|
{
|
|
|
var item = items.FirstOrDefault(x => x.DetailId == detail.Id);
|
|
|
detail.ProcessedAmount -= detail.Currency == item.Currency ? item.ApplyAmount : item.OriginalAmount;
|
|
|
detail.OriginalProcessedAmount -= item.OriginalAmount;
|
|
|
}
|
|
|
|
|
|
await TenantDb.Updateable(details)
|
|
|
.UpdateColumns(x => new { x.ProcessedAmount, x.OriginalProcessedAmount })
|
|
|
.ExecuteCommandAsync();
|
|
|
|
|
|
var gpList = details.GroupBy(x => x.ApplicationId);
|
|
|
List<PaymentApplication> 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();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
}
|