///|
pub fn quote(
rule : BillingRule,
parcel : ParcelDim,
zone : String,
declared_value : Money,
) -> Quote {
let billable = billable_weight_g(parcel, rule.volumetric_divisor)
let volumetric = volumetric_weight_g(parcel, rule.volumetric_divisor)
let lines : Array[ChargeLine] = []
lines.push(Base(weight_g=rule.first_weight_limit_g, rule.first_weight_rate))
let extra_weight = (billable - rule.first_weight_limit_g).max(0)
let extra_units = div_ceil(extra_weight, rule.additional_weight_unit_g)
if extra_units > 0 {
lines.push(
Additional(
units=extra_units,
rule.additional_weight_rate.mul_int(extra_units),
),
)
}
let zone_rate = rule.zone_rate(zone)
let base = rule.first_weight_rate.add(
rule.additional_weight_rate.mul_int(extra_units),
)
let zoned = base.scale_bp(zone_rate.multiplier_bp)
if zone_rate.multiplier_bp != 10000 {
lines.push(
Zone(
zone=zone_rate.zone,
multiplier_bp=zone_rate.multiplier_bp,
zoned.sub(base),
),
)
}
if zone_rate.remote_fee.cents > 0 {
lines.push(Remote(zone=zone_rate.zone, zone_rate.remote_fee))
}
let mut subtotal = zoned.add(zone_rate.remote_fee)
if billable > rule.surcharge.overweight_limit_g {
subtotal = subtotal.add(rule.surcharge.overweight_fee)
lines.push(
Overweight(
limit_g=rule.surcharge.overweight_limit_g,
rule.surcharge.overweight_fee,
),
)
}
if parcel.longest_side_cm() > rule.surcharge.oversize_limit_cm {
subtotal = subtotal.add(rule.surcharge.oversize_fee)
lines.push(
Oversize(
limit_cm=rule.surcharge.oversize_limit_cm,
rule.surcharge.oversize_fee,
),
)
}
let fuel = subtotal.percent_bp(rule.surcharge.fuel_percent_bp)
if fuel.cents > 0 {
subtotal = subtotal.add(fuel)
lines.push(Fuel(percent_bp=rule.surcharge.fuel_percent_bp, fuel))
}
let insurance = declared_value.percent_bp(rule.surcharge.insurance_percent_bp)
if insurance.cents > 0 {
subtotal = subtotal.add(insurance)
lines.push(
Insurance(
percent_bp=rule.surcharge.insurance_percent_bp,
declared_value~,
insurance,
),
)
}
let total = subtotal.max(rule.min_charge)
if total.cents != subtotal.cents {
lines.push(Minimum(total.sub(subtotal)))
}
{
billable_weight_g: billable,
actual_weight_g: parcel.weight_g,
volumetric_weight_g: volumetric,
subtotal,
total,
lines,
}
}
///|
pub fn quote_manifest(
rule : BillingRule,
items : Array[ManifestItem],
) -> ManifestQuote {
let quotes : Array[Quote] = []
let mut total = Money::zero()
for item in items {
let q = quote(rule, item.parcel, item.zone, item.declared_value)
total = total.add(q.total)
quotes.push(q)
}
{ items: quotes, total }
}