hospitality.gross-profit-menu@1.0.0
impl/rust/menu_gross_profit.rs
2,292 bytes · the Rust implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::finance_margin::margin; ← from finance.margin ^1.0.0 · built alongside by fune
use super::finance_tax_remove_vat::remove_vat; ← from finance.tax.remove-vat ^1.0.0 · built alongside by fune
use super::money_amount::{money_from_value, money_to_value, Money}; ← from money.amount ^1.0.0 · built alongside by fune
/// Gross profit on a menu price.
///
/// Hospitality GP is worked out on the price without VAT, because the VAT is
/// the tax authority's money, not the kitchen's: a 14.50 dish costing 3.50 at
/// 20% VAT is a 71.03% GP, not the 75.86% the VAT-inclusive price suggests.
///
/// # Panics
/// Panics on a negative cost or price, mixed currencies, or no VAT rule.
pub fn menu_gross_profit(cost: &Money, menu_price: &Money, jurisdiction: &str, category: &str, on_date: &str) -> MenuGrossProfit {
if cost.minor < 0 {
panic!("cost must not be negative, received {}", cost.minor);
}
if menu_price.minor < 0 {
panic!("menuPrice must not be negative, received {}", menu_price.minor);
}
let vat = remove_vat(menu_price, jurisdiction, category, on_date);
let breakdown = margin(cost, &vat.net);
let gp = breakdown.margin_basis_points;
MenuGrossProfit {
menu_price: menu_price.clone(),
vat: vat.tax.clone(),
net_price: vat.net.clone(),
cost: cost.clone(),
gross_profit: breakdown.profit,
gp_basis_points: gp,
cost_basis_points: gp.map(|g| 10000 - g),
vat_rate_basis_points: vat.basis_points,
}
}
fn opt_int(v: Option<i64>) -> Value {
match v {
Some(i) => Value::Int(i),
None => Value::Null,
}
}
pub fn menu_gross_profit_to_value(g: &MenuGrossProfit) -> Value {
Value::obj(vec![
("menuPrice", money_to_value(&g.menu_price)),
("vat", money_to_value(&g.vat)),
("netPrice", money_to_value(&g.net_price)),
("cost", money_to_value(&g.cost)),
("grossProfit", money_to_value(&g.gross_profit)),
("gpBasisPoints", opt_int(g.gp_basis_points)),
("costBasisPoints", opt_int(g.cost_basis_points)),
("vatRateBasisPoints", Value::Int(g.vat_rate_basis_points)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
menu_gross_profit_to_value(&menu_gross_profit(
&money_from_value(&args[0]),
&money_from_value(&args[1]),
args[2].as_str(),
args[3].as_str(),
args[4].as_str(),
))
}