diff --git a/src/Oms.Application/Dtos/Orders/OrderFeeAddInput.cs b/src/Oms.Application/Dtos/Orders/OrderFeeAddInput.cs new file mode 100644 index 0000000..dd7e173 --- /dev/null +++ b/src/Oms.Application/Dtos/Orders/OrderFeeAddInput.cs @@ -0,0 +1,66 @@ +using System.ComponentModel.DataAnnotations; + +namespace Oms.Application.Dtos.Orders; + +/// +/// 手动添加订单费用入参。 +/// +public class OrderFeeAddInput +{ + /// + /// 订单主键。 + /// + [Required] + public int Id { get; set; } + + /// + /// 费用类型:receivable / payable。 + /// + [Required] + [StringLength(16)] + public string Type { get; set; } = string.Empty; + + /// + /// 费用项编码。 + /// + [Required] + [StringLength(64)] + public string CostItem { get; set; } = string.Empty; + + /// + /// 金额。 + /// + [Required] + public decimal Amount { get; set; } + + /// + /// 币种。 + /// + [Required] + [StringLength(16)] + public string Currency { get; set; } = string.Empty; + + /// + /// 计费阶段:forecast / inbound / outbound。 + /// + [Required] + [StringLength(16)] + public string ChargingStage { get; set; } = string.Empty; + + /// + /// 下游服务商(应付可选)。 + /// + [StringLength(128)] + public string? DownstreamProvider { get; set; } + + /// + /// 价格编号(可选)。 + /// + [StringLength(64)] + public string? PriceNo { get; set; } + + /// + /// 价格生效时间(可选)。 + /// + public DateTime? PriceEffectiveTime { get; set; } +} diff --git a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs index 1474bbb..deeacca 100644 --- a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs +++ b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs @@ -233,6 +233,69 @@ public partial class OrderService return (await MapOrderOutputsAsync([order])).First(); } + /// + /// 手动添加订单费用(应收或应付)。 + /// + [HttpPost("/order/fee/add")] + public async Task AddOrderFeeAsync(OrderFeeAddInput input) + { + var order = await GetOrderOrThrowAsync(input.Id); + + var type = input.Type?.Trim().ToLowerInvariant() ?? string.Empty; + if (type is not (OrderFeeTypeCodes.Receivable or OrderFeeTypeCodes.Payable)) + { + throw new BusinessException("费用类型无效"); + } + + var costItem = input.CostItem?.Trim() ?? string.Empty; + if (string.IsNullOrWhiteSpace(costItem)) + { + throw new BusinessException("费用项不能为空"); + } + + if (input.Amount <= 0) + { + throw new BusinessException("金额必须大于0"); + } + + var currency = input.Currency?.Trim() ?? string.Empty; + if (string.IsNullOrWhiteSpace(currency)) + { + throw new BusinessException("币种不能为空"); + } + + var chargingStage = input.ChargingStage?.Trim() ?? string.Empty; + if (chargingStage is not ( + OrderFeeChargingStageCodes.Forecast + or OrderFeeChargingStageCodes.Inbound + or OrderFeeChargingStageCodes.Outbound)) + { + throw new BusinessException("计费阶段无效"); + } + + var fee = new OrderFee + { + Type = type, + CostItem = costItem, + Amount = input.Amount, + Currency = currency, + ChargingStage = chargingStage, + DownstreamProvider = string.IsNullOrWhiteSpace(input.DownstreamProvider) + ? null + : input.DownstreamProvider.Trim(), + PriceNo = string.IsNullOrWhiteSpace(input.PriceNo) ? null : input.PriceNo.Trim(), + PriceEffectiveTime = input.PriceEffectiveTime, + CreateTime = DateTime.Now, + UpdateTime = DateTime.Now, + }; + + await OrderRepository.UpsertFeesAsync(order.Id, [fee]); + order.UpdateTime = DateTime.Now; + await OrderRepository.UpdateAsync(order); + + return (await MapOrderOutputsAsync([order])).First(); + } + private async Task RecalculateReceivableCoreAsync(Order order) { if (string.IsNullOrWhiteSpace(order.ProductCode))