feat: extend receivable-payable APIs with summary, export, and filters
Add summary/export endpoints plus customer code, effective stage, time field, and payment status filtering for finance reconciliation.
This commit is contained in:
parent
2018e45a7d
commit
ce1349572d
@ -32,12 +32,32 @@ public class ReceivablePayablePaginatedInput
|
||||
public int? CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单创建时间起。
|
||||
/// 客户编码关键词(模糊匹配)。
|
||||
/// </summary>
|
||||
public string? CustomerCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 仅保留各运单费用项的当前生效阶段。
|
||||
/// </summary>
|
||||
public bool EffectiveStageOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单创建时间起(timeField=createTime 时生效,兼容旧参数)。
|
||||
/// </summary>
|
||||
public DateTime? StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单创建时间止。
|
||||
/// 订单创建时间止(timeField=createTime 时生效,兼容旧参数)。
|
||||
/// </summary>
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间筛选字段:createTime / receiveTime / shipTime / payTime。
|
||||
/// </summary>
|
||||
public string? TimeField { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付状态:0 未支付,1 已支付。
|
||||
/// </summary>
|
||||
public int? IsPaid { get; set; }
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ namespace Oms.Application.Dtos.Finance;
|
||||
/// </summary>
|
||||
public class ReceivablePayablePaginatedOutput
|
||||
{
|
||||
public int OrderId { get; set; }
|
||||
|
||||
public string ServiceNo { get; set; } = string.Empty;
|
||||
|
||||
public string? ClientNo { get; set; }
|
||||
@ -37,6 +39,8 @@ public class ReceivablePayablePaginatedOutput
|
||||
|
||||
public string CostItem { get; set; } = string.Empty;
|
||||
|
||||
public string? CostItemName { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
public string Currency { get; set; } = string.Empty;
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
namespace Oms.Application.Dtos.Finance;
|
||||
|
||||
/// <summary>
|
||||
/// 应收应付汇总出参。
|
||||
/// </summary>
|
||||
public class ReceivablePayableSummaryOutput
|
||||
{
|
||||
/// <summary>
|
||||
/// 匹配筛选条件的费用明细总行数。
|
||||
/// </summary>
|
||||
public int TotalLines { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 按费用类型 + 币种汇总。
|
||||
/// </summary>
|
||||
public List<ReceivablePayableSummaryItemOutput> Items { get; set; } = [];
|
||||
}
|
||||
|
||||
public class ReceivablePayableSummaryItemOutput
|
||||
{
|
||||
public string FeeType { get; set; } = string.Empty;
|
||||
|
||||
public string? FeeTypeName { get; set; }
|
||||
|
||||
public string Currency { get; set; } = string.Empty;
|
||||
|
||||
public decimal TotalAmount { get; set; }
|
||||
|
||||
public int LineCount { get; set; }
|
||||
}
|
||||
@ -1,46 +1,49 @@
|
||||
using Fake.Application;
|
||||
using Fake.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MiniExcelLibs;
|
||||
using Oms.Application.DataPerm;
|
||||
using Oms.Application.Dtos.Finance;
|
||||
using Oms.Application.Dtos.Shared;
|
||||
using Oms.Application.Services.Orders;
|
||||
using Oms.Application.Shared;
|
||||
using Oms.Domain.CustomersAggregate.Repositories;
|
||||
using Oms.Domain.OrdersAggregate;
|
||||
using Oms.Domain.OrdersAggregate.Consts;
|
||||
using Oms.Domain.OrdersAggregate.Repositories;
|
||||
using Oms.Domain.SystemAggregate.Repositories;
|
||||
|
||||
namespace Oms.Application.Services.Finance;
|
||||
|
||||
/// <summary>
|
||||
/// 财务相关 API<EFBFBD>。
|
||||
/// </summary>
|
||||
/// 财务相关 API。
|
||||
/// </summary>
|
||||
[ApiExplorerSettings(GroupName = "OMS-Finance")]
|
||||
public class FinanceService : ApplicationService
|
||||
{
|
||||
private const string FeeItemDictType = "fee_item";
|
||||
private const int ExportMaxRows = 50000;
|
||||
|
||||
private IOrderRepository OrderRepository =>
|
||||
LazyServiceProvider.GetRequiredService<IOrderRepository>();
|
||||
|
||||
private ICustomerRepository CustomerRepository =>
|
||||
LazyServiceProvider.GetRequiredService<ICustomerRepository>();
|
||||
|
||||
private IDictRepository DictRepository =>
|
||||
LazyServiceProvider.GetRequiredService<IDictRepository>();
|
||||
|
||||
private EffectiveScopeBuilder DataScopeBuilder =>
|
||||
LazyServiceProvider.GetRequiredService<EffectiveScopeBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询应收应付(订单费用联查订单)<EFBFBD>。
|
||||
/// 分页查询应收应付(订单费用联查订单)。
|
||||
/// </summary>
|
||||
[HttpGet("/finance/receivable-payable/list")]
|
||||
public async Task<OmsPageResult<ReceivablePayablePaginatedOutput>> GetReceivablePayableListAsync(
|
||||
[FromQuery] ReceivablePayablePaginatedInput input)
|
||||
{
|
||||
var customerId = await OmsSessionHelper.ResolveListCustomerIdAsync(
|
||||
CustomerRepository,
|
||||
CurrentUser,
|
||||
input.CustomerId);
|
||||
var filter = BuildFilter(input, customerId);
|
||||
await ApplyInternalOrderDataScopeAsync(filter);
|
||||
|
||||
var filter = await BuildScopedFilterAsync(input);
|
||||
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
|
||||
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
|
||||
var (rows, total) = await OrderRepository.GetReceivablePayablePagedListAsync(
|
||||
@ -48,6 +51,102 @@ public class FinanceService : ApplicationService
|
||||
pageSize,
|
||||
filter);
|
||||
|
||||
var items = await MapReceivablePayableRowsAsync(rows);
|
||||
return new OmsPageResult<ReceivablePayablePaginatedOutput>
|
||||
{
|
||||
Items = items,
|
||||
Total = total
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应收应付汇总(当前筛选条件下全量费用明细)。
|
||||
/// </summary>
|
||||
[HttpGet("/finance/receivable-payable/summary")]
|
||||
public async Task<ReceivablePayableSummaryOutput> GetReceivablePayableSummaryAsync(
|
||||
[FromQuery] ReceivablePayablePaginatedInput input)
|
||||
{
|
||||
var filter = await BuildScopedFilterAsync(input);
|
||||
var rows = await OrderRepository.GetReceivablePayableSummaryAsync(filter);
|
||||
var items = rows
|
||||
.Select(row => new ReceivablePayableSummaryItemOutput
|
||||
{
|
||||
FeeType = row.FeeType,
|
||||
FeeTypeName = GetFeeTypeName(row.FeeType),
|
||||
Currency = row.Currency,
|
||||
TotalAmount = row.TotalAmount,
|
||||
LineCount = row.LineCount
|
||||
})
|
||||
.OrderBy(x => x.FeeType)
|
||||
.ThenBy(x => x.Currency)
|
||||
.ToList();
|
||||
|
||||
return new ReceivablePayableSummaryOutput
|
||||
{
|
||||
TotalLines = items.Sum(x => x.LineCount),
|
||||
Items = items
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出应收应付 Excel。
|
||||
/// </summary>
|
||||
[HttpGet("/finance/receivable-payable/export")]
|
||||
public async Task<FileContentResult> ExportReceivablePayableAsync(
|
||||
[FromQuery] ReceivablePayablePaginatedInput input)
|
||||
{
|
||||
var filter = await BuildScopedFilterAsync(input);
|
||||
var rows = await OrderRepository.GetReceivablePayableExportListAsync(filter, ExportMaxRows);
|
||||
var items = await MapReceivablePayableRowsAsync(rows);
|
||||
var exportRows = items.Select(item => new
|
||||
{
|
||||
运单号 = item.ServiceNo,
|
||||
客户单号 = item.ClientNo,
|
||||
跟踪号 = item.TrackNo,
|
||||
客户编码 = item.CustomerCode,
|
||||
费用类型 = item.FeeTypeName,
|
||||
费用项 = item.CostItemName ?? item.CostItem,
|
||||
金额 = item.Amount,
|
||||
币种 = item.Currency,
|
||||
计费阶段 = item.ChargingStageName,
|
||||
订单状态 = item.OrderStatusName,
|
||||
支付状态 = item.IsPaid == 1 ? "已支付" : "未支付",
|
||||
产品编码 = item.ProductCode,
|
||||
渠道编码 = item.ChannelCode,
|
||||
目的国 = item.ConsigneeCountryName,
|
||||
下游服务商 = item.DownstreamProvider,
|
||||
价格编号 = item.PriceNo,
|
||||
价格生效时间 = item.PriceEffectiveTime,
|
||||
支付时间 = item.PayTime,
|
||||
收货时间 = item.ReceiveTime,
|
||||
发货时间 = item.ShipTime
|
||||
}).ToList();
|
||||
|
||||
await using var stream = new MemoryStream();
|
||||
await MiniExcel.SaveAsAsync(stream, exportRows);
|
||||
stream.Position = 0;
|
||||
return new FileContentResult(stream.ToArray(),
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
{
|
||||
FileDownloadName = $"receivable-payable-{DateTime.Now:yyyyMMddHHmmss}.xlsx"
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<OrderReceivablePayableFilter> BuildScopedFilterAsync(
|
||||
ReceivablePayablePaginatedInput input)
|
||||
{
|
||||
var customerId = await OmsSessionHelper.ResolveListCustomerIdAsync(
|
||||
CustomerRepository,
|
||||
CurrentUser,
|
||||
input.CustomerId);
|
||||
var filter = BuildFilter(input, customerId);
|
||||
await ApplyInternalOrderDataScopeAsync(filter);
|
||||
return filter;
|
||||
}
|
||||
|
||||
private async Task<List<ReceivablePayablePaginatedOutput>> MapReceivablePayableRowsAsync(
|
||||
List<OrderReceivablePayableRow> rows)
|
||||
{
|
||||
var customerIds = rows
|
||||
.Select(x => x.Order.CustomerId)
|
||||
.Where(x => x.HasValue)
|
||||
@ -58,8 +157,9 @@ public class FinanceService : ApplicationService
|
||||
? []
|
||||
: await CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id));
|
||||
var customerMap = customers.ToDictionary(x => x.Id);
|
||||
var feeItemNameMap = await BuildFeeItemNameMapAsync();
|
||||
|
||||
var items = rows.Select(row =>
|
||||
return rows.Select(row =>
|
||||
{
|
||||
string? customerCode = null;
|
||||
if (row.Order.CustomerId.HasValue
|
||||
@ -68,8 +168,12 @@ public class FinanceService : ApplicationService
|
||||
customerCode = customer.CustomerCode;
|
||||
}
|
||||
|
||||
var costItem = row.Fee.CostItem;
|
||||
feeItemNameMap.TryGetValue(costItem, out var costItemName);
|
||||
|
||||
return new ReceivablePayablePaginatedOutput
|
||||
{
|
||||
OrderId = row.Order.Id,
|
||||
ServiceNo = row.Order.ServiceNo,
|
||||
ClientNo = row.Order.ClientNo,
|
||||
TrackNo = row.Order.TrackNo,
|
||||
@ -85,7 +189,8 @@ public class FinanceService : ApplicationService
|
||||
ConsigneeCountryName = row.Order.ConsigneeCountryName,
|
||||
FeeType = row.Fee.Type,
|
||||
FeeTypeName = GetFeeTypeName(row.Fee.Type),
|
||||
CostItem = row.Fee.CostItem,
|
||||
CostItem = costItem,
|
||||
CostItemName = costItemName ?? ParcelBillingCalculator.ResolveFeeDisplayName(costItem, null),
|
||||
Amount = row.Fee.Amount,
|
||||
Currency = row.Fee.Currency,
|
||||
DownstreamProvider = row.Fee.DownstreamProvider,
|
||||
@ -95,27 +200,59 @@ public class FinanceService : ApplicationService
|
||||
ChargingStageName = GetChargingStageName(row.Fee.ChargingStage)
|
||||
};
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
return new OmsPageResult<ReceivablePayablePaginatedOutput>
|
||||
{
|
||||
Items = items,
|
||||
Total = total
|
||||
};
|
||||
private async Task<Dictionary<string, string>> BuildFeeItemNameMapAsync()
|
||||
{
|
||||
var dictRows = await DictRepository.GetListAsync(x => x.DictType == FeeItemDictType);
|
||||
return dictRows
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.DictCode))
|
||||
.GroupBy(x => x.DictCode.Trim(), StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(
|
||||
g => g.Key,
|
||||
g => g.First().DictName?.Trim() ?? g.Key,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static OrderReceivablePayableFilter BuildFilter(
|
||||
ReceivablePayablePaginatedInput input,
|
||||
int? customerId) =>
|
||||
new()
|
||||
int? customerId)
|
||||
{
|
||||
var timeField = NormalizeTimeField(input.TimeField);
|
||||
var useCreateTime = timeField == "createTime";
|
||||
return new()
|
||||
{
|
||||
Keyword = input.Keyword,
|
||||
CustomerId = customerId,
|
||||
CreateTimeStart = input.StartTime,
|
||||
CreateTimeEnd = input.EndTime,
|
||||
CustomerCode = input.CustomerCode,
|
||||
CreateTimeStart = useCreateTime ? input.StartTime : null,
|
||||
CreateTimeEnd = useCreateTime ? input.EndTime : null,
|
||||
TimeField = timeField,
|
||||
TimeStart = input.StartTime,
|
||||
TimeEnd = input.EndTime,
|
||||
FeeType = input.FeeType,
|
||||
CostItem = input.CostItem,
|
||||
ChargingStage = input.ChargingStage
|
||||
ChargingStage = input.ChargingStage,
|
||||
EffectiveStageOnly = input.EffectiveStageOnly,
|
||||
IsPaid = NormalizeIsPaid(input.IsPaid)
|
||||
};
|
||||
}
|
||||
|
||||
private static int? NormalizeIsPaid(int? isPaid)
|
||||
{
|
||||
if (!isPaid.HasValue) return null;
|
||||
return isPaid.Value == 1 ? 1 : isPaid.Value == 0 ? 0 : null;
|
||||
}
|
||||
|
||||
private static string NormalizeTimeField(string? timeField)
|
||||
{
|
||||
var raw = timeField?.Trim();
|
||||
return raw switch
|
||||
{
|
||||
"receiveTime" or "shipTime" or "payTime" => raw,
|
||||
_ => "createTime"
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ApplyInternalOrderDataScopeAsync(OrderReceivablePayableFilter filter)
|
||||
{
|
||||
|
||||
@ -19,4 +19,34 @@ public class OrderReceivablePayableFilter : OrderListFilter
|
||||
/// 计费阶段:forecast / inbound / outbound。
|
||||
/// </summary>
|
||||
public string? ChargingStage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户编码关键词(模糊匹配)。
|
||||
/// </summary>
|
||||
public string? CustomerCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 仅保留各运单费用项的当前生效阶段(出库 > 入库 > 预报)。
|
||||
/// </summary>
|
||||
public bool EffectiveStageOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间筛选字段:createTime / receiveTime / shipTime / payTime。
|
||||
/// </summary>
|
||||
public string? TimeField { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间区间起(与 TimeField 对应字段)。
|
||||
/// </summary>
|
||||
public DateTime? TimeStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 时间区间止(与 TimeField 对应字段)。
|
||||
/// </summary>
|
||||
public DateTime? TimeEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 支付状态:0 未支付,1 已支付;null 表示全部。
|
||||
/// </summary>
|
||||
public int? IsPaid { get; set; }
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
namespace Oms.Domain.OrdersAggregate;
|
||||
|
||||
/// <summary>
|
||||
/// 应收应付汇总行(按费用类型 + 币种)。
|
||||
/// </summary>
|
||||
public class OrderReceivablePayableSummaryRow
|
||||
{
|
||||
public string FeeType { get; set; } = string.Empty;
|
||||
|
||||
public string Currency { get; set; } = string.Empty;
|
||||
|
||||
public decimal TotalAmount { get; set; }
|
||||
|
||||
public int LineCount { get; set; }
|
||||
}
|
||||
@ -76,6 +76,15 @@ public interface IOrderRepository : IRepository<Order>
|
||||
OrderReceivablePayableFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<List<OrderReceivablePayableSummaryRow>> GetReceivablePayableSummaryAsync(
|
||||
OrderReceivablePayableFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<List<OrderReceivablePayableRow>> GetReceivablePayableExportListAsync(
|
||||
OrderReceivablePayableFilter filter,
|
||||
int maxRows,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<List<OrderStatusCountRow>> GetStatusCountsAsync(int? customerId, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<List<OrderStatusCountRow>> GetStatusCountsByFilterAsync(
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using Fake.DependencyInjection;
|
||||
using Oms.Domain.CustomersAggregate.Entities;
|
||||
using Oms.Domain.OrdersAggregate;
|
||||
using Oms.Domain.OrdersAggregate.Consts;
|
||||
using Oms.Domain.OrdersAggregate.Entities;
|
||||
@ -1088,8 +1089,81 @@ public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, I
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var db = await GetDbContextAsync(cancellationToken);
|
||||
var orderIdsQuery = ApplyListFilter(db.Queryable<Order>(), filter).Select(x => x.Id);
|
||||
var feeQuery = await BuildReceivablePayableFeeQueryAsync(db, filter, cancellationToken);
|
||||
|
||||
var total = await feeQuery.CountAsync(cancellationToken);
|
||||
var fees = await feeQuery
|
||||
.OrderBy(fee => fee.CreateTime, OrderByType.Desc)
|
||||
.OrderBy(fee => fee.Id, OrderByType.Desc)
|
||||
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
|
||||
|
||||
return (await MapReceivablePayableRowsAsync(db, fees, cancellationToken), total);
|
||||
}
|
||||
|
||||
public async Task<List<OrderReceivablePayableSummaryRow>> GetReceivablePayableSummaryAsync(
|
||||
OrderReceivablePayableFilter filter,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var db = await GetDbContextAsync(cancellationToken);
|
||||
var feeQuery = await BuildReceivablePayableFeeQueryAsync(db, filter, cancellationToken);
|
||||
|
||||
return await feeQuery
|
||||
.GroupBy(fee => new { fee.Type, fee.Currency })
|
||||
.Select(g => new OrderReceivablePayableSummaryRow
|
||||
{
|
||||
FeeType = g.Type,
|
||||
Currency = g.Currency,
|
||||
TotalAmount = SqlFunc.AggregateSum(g.Amount),
|
||||
LineCount = SqlFunc.AggregateCount(g.Id)
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<List<OrderReceivablePayableRow>> GetReceivablePayableExportListAsync(
|
||||
OrderReceivablePayableFilter filter,
|
||||
int maxRows,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (maxRows <= 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var db = await GetDbContextAsync(cancellationToken);
|
||||
var feeQuery = await BuildReceivablePayableFeeQueryAsync(db, filter, cancellationToken);
|
||||
var fees = await feeQuery
|
||||
.OrderBy(fee => fee.CreateTime, OrderByType.Desc)
|
||||
.OrderBy(fee => fee.Id, OrderByType.Desc)
|
||||
.Take(maxRows)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return await MapReceivablePayableRowsAsync(db, fees, cancellationToken);
|
||||
}
|
||||
|
||||
private static async Task<ISugarQueryable<OrderFee>> BuildReceivablePayableFeeQueryAsync(
|
||||
ISqlSugarClient db,
|
||||
OrderReceivablePayableFilter filter,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var orderQuery = ApplyListFilter(db.Queryable<Order>(), filter);
|
||||
var customerCode = filter.CustomerCode?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(customerCode))
|
||||
{
|
||||
orderQuery = orderQuery.Where(o =>
|
||||
SqlFunc.Subqueryable<Customer>()
|
||||
.Where(c => c.Id == o.CustomerId && c.CustomerCode.Contains(customerCode))
|
||||
.Any());
|
||||
}
|
||||
|
||||
orderQuery = ApplyReceivablePayableTimeFilter(orderQuery, filter);
|
||||
|
||||
if (filter.IsPaid is 0 or 1)
|
||||
{
|
||||
var paidFlag = (byte)filter.IsPaid.Value;
|
||||
orderQuery = orderQuery.Where(o => o.IsPaid == paidFlag);
|
||||
}
|
||||
|
||||
var orderIdsQuery = orderQuery.Select(x => x.Id);
|
||||
var feeQuery = db.Queryable<OrderFee>()
|
||||
.Where(fee => !fee.IsDeleted)
|
||||
.In(fee => fee.OrderId, orderIdsQuery);
|
||||
@ -1104,7 +1178,7 @@ public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, I
|
||||
var costItem = filter.CostItem?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(costItem))
|
||||
{
|
||||
feeQuery = feeQuery.Where(fee => fee.CostItem.Contains(costItem));
|
||||
feeQuery = feeQuery.Where(fee => fee.CostItem == costItem || fee.CostItem.Contains(costItem));
|
||||
}
|
||||
|
||||
var chargingStage = filter.ChargingStage?.Trim();
|
||||
@ -1114,15 +1188,85 @@ public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, I
|
||||
feeQuery = feeQuery.Where(fee => fee.ChargingStage == chargingStage);
|
||||
}
|
||||
|
||||
var total = await feeQuery.CountAsync(cancellationToken);
|
||||
var fees = await feeQuery
|
||||
.OrderBy(fee => fee.CreateTime, OrderByType.Desc)
|
||||
.OrderBy(fee => fee.Id, OrderByType.Desc)
|
||||
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
|
||||
if (!filter.EffectiveStageOnly)
|
||||
{
|
||||
return feeQuery;
|
||||
}
|
||||
|
||||
var candidates = await feeQuery
|
||||
.Select(f => new { f.Id, f.OrderId, f.Type, f.CostItem, f.ChargingStage })
|
||||
.ToListAsync(cancellationToken);
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return feeQuery.Where(_ => false);
|
||||
}
|
||||
|
||||
var winningIds = candidates
|
||||
.GroupBy(f => (f.OrderId, f.Type, f.CostItem))
|
||||
.Select(g => g
|
||||
.OrderByDescending(f => GetChargingStageRank(f.ChargingStage))
|
||||
.ThenByDescending(f => f.Id)
|
||||
.First()
|
||||
.Id)
|
||||
.ToArray();
|
||||
|
||||
return feeQuery.Where(f => winningIds.Contains(f.Id));
|
||||
}
|
||||
|
||||
private static int GetChargingStageRank(string? stage) =>
|
||||
stage switch
|
||||
{
|
||||
OrderFeeChargingStageCodes.Outbound => 3,
|
||||
OrderFeeChargingStageCodes.Inbound => 2,
|
||||
OrderFeeChargingStageCodes.Forecast => 1,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
private static ISugarQueryable<Order> ApplyReceivablePayableTimeFilter(
|
||||
ISugarQueryable<Order> query,
|
||||
OrderReceivablePayableFilter filter)
|
||||
{
|
||||
var timeField = filter.TimeField?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(timeField) || string.Equals(timeField, "createTime", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return query;
|
||||
}
|
||||
|
||||
if (filter.TimeStart.HasValue)
|
||||
{
|
||||
var start = filter.TimeStart.Value;
|
||||
query = timeField switch
|
||||
{
|
||||
"receiveTime" => query.Where(o => o.ReceiveTime != null && o.ReceiveTime >= start),
|
||||
"shipTime" => query.Where(o => o.ShipTime != null && o.ShipTime >= start),
|
||||
"payTime" => query.Where(o => o.PayTime != null && o.PayTime >= start),
|
||||
_ => query
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.TimeEnd.HasValue)
|
||||
{
|
||||
var end = filter.TimeEnd.Value;
|
||||
query = timeField switch
|
||||
{
|
||||
"receiveTime" => query.Where(o => o.ReceiveTime != null && o.ReceiveTime <= end),
|
||||
"shipTime" => query.Where(o => o.ShipTime != null && o.ShipTime <= end),
|
||||
"payTime" => query.Where(o => o.PayTime != null && o.PayTime <= end),
|
||||
_ => query
|
||||
};
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private static async Task<List<OrderReceivablePayableRow>> MapReceivablePayableRowsAsync(
|
||||
ISqlSugarClient db,
|
||||
List<OrderFee> fees,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (fees.Count == 0)
|
||||
{
|
||||
return ([], total);
|
||||
return [];
|
||||
}
|
||||
|
||||
var orderIds = fees.Select(x => x.OrderId).Distinct().ToArray();
|
||||
@ -1131,7 +1275,7 @@ public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, I
|
||||
.ToListAsync(cancellationToken);
|
||||
var orderMap = orders.ToDictionary(x => x.Id);
|
||||
|
||||
var items = fees
|
||||
return fees
|
||||
.Where(fee => orderMap.ContainsKey(fee.OrderId))
|
||||
.Select(fee => new OrderReceivablePayableRow
|
||||
{
|
||||
@ -1139,8 +1283,6 @@ public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, I
|
||||
Order = orderMap[fee.OrderId]
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return (items, total);
|
||||
}
|
||||
|
||||
public async Task<List<OrderStatusCountRow>> GetStatusCountsAsync(int? customerId, CancellationToken cancellationToken = default)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user