from typing import Optional from .construction_day_rate_quote_types import DayRateQuote from .finance_markup_price import markup_price from .finance_tax_add_vat import add_vat from .math_round_div import round_div from .money_add import add_money from .money_amount import Money, money MAX_SAFE = 9007199254740991 def day_rate_quote( labour_days_hundredths: int, day_rate: Money, materials_cost: Money, materials_markup_basis_points: int, vat_category: Optional[str], on_date: str, ) -> DayRateQuote: """A quote from labour days, a day rate, marked-up materials and VAT. The markup goes on the materials only: the day rate already is the price of labour. Labour is rounded once, to the penny, from days x rate; VAT is one rounding on the net total, as on the invoice that follows. """ if ( isinstance(labour_days_hundredths, bool) or not isinstance(labour_days_hundredths, int) or labour_days_hundredths < 0 ): raise ValueError( "labourDaysHundredths must be a whole number of hundredths, not negative, received %r" % (labour_days_hundredths,) ) if day_rate.minor < 0: raise ValueError("dayRate must not be negative, received %d" % (day_rate.minor,)) product = day_rate.minor * labour_days_hundredths # Python would carry on, but TypeScript cannot, and all three must agree. if product > MAX_SAFE: raise ValueError("the labour calculation exceeds 2^53 - 1") labour = money(round_div(product, 100, "half-up"), day_rate.currency) materials = markup_price(materials_cost, materials_markup_basis_points, "markup", "half-up") net = add_money(labour, materials) if vat_category is None: return DayRateQuote(labour=labour, materials=materials, net=net, vat=money(0, net.currency), gross=net, vat_basis_points=0) vat = add_vat(net, "GB", vat_category, on_date) return DayRateQuote( labour=labour, materials=materials, net=net, vat=vat.tax, gross=vat.gross, vat_basis_points=vat.basis_points )