Functional Weave
Code in Python

charity.donation-matching@1.0.0

impl/python.py

2,172 bytes · the Python 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.

from typing import Optional

from .charity_donation_matching_types import DonationMatch, MatchLimit
from .money_amount import Money, money  ← from money.amount ^1.0.0 · built alongside by fune
from .money_apply_rate import apply_rate  ← from money.apply-rate ^1.0.0 · built alongside by fune


def _check(name: str, currency: str, amount: Money) -> None:
    if amount.currency != currency:
        raise ValueError("currency mismatch: %s and %s" % (amount.currency, currency))
    if amount.minor < 0:
        raise ValueError("%s must not be negative, received %d" % (name, amount.minor))


def donation_match(
    donation: Money,
    ratio_basis_points: int,
    donor_cap: Optional[Money],
    donor_matched_so_far: Money,
    programme_cap: Optional[Money],
    programme_matched_so_far: Money,
) -> DonationMatch:
    """An employer's matched-giving contribution. The ratio is applied first
    and rounded down; then the match is held to whatever room is left under
    the donor's cap and the programme's budget, never below zero.
    """
    currency = donation.currency
    _check("donation", currency, donation)
    if isinstance(ratio_basis_points, bool) or not isinstance(ratio_basis_points, int) or ratio_basis_points < 0:
        raise ValueError("ratioBasisPoints must be a non-negative integer, received %s" % (ratio_basis_points,))
    if donor_cap is not None:
        _check("donorCap", currency, donor_cap)
    _check("donorMatchedSoFar", currency, donor_matched_so_far)
    if programme_cap is not None:
        _check("programmeCap", currency, programme_cap)
    _check("programmeMatchedSoFar", currency, programme_matched_so_far)

    uncapped = apply_rate(donation, ratio_basis_points, "down").minor
    matched = uncapped
    capped_by: MatchLimit = "none"
    if donor_cap is not None:
        room = max(0, donor_cap.minor - donor_matched_so_far.minor)
        if room < matched:
            matched = room
            capped_by = "donor"
    if programme_cap is not None:
        room = max(0, programme_cap.minor - programme_matched_so_far.minor)
        if room < matched:
            matched = room
            capped_by = "programme"
    return DonationMatch(matched=money(matched, currency), uncapped=money(uncapped, currency), capped_by=capped_by)