From 0646bf7b9b76c55360fbd74d9bdaec00e5baf65b Mon Sep 17 00:00:00 2001 From: Binh Van Nguyen Date: Sat, 11 Jun 2011 14:45:12 -0500 Subject: [PATCH] Added record validation functions for everything (that we saw in the PDF), 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. --- enums.py | 34 +++++++++++++++++++++++ fields.py | 8 +++--- record.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 111 insertions(+), 11 deletions(-) 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):