feat: support remote-zone factor matching and all-customer price tier
Split price/remote zone matching in billing and prefer the 全部客户 column for any price tier key. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
c8107cb565
commit
cc100d40cf
@ -118,6 +118,11 @@ public partial class SalesProductService
|
||||
: OmsDocHelper.SerializeJson(additionalServices)
|
||||
});
|
||||
|
||||
var remoteAreaId = product.RemoteAreaId is > 0 ? product.RemoteAreaId : rulesSnapshot.RemoteAreaId;
|
||||
var remoteZoneKeys = isRemote
|
||||
? ParcelBillingCalculator.BuildRemoteZoneKeys(remoteAreaId, rulesSnapshot.RemoteAreaName)
|
||||
: Array.Empty<string>();
|
||||
|
||||
var receivableLines = ParcelBillingCalculator.CalculateQuoteReceivableFeeLines(
|
||||
priceSnapshot,
|
||||
priceVersion,
|
||||
@ -130,7 +135,8 @@ public partial class SalesProductService
|
||||
warehouseName,
|
||||
conditionContext,
|
||||
input.DeclaredSum,
|
||||
input.DeclaredCurrency);
|
||||
input.DeclaredCurrency,
|
||||
remoteZoneKeys);
|
||||
|
||||
if (receivableLines.Count == 0 && ParcelBillingCalculator.HasEnabledReceivablePriceItems(priceSnapshot))
|
||||
{
|
||||
@ -165,6 +171,7 @@ public partial class SalesProductService
|
||||
input.WarehouseId,
|
||||
warehouseName,
|
||||
isRemote,
|
||||
remoteZoneKeys,
|
||||
input.DeclaredSum,
|
||||
input.DeclaredCurrency,
|
||||
priceVersion,
|
||||
@ -307,6 +314,7 @@ public partial class SalesProductService
|
||||
int? warehouseId,
|
||||
string? warehouseName,
|
||||
bool isRemote,
|
||||
IReadOnlyList<string> remoteZoneKeys,
|
||||
decimal? declaredSum,
|
||||
string? declaredCurrency,
|
||||
string priceVersion,
|
||||
@ -333,6 +341,17 @@ public partial class SalesProductService
|
||||
return [];
|
||||
}
|
||||
|
||||
var channel = await ServiceChannelRepository.FirstOrDefaultAsync(x => x.Id == provider.ServiceChannelId.Value);
|
||||
var payableRemoteZoneKeys = isRemote
|
||||
? ParcelBillingCalculator.BuildRemoteZoneKeys(
|
||||
channel?.RemoteAreaId is > 0 ? channel.RemoteAreaId : null)
|
||||
: Array.Empty<string>();
|
||||
// 渠道未配偏远分区时回落产品偏远键,便于模拟偏远费行
|
||||
if (isRemote && payableRemoteZoneKeys.Count == 0)
|
||||
{
|
||||
payableRemoteZoneKeys = remoteZoneKeys;
|
||||
}
|
||||
|
||||
var payableContext = OrderRuleEvaluationContext.FromOrderOnly(new Order
|
||||
{
|
||||
ProductCode = product.ProductCode,
|
||||
@ -356,7 +375,8 @@ public partial class SalesProductService
|
||||
warehouseName,
|
||||
payableContext,
|
||||
declaredSum,
|
||||
declaredCurrency);
|
||||
declaredCurrency,
|
||||
payableRemoteZoneKeys);
|
||||
|
||||
if (payable.Count == 0)
|
||||
{
|
||||
|
||||
@ -689,6 +689,13 @@ public partial class OrderService
|
||||
string? warehouseName = null)
|
||||
{
|
||||
var conditionContext = BuildBillingConditionContext(order);
|
||||
var remoteAreaId = order.IsRemote > 0
|
||||
? (snapshotBinding.Rules?.RemoteAreaId
|
||||
?? snapshotBinding.Rules?.Product?.RemoteAreaId)
|
||||
: null;
|
||||
var remoteZoneKeys = ParcelBillingCalculator.BuildRemoteZoneKeys(
|
||||
remoteAreaId is > 0 ? remoteAreaId : null,
|
||||
snapshotBinding.Rules?.RemoteAreaName);
|
||||
var fees = ParcelBillingCalculator.CalculateReceivableFees(
|
||||
snapshotBinding.Price,
|
||||
snapshotBinding.PriceVersion,
|
||||
@ -702,7 +709,8 @@ public partial class OrderService
|
||||
warehouseName,
|
||||
conditionContext,
|
||||
order.DeclaredSum,
|
||||
order.DeclaredCurrency);
|
||||
order.DeclaredCurrency,
|
||||
remoteZoneKeys);
|
||||
|
||||
if (fees.Count == 0 && ParcelBillingCalculator.HasEnabledReceivablePriceItems(snapshotBinding.Price))
|
||||
{
|
||||
@ -781,6 +789,9 @@ public partial class OrderService
|
||||
channelIsRemote ? (byte)1 : (byte)0);
|
||||
|
||||
var channelPriceBinding = await ResolveChannelPriceBindingAsync(channel.Id, bindTime);
|
||||
var channelRemoteZoneKeys = channelIsRemote
|
||||
? ParcelBillingCalculator.BuildRemoteZoneKeys(channel.RemoteAreaId)
|
||||
: Array.Empty<string>();
|
||||
var previewFees = ParcelBillingCalculator.CalculatePayableFees(
|
||||
channelPriceBinding.Prices,
|
||||
channelPriceBinding.PriceVersion,
|
||||
@ -793,7 +804,8 @@ public partial class OrderService
|
||||
warehouseName,
|
||||
payableContext,
|
||||
order.DeclaredSum,
|
||||
order.DeclaredCurrency);
|
||||
order.DeclaredCurrency,
|
||||
channelRemoteZoneKeys);
|
||||
if (previewFees.Count == 0)
|
||||
{
|
||||
continue;
|
||||
|
||||
@ -595,6 +595,12 @@ public partial class OrderService
|
||||
context.PostalCode);
|
||||
var conditionContext = BuildQuoteBillingConditionContext(context, isRemote);
|
||||
|
||||
var remoteZoneKeys = isRemote
|
||||
? ParcelBillingCalculator.BuildRemoteZoneKeys(
|
||||
ResolveEffectiveRemoteAreaId(product, snapshotBinding.Rules),
|
||||
snapshotBinding.Rules?.RemoteAreaName)
|
||||
: Array.Empty<string>();
|
||||
|
||||
foreach (var zoneAttempt in zoneAttempts)
|
||||
{
|
||||
var feeLines = ParcelBillingCalculator.CalculateQuoteReceivableFeeLines(
|
||||
@ -609,7 +615,8 @@ public partial class OrderService
|
||||
context.WarehouseName,
|
||||
conditionContext,
|
||||
context.DeclaredSum,
|
||||
context.DeclaredCurrency);
|
||||
context.DeclaredCurrency,
|
||||
remoteZoneKeys);
|
||||
if (feeLines.Count == 0)
|
||||
{
|
||||
continue;
|
||||
|
||||
@ -374,14 +374,34 @@ internal static class ParcelBillingCalculator
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 卖价/买价明细匹配共用上下文(分区、货物属性、计费重、仓库)。
|
||||
/// 卖价/买价明细匹配共用上下文(价格分区、偏远分区、货物属性、计费重、仓库)。
|
||||
/// </summary>
|
||||
internal readonly record struct PriceDetailMatchContext(
|
||||
IReadOnlyList<string> ZoneKeys,
|
||||
string CargoAttribute,
|
||||
decimal ChargeableWeight,
|
||||
int? WarehouseId = null,
|
||||
string? WarehouseName = null);
|
||||
string? WarehouseName = null,
|
||||
IReadOnlyList<string>? RemoteZoneKeys = null);
|
||||
|
||||
/// <summary>
|
||||
/// 偏远命中时用生效 RemoteAreaId(及可选名称)作为偏远分区匹配键;非偏远返回空。
|
||||
/// </summary>
|
||||
public static IReadOnlyList<string> BuildRemoteZoneKeys(int? remoteAreaId, string? remoteAreaName = null)
|
||||
{
|
||||
if (remoteAreaId is not > 0)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
var keys = new List<string> { remoteAreaId.Value.ToString(CultureInfo.InvariantCulture) };
|
||||
if (!string.IsNullOrWhiteSpace(remoteAreaName))
|
||||
{
|
||||
keys.Add(remoteAreaName.Trim());
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static List<OrderFee> CalculateQuoteReceivableFees(
|
||||
SalesProductPricePublishSnapshotDto priceSnapshot,
|
||||
@ -395,7 +415,8 @@ internal static class ParcelBillingCalculator
|
||||
string? warehouseName = null,
|
||||
OrderRuleEvaluationContext? conditionContext = null,
|
||||
decimal? declaredSum = null,
|
||||
string? declaredCurrency = null) =>
|
||||
string? declaredCurrency = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null) =>
|
||||
CalculateQuoteReceivableFeeLines(
|
||||
priceSnapshot,
|
||||
priceVersion,
|
||||
@ -408,7 +429,8 @@ internal static class ParcelBillingCalculator
|
||||
warehouseName,
|
||||
conditionContext,
|
||||
declaredSum,
|
||||
declaredCurrency).Select(x => x.Fee).ToList();
|
||||
declaredCurrency,
|
||||
remoteZoneKeys).Select(x => x.Fee).ToList();
|
||||
|
||||
public static List<BillingFeeLine> CalculateQuoteReceivableFeeLines(
|
||||
SalesProductPricePublishSnapshotDto priceSnapshot,
|
||||
@ -422,7 +444,8 @@ internal static class ParcelBillingCalculator
|
||||
string? warehouseName = null,
|
||||
OrderRuleEvaluationContext? conditionContext = null,
|
||||
decimal? declaredSum = null,
|
||||
string? declaredCurrency = null)
|
||||
string? declaredCurrency = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null)
|
||||
{
|
||||
var items = priceSnapshot.Prices.Select(p => new ReceivableBillingPriceItem(
|
||||
p.Status, p.CostItem, p.PriceName, p.Currency, p.Details,
|
||||
@ -440,7 +463,8 @@ internal static class ParcelBillingCalculator
|
||||
warehouseName,
|
||||
conditionContext,
|
||||
declaredSum,
|
||||
declaredCurrency);
|
||||
declaredCurrency,
|
||||
remoteZoneKeys);
|
||||
}
|
||||
|
||||
public static bool HasEnabledQuotePriceItems(SalesProductPricePublishSnapshotDto priceSnapshot) =>
|
||||
@ -656,7 +680,8 @@ internal static class ParcelBillingCalculator
|
||||
string? warehouseName = null,
|
||||
OrderRuleEvaluationContext? conditionContext = null,
|
||||
decimal? declaredSum = null,
|
||||
string? declaredCurrency = null)
|
||||
string? declaredCurrency = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null)
|
||||
{
|
||||
var items = priceSnapshot.Prices.Select(p => new ReceivableBillingPriceItem(
|
||||
p.Status, p.CostItem, p.PriceName, p.Currency, p.Details,
|
||||
@ -674,7 +699,8 @@ internal static class ParcelBillingCalculator
|
||||
warehouseName,
|
||||
conditionContext,
|
||||
declaredSum,
|
||||
declaredCurrency);
|
||||
declaredCurrency,
|
||||
remoteZoneKeys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -692,7 +718,8 @@ internal static class ParcelBillingCalculator
|
||||
string? warehouseName = null,
|
||||
OrderRuleEvaluationContext? conditionContext = null,
|
||||
decimal? declaredSum = null,
|
||||
string? declaredCurrency = null)
|
||||
string? declaredCurrency = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null)
|
||||
{
|
||||
var items = prices.Select(p => new PayableBillingPriceItem(
|
||||
p.Status, p.CostItem, p.Currency, p.Details,
|
||||
@ -701,7 +728,13 @@ internal static class ParcelBillingCalculator
|
||||
items,
|
||||
priceVersion,
|
||||
priceEffectiveTime,
|
||||
new PriceDetailMatchContext(zoneKeys, cargoAttribute, chargeableWeight, warehouseId, warehouseName),
|
||||
new PriceDetailMatchContext(
|
||||
zoneKeys,
|
||||
cargoAttribute,
|
||||
chargeableWeight,
|
||||
warehouseId,
|
||||
warehouseName,
|
||||
remoteZoneKeys),
|
||||
chargingStage,
|
||||
conditionContext,
|
||||
declaredSum,
|
||||
@ -899,7 +932,8 @@ internal static class ParcelBillingCalculator
|
||||
string? warehouseName = null,
|
||||
OrderRuleEvaluationContext? conditionContext = null,
|
||||
decimal? declaredSum = null,
|
||||
string? declaredCurrency = null) =>
|
||||
string? declaredCurrency = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null) =>
|
||||
CalculateReceivableFeeLines(
|
||||
priceItems,
|
||||
priceVersion,
|
||||
@ -913,7 +947,8 @@ internal static class ParcelBillingCalculator
|
||||
warehouseName,
|
||||
conditionContext,
|
||||
declaredSum,
|
||||
declaredCurrency).Select(x => x.Fee).ToList();
|
||||
declaredCurrency,
|
||||
remoteZoneKeys).Select(x => x.Fee).ToList();
|
||||
|
||||
private static List<BillingFeeLine> CalculateReceivableFeeLines(
|
||||
IEnumerable<ReceivableBillingPriceItem> priceItems,
|
||||
@ -928,7 +963,8 @@ internal static class ParcelBillingCalculator
|
||||
string? warehouseName = null,
|
||||
OrderRuleEvaluationContext? conditionContext = null,
|
||||
decimal? declaredSum = null,
|
||||
string? declaredCurrency = null)
|
||||
string? declaredCurrency = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null)
|
||||
{
|
||||
var pass1 = new List<(int SerialNumber, BillingFeeLine Line)>();
|
||||
var percentFreightPending = new List<(int SerialNumber, ReceivableBillingPriceItem PriceItem, ChannelPriceDetailOutput Detail)>();
|
||||
@ -951,7 +987,8 @@ internal static class ParcelBillingCalculator
|
||||
cargoAttribute,
|
||||
chargeableWeight,
|
||||
warehouseId,
|
||||
warehouseName);
|
||||
warehouseName,
|
||||
remoteZoneKeys);
|
||||
if (detail == null)
|
||||
{
|
||||
continue;
|
||||
@ -1084,7 +1121,8 @@ internal static class ParcelBillingCalculator
|
||||
match.CargoAttribute,
|
||||
match.ChargeableWeight,
|
||||
match.WarehouseId,
|
||||
match.WarehouseName);
|
||||
match.WarehouseName,
|
||||
match.RemoteZoneKeys);
|
||||
if (detail == null)
|
||||
{
|
||||
continue;
|
||||
@ -1269,6 +1307,7 @@ internal static class ParcelBillingCalculator
|
||||
{
|
||||
var factors = ParseFactorDictionary(candidate.FactorValuesJson);
|
||||
if (!ZoneMatches(factors, match.ZoneKeys)
|
||||
|| !RemoteZoneMatches(factors, match.RemoteZoneKeys)
|
||||
|| !MatchCargoTypeFactor(factors, match.CargoAttribute)
|
||||
|| !MatchWarehouseFactor(factors, match.WarehouseId, match.WarehouseName))
|
||||
{
|
||||
@ -1343,7 +1382,8 @@ internal static class ParcelBillingCalculator
|
||||
string cargoAttribute,
|
||||
decimal chargeableWeight,
|
||||
int? warehouseId = null,
|
||||
string? warehouseName = null)
|
||||
string? warehouseName = null,
|
||||
IReadOnlyList<string>? remoteZoneKeys = null)
|
||||
{
|
||||
ChannelPriceDetailOutput? bestMatch = null;
|
||||
var bestIntervalWidth = decimal.MaxValue;
|
||||
@ -1356,6 +1396,11 @@ internal static class ParcelBillingCalculator
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!RemoteZoneMatches(factors, remoteZoneKeys))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!MatchCargoTypeFactor(factors, cargoAttribute))
|
||||
{
|
||||
continue;
|
||||
@ -1446,6 +1491,24 @@ internal static class ParcelBillingCalculator
|
||||
return zoneKeys.Any(key => string.Equals(key.Trim(), expected, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool RemoteZoneMatches(Dictionary<string, string> factors, IReadOnlyList<string>? remoteZoneKeys)
|
||||
{
|
||||
if (!TryGetRemoteZoneFactorText(factors, out var remoteZoneValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (remoteZoneKeys == null || remoteZoneKeys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var expected = remoteZoneValue.Trim();
|
||||
return remoteZoneKeys.Any(key =>
|
||||
!string.IsNullOrWhiteSpace(key)
|
||||
&& string.Equals(key.Trim(), expected, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool TryGetZoneFactorText(Dictionary<string, string> factors, out string zoneValue)
|
||||
{
|
||||
if (TryGetFactorValue(factors, out zoneValue, "分区", "价格分区", "zone"))
|
||||
@ -1457,6 +1520,17 @@ internal static class ParcelBillingCalculator
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetRemoteZoneFactorText(Dictionary<string, string> factors, out string remoteZoneValue)
|
||||
{
|
||||
if (TryGetFactorValue(factors, out remoteZoneValue, "偏远分区", "remoteZone"))
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(remoteZoneValue);
|
||||
}
|
||||
|
||||
remoteZoneValue = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetWeightFactorText(Dictionary<string, string> factors, out string weightText) =>
|
||||
TryGetFactorValue(factors, out weightText, "重量", "weight") && !string.IsNullOrWhiteSpace(weightText);
|
||||
|
||||
@ -1810,6 +1884,7 @@ internal static class ParcelBillingCalculator
|
||||
continue;
|
||||
}
|
||||
|
||||
// 续重邻档总价查找不按偏远分区过滤:邻档通常与运费同组,偏远费单独成项。
|
||||
if (NormalizeCalculationFormula(candidate.CalculationFormula) != "total")
|
||||
{
|
||||
continue;
|
||||
@ -1933,6 +2008,12 @@ internal static class ParcelBillingCalculator
|
||||
return null;
|
||||
}
|
||||
|
||||
// 「全部客户」优先:任意价区键共用这一列
|
||||
if (TryGetMapPrice(map, out var allPrice, "全部客户", "all"))
|
||||
{
|
||||
return allPrice;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(priceTierKey)
|
||||
&& map.TryGetValue(priceTierKey.Trim(), out var tierPrice))
|
||||
{
|
||||
@ -1947,6 +2028,32 @@ internal static class ParcelBillingCalculator
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool TryGetMapPrice(
|
||||
Dictionary<string, decimal> map,
|
||||
out decimal price,
|
||||
params string[] keys)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
if (map.TryGetValue(key, out price))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var pair in map)
|
||||
{
|
||||
if (string.Equals(pair.Key, key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
price = pair.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
price = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool EvaluateCondition(string condition, decimal gw, decimal vw)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(condition))
|
||||
|
||||
@ -557,5 +557,63 @@ public class ParcelBillingCalculatorTests
|
||||
fees[0].Amount.Should().Be(12M);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateReceivableFees_AllCustomerColumn_ShouldApplyToAnyPriceTierKey()
|
||||
{
|
||||
var snapshot = ParcelBillingTestData.BuildReceivableSnapshot(
|
||||
ParcelBillingTestData.ReceivablePriceItem(
|
||||
1,
|
||||
"freight",
|
||||
ParcelBillingTestData.AllCustomerTierDetail(Zone, 33M)));
|
||||
|
||||
var fees = ParcelBillingCalculator.CalculateReceivableFees(
|
||||
snapshot,
|
||||
_meta.PriceVersion,
|
||||
_meta.EffectiveTime,
|
||||
1.2M,
|
||||
new[] { Zone },
|
||||
"general",
|
||||
"VIP5",
|
||||
OrderFeeChargingStageCodes.Forecast);
|
||||
|
||||
fees.Should().ContainSingle();
|
||||
fees[0].Amount.Should().Be(33M);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CalculateReceivableFees_RemoteZoneFactor_ShouldRequireMatchingRemoteKeys()
|
||||
{
|
||||
var snapshot = ParcelBillingTestData.BuildReceivableSnapshot(
|
||||
ParcelBillingTestData.ReceivablePriceItem(
|
||||
1,
|
||||
"remoteFee",
|
||||
ParcelBillingTestData.RemoteZoneFixedDetail(15M, Zone, "88")));
|
||||
|
||||
var miss = ParcelBillingCalculator.CalculateReceivableFees(
|
||||
snapshot,
|
||||
_meta.PriceVersion,
|
||||
_meta.EffectiveTime,
|
||||
1.2M,
|
||||
new[] { Zone },
|
||||
"general",
|
||||
null,
|
||||
OrderFeeChargingStageCodes.Forecast,
|
||||
remoteZoneKeys: Array.Empty<string>());
|
||||
miss.Should().BeEmpty();
|
||||
|
||||
var hit = ParcelBillingCalculator.CalculateReceivableFees(
|
||||
snapshot,
|
||||
_meta.PriceVersion,
|
||||
_meta.EffectiveTime,
|
||||
1.2M,
|
||||
new[] { Zone },
|
||||
"general",
|
||||
null,
|
||||
OrderFeeChargingStageCodes.Forecast,
|
||||
remoteZoneKeys: ParcelBillingCalculator.BuildRemoteZoneKeys(88));
|
||||
hit.Should().ContainSingle();
|
||||
hit[0].Amount.Should().Be(15M);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -150,6 +150,37 @@ internal static class ParcelBillingTestData
|
||||
CustomerGroupValues = new Dictionary<string, decimal> { ["普通客户"] = normalCustomerPrice }
|
||||
};
|
||||
|
||||
public static ChannelPriceDetailOutput AllCustomerTierDetail(
|
||||
string zone,
|
||||
decimal allCustomerPrice,
|
||||
string? weightInterval = null) =>
|
||||
new()
|
||||
{
|
||||
CalculationFormula = "total",
|
||||
CalculatedValue = 0,
|
||||
FactorValuesJson = new Dictionary<string, string>
|
||||
{
|
||||
["价格分区"] = zone,
|
||||
["重量"] = weightInterval ?? "999"
|
||||
},
|
||||
CustomerGroupValues = new Dictionary<string, decimal> { ["全部客户"] = allCustomerPrice }
|
||||
};
|
||||
|
||||
public static ChannelPriceDetailOutput RemoteZoneFixedDetail(
|
||||
decimal amount,
|
||||
string priceZone,
|
||||
string remoteZoneId) =>
|
||||
new()
|
||||
{
|
||||
CalculationFormula = "固定价",
|
||||
CalculatedValue = amount,
|
||||
FactorValuesJson = new Dictionary<string, string>
|
||||
{
|
||||
["价格分区"] = priceZone,
|
||||
["偏远分区"] = remoteZoneId
|
||||
}
|
||||
};
|
||||
|
||||
public static WeightRuleInfo GwRule(string condition = "gw>=vw", string factor = "6000") =>
|
||||
new()
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user