1 # -*- coding: utf-8 -*-
3 Swedish specific Form helpers
6 from django
import forms
7 from django
.utils
.translation
import ugettext_lazy
as _
8 from django
.core
.validators
import EMPTY_VALUES
9 from django
.contrib
.localflavor
.se
.utils
import (id_number_checksum
,
10 validate_id_birthday
, format_personal_id_number
, valid_organisation
,
11 format_organisation_number
)
13 __all__
= ('SECountySelect', 'SEOrganisationNumberField',
14 'SEPersonalIdentityNumberField', 'SEPostalCodeField')
16 SWEDISH_ID_NUMBER
= re
.compile(r
'^(?P<century>\d{2})?(?P<year>\d{2})(?P<month>\d{2})(?P<day>\d{2})(?P<sign>[\-+])?(?P<serial>\d{3})(?P<checksum>\d)$')
17 SE_POSTAL_CODE
= re
.compile(r
'^[1-9]\d{2} ?\d{2}$')
19 class SECountySelect(forms
.Select
):
21 A Select form widget that uses a list of the Swedish counties (län) as its
24 The cleaned value is the official county code -- see
25 http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
28 def __init__(self
, attrs
=None):
29 from se_counties
import COUNTY_CHOICES
30 super(SECountySelect
, self
).__init
__(attrs
=attrs
,
31 choices
=COUNTY_CHOICES
)
33 class SEOrganisationNumberField(forms
.CharField
):
35 A form field that validates input as a Swedish organisation number
36 (organisationsnummer).
38 It accepts the same input as SEPersonalIdentityField (for sole
39 proprietorships (enskild firma). However, co-ordination numbers are not
42 It also accepts ordinary Swedish organisation numbers with the format
45 The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
46 for other organisations.
49 default_error_messages
= {
50 'invalid': _('Enter a valid Swedish organisation number.'),
53 def clean(self
, value
):
54 value
= super(SEOrganisationNumberField
, self
).clean(value
)
56 if value
in EMPTY_VALUES
:
59 match
= SWEDISH_ID_NUMBER
.match(value
)
61 raise forms
.ValidationError(self
.error_messages
['invalid'])
63 gd
= match
.groupdict()
65 # Compare the calculated value with the checksum
66 if id_number_checksum(gd
) != int(gd
['checksum']):
67 raise forms
.ValidationError(self
.error_messages
['invalid'])
69 # First: check if this is a real organisation_number
70 if valid_organisation(gd
):
71 return format_organisation_number(gd
)
73 # Is this a single properitor (enskild firma)?
75 birth_day
= validate_id_birthday(gd
, False)
76 return format_personal_id_number(birth_day
, gd
)
78 raise forms
.ValidationError(self
.error_messages
['invalid'])
81 class SEPersonalIdentityNumberField(forms
.CharField
):
83 A form field that validates input as a Swedish personal identity number
86 The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
87 YYMMDDXXXX and YYMMDD+XXXX.
89 A + indicates that the person is older than 100 years, which will be taken
90 into consideration when the date is validated.
92 The checksum will be calculated and checked. The birth date is checked to
95 By default, co-ordination numbers (samordningsnummer) will be accepted. To
96 only allow real personal identity numbers, pass the keyword argument
97 coordination_number=False to the constructor.
99 The cleaned value will always have the format YYYYMMDDXXXX.
102 def __init__(self
, coordination_number
=True, *args
, **kwargs
):
103 self
.coordination_number
= coordination_number
104 super(SEPersonalIdentityNumberField
, self
).__init
__(*args
, **kwargs
)
106 default_error_messages
= {
107 'invalid': _('Enter a valid Swedish personal identity number.'),
108 'coordination_number': _('Co-ordination numbers are not allowed.'),
111 def clean(self
, value
):
112 value
= super(SEPersonalIdentityNumberField
, self
).clean(value
)
114 if value
in EMPTY_VALUES
:
117 match
= SWEDISH_ID_NUMBER
.match(value
)
119 raise forms
.ValidationError(self
.error_messages
['invalid'])
121 gd
= match
.groupdict()
123 # compare the calculated value with the checksum
124 if id_number_checksum(gd
) != int(gd
['checksum']):
125 raise forms
.ValidationError(self
.error_messages
['invalid'])
127 # check for valid birthday
129 birth_day
= validate_id_birthday(gd
)
131 raise forms
.ValidationError(self
.error_messages
['invalid'])
133 # make sure that co-ordination numbers do not pass if not allowed
134 if not self
.coordination_number
and int(gd
['day']) > 60:
135 raise forms
.ValidationError(self
.error_messages
['coordination_number'])
137 return format_personal_id_number(birth_day
, gd
)
140 class SEPostalCodeField(forms
.RegexField
):
142 A form field that validates input as a Swedish postal code (postnummer).
143 Valid codes consist of five digits (XXXXX). The number can optionally be
144 formatted with a space after the third digit (XXX XX).
146 The cleaned value will never contain the space.
149 default_error_messages
= {
150 'invalid': _('Enter a Swedish postal code in the format XXXXX.'),
153 def __init__(self
, *args
, **kwargs
):
154 super(SEPostalCodeField
, self
).__init
__(SE_POSTAL_CODE
, *args
, **kwargs
)
156 def clean(self
, value
):
157 return super(SEPostalCodeField
, self
).clean(value
).replace(' ', '')