we should go over them once more to make sure we didn't miss anything, but testing validation should probably be done after that. Verify that the record ordering enforcement code is correct, then start thinking of how to get data from external sources into the record generator.
348 lines
19 KiB
Python
348 lines
19 KiB
Python
import model
|
|
from fields import *
|
|
import enums
|
|
|
|
__all__ = RECORD_TYPES = ['SubmitterRecord', 'EmployerRecord',
|
|
'EmployeeWageRecord', 'OptionalEmployeeWageRecord',
|
|
'TotalRecord', 'OptionalTotalRecord',
|
|
'StateTotalRecord', 'FinalRecord',]
|
|
|
|
class SubmitterRecord(model.Model):
|
|
record_identifier = 'RA'
|
|
required = True
|
|
|
|
submitter_ein = NumericField(max_length=9)
|
|
user_id = TextField(max_length=8)
|
|
software_vendor = TextField(max_length=4, required=False)
|
|
blank1 = BlankField(max_length=5)
|
|
resub_indictator = BooleanField(required=False)
|
|
resub_identifier = TextField(max_length=6, required=False)
|
|
software_code = StaticField(value='98') # In-house program
|
|
company_name = TextField(max_length=57)
|
|
company_address = TextField(max_length=22)
|
|
company_delivery_address = TextField(max_length=22)
|
|
company_city = TextField(max_length=22)
|
|
company_state = StateField()
|
|
company_zipcode = TextField(max_length=5)
|
|
company_zipcode_ext = TextField(max_length=4, required=False)
|
|
blank2 = BlankField(max_length=5)
|
|
company_foreign_state_province= TextField(max_length=23, required=False)
|
|
company_foreign_postal_code = TextField(max_length=15, required=False)
|
|
company_country_code = TextField(max_length=2, required=False)
|
|
submitter_name = TextField(max_length=57)
|
|
submitter_address = TextField(max_length=22)
|
|
submitter_delivery_address = TextField(max_length=22)
|
|
submitter_city = TextField(max_length=22)
|
|
submitter_state = StateField()
|
|
submitter_zipcode = TextField(max_length=5)
|
|
submitter_zipcode_ext = TextField(max_length=4, required=False)
|
|
blank3 = BlankField(max_length=5)
|
|
submitter_foreign_state_province = TextField(max_length=23, required=False)
|
|
submitter_foreign_postal_code = TextField(max_length=15, required=False)
|
|
submitter_country_code = TextField(max_length=2, required=False)
|
|
contact_name = TextField(max_length=27)
|
|
contact_phone = TextField(max_length=15)
|
|
contact_phone_ext = TextField(max_length=5, required=False)
|
|
blank4 = BlankField(max_length=3)
|
|
contact_email = EmailField(max_length=40)
|
|
blank5 = BlankField(max_length=3)
|
|
contact_fax = TextField(max_length=10, required=False)
|
|
preferred_notification = TextField(max_length=1)
|
|
preparer_code = TextField(max_length=1)
|
|
blank6 = BlankField(max_length=12)
|
|
|
|
def validate_submitter_ein(self, f):
|
|
f.value = f.value.replace('-','')
|
|
excluded_values = ('07','08','09','17','18','19','28','29','49','69', '70', '78', '79', '89')
|
|
if f.value[0:2] in excluded_values:
|
|
raise ValidationError("%s not one of %s" % (f.value, excluded_values), field=f)
|
|
try:
|
|
int(f.value)
|
|
except ValueError:
|
|
raise ValidationError("%s must be numeric values only" % f.value, field=f)
|
|
|
|
def validate_resub_identifier(self, f):
|
|
if self.resub_indicator.value == True:
|
|
if not f.value:
|
|
raise ValidationError("resub_identifier must be set because resub_indicator is True", field=f)
|
|
|
|
def validate_preferred_notification(self, f):
|
|
if self.preferred_notification.value == '1':
|
|
if not self.contact_email.value:
|
|
raise ValidationError("contact_email must be set if preferred notification method is email (1)", field=f)
|
|
|
|
def validate_preparer_code(self, f):
|
|
valid_options = ('A','L','S','P','O')
|
|
if self.preparer_code.value.upper() not in valid_options:
|
|
raise ValidationError("preparer_code %s not one of %s" % (self.preparer_code.value, valid_options), field=f)
|
|
|
|
|
|
|
|
class EmployerRecord(model.Model):
|
|
record_identifier = 'RE'
|
|
required = True
|
|
|
|
tax_year = NumericField(max_length=4)
|
|
agent_indicator = NumericField(max_length=1, required=False)
|
|
employer_ein = TextField(max_length=9)
|
|
agent_for_ein = TextField(max_length=9, required=False)
|
|
terminating_business_indicator = BooleanField()
|
|
establishment_number = TextField(max_length=4, required=False)
|
|
other_ein = TextField(max_length=9, required=False)
|
|
employer_name = TextField(max_length=57)
|
|
location_address = TextField(max_length=22)
|
|
delivery_address = TextField(max_length=22)
|
|
city = TextField(max_length=22)
|
|
state = StateField()
|
|
zipcode = TextField(max_length=5)
|
|
zipcode_ext = TextField(max_length=4, required=False)
|
|
kind_of_employer = TextField(max_length=1)
|
|
blank1 = BlankField(max_length=4)
|
|
foreign_state_province = TextField(max_length=23)
|
|
foreign_postal_code = TextField(max_length=15)
|
|
country_code = TextField(max_length=2, required=False)
|
|
employment_code = TextField(max_length=1)
|
|
tax_jurisdiction_code = TextField(max_length=1, required=False)
|
|
third_party_sick_pay = BooleanField()
|
|
blank2 = BlankField(max_length=291)
|
|
|
|
def validate_agent_indicator(self, f):
|
|
v = f.value
|
|
if v and v not in (1,2,3):
|
|
raise ValidationError("%s not in one of (1,2,3)" % v, field=f)
|
|
|
|
def validate_employer_ein(self, f):
|
|
excluded_values = ('00','07','08','09','17','18','19','28','29','49','69','70','78','79','89')
|
|
if f.value[0:2] in excluded_values:
|
|
raise ValidationError("%s not one of %s" % (f.value, excluded_values), field=f)
|
|
|
|
def validate_agent_for_ein(self, f):
|
|
if self.agent_indicator.value == 1 and not f.value:
|
|
raise ValidationError("agent_for_ein must be provided with agent_indicator=1", field=f)
|
|
|
|
def validate_kind_of_employer(self, f):
|
|
choices = [k for k,v in enums.employer_types]
|
|
if f.value.upper() not in choices:
|
|
raise ValidationError("%s not in one of %s" % (f.value, choices), field=f)
|
|
|
|
def validate_employment_code(self, f):
|
|
choices = [k for k,v in enums.employment_codes]
|
|
if f.value.upper() not in choices:
|
|
raise ValidationError("%s not in one of %s" % (f.value, choices), field=f)
|
|
|
|
def validate_tax_jurisdiction_code(self, f):
|
|
choices = [k for k,v in enums.tax_jurisdiction_codes]
|
|
if f.value and f.value.upper() not in choices:
|
|
raise ValidationError("%s not in one of %s" % (f.value, choices), field=f)
|
|
|
|
|
|
class EmployeeWageRecord(model.Model):
|
|
record_identifier = 'RW'
|
|
required = True
|
|
|
|
ssn = NumericField(max_length=9, required=False)
|
|
employee_first_name = TextField(max_length=15)
|
|
employee_middle_name = TextField(max_length=15)
|
|
employee_last_name = TextField(max_length=20)
|
|
employee_suffix = TextField(max_length=4, required=False)
|
|
location_address = TextField(max_length=22)
|
|
delivery_address = TextField(max_length=22)
|
|
city = TextField(max_length=22)
|
|
state = StateField()
|
|
zipcode = TextField(max_length=5, required=False)
|
|
zipcode_ext = TextField(max_length=4, required=False)
|
|
blank1 = BlankField(max_length=5)
|
|
foreign_state = TextField(max_length=23, required=False)
|
|
foreign_postal_code = TextField(max_length=15, required=False)
|
|
country = TextField(max_length=2)
|
|
wages_tips = MoneyField(max_length=11)
|
|
federal_income_tax_withheld = MoneyField(max_length=11)
|
|
social_security_wages = MoneyField(max_length=11)
|
|
social_security_tax_withheld = MoneyField(max_length=11)
|
|
medicare_wages_and_tips = MoneyField(max_length=11)
|
|
medicare_tax_withheld = MoneyField(max_length=11)
|
|
social_security_tips = MoneyField(max_length=11)
|
|
advance_eic = MoneyField(max_length=11)
|
|
dependent_care_benefits = MoneyField(max_length=11)
|
|
deferred_compensation_401k = MoneyField(max_length=11)
|
|
deferred_compensation_403b = MoneyField(max_length=11)
|
|
deferred_compensation_408k = MoneyField(max_length=11)
|
|
deferred_compensation_457b = MoneyField(max_length=11)
|
|
deferred_compensation_501c = MoneyField(max_length=11)
|
|
military_pay = MoneyField(max_length=11)
|
|
non_qualified_457 = MoneyField(max_length=11)
|
|
employer_contrib_to_hsa = MoneyField(max_length=11)
|
|
non_qualified_not_457 = MoneyField(max_length=11)
|
|
nontaxable_combat_pay = MoneyField(max_length=11)
|
|
blank2 = BlankField(max_length=11)
|
|
cost_of_premiums_for_insurance = MoneyField(max_length=11)
|
|
income_nonstatutory_stock_opts = MoneyField(max_length=11)
|
|
deferred_compensation_409a = MoneyField(max_length=11)
|
|
designated_roth_contrib_401k = MoneyField(max_length=11)
|
|
designated_roth_contrib_403b = MoneyField(max_length=11)
|
|
blank3 = BlankField(max_length=23)
|
|
statutory_employee_indicator = BooleanField()
|
|
blank4 = BlankField(max_length=1)
|
|
retirement_plan_indicator = BooleanField()
|
|
third_party_sick_pay = BooleanField()
|
|
blank5 = BlankField(max_length=23)
|
|
|
|
def validate_ssn(self, f):
|
|
if str(f.value).startswith('666','9'):
|
|
raise ValidationError("ssn cannot start with 666 or 9", field=f)
|
|
|
|
|
|
|
|
class OptionalEmployeeWageRecord(model.Model):
|
|
record_identifier = 'RO'
|
|
required = False
|
|
|
|
blank1 = BlankField(max_length=9)
|
|
allocated_tips = MoneyField(max_length=11)
|
|
uncollected_tax_on_tips = MoneyField(max_length=11)
|
|
medical_savings_account = MoneyField(max_length=11)
|
|
simple_retirement_account = MoneyField(max_length=11)
|
|
qualified_adoption_expenses = MoneyField(max_length=11)
|
|
uncollected_ss_life_ins = MoneyField(max_length=11)
|
|
uncollected_medicare_life_ins = MoneyField(max_length=11)
|
|
income_under_409a = MoneyField(max_length=11)
|
|
hire_exempt_wages_and_tips = MoneyField(max_length=11)
|
|
blank2 = BlankField(max_length=164)
|
|
wages_subject_to_puerto_rico_tax = MoneyField(max_length=11, required=False)
|
|
commissions_subject_to_puerto_rico_tax = MoneyField(max_length=11, required=False)
|
|
allowances_subject_to_puerto_rico_tax = MoneyField(max_length=11, required=False)
|
|
tips_subject_to_puerto_rico_tax = MoneyField(max_length=11, required=False)
|
|
total_wages_subject_to_puerto_rico_tax = MoneyField(max_length=11, required=False)
|
|
puerto_rico_tax_withheld = MoneyField(max_length=11, required=False)
|
|
retirement_fund_contrib = MoneyField(max_length=11, required=False)
|
|
blank3 = BlankField(max_length=11)
|
|
total_wages_tips_virgin_islands = MoneyField(max_length=11, required=False)
|
|
virgin_islands_income_tax_withheld = MoneyField(max_length=11, required=False)
|
|
blank4 = BlankField(max_length=128)
|
|
|
|
|
|
|
|
|
|
class StateWageRecord(model.Model):
|
|
record_identifier = 'RS'
|
|
required = False
|
|
|
|
state_code = StateField(use_numeric=True)
|
|
taxing_entity_code = TextField(max_length=5, required=False)
|
|
ssn = NumericField(max_length=9, required=False)
|
|
employee_first_name = TextField(max_length=15)
|
|
employee_middle_name = TextField(max_length=15)
|
|
employee_last_name = TextField(max_length=20)
|
|
employee_suffix = TextField(max_length=4, required=False)
|
|
location_address = TextField(max_length=22)
|
|
delivery_address = TextField(max_length=22)
|
|
city = TextField(max_length=22)
|
|
state = StateField()
|
|
zipcode = TextField(max_length=5, required=False)
|
|
zipcode_ext = TextField(max_length=4, required=False)
|
|
blank1 = BlankField(max_length=5)
|
|
foreign_state_province = TextField(max_length=23, required=False)
|
|
foreign_postal_code = TextField(max_length=15, required=False)
|
|
country_code = TextField(max_length=2, required=False)
|
|
optional_code = TextField(max_length=2, required=False)
|
|
reporting_period = MonthYearField()
|
|
quarterly_unemp_ins_wages = MoneyField(max_length=11)
|
|
quarterly_unemp_ins_taxable_wages = MoneyField(max_length=11)
|
|
number_of_weeks_worked = NumericField(max_length=2)
|
|
date_first_employed = DateField(required=False)
|
|
date_of_separation = DateField(required=False)
|
|
blank2 = BlankField(max_length=5)
|
|
state_employer_account_num = NumericField(max_length=20)
|
|
blank3 = BlankField(max_length=6)
|
|
state_code_2 = StateField(use_numeric=True)
|
|
state_taxable_wages = MoneyField(max_length=11)
|
|
state_income_tax_wh = MoneyField(max_length=11)
|
|
other_state_data = TextField(max_length=10, required=False)
|
|
tax_type_code = TextField(max_length=1) # VALIDATE C, D, E, or F
|
|
local_taxable_wages = MoneyField(max_length=11)
|
|
local_income_tax_wh = MoneyField(max_length=11)
|
|
state_control_number = NumericField(max_length=7, required=False)
|
|
supplemental_data1 = TextField(max_length=75, required=False)
|
|
supplemental_data2 = TextField(max_length=75, required=False)
|
|
blank4 = BlankField(max_length=25)
|
|
|
|
def validate_tax_type_code(self, field):
|
|
choices [x for x,y in enums.tax_type_codes]
|
|
if field.value.upper() not in choices
|
|
raise ValidationError("%s not one of %s" % (field.value,choices), field=f)
|
|
|
|
|
|
class TotalRecord(model.Model):
|
|
record_identifier = 'RT'
|
|
required = True
|
|
|
|
number_of_rw_records = NumericField(max_length=7)
|
|
wages_tips = NumericField(max_length=15)
|
|
federal_income_tax_withheld = NumericField(max_length=15)
|
|
social_security_wages = NumericField(max_length=15)
|
|
social_security_tax_withheld = NumericField(max_length=15)
|
|
medicare_wages_and_tips = NumericField(max_length=15)
|
|
medicare_tax_withheld = NumericField(max_length=15)
|
|
social_security_tips = NumericField(max_length=15)
|
|
advance_eic = NumericField(max_length=15)
|
|
dependent_care_benefits = NumericField(max_length=15)
|
|
deferred_compensation_401k = NumericField(max_length=15)
|
|
deferred_compensation_403b = NumericField(max_length=15)
|
|
deferred_compensation_408k = NumericField(max_length=15)
|
|
deferred_compensation_457b = NumericField(max_length=15)
|
|
deferred_compensation_501c = NumericField(max_length=15)
|
|
military_pay = NumericField(max_length=15)
|
|
non_qualified_457 = NumericField(max_length=15)
|
|
employer_contrib_to_hsa = NumericField(max_length=15)
|
|
non_qualified_not_457 = NumericField(max_length=15)
|
|
nontaxable_combat_pay = NumericField(max_length=15)
|
|
blank1 = BlankField(max_length=15)
|
|
employer_cost_term_life_ins = NumericField(max_length=15)
|
|
income_tax_wh_sick_pay = NumericField(max_length=15)
|
|
income_exercise_nonstat_stock_opts = NumericField(max_length=15)
|
|
deferred_409a_compensation_plan = NumericField(max_length=15)
|
|
designated_roth_contribs_401k = NumericField(max_length=15)
|
|
disignated_roth_contribs_403b = NumericField(max_length=15)
|
|
blank2 = BlankField(max_length=113)
|
|
|
|
|
|
class OptionalTotalRecord(model.Model):
|
|
record_identifier = 'RU'
|
|
required = False
|
|
|
|
number_of_ro_records = NumericField(max_length=7)
|
|
allocated_tips = NumericField(max_length=15)
|
|
uncollected_tax_on_tips = NumericField(max_length=15)
|
|
medical_savings_account = NumericField(max_length=15)
|
|
simple_retirement_account = NumericField(max_length=15)
|
|
qualified_adoption_expenses = NumericField(max_length=15)
|
|
uncollected_ss_life_ins = NumericField(max_length=15)
|
|
uncollected_medicare_life_ins = NumericField(max_length=15)
|
|
income_under_409a = NumericField(max_length=15)
|
|
hire_exempt_wages_and_tips = NumericField(max_length=15)
|
|
blank1 = BlankField(max_length=210)
|
|
wages_subject_to_puerto_rico_tax = NumericField(max_length=15, required=False)
|
|
commissions_subject_to_puerto_rico_tax = NumericField(max_length=15, required=False)
|
|
allowances_subject_to_puerto_rico_tax = NumericField(max_length=15, required=False)
|
|
tips_subject_to_puerto_rico_tax = NumericField(max_length=15, required=False)
|
|
total_wages_subject_to_puerto_rico_tax = NumericField(max_length=15, required=False)
|
|
puerto_rico_tax_withheld = NumericField(max_length=15, required=False)
|
|
retirement_fund_contrib = NumericField(max_length=15, required=False)
|
|
total_wages_tips_virgin_islands = NumericField(max_length=15, required=False)
|
|
virgin_islands_income_tax_withheld = NumericField(max_length=15, required=False)
|
|
blank2 = BlankField(max_length=23)
|
|
|
|
|
|
class StateTotalRecord(model.Model):
|
|
record_identifier = 'RV'
|
|
required = False
|
|
|
|
supplemental_data = TextField(max_length=510)
|
|
|
|
class FinalRecord(model.Model):
|
|
record_identifier = 'RF'
|
|
required = True
|
|
|
|
blank1 = BlankField(max_length=5)
|
|
number_of_rw_records = NumericField(max_length=9)
|
|
blank2 = BlankField(max_length=496)
|