From 5e26de6ff3dca2d41662346f02f3be4986f76890 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Fri, 26 Jun 2026 16:24:53 +0800 Subject: [PATCH] feat: remove ChargingTime and bill receivables by weight stage Drop sell/buy price charging-time config; write receivable fees after forecast or receive chargeable weight, hook inbound billing, fix order list status filter, and add DB migration. Co-authored-by: Cursor --- db.sql | 2 - sql/migrate_drop_charging_time.sql | 3 + .../Channels/SalesProductPriceListOutput.cs | 5 - .../Channels/SalesProductPriceSaveInput.cs | 5 - .../Channels/ServiceChannelPriceListOutput.cs | 5 - .../Channels/ServiceChannelPriceSaveInput.cs | 5 - .../Dtos/Orders/OrderExportInput.cs | 6 +- .../Dtos/Orders/OrderPaginatedInput.cs | 8 +- .../Dtos/Orders/PublishSnapshotRuleDtos.cs | 2 - .../Channels/SalesProductService.Helpers.cs | 3 - .../Services/Channels/SalesProductService.cs | 3 - .../Channels/ServiceChannelService.Helpers.cs | 2 - .../Channels/ServiceChannelService.cs | 3 - .../Operations/InboundRecordService.cs | 8 + .../Orders/OrderService.ParcelBilling.cs | 267 ++++++++++++++++-- .../Orders/OrderService.ParcelQuote.cs | 2 +- .../Services/Orders/OrderService.cs | 16 +- .../Orders/ParcelBillingCalculator.cs | 191 ++++++++----- .../Shared/PublishSnapshotSlimmer.cs | 1 - .../Entities/SalesProductPrice.cs | 2 - .../Entities/ServiceChannelPrice.cs | 2 - .../Repositories/ISalesProductRepository.cs | 6 + .../Channels/SalesProductRepository.cs | 22 ++ .../ParcelBillingCalculatorDemoTests.cs | 6 +- src/Oms.Tests/ParcelBillingCalculatorTests.cs | 69 +++-- .../PriceCheckSnapshotVerificationTests.cs | 4 +- .../Support/ParcelBillingTestData.cs | 4 - 27 files changed, 484 insertions(+), 168 deletions(-) create mode 100644 sql/migrate_drop_charging_time.sql diff --git a/db.sql b/db.sql index 3931b2b..1a2694e 100644 --- a/db.sql +++ b/db.sql @@ -336,7 +336,6 @@ CREATE TABLE `t_sales_product_price` ( `CostItem` VARCHAR(64) NOT NULL COMMENT '费用项', `CostUnit` VARCHAR(16) NOT NULL COMMENT '费用单位:kg/票/件', `Currency` VARCHAR(16) NOT NULL COMMENT '币种:RMB/USD/EUR', - `ChargingTime` VARCHAR(32) NULL COMMENT '收费时机:预报/出库/签收', `ChargingCondition` VARCHAR(64) NULL COMMENT '收费条件', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', @@ -466,7 +465,6 @@ CREATE TABLE `t_service_channel_price` ( `CostItem` VARCHAR(64) NOT NULL COMMENT '费用项', `CostUnit` VARCHAR(16) NOT NULL COMMENT '费用单位:kg/票/件', `Currency` VARCHAR(16) NOT NULL COMMENT '币种:RMB/USD/EUR', - `ChargingTime` VARCHAR(32) NULL COMMENT '收费时机:预报/出库/签收', `ChargingCondition` VARCHAR(64) NULL COMMENT '收费条件', `IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是', `CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人', diff --git a/sql/migrate_drop_charging_time.sql b/sql/migrate_drop_charging_time.sql new file mode 100644 index 0000000..6be5730 --- /dev/null +++ b/sql/migrate_drop_charging_time.sql @@ -0,0 +1,3 @@ +-- 移除卖价/买价主表「收费时机」字段(已由件重尺阶段触发算价,不再配置 ChargingTime) +ALTER TABLE `t_sales_product_price` DROP COLUMN IF EXISTS `ChargingTime`; +ALTER TABLE `t_service_channel_price` DROP COLUMN IF EXISTS `ChargingTime`; diff --git a/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs b/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs index d12d01d..4c843f9 100644 --- a/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs +++ b/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs @@ -45,11 +45,6 @@ public class SalesProductPriceListOutput /// public string Currency { get; set; } = string.Empty; - /// - /// 计费时机。 - /// - public string? ChargingTime { get; set; } - /// /// 计费条件。 /// diff --git a/src/Oms.Application/Dtos/Channels/SalesProductPriceSaveInput.cs b/src/Oms.Application/Dtos/Channels/SalesProductPriceSaveInput.cs index 91ce695..49cc0dc 100644 --- a/src/Oms.Application/Dtos/Channels/SalesProductPriceSaveInput.cs +++ b/src/Oms.Application/Dtos/Channels/SalesProductPriceSaveInput.cs @@ -47,11 +47,6 @@ public class SalesProductPriceSaveInput /// public string Currency { get; set; } = string.Empty; - /// - /// 计费时机。 - /// - public string? ChargingTime { get; set; } - /// /// 计费条件。 /// diff --git a/src/Oms.Application/Dtos/Channels/ServiceChannelPriceListOutput.cs b/src/Oms.Application/Dtos/Channels/ServiceChannelPriceListOutput.cs index a00582a..bf32e9d 100644 --- a/src/Oms.Application/Dtos/Channels/ServiceChannelPriceListOutput.cs +++ b/src/Oms.Application/Dtos/Channels/ServiceChannelPriceListOutput.cs @@ -45,11 +45,6 @@ public class ServiceChannelPriceListOutput /// public string Currency { get; set; } = string.Empty; - /// - /// 计费时机。 - /// - public string? ChargingTime { get; set; } - /// /// 计费条件。 /// diff --git a/src/Oms.Application/Dtos/Channels/ServiceChannelPriceSaveInput.cs b/src/Oms.Application/Dtos/Channels/ServiceChannelPriceSaveInput.cs index 85266b7..8346e22 100644 --- a/src/Oms.Application/Dtos/Channels/ServiceChannelPriceSaveInput.cs +++ b/src/Oms.Application/Dtos/Channels/ServiceChannelPriceSaveInput.cs @@ -47,11 +47,6 @@ public class ServiceChannelPriceSaveInput /// public string Currency { get; set; } = string.Empty; - /// - /// 计费时机。 - /// - public string? ChargingTime { get; set; } - /// /// 计费条件。 /// diff --git a/src/Oms.Application/Dtos/Orders/OrderExportInput.cs b/src/Oms.Application/Dtos/Orders/OrderExportInput.cs index bbd95e8..1b757f4 100644 --- a/src/Oms.Application/Dtos/Orders/OrderExportInput.cs +++ b/src/Oms.Application/Dtos/Orders/OrderExportInput.cs @@ -6,22 +6,26 @@ namespace Oms.Application.Dtos.Orders; public class OrderExportInput { /// - /// 状态。 + /// 状态:all 或 OrderStatusCodes 枚举值。 /// + [Newtonsoft.Json.JsonProperty("status")] public string? Status { get; set; } /// /// 关键词。 /// + [Newtonsoft.Json.JsonProperty("keyword")] public string? Keyword { get; set; } /// /// 创建TimeStart。 /// + [Newtonsoft.Json.JsonProperty("createTimeStart")] public DateTime? CreateTimeStart { get; set; } /// /// 创建TimeEnd。 /// + [Newtonsoft.Json.JsonProperty("createTimeEnd")] public DateTime? CreateTimeEnd { get; set; } } diff --git a/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs b/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs index 45f6c35..642ffb8 100644 --- a/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs +++ b/src/Oms.Application/Dtos/Orders/OrderPaginatedInput.cs @@ -8,30 +8,36 @@ public class OrderPaginatedInput /// /// 页码,从 1 开始。 /// + [Newtonsoft.Json.JsonProperty("page")] public int Page { get; set; } = 1; /// /// 每页条数。 /// + [Newtonsoft.Json.JsonProperty("pageSize")] public int PageSize { get; set; } = 20; /// - /// 状态。 + /// 状态:all 或 OrderStatusCodes 枚举值。 /// + [Newtonsoft.Json.JsonProperty("status")] public string? Status { get; set; } /// /// 关键词。 /// + [Newtonsoft.Json.JsonProperty("keyword")] public string? Keyword { get; set; } /// /// 创建TimeStart。 /// + [Newtonsoft.Json.JsonProperty("createTimeStart")] public DateTime? CreateTimeStart { get; set; } /// /// 创建TimeEnd。 /// + [Newtonsoft.Json.JsonProperty("createTimeEnd")] public DateTime? CreateTimeEnd { get; set; } } diff --git a/src/Oms.Application/Dtos/Orders/PublishSnapshotRuleDtos.cs b/src/Oms.Application/Dtos/Orders/PublishSnapshotRuleDtos.cs index c28a384..e61ac43 100644 --- a/src/Oms.Application/Dtos/Orders/PublishSnapshotRuleDtos.cs +++ b/src/Oms.Application/Dtos/Orders/PublishSnapshotRuleDtos.cs @@ -84,8 +84,6 @@ public class SalesProductPriceSnapshotItemDto public string Currency { get; set; } = string.Empty; - public string? ChargingTime { get; set; } - public string? ChargingCondition { get; set; } public List Factors { get; set; } = []; diff --git a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs index 9306171..c2b4372 100644 --- a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs +++ b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs @@ -84,7 +84,6 @@ public partial class SalesProductService CostItem = price.CostItem, CostUnit = price.CostUnit, Currency = price.Currency, - ChargingTime = price.ChargingTime, ChargingCondition = price.ChargingCondition, Factors = factorMap.TryGetValue(price.Id, out var factors) ? factors.Select(MapPriceFactorOutput).ToList() : [], Details = detailMap.TryGetValue(price.Id, out var details) ? details.Select(MapPriceDetailOutput).ToList() : [], @@ -423,7 +422,6 @@ public partial class SalesProductService CostItem = output.CostItem, CostUnit = output.CostUnit, Currency = output.Currency, - ChargingTime = output.ChargingTime, ChargingCondition = output.ChargingCondition, Factors = output.Factors.Select(item => new ChannelPriceFactorInput { @@ -455,7 +453,6 @@ public partial class SalesProductService CostItem = output.CostItem, CostUnit = output.CostUnit, Currency = output.Currency, - ChargingTime = output.ChargingTime, ChargingCondition = output.ChargingCondition, Factors = output.Factors.Select(item => new ChannelPriceFactorInput { diff --git a/src/Oms.Application/Services/Channels/SalesProductService.cs b/src/Oms.Application/Services/Channels/SalesProductService.cs index c61690b..0efc684 100644 --- a/src/Oms.Application/Services/Channels/SalesProductService.cs +++ b/src/Oms.Application/Services/Channels/SalesProductService.cs @@ -197,7 +197,6 @@ public partial class SalesProductService : ApplicationService CostItem = input.CostItem, CostUnit = input.CostUnit, Currency = input.Currency, - ChargingTime = input.ChargingTime, ChargingCondition = input.ChargingCondition }, BuildSalesProductPriceFactors(input.Factors), @@ -230,7 +229,6 @@ public partial class SalesProductService : ApplicationService price.CostItem = input.CostItem; price.CostUnit = input.CostUnit; price.Currency = input.Currency; - price.ChargingTime = input.ChargingTime; price.ChargingCondition = input.ChargingCondition; await SalesProductRepository.UpdatePriceAsync( price, @@ -308,7 +306,6 @@ public partial class SalesProductService : ApplicationService CostItem = priceInput.CostItem, CostUnit = priceInput.CostUnit, Currency = priceInput.Currency, - ChargingTime = priceInput.ChargingTime, ChargingCondition = priceInput.ChargingCondition }); factors.AddRange(BuildSalesProductPriceFactors(priceInput.Factors, currentTempId)); diff --git a/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs b/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs index c97ff16..ccfa5ca 100644 --- a/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs +++ b/src/Oms.Application/Services/Channels/ServiceChannelService.Helpers.cs @@ -136,7 +136,6 @@ public partial class ServiceChannelService CostItem = price.CostItem, CostUnit = price.CostUnit, Currency = price.Currency, - ChargingTime = price.ChargingTime, ChargingCondition = price.ChargingCondition, Factors = factorMap.TryGetValue(price.Id, out var factors) ? factors.Select(MapPriceFactorOutput).ToList() : [], Details = detailMap.TryGetValue(price.Id, out var details) ? details.Select(MapPriceDetailOutput).ToList() : [], @@ -392,7 +391,6 @@ public partial class ServiceChannelService CostItem = output.CostItem, CostUnit = output.CostUnit, Currency = output.Currency, - ChargingTime = output.ChargingTime, ChargingCondition = output.ChargingCondition, Factors = output.Factors.Select(item => new ChannelPriceFactorInput { diff --git a/src/Oms.Application/Services/Channels/ServiceChannelService.cs b/src/Oms.Application/Services/Channels/ServiceChannelService.cs index 9a97747..e41f7f6 100644 --- a/src/Oms.Application/Services/Channels/ServiceChannelService.cs +++ b/src/Oms.Application/Services/Channels/ServiceChannelService.cs @@ -284,7 +284,6 @@ public partial class ServiceChannelService : ApplicationService CostItem = input.CostItem, CostUnit = input.CostUnit, Currency = input.Currency, - ChargingTime = input.ChargingTime, ChargingCondition = input.ChargingCondition }, BuildPriceFactors(input.Factors), @@ -317,7 +316,6 @@ public partial class ServiceChannelService : ApplicationService price.CostItem = input.CostItem; price.CostUnit = input.CostUnit; price.Currency = input.Currency; - price.ChargingTime = input.ChargingTime; price.ChargingCondition = input.ChargingCondition; await ServiceChannelRepository.UpdatePriceAsync( price, @@ -395,7 +393,6 @@ public partial class ServiceChannelService : ApplicationService CostItem = priceInput.CostItem, CostUnit = priceInput.CostUnit, Currency = priceInput.Currency, - ChargingTime = priceInput.ChargingTime, ChargingCondition = priceInput.ChargingCondition }); factors.AddRange(BuildPriceFactors(priceInput.Factors, currentTempId)); diff --git a/src/Oms.Application/Services/Operations/InboundRecordService.cs b/src/Oms.Application/Services/Operations/InboundRecordService.cs index ca15f83..524958f 100644 --- a/src/Oms.Application/Services/Operations/InboundRecordService.cs +++ b/src/Oms.Application/Services/Operations/InboundRecordService.cs @@ -2,6 +2,7 @@ using Fake.Application; using Microsoft.AspNetCore.Mvc; using Oms.Application.Dtos.Operations; using Oms.Application.Dtos.Shared; +using Oms.Application.Services.Orders; using Oms.Domain.ChannelsAggregate.Repositories; using Oms.Domain.OperationsAggregate.Entities; using Oms.Domain.OperationsAggregate.Repositories; @@ -18,6 +19,7 @@ public class InboundRecordService : ApplicationService private IInboundRecordRepository InboundRecordRepository => LazyServiceProvider.GetRequiredService(); private ISalesProductRepository SalesProductRepository => LazyServiceProvider.GetRequiredService(); private IWarehouseAddressRepository WarehouseAddressRepository => LazyServiceProvider.GetRequiredService(); + private OrderService OrderService => LazyServiceProvider.GetRequiredService(); /// /// 新增入库记录。 @@ -50,6 +52,12 @@ public class InboundRecordService : ApplicationService }; var savedEntity = await InboundRecordRepository.InsertAsync(entity); + await OrderService.ApplyReceiveBillingByWaybillAsync( + input.WaybillNo, + input.Length, + input.Width, + input.Height, + input.Weight); return ObjectMapper.Map(savedEntity); } diff --git a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs index 4ee7d7e..7577f5e 100644 --- a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs +++ b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs @@ -102,24 +102,14 @@ public partial class OrderService } var customer = await CustomerRepository.FirstOrDefaultAsync(x => x.Id == order.CustomerId); - var fees = ParcelBillingCalculator.CalculateForecastReceivableFees( - snapshotBinding.Price, - snapshotBinding.PriceVersion, - snapshotBinding.PriceEffectiveTime, + await UpsertReceivableFeesAsync( + order, + snapshotBinding, forecast.ChargeableWeight ?? 0, zoneKeys, order.CargoAttribute ?? CargoAttributeCodes.General, - customer?.CustomerGroup); - - if (fees.Count == 0 && ParcelBillingCalculator.HasEnabledForecastPriceItems(snapshotBinding.Price)) - { - throw new BusinessException("产品卖价快照未配置或未匹配到有效价格"); - } - - if (fees.Count > 0) - { - await OrderRepository.UpsertFeesAsync(order.Id, fees); - } + customer?.CustomerGroup, + OrderFeeChargingStageCodes.Forecast); order.OrderStatus = OrderStatusCodes.Predeclared; order.PredeclareFailReason = string.Empty; @@ -289,6 +279,116 @@ public partial class OrderService }; } + private async Task ResolveSnapshotBindingFromOrderAsync(SalesProduct product, Order order) + { + var bindTime = order.CreateTime == default ? DateTime.Now : order.CreateTime; + SalesProductRulesPublishSnapshotDto rulesSnapshot; + string rulesVersion; + DateTime rulesEffectiveTime; + + if (string.IsNullOrWhiteSpace(order.SalesProductRulesVersion) + || order.SalesProductRulesVersion.StartsWith("LIVE-", StringComparison.OrdinalIgnoreCase)) + { + rulesSnapshot = await BuildLiveRulesSnapshotAsync(product); + rulesVersion = order.SalesProductRulesVersion ?? $"LIVE-{product.Id}"; + rulesEffectiveTime = bindTime; + } + else + { + var rulesRecord = await SalesProductRepository.GetPublishRecordByVersionAsync( + product.Id, + RulesOperatorType, + order.SalesProductRulesVersion); + if (rulesRecord?.Content == null) + { + throw new BusinessException("未命中订单绑定的规则快照版本"); + } + + rulesSnapshot = OmsDocHelper.DeserializeRulesSnapshot(rulesRecord.Content) + ?? throw new BusinessException("未命中订单绑定的规则快照版本"); + rulesVersion = rulesRecord.Version; + rulesEffectiveTime = rulesRecord.PublishTime; + } + + SalesProductPricePublishSnapshotDto priceSnapshot; + string priceVersion; + DateTime priceEffectiveTime; + + if (string.IsNullOrWhiteSpace(order.SalesProductPriceVersion) + || order.SalesProductPriceVersion.StartsWith("LIVE-", StringComparison.OrdinalIgnoreCase)) + { + priceSnapshot = await BuildLivePriceSnapshotAsync(product); + if (priceSnapshot.Prices.Count == 0) + { + throw new BusinessException("未命中订单绑定的价格快照版本"); + } + + priceVersion = order.SalesProductPriceVersion ?? $"LIVE-{product.Id}"; + priceEffectiveTime = bindTime; + } + else + { + var priceRecord = await SalesProductRepository.GetPublishRecordByVersionAsync( + product.Id, + PriceOperatorType, + order.SalesProductPriceVersion); + if (priceRecord?.Content == null) + { + throw new BusinessException("未命中订单绑定的价格快照版本"); + } + + priceSnapshot = OmsDocHelper.DeserializePriceSnapshot(priceRecord.Content) + ?? throw new BusinessException("未命中订单绑定的价格快照版本"); + if (priceSnapshot.Prices.Count == 0) + { + throw new BusinessException("未命中订单绑定的价格快照版本"); + } + + priceVersion = priceRecord.Version; + priceEffectiveTime = priceRecord.PublishTime; + } + + return new ProductSnapshotBinding + { + RulesVersion = rulesVersion, + PriceVersion = priceVersion, + RulesEffectiveTime = rulesEffectiveTime, + PriceEffectiveTime = priceEffectiveTime, + Rules = rulesSnapshot, + Price = priceSnapshot + }; + } + + private async Task UpsertReceivableFeesAsync( + Order order, + ProductSnapshotBinding snapshotBinding, + decimal chargeableWeight, + IReadOnlyList zoneKeys, + string cargoAttribute, + CustomerGroup? customerGroup, + string chargingStage) + { + var fees = ParcelBillingCalculator.CalculateReceivableFees( + snapshotBinding.Price, + snapshotBinding.PriceVersion, + snapshotBinding.PriceEffectiveTime, + chargeableWeight, + zoneKeys, + cargoAttribute, + customerGroup, + chargingStage); + + if (fees.Count == 0 && ParcelBillingCalculator.HasEnabledReceivablePriceItems(snapshotBinding.Price)) + { + throw new BusinessException("产品卖价快照未配置或未匹配到有效价格"); + } + + if (fees.Count > 0) + { + await OrderRepository.UpsertFeesAsync(order.Id, fees); + } + } + private async Task BuildLiveRulesSnapshotAsync(SalesProduct product) { var snapshot = new SalesProductRulesPublishSnapshotDto @@ -340,7 +440,6 @@ public partial class OrderService CostItem = price.CostItem, CostUnit = price.CostUnit, Currency = price.Currency, - ChargingTime = price.ChargingTime, ChargingCondition = price.ChargingCondition, Factors = factorMap.TryGetValue(price.Id, out var factors) ? factors.Select(MapPriceFactorOutput).ToList() @@ -738,6 +837,26 @@ public partial class OrderService } } + private static void SyncReceiveToShipAndDownstream(List weights, OrderWeight receive) + { + foreach (var type in new[] { OrderWeightTypeCodes.Ship, OrderWeightTypeCodes.Downstream }) + { + var row = weights.FirstOrDefault(x => x.Type == type); + if (row == null) + { + continue; + } + + row.Length = receive.Length; + row.Width = receive.Width; + row.Height = receive.Height; + row.ActualWeight = receive.ActualWeight; + row.VolumetricWeight = receive.VolumetricWeight; + row.ChargeableWeight = receive.ChargeableWeight; + row.Cubic = receive.Cubic; + } + } + private static Order BuildDraftOrderEntity( OrderParcelCreateInput input, SalesProduct product, @@ -908,6 +1027,122 @@ public partial class OrderService await OrderRepository.UpdateAsync(order); } + /// + /// 入库收货后:覆盖 receive 件重尺、重算计费重,写入 inbound 阶段应收,并同步 ship/downstream 重量行。 + /// + internal async Task ApplyReceiveBillingByWaybillAsync( + string waybillNo, + decimal? length, + decimal? width, + decimal? height, + decimal? actualWeight) + { + if (string.IsNullOrWhiteSpace(waybillNo)) + { + return; + } + + if (!length.HasValue || !width.HasValue || !height.HasValue || !actualWeight.HasValue + || length <= 0 || width <= 0 || height <= 0 || actualWeight <= 0) + { + return; + } + + var order = await OrderRepository.GetByServiceNoAsync(waybillNo.Trim()); + if (order == null || string.IsNullOrWhiteSpace(order.ProductCode)) + { + return; + } + + if (order.OrderStatus is not (OrderStatusCodes.Predeclared or OrderStatusCodes.Received)) + { + return; + } + + var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim()); + if (product == null) + { + return; + } + + var weights = (await OrderRepository.GetWeightsByOrderIdsAsync([order.Id])) + .OrderBy(x => x.Id) + .ToList(); + var receive = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Receive); + if (receive == null) + { + return; + } + + receive.Length = length; + receive.Width = width; + receive.Height = height; + receive.ActualWeight = actualWeight; + receive.Cubic = ParcelBillingCalculator.CalculateCubic(length.Value, width.Value, height.Value); + + ProductSnapshotBinding snapshotBinding; + try + { + snapshotBinding = await ResolveSnapshotBindingFromOrderAsync(product, order); + } + catch (BusinessException) + { + return; + } + + try + { + await ApplyChargeableWeightAsync(product, snapshotBinding.Rules, receive); + } + catch (BusinessException) + { + return; + } + + SyncReceiveToShipAndDownstream(weights, receive); + await OrderRepository.ReplaceWeightsAsync(order.Id, weights); + + var recipients = await OrderRepository.GetRecipientsByOrderIdsAsync([order.Id]); + var recipient = recipients.FirstOrDefault(); + var zoneKeys = await ResolvePriceZoneKeysAsync( + product, + snapshotBinding.Rules, + snapshotBinding.Price, + order.ConsigneeCountry ?? string.Empty, + recipient?.Province, + recipient?.City, + recipient?.ZipCode); + if (zoneKeys == null || zoneKeys.Count == 0) + { + return; + } + + var customer = order.CustomerId.HasValue + ? await CustomerRepository.FirstOrDefaultAsync(x => x.Id == order.CustomerId.Value) + : null; + + try + { + await UpsertReceivableFeesAsync( + order, + snapshotBinding, + receive.ChargeableWeight ?? 0, + zoneKeys, + order.CargoAttribute ?? CargoAttributeCodes.General, + customer?.CustomerGroup, + OrderFeeChargingStageCodes.Inbound); + } + catch (BusinessException) + { + return; + } + + order.OrderStatus = OrderStatusCodes.Received; + order.ReceiveTime = DateTime.Now; + order.UpdateTime = DateTime.Now; + await OrderRepository.UpdateAsync(order); + } + private async Task BuildParcelCreateOutputAsync(Order order) { var mapped = (await MapOrderOutputsAsync([order])).First(); diff --git a/src/Oms.Application/Services/Orders/OrderService.ParcelQuote.cs b/src/Oms.Application/Services/Orders/OrderService.ParcelQuote.cs index 125e0d8..460637b 100644 --- a/src/Oms.Application/Services/Orders/OrderService.ParcelQuote.cs +++ b/src/Oms.Application/Services/Orders/OrderService.ParcelQuote.cs @@ -171,7 +171,7 @@ public partial class OrderService } /// - /// 查价试算:按国家+邮编匹配分区,在卖价快照中匹配明细(忽略收费时机)。 + /// 查价试算:按国家+邮编匹配分区,在卖价快照中匹配明细。 /// private async Task TryComputePriceCheckQuoteAsync( SalesProduct product, diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs index 5fc0b31..d582717 100644 --- a/src/Oms.Application/Services/Orders/OrderService.cs +++ b/src/Oms.Application/Services/Orders/OrderService.cs @@ -40,7 +40,7 @@ public partial class OrderService : ApplicationService /// 3. 按接口约定返回列表结果。 /// [HttpPost("/order/list")] - public async Task> GetPaginatedListAsync(OrderPaginatedInput input) + public async Task> GetPaginatedListAsync([FromBody] OrderPaginatedInput input) { var customerId = await OmsSessionHelper.ResolveListCustomerIdAsync( CustomerRepository, @@ -204,7 +204,7 @@ public partial class OrderService : ApplicationService /// 3. 以文件形式返回结果。 /// [HttpPost("/order/export")] - public async Task ExportAsync(OrderExportInput input) + public async Task ExportAsync([FromBody] OrderExportInput input) { await CleanupExpiredFilesAsync(GetOrderExportDirectory()); var customerId = await OmsSessionHelper.ResolveListCustomerIdAsync( @@ -473,7 +473,9 @@ public partial class OrderService : ApplicationService { status = status?.Trim(); keyword = keyword?.Trim(); - var hasStatusFilter = !string.IsNullOrWhiteSpace(status) && status != "all"; + var statusValue = !string.IsNullOrWhiteSpace(status) && !string.Equals(status, "all", StringComparison.OrdinalIgnoreCase) + ? status + : null; var hasKeywordFilter = !string.IsNullOrWhiteSpace(keyword); var hasCustomerFilter = customerId.HasValue; var customerIdValue = customerId.GetValueOrDefault(); @@ -485,7 +487,7 @@ public partial class OrderService : ApplicationService var end = createTimeEnd.Value; return order => (!hasCustomerFilter || order.CustomerId == customerIdValue) && - (!hasStatusFilter || order.OrderStatus == status) && + (statusValue == null || order.OrderStatus == statusValue) && (!hasKeywordFilter || order.ServiceNo.Contains(keyword!) || (order.ClientNo ?? string.Empty).Contains(keyword!) || @@ -499,7 +501,7 @@ public partial class OrderService : ApplicationService var start = createTimeStart.Value; return order => (!hasCustomerFilter || order.CustomerId == customerIdValue) && - (!hasStatusFilter || order.OrderStatus == status) && + (statusValue == null || order.OrderStatus == statusValue) && (!hasKeywordFilter || order.ServiceNo.Contains(keyword!) || (order.ClientNo ?? string.Empty).Contains(keyword!) || @@ -512,7 +514,7 @@ public partial class OrderService : ApplicationService var end = createTimeEnd.Value; return order => (!hasCustomerFilter || order.CustomerId == customerIdValue) && - (!hasStatusFilter || order.OrderStatus == status) && + (statusValue == null || order.OrderStatus == statusValue) && (!hasKeywordFilter || order.ServiceNo.Contains(keyword!) || (order.ClientNo ?? string.Empty).Contains(keyword!) || @@ -522,7 +524,7 @@ public partial class OrderService : ApplicationService return order => (!hasCustomerFilter || order.CustomerId == customerIdValue) && - (!hasStatusFilter || order.OrderStatus == status) && + (statusValue == null || order.OrderStatus == statusValue) && (!hasKeywordFilter || order.ServiceNo.Contains(keyword!) || (order.ClientNo ?? string.Empty).Contains(keyword!) || diff --git a/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs b/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs index 29ca860..a81786d 100644 --- a/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs +++ b/src/Oms.Application/Services/Orders/ParcelBillingCalculator.cs @@ -353,23 +353,21 @@ internal static class ParcelBillingCalculator decimal chargeableWeight, IReadOnlyList zoneKeys, string cargoAttribute, - CustomerGroup? customerGroup) - { - var items = priceSnapshot.Prices.Select(p => new BillingPriceItem( - p.Status, p.ChargingTime, p.CostItem, p.Currency, p.Details)); - return CalculateForecastFees( - items, - OrderFeeTypeCodes.Receivable, + CustomerGroup? customerGroup) => + CalculateReceivableFees( + priceSnapshot, priceVersion, priceEffectiveTime, chargeableWeight, zoneKeys, cargoAttribute, customerGroup, - onlyForecastChargingTime: false); - } + OrderFeeChargingStageCodes.Forecast); public static bool HasEnabledQuotePriceItems(SalesProductPricePublishSnapshotDto priceSnapshot) => + HasEnabledReceivablePriceItems(priceSnapshot); + + public static bool HasEnabledReceivablePriceItems(SalesProductPricePublishSnapshotDto priceSnapshot) => priceSnapshot.Prices.Any(x => x.Status == 1); /// @@ -510,14 +508,15 @@ internal static class ParcelBillingCalculator string zoneName, string cargoAttribute, CustomerGroup? customerGroup) => - CalculateForecastReceivableFees( + CalculateReceivableFees( priceSnapshot, priceVersion, priceEffectiveTime, chargeableWeight, new[] { zoneName }, cargoAttribute, - customerGroup); + customerGroup, + OrderFeeChargingStageCodes.Forecast); public static List CalculateForecastReceivableFees( SalesProductPricePublishSnapshotDto priceSnapshot, @@ -526,20 +525,67 @@ internal static class ParcelBillingCalculator decimal chargeableWeight, IReadOnlyList zoneKeys, string cargoAttribute, - CustomerGroup? customerGroup) - { - var items = priceSnapshot.Prices.Select(p => new BillingPriceItem( - p.Status, p.ChargingTime, p.CostItem, p.Currency, p.Details)); - return CalculateForecastFees( - items, - OrderFeeTypeCodes.Receivable, + CustomerGroup? customerGroup) => + CalculateReceivableFees( + priceSnapshot, priceVersion, priceEffectiveTime, chargeableWeight, zoneKeys, cargoAttribute, customerGroup, - onlyForecastChargingTime: true); + OrderFeeChargingStageCodes.Forecast); + + /// + /// 应收费用:卖价全部启用项参与;ChargingStage 由件重尺阶段决定。 + /// + public static List CalculateReceivableFees( + SalesProductPricePublishSnapshotDto priceSnapshot, + string priceVersion, + DateTime priceEffectiveTime, + decimal chargeableWeight, + IReadOnlyList zoneKeys, + string cargoAttribute, + CustomerGroup? customerGroup, + string chargingStage) + { + var items = priceSnapshot.Prices.Select(p => new ReceivableBillingPriceItem( + p.Status, p.CostItem, p.Currency, p.Details)); + return CalculateReceivableFeesFromItems( + items, + priceVersion, + priceEffectiveTime, + chargeableWeight, + zoneKeys, + cargoAttribute, + customerGroup, + chargingStage); + } + + /// + /// 应付费用:买价全部启用项参与;ChargingStage 由件重尺阶段决定。 + /// + public static List CalculatePayableFees( + IReadOnlyList prices, + string priceVersion, + DateTime priceEffectiveTime, + decimal chargeableWeight, + IReadOnlyList zoneKeys, + string cargoAttribute, + CustomerGroup? customerGroup, + string chargingStage) + { + var items = prices.Select(p => new PayableBillingPriceItem( + p.Status, p.CostItem, p.Currency, p.Details)); + return CalculatePayableFeesFromItems( + items, + priceVersion, + priceEffectiveTime, + chargeableWeight, + zoneKeys, + cargoAttribute, + customerGroup, + chargingStage); } /// @@ -552,44 +598,25 @@ internal static class ParcelBillingCalculator decimal chargeableWeight, string zoneName, string cargoAttribute, - CustomerGroup? customerGroup) - { - var items = prices.Select(p => new BillingPriceItem( - p.Status, p.ChargingTime, p.CostItem, p.Currency, p.Details)); - return CalculateForecastFees( - items, - OrderFeeTypeCodes.Payable, + CustomerGroup? customerGroup) => + CalculatePayableFees( + prices, priceVersion, priceEffectiveTime, chargeableWeight, new[] { zoneName }, cargoAttribute, customerGroup, - onlyForecastChargingTime: true); - } + OrderFeeChargingStageCodes.Forecast); public static bool HasEnabledForecastPriceItems(SalesProductPricePublishSnapshotDto priceSnapshot) => - priceSnapshot.Prices.Any(x => x.Status == 1 && IsForecastChargingTime(x.ChargingTime)); + HasEnabledReceivablePriceItems(priceSnapshot); + + public static bool HasEnabledPayablePriceItems(IReadOnlyList prices) => + prices.Any(x => x.Status == 1); public static bool HasEnabledForecastPayablePriceItems(IReadOnlyList prices) => - prices.Any(x => x.Status == 1 && IsForecastChargingTime(x.ChargingTime)); - - public static string NormalizeChargingTime(string? chargingTime) - { - var value = chargingTime?.Trim(); - if (string.IsNullOrWhiteSpace(value)) - { - return OrderFeeChargingStageCodes.Forecast; - } - - return value switch - { - "预报" => OrderFeeChargingStageCodes.Forecast, - "入库" => OrderFeeChargingStageCodes.Inbound, - "出库" => OrderFeeChargingStageCodes.Outbound, - _ => value.ToLowerInvariant() - }; - } + HasEnabledPayablePriceItems(prices); public static string GetCargoAttributeLabel(string? cargoAttribute) => cargoAttribute switch { @@ -662,25 +689,19 @@ internal static class ParcelBillingCalculator Cubic = CalculateCubic(context.Length, context.Width, context.Height) }; - private static List CalculateForecastFees( - IEnumerable priceItems, - string feeType, + private static List CalculateReceivableFeesFromItems( + IEnumerable priceItems, string priceVersion, DateTime priceEffectiveTime, decimal chargeableWeight, IReadOnlyList zoneKeys, string cargoAttribute, CustomerGroup? customerGroup, - bool onlyForecastChargingTime) + string chargingStage) { var fees = new List(); foreach (var priceItem in priceItems.Where(x => x.Status == 1)) { - if (onlyForecastChargingTime && !IsForecastChargingTime(priceItem.ChargingTime)) - { - continue; - } - var detail = MatchPriceDetail(priceItem.Details, zoneKeys, cargoAttribute, chargeableWeight); if (detail == null) { @@ -696,31 +717,71 @@ internal static class ParcelBillingCalculator customerGroup); fees.Add(new OrderFee { - Type = feeType, + Type = OrderFeeTypeCodes.Receivable, CostItem = priceItem.CostItem, Amount = amount, Currency = priceItem.Currency, PriceNo = priceVersion, PriceEffectiveTime = priceEffectiveTime, - ChargingStage = OrderFeeChargingStageCodes.Forecast + ChargingStage = chargingStage }); } return fees; } - private readonly record struct BillingPriceItem( + private static List CalculatePayableFeesFromItems( + IEnumerable priceItems, + string priceVersion, + DateTime priceEffectiveTime, + decimal chargeableWeight, + IReadOnlyList zoneKeys, + string cargoAttribute, + CustomerGroup? customerGroup, + string chargingStage) + { + var fees = new List(); + foreach (var priceItem in priceItems.Where(x => x.Status == 1)) + { + var detail = MatchPriceDetail(priceItem.Details, zoneKeys, cargoAttribute, chargeableWeight); + if (detail == null) + { + continue; + } + + var amount = CalculateDetailAmount( + priceItem.Details, + detail, + zoneKeys, + cargoAttribute, + chargeableWeight, + customerGroup); + fees.Add(new OrderFee + { + Type = OrderFeeTypeCodes.Payable, + CostItem = priceItem.CostItem, + Amount = amount, + Currency = priceItem.Currency, + PriceNo = priceVersion, + PriceEffectiveTime = priceEffectiveTime, + ChargingStage = chargingStage + }); + } + + return fees; + } + + private readonly record struct ReceivableBillingPriceItem( byte Status, - string? ChargingTime, string CostItem, string Currency, List Details); - private static bool IsForecastChargingTime(string? chargingTime) - { - var normalized = NormalizeChargingTime(chargingTime); - return normalized is OrderFeeChargingStageCodes.Forecast or ""; - } + private readonly record struct PayableBillingPriceItem( + byte Status, + string CostItem, + string Currency, + List Details); private static ChannelPriceDetailOutput? MatchPriceDetail( IReadOnlyList details, diff --git a/src/Oms.Application/Shared/PublishSnapshotSlimmer.cs b/src/Oms.Application/Shared/PublishSnapshotSlimmer.cs index 99e01fe..f622062 100644 --- a/src/Oms.Application/Shared/PublishSnapshotSlimmer.cs +++ b/src/Oms.Application/Shared/PublishSnapshotSlimmer.cs @@ -19,7 +19,6 @@ public static class PublishSnapshotSlimmer CostItem = price.CostItem, CostUnit = price.CostUnit, Currency = price.Currency, - ChargingTime = price.ChargingTime, ChargingCondition = price.ChargingCondition, Factors = price.Factors, Details = price.Details diff --git a/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPrice.cs b/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPrice.cs index 00cb835..ee55ea9 100644 --- a/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPrice.cs +++ b/src/Oms.Domain/ChannelsAggregate/Entities/SalesProductPrice.cs @@ -21,7 +21,5 @@ public class SalesProductPrice : OmsAuditedEntity public string Currency { get; set; } = string.Empty; - public string? ChargingTime { get; set; } - public string? ChargingCondition { get; set; } } diff --git a/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPrice.cs b/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPrice.cs index f23aa88..665759b 100644 --- a/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPrice.cs +++ b/src/Oms.Domain/ChannelsAggregate/Entities/ServiceChannelPrice.cs @@ -21,7 +21,5 @@ public class ServiceChannelPrice : OmsAuditedEntity public string Currency { get; set; } = string.Empty; - public string? ChargingTime { get; set; } - public string? ChargingCondition { get; set; } } diff --git a/src/Oms.Domain/ChannelsAggregate/Repositories/ISalesProductRepository.cs b/src/Oms.Domain/ChannelsAggregate/Repositories/ISalesProductRepository.cs index b8906e5..ea41dad 100644 --- a/src/Oms.Domain/ChannelsAggregate/Repositories/ISalesProductRepository.cs +++ b/src/Oms.Domain/ChannelsAggregate/Repositories/ISalesProductRepository.cs @@ -52,6 +52,12 @@ public interface ISalesProductRepository : IRepository Task GetPublishRecordByIdAsync(int id, CancellationToken cancellationToken = default); + Task GetPublishRecordByVersionAsync( + int salesProductId, + string operatorType, + string version, + CancellationToken cancellationToken = default); + Task GetEffectivePublishRecordAsync( int salesProductId, string operatorType, diff --git a/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs b/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs index 7526450..06ec59c 100644 --- a/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs +++ b/src/Oms.SqlSugarCore/Channels/SalesProductRepository.cs @@ -195,6 +195,28 @@ public class SalesProductRepository : OmsSqlSugarRepository, ISale return await db.Queryable().FirstAsync(x => x.Id == id, cancellationToken); } + public async Task GetPublishRecordByVersionAsync( + int salesProductId, + string operatorType, + string version, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(version)) + { + return null; + } + + var db = await GetDbContextAsync(cancellationToken); + var records = await db.Queryable() + .Where(x => x.SalesProductId == salesProductId + && x.OperatorType == operatorType + && x.Version == version) + .OrderBy(x => x.PublishTime, SqlSugar.OrderByType.Desc) + .Take(1) + .ToListAsync(cancellationToken); + return records.FirstOrDefault(); + } + public async Task GetEffectivePublishRecordAsync( int salesProductId, string operatorType, diff --git a/src/Oms.Tests/ParcelBillingCalculatorDemoTests.cs b/src/Oms.Tests/ParcelBillingCalculatorDemoTests.cs index 8bc1660..792adcf 100644 --- a/src/Oms.Tests/ParcelBillingCalculatorDemoTests.cs +++ b/src/Oms.Tests/ParcelBillingCalculatorDemoTests.cs @@ -89,10 +89,10 @@ public class ParcelBillingCalculatorDemoTests var (priceVersion, effectiveTime) = ParcelBillingTestData.SnapshotMeta(); var receivableSnapshot = ParcelBillingTestData.BuildReceivableSnapshot( ParcelBillingTestData.ReceivablePriceItem( - "预报", 1, "freight", + 1, "freight", ParcelBillingTestData.ContinueFreightDetail()), ParcelBillingTestData.ReceivablePriceItem( - "预报", 1, "操作费", + 1, "操作费", ParcelBillingTestData.FixedPriceDetail(5M))); var receivableFees = ParcelBillingCalculator.CalculateForecastReceivableFees( @@ -119,7 +119,7 @@ public class ParcelBillingCalculatorDemoTests // —— 5. 应付(买价)—— var payablePrices = ParcelBillingTestData.BuildPayablePrices( ParcelBillingTestData.PayablePriceItem( - "预报", 1, "freight", + 1, "freight", ParcelBillingTestData.ContinueFreightDetail( firstWeight: 1M, firstPrice: 12M, continueUnit: 0.5M, continuePrice: 4M))); diff --git a/src/Oms.Tests/ParcelBillingCalculatorTests.cs b/src/Oms.Tests/ParcelBillingCalculatorTests.cs index f9721e2..d40a06f 100644 --- a/src/Oms.Tests/ParcelBillingCalculatorTests.cs +++ b/src/Oms.Tests/ParcelBillingCalculatorTests.cs @@ -61,7 +61,6 @@ public class ParcelBillingCalculatorTests { var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( ParcelBillingTestData.ReceivablePriceItem( - "预报", 1, "freight", ParcelBillingTestData.ContinueFreightDetail())); @@ -83,30 +82,50 @@ public class ParcelBillingCalculatorTests fees[0].PriceNo.Should().Be(_meta.PriceVersion); } - [Theory] - [InlineData("入库")] - [InlineData("出库")] - [InlineData("inbound")] - [InlineData("outbound")] - public void CalculateForecastReceivableFees_ShouldSkipNonForecastChargingTime(string chargingTime) + [Fact] + public void CalculateReceivableFees_ShouldIncludeAllEnabledPriceItems() { var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( ParcelBillingTestData.ReceivablePriceItem( - chargingTime, 1, "freight", ParcelBillingTestData.ContinueFreightDetail())); - var fees = ParcelBillingCalculator.CalculateForecastReceivableFees( + var fees = ParcelBillingCalculator.CalculateReceivableFees( snapshot, _meta.PriceVersion, _meta.EffectiveTime, 1.2M, - Zone, + new[] { Zone }, "general", - null); + null, + OrderFeeChargingStageCodes.Forecast); - fees.Should().BeEmpty(); + fees.Should().ContainSingle(); + fees[0].ChargingStage.Should().Be(OrderFeeChargingStageCodes.Forecast); + } + + [Fact] + public void CalculateReceivableFees_InboundStage_ShouldSetChargingStage() + { + var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( + ParcelBillingTestData.ReceivablePriceItem( + 1, + "freight", + ParcelBillingTestData.ContinueFreightDetail())); + + var fees = ParcelBillingCalculator.CalculateReceivableFees( + snapshot, + _meta.PriceVersion, + _meta.EffectiveTime, + 1.2M, + new[] { Zone }, + "general", + null, + OrderFeeChargingStageCodes.Inbound); + + fees.Should().ContainSingle(); + fees[0].ChargingStage.Should().Be(OrderFeeChargingStageCodes.Inbound); } [Fact] @@ -114,12 +133,10 @@ public class ParcelBillingCalculatorTests { var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( ParcelBillingTestData.ReceivablePriceItem( - "预报", 1, "freight", ParcelBillingTestData.ContinueFreightDetail()), ParcelBillingTestData.ReceivablePriceItem( - "预报", 1, "操作费", ParcelBillingTestData.FixedPriceDetail(5M))); @@ -150,7 +167,7 @@ public class ParcelBillingCalculatorTests detail.CustomerGroupValues = new Dictionary { ["客户组1"] = 15M }; var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( - ParcelBillingTestData.ReceivablePriceItem("预报", 1, "freight", detail)); + ParcelBillingTestData.ReceivablePriceItem(1, "freight", detail)); var fees = ParcelBillingCalculator.CalculateForecastReceivableFees( snapshot, @@ -170,7 +187,6 @@ public class ParcelBillingCalculatorTests { var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( ParcelBillingTestData.ReceivablePriceItem( - "outbound", 1, "freight", ParcelBillingTestData.TotalTierDetail("3", 0.5M, 10M, weightInterval: "(0-0.5]"), @@ -200,7 +216,7 @@ public class ParcelBillingCalculatorTests } [Fact] - public void CalculateQuoteReceivableFees_ShouldSupportOutboundTotalPriceTiers() + public void CalculateQuoteReceivableFees_ShouldSupportTotalPriceTiers() { var details = new[] { @@ -210,7 +226,7 @@ public class ParcelBillingCalculatorTests ParcelBillingTestData.TotalTierDetail("3", 2M, 14M), }; var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( - ParcelBillingTestData.ReceivablePriceItem("outbound", 1, "freight", details)); + ParcelBillingTestData.ReceivablePriceItem(1, "freight", details)); var fees = ParcelBillingCalculator.CalculateQuoteReceivableFees( snapshot, @@ -232,7 +248,7 @@ public class ParcelBillingCalculatorTests detail.CustomerGroupValues = new Dictionary { ["普通客户"] = 12M }; var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( - ParcelBillingTestData.ReceivablePriceItem("预报", 1, "freight", detail)); + ParcelBillingTestData.ReceivablePriceItem(1, "freight", detail)); var fees = ParcelBillingCalculator.CalculateForecastReceivableFees( snapshot, @@ -248,12 +264,13 @@ public class ParcelBillingCalculatorTests } [Fact] - public void HasEnabledForecastPriceItems_ShouldDetectForecastEnabledItems() + public void HasEnabledReceivablePriceItems_ShouldDetectAnyEnabledItems() { var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( - ParcelBillingTestData.ReceivablePriceItem("入库", 1, "freight", ParcelBillingTestData.ContinueFreightDetail()), - ParcelBillingTestData.ReceivablePriceItem("预报", 1, "freight", ParcelBillingTestData.ContinueFreightDetail())); + ParcelBillingTestData.ReceivablePriceItem(1, "freight", ParcelBillingTestData.ContinueFreightDetail()), + ParcelBillingTestData.ReceivablePriceItem(0, "freight", ParcelBillingTestData.ContinueFreightDetail())); + ParcelBillingCalculator.HasEnabledReceivablePriceItems(snapshot).Should().BeTrue(); ParcelBillingCalculator.HasEnabledForecastPriceItems(snapshot).Should().BeTrue(); } @@ -271,7 +288,6 @@ public class ParcelBillingCalculatorTests { var prices = ParcelBillingTestData.BuildPayablePrices( ParcelBillingTestData.PayablePriceItem( - "预报", 1, "freight", ParcelBillingTestData.ContinueFreightDetail( @@ -300,7 +316,6 @@ public class ParcelBillingCalculatorTests { var prices = ParcelBillingTestData.BuildPayablePrices( ParcelBillingTestData.PayablePriceItem( - "预报", 0, "freight", ParcelBillingTestData.ContinueFreightDetail())); @@ -315,7 +330,7 @@ public class ParcelBillingCalculatorTests null); fees.Should().BeEmpty(); - ParcelBillingCalculator.HasEnabledForecastPayablePriceItems(prices).Should().BeFalse(); + ParcelBillingCalculator.HasEnabledPayablePriceItems(prices).Should().BeFalse(); } [Fact] @@ -323,9 +338,9 @@ public class ParcelBillingCalculatorTests { var detail = ParcelBillingTestData.ContinueFreightDetail(); var snapshot = ParcelBillingTestData.BuildReceivableSnapshot( - ParcelBillingTestData.ReceivablePriceItem("预报", 1, "freight", detail)); + ParcelBillingTestData.ReceivablePriceItem(1, "freight", detail)); var payablePrices = ParcelBillingTestData.BuildPayablePrices( - ParcelBillingTestData.PayablePriceItem("预报", 1, "freight", detail)); + ParcelBillingTestData.PayablePriceItem(1, "freight", detail)); var receivable = ParcelBillingCalculator.CalculateForecastReceivableFees( snapshot, _meta.PriceVersion, _meta.EffectiveTime, 1.2M, Zone, "general", null); diff --git a/src/Oms.Tests/PriceCheckSnapshotVerificationTests.cs b/src/Oms.Tests/PriceCheckSnapshotVerificationTests.cs index cdf68e6..ff24576 100644 --- a/src/Oms.Tests/PriceCheckSnapshotVerificationTests.cs +++ b/src/Oms.Tests/PriceCheckSnapshotVerificationTests.cs @@ -9,7 +9,7 @@ using Oms.Domain.OrdersAggregate.Consts; namespace Oms.Tests; /// -/// 验证查价卖价快照匹配:忽略收费时机、unit 公式、重量左开右闭区间。 +/// 验证查价卖价快照匹配:unit 公式、重量左开右闭区间。 /// public class PriceCheckSnapshotVerificationTests { @@ -23,7 +23,6 @@ public class PriceCheckSnapshotVerificationTests Id = 1, Status = 1, CostItem = "freight", - ChargingTime = "outbound", Details = [ Detail(24, 1, "total", "3", "0,0.5", 11), @@ -37,7 +36,6 @@ public class PriceCheckSnapshotVerificationTests Id = 2, Status = 1, CostItem = "handling", - ChargingTime = "outbound", Details = [Detail(28, 2, "total", "3", "0,10", 20)] }) ] diff --git a/src/Oms.Tests/Support/ParcelBillingTestData.cs b/src/Oms.Tests/Support/ParcelBillingTestData.cs index dbb6595..b7755dc 100644 --- a/src/Oms.Tests/Support/ParcelBillingTestData.cs +++ b/src/Oms.Tests/Support/ParcelBillingTestData.cs @@ -24,7 +24,6 @@ internal static class ParcelBillingTestData params ServiceChannelPriceListOutput[] prices) => prices.ToList(); public static SalesProductPriceListOutput ReceivablePriceItem( - string chargingTime, byte status, string costItem, params ChannelPriceDetailOutput[] details) => @@ -33,12 +32,10 @@ internal static class ParcelBillingTestData Status = status, CostItem = costItem, Currency = "CNY", - ChargingTime = chargingTime, Details = details.ToList() }; public static ServiceChannelPriceListOutput PayablePriceItem( - string chargingTime, byte status, string costItem, params ChannelPriceDetailOutput[] details) => @@ -47,7 +44,6 @@ internal static class ParcelBillingTestData Status = status, CostItem = costItem, Currency = "CNY", - ChargingTime = chargingTime, Details = details.ToList() };