Added a MonthYear field, fixed some field required values and fixed
validation functions. Added numeric state abbreviation capability. So far everything appears to be working good.
This commit is contained in:
parent
5781cbf335
commit
a0014ca451
4 changed files with 173 additions and 35 deletions
53
enums.py
Normal file
53
enums.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
STATE_POSTAL_NUMERIC = {
|
||||||
|
'AL': 1,
|
||||||
|
'AK': 2,
|
||||||
|
'AZ': 4,
|
||||||
|
'AR': 5,
|
||||||
|
'CA': 6,
|
||||||
|
'CO': 8,
|
||||||
|
'CT': 9,
|
||||||
|
'DE': 10,
|
||||||
|
'DC': 11,
|
||||||
|
'FL': 12,
|
||||||
|
'GA': 13,
|
||||||
|
'HI': 15,
|
||||||
|
'ID': 16,
|
||||||
|
'IL': 17,
|
||||||
|
'IN': 18,
|
||||||
|
'IA': 19,
|
||||||
|
'KS': 20,
|
||||||
|
'KY': 21,
|
||||||
|
'LA': 22,
|
||||||
|
'ME': 23,
|
||||||
|
'MD': 24,
|
||||||
|
'MA': 25,
|
||||||
|
'MI': 26,
|
||||||
|
'MN': 27,
|
||||||
|
'MS': 28,
|
||||||
|
'MO': 29,
|
||||||
|
'MT': 30,
|
||||||
|
'NE': 31,
|
||||||
|
'NV': 32,
|
||||||
|
'NH': 33,
|
||||||
|
'NJ': 34,
|
||||||
|
'NM': 35,
|
||||||
|
'NY': 36,
|
||||||
|
'NC': 37,
|
||||||
|
'ND': 38,
|
||||||
|
'OH': 39,
|
||||||
|
'OK': 40,
|
||||||
|
'OR': 41,
|
||||||
|
'PA': 42,
|
||||||
|
'RI': 44,
|
||||||
|
'SC': 45,
|
||||||
|
'SD': 46,
|
||||||
|
'TN': 47,
|
||||||
|
'TX': 48,
|
||||||
|
'UT': 49,
|
||||||
|
'VT': 50,
|
||||||
|
'VA': 51,
|
||||||
|
'WA': 53,
|
||||||
|
'WV': 54,
|
||||||
|
'WI': 55,
|
||||||
|
'WY': 56,
|
||||||
|
}
|
121
fields.py
121
fields.py
|
@ -1,7 +1,17 @@
|
||||||
import decimal, datetime
|
import decimal, datetime
|
||||||
|
import inspect
|
||||||
|
from enums import STATE_POSTAL_NUMERIC
|
||||||
|
|
||||||
class ValidationError(Exception):
|
class ValidationError(Exception):
|
||||||
pass
|
def __init__(self, msg, field=None):
|
||||||
|
self.msg = msg
|
||||||
|
self.field = field
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.field:
|
||||||
|
return "(%s.%s) %s" % (self.field.parent_name, self.field.name, self.msg)
|
||||||
|
else:
|
||||||
|
return repr(self.msg)
|
||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
creation_counter = 0
|
creation_counter = 0
|
||||||
|
@ -42,9 +52,9 @@ class Field(object):
|
||||||
class TextField(Field):
|
class TextField(Field):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.value == None and self.required:
|
if self.value == None and self.required:
|
||||||
raise ValidationError("value required")
|
raise ValidationError("value required", field=self)
|
||||||
if len(self.value) > self.max_length:
|
if len(self.get_data()) > self.max_length:
|
||||||
raise ValidationError("value is too long")
|
raise ValidationError("value is too long", field=self)
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
value = self.value or ""
|
value = self.value or ""
|
||||||
|
@ -54,9 +64,28 @@ class TextField(Field):
|
||||||
|
|
||||||
|
|
||||||
class StateField(TextField):
|
class StateField(TextField):
|
||||||
def __init__(self, name=None, required=True):
|
def __init__(self, name=None, required=True, use_numeric=False):
|
||||||
return super(StateField, self).__init__(name=name, max_length=2, required=required)
|
super(StateField, self).__init__(name=name, max_length=2, required=required)
|
||||||
|
self.use_numeric = use_numeric
|
||||||
|
|
||||||
|
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)
|
||||||
|
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():
|
||||||
|
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()] )
|
||||||
|
self.value = states[int(s)]
|
||||||
|
else:
|
||||||
|
self.value = s
|
||||||
|
|
||||||
class EmailField(TextField):
|
class EmailField(TextField):
|
||||||
def __init__(self, name=None, required=True, max_length=None):
|
def __init__(self, name=None, required=True, max_length=None):
|
||||||
|
@ -66,14 +95,16 @@ class EmailField(TextField):
|
||||||
class NumericField(TextField):
|
class NumericField(TextField):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(NumericField, self).validate()
|
super(NumericField, self).validate()
|
||||||
try:
|
if self.value:
|
||||||
int(self.value)
|
try:
|
||||||
except ValueError:
|
int(self.value)
|
||||||
raise ValidationError("field contains non-numeric characters")
|
except ValueError:
|
||||||
|
raise ValidationError("field contains non-numeric characters", field=self)
|
||||||
|
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
value = self.value or ""
|
value = self.value or ""
|
||||||
return value.zfill(self.max_length)
|
return str(value).zfill(self.max_length)
|
||||||
|
|
||||||
def parse(self, s):
|
def parse(self, s):
|
||||||
self.value = int(s)
|
self.value = int(s)
|
||||||
|
@ -89,6 +120,9 @@ class StaticField(TextField):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class BlankField(TextField):
|
class BlankField(TextField):
|
||||||
|
def __init__(self, name=None, max_length=0, required=False):
|
||||||
|
super(TextField, self).__init__(name=name, max_length=max_length, required=required, uppercase=False)
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return " " * self.max_length
|
return " " * self.max_length
|
||||||
|
|
||||||
|
@ -109,12 +143,13 @@ class BooleanField(Field):
|
||||||
def parse(self, s):
|
def parse(self, s):
|
||||||
self.value = (s == '1')
|
self.value = (s == '1')
|
||||||
|
|
||||||
|
|
||||||
class MoneyField(Field):
|
class MoneyField(Field):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.value == None and self.required:
|
if self.value == None and self.required:
|
||||||
raise ValidationError("value required")
|
raise ValidationError("value required", field=self)
|
||||||
if len(str(int((self.value or 0)*100))) > self.max_length:
|
if len(str(int((self.value or 0)*100))) > self.max_length:
|
||||||
raise ValidationError("value is too long")
|
raise ValidationError("value is too long", field=self)
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return str(int((self.value or 0)*100)).encode('ascii').zfill(self.max_length)
|
return str(int((self.value or 0)*100)).encode('ascii').zfill(self.max_length)
|
||||||
|
@ -122,15 +157,12 @@ class MoneyField(Field):
|
||||||
def parse(self, s):
|
def parse(self, s):
|
||||||
self.value = decimal.Decimal(s) * decimal.Decimal('0.01')
|
self.value = decimal.Decimal(s) * decimal.Decimal('0.01')
|
||||||
|
|
||||||
|
|
||||||
class DateField(TextField):
|
class DateField(TextField):
|
||||||
def __init__(self, name=None, required=True, value=None):
|
def __init__(self, name=None, required=True, value=None):
|
||||||
super(TextField, self).__init__(name=name, required=required, max_length=8)
|
super(TextField, self).__init__(name=name, required=required, max_length=8)
|
||||||
if isinstance(value, datetime.date):
|
if value:
|
||||||
self._value = value
|
self.value = value
|
||||||
elif value:
|
|
||||||
self._value = datetime.date(*[int(x) for x in value[4:8], value[0:2], value[2:4]])
|
|
||||||
else:
|
|
||||||
self._value = None
|
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
if self._value:
|
if self._value:
|
||||||
|
@ -138,6 +170,53 @@ class DateField(TextField):
|
||||||
return '0' * self.max_length
|
return '0' * self.max_length
|
||||||
|
|
||||||
def parse(self, s):
|
def parse(self, s):
|
||||||
self.value = datetime.date(*[int(x) for x in s[4:8], s[0:2], s[2:4]])
|
if int(s) > 0:
|
||||||
|
self.value = datetime.date(*[int(x) for x in s[4:8], s[0:2], s[2:4]])
|
||||||
|
else:
|
||||||
|
self.value = None
|
||||||
|
|
||||||
|
def __setvalue(self, value):
|
||||||
|
if isinstance(value, datetime.date):
|
||||||
|
self._value = value
|
||||||
|
elif value:
|
||||||
|
self._value = datetime.date(*[int(x) for x in value[4:8], value[0:2], value[2:4]])
|
||||||
|
else:
|
||||||
|
self._value = None
|
||||||
|
|
||||||
|
def __getvalue(self):
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
value = property(__getvalue, __setvalue)
|
||||||
|
|
||||||
|
|
||||||
|
class MonthYearField(TextField):
|
||||||
|
def __init__(self, name=None, required=True, value=None):
|
||||||
|
super(TextField, self).__init__(name=name, required=required, max_length=6)
|
||||||
|
|
||||||
|
if value:
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
if self._value:
|
||||||
|
return self._value.strftime("%m%Y")
|
||||||
|
return '0' * self.max_length
|
||||||
|
|
||||||
|
def parse(self, s):
|
||||||
|
if int(s) > 0:
|
||||||
|
self.value = datetime.date(*[int(x) for x in s[2:6], s[0:2], 1])
|
||||||
|
else:
|
||||||
|
self.value = None
|
||||||
|
|
||||||
|
def __setvalue(self, value):
|
||||||
|
if isinstance(value, datetime.date):
|
||||||
|
self._value = value
|
||||||
|
elif value:
|
||||||
|
self._value = datetime.date(*[int(x) for x in value[2:6], value[0:2], 1])
|
||||||
|
else:
|
||||||
|
self._value = None
|
||||||
|
|
||||||
|
def __getvalue(self):
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
value = property(__getvalue, __setvalue)
|
||||||
|
|
||||||
|
|
1
model.py
1
model.py
|
@ -10,6 +10,7 @@ class Model(object):
|
||||||
field = getattr(self, key)
|
field = getattr(self, key)
|
||||||
if not field.name:
|
if not field.name:
|
||||||
setattr(field, 'name', key)
|
setattr(field, 'name', key)
|
||||||
|
setattr(field, 'parent_name', self.__class__.__name__)
|
||||||
|
|
||||||
def __setattr__(self, key, value):
|
def __setattr__(self, key, value):
|
||||||
if hasattr(self, key) and isinstance(getattr(self, key), Field):
|
if hasattr(self, key) and isinstance(getattr(self, key), Field):
|
||||||
|
|
33
record.py
33
record.py
|
@ -27,7 +27,7 @@ class SubmitterRecord(model.Model):
|
||||||
blank2 = BlankField(max_length=5)
|
blank2 = BlankField(max_length=5)
|
||||||
company_foreign_state_province= TextField(max_length=23, required=False)
|
company_foreign_state_province= TextField(max_length=23, required=False)
|
||||||
company_foreign_postal_code = TextField(max_length=15, required=False)
|
company_foreign_postal_code = TextField(max_length=15, required=False)
|
||||||
company_country_code = TextField(max_length=2)
|
company_country_code = TextField(max_length=2, required=False)
|
||||||
submitter_name = TextField(max_length=57)
|
submitter_name = TextField(max_length=57)
|
||||||
submitter_address = TextField(max_length=22)
|
submitter_address = TextField(max_length=22)
|
||||||
submitter_delivery_address = TextField(max_length=22)
|
submitter_delivery_address = TextField(max_length=22)
|
||||||
|
@ -38,7 +38,7 @@ class SubmitterRecord(model.Model):
|
||||||
blank3 = BlankField(max_length=5)
|
blank3 = BlankField(max_length=5)
|
||||||
submitter_foreign_state_province = TextField(max_length=23, required=False)
|
submitter_foreign_state_province = TextField(max_length=23, required=False)
|
||||||
submitter_foreign_postal_code = TextField(max_length=15, required=False)
|
submitter_foreign_postal_code = TextField(max_length=15, required=False)
|
||||||
submitter_country_code = TextField(max_length=2)
|
submitter_country_code = TextField(max_length=2, required=False)
|
||||||
contact_name = TextField(max_length=27)
|
contact_name = TextField(max_length=27)
|
||||||
contact_phone = TextField(max_length=15)
|
contact_phone = TextField(max_length=15)
|
||||||
contact_phone_ext = TextField(max_length=5, required=False)
|
contact_phone_ext = TextField(max_length=5, required=False)
|
||||||
|
@ -86,7 +86,7 @@ class EmployeeWageRecord(model.Model):
|
||||||
employee_first_name = TextField(max_length=15)
|
employee_first_name = TextField(max_length=15)
|
||||||
employee_middle_name = TextField(max_length=15)
|
employee_middle_name = TextField(max_length=15)
|
||||||
employee_last_name = TextField(max_length=20)
|
employee_last_name = TextField(max_length=20)
|
||||||
employee_suffix = TextField(max_length=4)
|
employee_suffix = TextField(max_length=4, required=False)
|
||||||
location_address = TextField(max_length=22)
|
location_address = TextField(max_length=22)
|
||||||
delivery_address = TextField(max_length=22)
|
delivery_address = TextField(max_length=22)
|
||||||
city = TextField(max_length=22)
|
city = TextField(max_length=22)
|
||||||
|
@ -162,13 +162,13 @@ class StateWageRecord(model.Model):
|
||||||
record_identifier = 'RS'
|
record_identifier = 'RS'
|
||||||
required = False
|
required = False
|
||||||
|
|
||||||
state_code = NumericField(max_length=2)
|
state_code = StateField(use_numeric=True)
|
||||||
taxing_entity_code = TextField(max_length=5)
|
taxing_entity_code = TextField(max_length=5, required=False)
|
||||||
ssn = NumericField(max_length=9, required=False)
|
ssn = NumericField(max_length=9, required=False)
|
||||||
employee_first_name = TextField(max_length=15)
|
employee_first_name = TextField(max_length=15)
|
||||||
employee_middle_name = TextField(max_length=15)
|
employee_middle_name = TextField(max_length=15)
|
||||||
employee_last_name = TextField(max_length=20)
|
employee_last_name = TextField(max_length=20)
|
||||||
employee_suffix = TextField(max_length=4)
|
employee_suffix = TextField(max_length=4, required=False)
|
||||||
location_address = TextField(max_length=22)
|
location_address = TextField(max_length=22)
|
||||||
delivery_address = TextField(max_length=22)
|
delivery_address = TextField(max_length=22)
|
||||||
city = TextField(max_length=22)
|
city = TextField(max_length=22)
|
||||||
|
@ -178,29 +178,34 @@ class StateWageRecord(model.Model):
|
||||||
blank1 = BlankField(max_length=5)
|
blank1 = BlankField(max_length=5)
|
||||||
foreign_state_province = TextField(max_length=23, required=False)
|
foreign_state_province = TextField(max_length=23, required=False)
|
||||||
foreign_postal_code = TextField(max_length=15, required=False)
|
foreign_postal_code = TextField(max_length=15, required=False)
|
||||||
country_code = TextField(max_length=2)
|
country_code = TextField(max_length=2, required=False)
|
||||||
optional_code = TextField(max_length=2, required=False)
|
optional_code = TextField(max_length=2, required=False)
|
||||||
reporting_period = NumericField(max_length=6) # MAYBE MAKE A CUSTOM FIELD TYPE FOR THIS
|
reporting_period = MonthYearField()
|
||||||
quarterly_unemp_ins_wages = MoneyField(max_length=11)
|
quarterly_unemp_ins_wages = MoneyField(max_length=11)
|
||||||
quarterly_unemp_ins_taxable_wages = MoneyField(max_length=11)
|
quarterly_unemp_ins_taxable_wages = MoneyField(max_length=11)
|
||||||
number_of_weeks_worked = NumericField(max_length=2)
|
number_of_weeks_worked = NumericField(max_length=2)
|
||||||
date_first_employed = DateField()
|
date_first_employed = DateField(required=False)
|
||||||
date_of_separation = DateField()
|
date_of_separation = DateField(required=False)
|
||||||
blank2 = BlankField(max_length=5)
|
blank2 = BlankField(max_length=5)
|
||||||
state_employer_account_num = NumericField(max_length=20)
|
state_employer_account_num = NumericField(max_length=20)
|
||||||
blank3 = BlankField(max_length=6)
|
blank3 = BlankField(max_length=6)
|
||||||
state_code_2 = NumericField(max_length=2)
|
state_code_2 = StateField(use_numeric=True)
|
||||||
state_taxable_wages = MoneyField(max_length=11)
|
state_taxable_wages = MoneyField(max_length=11)
|
||||||
state_income_tax_wh = MoneyField(max_length=11)
|
state_income_tax_wh = MoneyField(max_length=11)
|
||||||
other_state_data = TextField(max_length=10)
|
other_state_data = TextField(max_length=10, required=False)
|
||||||
tax_type_code = TextField(max_length=1) # VALIDATE C, D, E, or F
|
tax_type_code = TextField(max_length=1) # VALIDATE C, D, E, or F
|
||||||
local_taxable_wages = MoneyField(max_length=11)
|
local_taxable_wages = MoneyField(max_length=11)
|
||||||
local_income_tax_wh = MoneyField(max_length=11)
|
local_income_tax_wh = MoneyField(max_length=11)
|
||||||
state_control_number = NumericField(max_length=7, required=False)
|
state_control_number = NumericField(max_length=7, required=False)
|
||||||
supplemental_data1 = TextField(max_length=75)
|
supplemental_data1 = TextField(max_length=75, required=False)
|
||||||
supplemental_data2 = TextField(max_length=75)
|
supplemental_data2 = TextField(max_length=75, required=False)
|
||||||
blank4 = BlankField(max_length=25)
|
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)
|
||||||
|
|
||||||
|
|
||||||
class TotalRecord(model.Model):
|
class TotalRecord(model.Model):
|
||||||
record_identifier = 'RT'
|
record_identifier = 'RT'
|
||||||
required = True
|
required = True
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue