Functional Weave
Code in TypeScript

finance.early-payment-discount@1.0.0

impl/rust.rs

2,527 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::dates_add_days::add_days;  ← from dates.add-days ^1.0.0 · built alongside by fune
use super::finance_payment_terms_due_date::{payment_due_date, payment_terms_from_value, PaymentTerms};  ← from finance.payment-terms-due-date ^1.0.0 · built alongside by fune
use super::money_add::subtract_money;  ← from money.add ^1.0.0 · built alongside by fune
use super::money_amount::{money, money_from_value, money_to_value, Money};  ← from money.amount ^1.0.0 · built alongside by fune
use super::money_apply_rate::apply_rate;  ← from money.apply-rate ^1.0.0 · built alongside by fune

/// The settlement discount open on `payment_date`, and what is left to pay.
///
/// The window is inclusive and counted in calendar days from the invoice date,
/// so under 2/10 the tenth day after the invoice still earns the discount.
///
/// # Panics
/// Panics on a negative total, malformed dates or invalid terms.
pub fn early_payment_discount(
    invoice_total: &Money,
    invoice_date: &str,
    terms: &PaymentTerms,
    payment_date: &str,
) -> EarlyPaymentDiscount {
    if invoice_total.minor < 0 {
        panic!("invoice total must not be negative");
    }
    // Validates the terms, including a discount window that outlasts the due date.
    let due_date = payment_due_date(invoice_date, terms);
    // Validates the payment date; ISO dates then compare as strings.
    add_days(payment_date, 0);

    let deadline = if terms.discount_basis_points > 0 {
        Some(add_days(invoice_date, terms.discount_days))
    } else {
        None
    };
    let available = match &deadline {
        Some(last) => payment_date <= last.as_str(),
        None => false,
    };
    let discount = if available {
        apply_rate(invoice_total, terms.discount_basis_points, "half-up")
    } else {
        money(0, &invoice_total.currency)
    };
    EarlyPaymentDiscount {
        available,
        amount_to_pay: subtract_money(invoice_total, &discount),
        discount,
        discount_deadline: deadline,
        due_date,
    }
}

pub fn early_payment_discount_to_value(d: &EarlyPaymentDiscount) -> Value {
    Value::obj(vec![
        ("available", Value::Bool(d.available)),
        ("discount", money_to_value(&d.discount)),
        ("amountToPay", money_to_value(&d.amount_to_pay)),
        (
            "discountDeadline",
            match &d.discount_deadline {
                Some(date) => Value::str(date),
                None => Value::Null,
            },
        ),
        ("dueDate", Value::str(&d.due_date)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    early_payment_discount_to_value(&early_payment_discount(
        &money_from_value(&args[0]),
        args[1].as_str(),
        &payment_terms_from_value(&args[2]),
        args[3].as_str(),
    ))
}