use super::funejson::Value; use super::finance_markup_price::markup_price; use super::finance_tax_add_vat::add_vat; use super::hospitality_gross_profit_menu_menu_gross_profit::{menu_gross_profit, menu_gross_profit_to_value}; use super::math_round_div::round_div; use super::money_amount::{money, money_from_value, Money}; /// The menu price that reaches a target GP. /// /// The net price is the smallest that makes the target on the VAT-exclusive /// price (finance.markup-price, rounded up), VAT goes on top, and the menu /// price is rounded up to the step a menu is printed in. Rounding only ever /// goes up, so the GP returned is never below the target. /// /// # Panics /// Panics on a target outside 0 to 9999, a step below 1, a negative cost, or /// no VAT rule. pub fn menu_price_for_gp( cost: &Money, target_gp_basis_points: i64, price_step: i64, jurisdiction: &str, category: &str, on_date: &str, ) -> MenuGrossProfit { if !(0..=9999).contains(&target_gp_basis_points) { panic!( "targetGpBasisPoints must be a whole number from 0 to 9999, received {}", target_gp_basis_points ); } if price_step < 1 { panic!("priceStep must be at least 1, received {}", price_step); } if cost.minor < 0 { panic!("cost must not be negative, received {}", cost.minor); } let net = markup_price(cost, target_gp_basis_points, "margin", "up"); let gross = add_vat(&net, jurisdiction, category, on_date).gross; let menu_price = money(round_div(gross.minor, price_step, "up") * price_step, &gross.currency); menu_gross_profit(cost, &menu_price, jurisdiction, category, on_date) } pub fn fune_vector(args: &[Value]) -> Value { menu_gross_profit_to_value(&menu_price_for_gp( &money_from_value(&args[0]), args[1].as_i64(), args[2].as_i64(), args[3].as_str(), args[4].as_str(), args[5].as_str(), )) }