use super::funejson::Value; use super::dates_days_between::days_between; use super::math_round_div::round_div; use super::money_add::add_money; use super::money_amount::{assert_same_currency, money, money_from_value, money_to_value, Money}; /// Stripe-style proration for a mid-cycle plan change, by whole days: a credit /// for the unused remainder of the old plan and a charge for the same /// remainder on the new plan, each rounded once from the exact fraction. /// /// # Panics /// Panics if the period is empty or reversed, the change date is outside it, /// the prices are in different currencies, or the mode is unknown. pub fn prorate_change( old_price: &Money, new_price: &Money, period_start: &str, period_end: &str, change_date: &str, mode: &str, ) -> PlanChangeProration { let total_days = days_between(period_start, period_end); if total_days <= 0 { panic!( "period end must be after period start, received {} to {}", period_start, period_end ); } let elapsed = days_between(period_start, change_date); if elapsed < 0 || elapsed > total_days { panic!( "change date must be within the billing period, received {} for {} to {}", change_date, period_start, period_end ); } assert_same_currency(old_price, new_price); let remaining_days = total_days - elapsed; let credit = money( -round_div(old_price.minor * remaining_days, total_days, mode), &old_price.currency, ); let charge = money( round_div(new_price.minor * remaining_days, total_days, mode), &new_price.currency, ); let net = add_money(&charge, &credit); PlanChangeProration { total_days, remaining_days, credit, charge, net, } } pub fn plan_change_proration_to_value(p: &PlanChangeProration) -> Value { Value::obj(vec![ ("totalDays", Value::Int(p.total_days)), ("remainingDays", Value::Int(p.remaining_days)), ("credit", money_to_value(&p.credit)), ("charge", money_to_value(&p.charge)), ("net", money_to_value(&p.net)), ]) } pub fn fune_vector(args: &[Value]) -> Value { plan_change_proration_to_value(&prorate_change( &money_from_value(&args[0]), &money_from_value(&args[1]), args[2].as_str(), args[3].as_str(), args[4].as_str(), args[5].as_str(), )) }