from .charity_fundraising_ratio_types import FundraisingRatios from .math_round_div import round_div from .money_amount import Money, money from .money_currency_digits import currency_digits 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 fundraising_ratios( fundraising_costs: Money, funds_raised: Money, charitable_spend: Money, total_expenditure: Money, ) -> FundraisingRatios: """Cost to raise £1 and the share of spending that went on the charity's purposes. Each ratio is divided once from the integer amounts and rounded half up. """ currency = fundraising_costs.currency _check("fundraisingCosts", currency, fundraising_costs) _check("fundsRaised", currency, funds_raised) _check("charitableSpend", currency, charitable_spend) _check("totalExpenditure", currency, total_expenditure) if funds_raised.minor == 0: raise ValueError("fundsRaised must be positive to work out a cost to raise") if total_expenditure.minor == 0: raise ValueError("totalExpenditure must be positive to work out a charitable spend ratio") if charitable_spend.minor > total_expenditure.minor: raise ValueError( "charitableSpend of %d is more than totalExpenditure of %d" % (charitable_spend.minor, total_expenditure.minor) ) unit = 10 ** currency_digits(currency) return FundraisingRatios( cost_to_raise_one=money(round_div(fundraising_costs.minor * unit, funds_raised.minor, "half-up"), currency), cost_ratio_basis_points=round_div(fundraising_costs.minor * 10000, funds_raised.minor, "half-up"), charitable_spend_basis_points=round_div(charitable_spend.minor * 10000, total_expenditure.minor, "half-up"), )