Functional Weave
Code in TypeScript

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(),
    ))
}