diff --git a/enums.py b/enums.py index bd8e2cd..fe1c3fd 100644 --- a/enums.py +++ b/enums.py @@ -301,3 +301,37 @@ COUNTRIES = ( ('ZM', 'Zambia'), ('ZW', 'Zimbabwe')) + +employer_types = ( + ('F','Federal Government'), + ('S','State and Local Governmental Employer'), + ('T','Tax Exempt Employer'), + ('Y','State and Local Tax Exempt Employer'), + ('N','None Apply'), + ) + +employment_codes = ( + ('A', 'Agriculture'), + ('H', 'Household'), + ('M', 'Military'), + ('Q', 'Medicare Qualified Government Employee'), + ('X', 'Railroad'), + ('F', 'Regular'), + ('R', 'Regular (all others)'), + ) + +tax_jurisdiction_codes = ( + ('V', 'Virgin Islands'), + ('G', 'Guam'), + ('S', 'American Samoa'), + ('N', 'Northern Mariana Islands'), + ('P', 'Puerto Rico'), + ) + +tax_type_codes = ( + ('C', 'City Income Tax'), + ('D', 'Country Income Tax'), + ('E', 'School District Income Tax'), + ('F', 'Other Income Tax'), + ) + diff --git a/fields.py b/fields.py index 99f8fc8..282376d 100644 --- a/fields.py +++ b/fields.py @@ -1,6 +1,6 @@ import decimal, datetime import inspect -from enums import STATE_POSTAL_NUMERIC +import enums class ValidationError(Exception): def __init__(self, msg, field=None): @@ -71,18 +71,18 @@ class StateField(TextField): def get_data(self): value = self.value or "" if value.strip() and self.use_numeric: - return str(STATE_POSTAL_NUMERIC[value.upper()]).zfill(self.max_length) + return str(enums.state_postal_numeric[value.upper()]).zfill(self.max_length) else: return value.ljust(self.max_length).encode('ascii') def validate(self): super(StateField, self).validate() - if self.value and self.value.upper() not in STATE_POSTAL_NUMERIC.keys(): + if self.value and self.value.upper() not in enums.state_postal_numeric.keys(): raise ValidationError("%s is not a valid state abbreviation" % self.value, field=self) def parse(self, s): if s.strip() and self.use_numeric: - states = dict( [(v,k) for (k,v) in STATE_POSTAL_NUMERIC.items()] ) + states = dict( [(v,k) for (k,v) in enums.state_postal_numeric.items()] ) self.value = states[int(s)] else: self.value = s diff --git a/record.py b/record.py index 39a82a5..21a6ac8 100644 --- a/record.py +++ b/record.py @@ -1,5 +1,6 @@ import model from fields import * +import enums __all__ = RECORD_TYPES = ['SubmitterRecord', 'EmployerRecord', 'EmployeeWageRecord', 'OptionalEmployeeWageRecord', @@ -12,9 +13,9 @@ class SubmitterRecord(model.Model): submitter_ein = NumericField(max_length=9) user_id = TextField(max_length=8) - software_vendor = TextField(max_length=4) + software_vendor = TextField(max_length=4, required=False) blank1 = BlankField(max_length=5) - resub_indictator = BooleanField() + 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) @@ -50,12 +51,39 @@ class SubmitterRecord(model.Model): 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) + 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() @@ -68,15 +96,45 @@ class EmployerRecord(model.Model): state = StateField() zipcode = TextField(max_length=5) zipcode_ext = TextField(max_length=4, required=False) - blank1 = BlankField(max_length=5) + 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) + 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' @@ -129,6 +187,11 @@ class EmployeeWageRecord(model.Model): 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' @@ -157,6 +220,8 @@ class OptionalEmployeeWageRecord(model.Model): virgin_islands_income_tax_withheld = MoneyField(max_length=11, required=False) blank4 = BlankField(max_length=128) + + class StateWageRecord(model.Model): record_identifier = 'RS' @@ -202,8 +267,9 @@ class StateWageRecord(model.Model): blank4 = BlankField(max_length=25) def validate_tax_type_code(self, field): - if field.value not in ['C','D','E','F']: - raise ValidationError("%s not one of (c,d,e,f)" % field.value, field=f) + 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):