fix: 查价分区键从国家+邮编解析,移除默认分区兜底
PriceAreaId 优先取产品表并从规则快照回退;未命中 t_kpi_area_info 时在 zone 步骤返回明确原因,避免与卖价明细分区键不匹配。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
148caae1e7
commit
94a503afb3
@ -88,6 +88,7 @@ public partial class OrderService
|
||||
|
||||
var zoneKeys = await ResolvePriceZoneKeysAsync(
|
||||
product,
|
||||
snapshotBinding.Rules,
|
||||
country.CountryCode,
|
||||
input.Receiver?.Province,
|
||||
input.Receiver?.City,
|
||||
@ -408,31 +409,55 @@ public partial class OrderService
|
||||
zipCode));
|
||||
}
|
||||
|
||||
private static int? ResolveEffectivePriceAreaId(
|
||||
SalesProduct product,
|
||||
SalesProductRulesPublishSnapshotDto? rulesSnapshot)
|
||||
{
|
||||
if (product.PriceAreaId is > 0)
|
||||
{
|
||||
return product.PriceAreaId;
|
||||
}
|
||||
|
||||
if (rulesSnapshot?.Product?.PriceAreaId is > 0)
|
||||
{
|
||||
return rulesSnapshot.Product.PriceAreaId;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<string>?> ResolvePriceZoneKeysAsync(
|
||||
SalesProduct product,
|
||||
SalesProductRulesPublishSnapshotDto? rulesSnapshot,
|
||||
string countryCode,
|
||||
string? province,
|
||||
string? city,
|
||||
string? zipCode)
|
||||
{
|
||||
if (!product.PriceAreaId.HasValue || product.PriceAreaId <= 0)
|
||||
var priceAreaId = ResolveEffectivePriceAreaId(product, rulesSnapshot);
|
||||
if (!priceAreaId.HasValue)
|
||||
{
|
||||
return new[] { "默认分区" };
|
||||
return null;
|
||||
}
|
||||
|
||||
var area = await KpiAreaRepository.FirstOrDefaultAsync(x => x.Id == product.PriceAreaId.Value);
|
||||
var area = await KpiAreaRepository.FirstOrDefaultAsync(x => x.Id == priceAreaId.Value);
|
||||
if (area == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rules = await KpiAreaRepository.GetAllInfosByAreaIdAsync(product.PriceAreaId.Value);
|
||||
var rules = await KpiAreaRepository.GetAllInfosByAreaIdAsync(priceAreaId.Value);
|
||||
var matched = rules.FirstOrDefault(rule => ParcelBillingCalculator.IsAreaMatched(
|
||||
rule,
|
||||
countryCode,
|
||||
province,
|
||||
city,
|
||||
zipCode));
|
||||
if (matched == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var keys = ParcelBillingCalculator.BuildPriceZoneKeys(matched, area);
|
||||
return keys.Count > 0 ? keys : null;
|
||||
}
|
||||
|
||||
@ -217,8 +217,18 @@ public partial class OrderService
|
||||
return PriceCheckFailure(product, "weight", ex.Message);
|
||||
}
|
||||
|
||||
var priceAreaId = ResolveEffectivePriceAreaId(product, snapshotBinding.Rules);
|
||||
if (!priceAreaId.HasValue)
|
||||
{
|
||||
return PriceCheckFailure(
|
||||
product,
|
||||
"zone",
|
||||
"产品未配置价格分区(PriceAreaId),无法根据国家+邮编匹配卖价明细中的分区键");
|
||||
}
|
||||
|
||||
var zoneKeys = await ResolvePriceZoneKeysAsync(
|
||||
product,
|
||||
snapshotBinding.Rules,
|
||||
context.CountryCode,
|
||||
context.Province,
|
||||
context.City,
|
||||
@ -228,7 +238,8 @@ public partial class OrderService
|
||||
return PriceCheckFailure(
|
||||
product,
|
||||
"zone",
|
||||
$"目的国 {context.CountryCode} 邮编 {FormatPostalCode(context.PostalCode)} 未匹配产品价格分区(请检查 PriceAreaId 及分区规则)");
|
||||
$"目的国 {context.CountryCode} 邮编 {FormatPostalCode(context.PostalCode)} 未命中价格分区规则(PriceAreaId={priceAreaId})," +
|
||||
$"卖价明细分区键(如 3) 无法解析,请检查 t_kpi_area_info 国家/邮编配置");
|
||||
}
|
||||
|
||||
var chargeableWeight = forecast.ChargeableWeight ?? 0;
|
||||
|
||||
@ -73,6 +73,57 @@ public class PriceCheckSnapshotVerificationTests
|
||||
ParcelBillingCalculator.WeightInLeftOpenRightClosedInterval(0M, start, end).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryParseWeightInterval_IntervalNotation_ShouldParseLeftOpenRightClosed()
|
||||
{
|
||||
ParcelBillingCalculator.TryParseWeightInterval("(0-0.5]", out var start, out var end).Should().BeTrue();
|
||||
start.Should().Be(0);
|
||||
end.Should().Be(0.5M);
|
||||
ParcelBillingCalculator.WeightInLeftOpenRightClosedInterval(0.5M, start, end).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserSnapshot_IntervalWeightFormat_Zone3_Weight05_ShouldMatchFreight11AndHandling20()
|
||||
{
|
||||
var snapshot = new SalesProductPricePublishSnapshotDto
|
||||
{
|
||||
SalesProductId = 1,
|
||||
Prices =
|
||||
[
|
||||
new SalesProductPriceListOutput
|
||||
{
|
||||
Id = 1,
|
||||
Status = 1,
|
||||
CostItem = "freight",
|
||||
Details =
|
||||
[
|
||||
Detail(34, 1, "unit", "3", "(0-0.5]", 11),
|
||||
Detail(35, 1, "unit", "3", "(0.5-1]", 12),
|
||||
]
|
||||
},
|
||||
new SalesProductPriceListOutput
|
||||
{
|
||||
Id = 2,
|
||||
Status = 1,
|
||||
CostItem = "handling",
|
||||
Details = [Detail(38, 2, "total", "3", "(0-10]", 20)]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var fees = ParcelBillingCalculator.CalculateQuoteReceivableFees(
|
||||
snapshot,
|
||||
"PRICE-20260626102033",
|
||||
DateTime.Now,
|
||||
0.5M,
|
||||
["3"],
|
||||
CargoAttributeCodes.General,
|
||||
CustomerGroup.Normal);
|
||||
|
||||
fees.Should().HaveCount(2);
|
||||
fees.Sum(x => x.Amount).Should().Be(31M);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserSnapshot_Zone3_Weight05_ShouldMatchFreight11AndHandling20()
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user