from typing import List from .money_amount import money from .payroll_gross_to_net_types import Payslip, PayslipInput from .payroll_income_tax import income_tax from .payroll_national_insurance import national_insurance from .payroll_pension_auto_enrolment import pension_contributions from .payroll_student_loan import student_loan from .payroll_tax_period import tax_period def gross_to_net(input: PayslipInput) -> Payslip: """One period's payslip, in the order a UK payroll works it out. The order matters because each deduction is taken from a different figure: a net pay arrangement pension comes off pay before tax but not before National Insurance; tax is on taxable pay to date; NI and student loans are on this period's gross pay alone; relief at source pension comes off net pay. """ gross = input.gross if gross.currency != "GBP": raise ValueError("payroll amounts must be in GBP, received %s for gross" % (gross.currency,)) when = tax_period(input.pay_date, input.frequency) pension_employee = 0 pension_employer = 0 before_tax = 0 if input.pension is not None: pension = pension_contributions(gross, input.frequency, input.pension, input.pay_date) pension_employee = pension.employee_deduction.minor pension_employer = pension.employer.minor if pension.deduct_before_tax: before_tax = pension.employee_deduction.minor taxable_pay = money(gross.minor - before_tax, "GBP") tax = income_tax(input.tax_code, input.frequency, when.period, taxable_pay, input.previous_taxable_pay_to_date, input.previous_tax_to_date, input.pay_date) ni = national_insurance(gross, input.ni_category, input.frequency, input.pay_date, None) # An employee with more than one undergraduate plan repays once, against the # lowest threshold, which is the largest of the per-plan deductions; a # postgraduate loan is repaid alongside it. undergraduate = 0 postgraduate = 0 seen: List[str] = [] for plan in input.student_loan_plans: if plan in seen: raise ValueError("student loan plan %s is listed twice" % (plan,)) seen.append(plan) deduction = student_loan(gross, plan, input.frequency, input.pay_date).minor if plan == "postgraduate": postgraduate = deduction elif deduction > undergraduate: undergraduate = deduction net = gross.minor - pension_employee - tax.tax.minor - ni.employee.minor - undergraduate - postgraduate return Payslip( gross=gross, pension_employee=money(pension_employee, "GBP"), pension_employer=money(pension_employer, "GBP"), taxable_pay=taxable_pay, tax=tax.tax, employee_ni=ni.employee, employer_ni=ni.employer, student_loan=money(undergraduate, "GBP"), postgraduate_loan=money(postgraduate, "GBP"), net=money(net, "GBP"), taxable_pay_to_date=tax.pay_to_date, tax_to_date=tax.tax_to_date, period=when.period, tax_year=when.tax_year, )