web/lib/django/contrib/localflavor/se/forms.py
changeset 29 cc9b7e14412b
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
       
     1 # -*- coding: utf-8 -*-
       
     2 """
       
     3 Swedish specific Form helpers
       
     4 """
       
     5 import re
       
     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)
       
    12 
       
    13 __all__ = ('SECountySelect', 'SEOrganisationNumberField',
       
    14     'SEPersonalIdentityNumberField', 'SEPostalCodeField')
       
    15 
       
    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}$')
       
    18 
       
    19 class SECountySelect(forms.Select):
       
    20     """
       
    21     A Select form widget that uses a list of the Swedish counties (län) as its
       
    22     choices.
       
    23 
       
    24     The cleaned value is the official county code -- see
       
    25     http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
       
    26     """
       
    27 
       
    28     def __init__(self, attrs=None):
       
    29         from se_counties import COUNTY_CHOICES
       
    30         super(SECountySelect, self).__init__(attrs=attrs,
       
    31                                              choices=COUNTY_CHOICES)
       
    32 
       
    33 class SEOrganisationNumberField(forms.CharField):
       
    34     """
       
    35     A form field that validates input as a Swedish organisation number
       
    36     (organisationsnummer).
       
    37 
       
    38     It accepts the same input as SEPersonalIdentityField (for sole
       
    39     proprietorships (enskild firma). However, co-ordination numbers are not
       
    40     accepted.
       
    41 
       
    42     It also accepts ordinary Swedish organisation numbers with the format
       
    43     NNNNNNNNNN.
       
    44 
       
    45     The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
       
    46     for other organisations.
       
    47     """
       
    48 
       
    49     default_error_messages = {
       
    50         'invalid': _('Enter a valid Swedish organisation number.'),
       
    51     }
       
    52 
       
    53     def clean(self, value):
       
    54         value = super(SEOrganisationNumberField, self).clean(value)
       
    55         
       
    56         if value in EMPTY_VALUES:
       
    57             return u''
       
    58         
       
    59         match = SWEDISH_ID_NUMBER.match(value)
       
    60         if not match:
       
    61             raise forms.ValidationError(self.error_messages['invalid'])
       
    62 
       
    63         gd = match.groupdict()
       
    64         
       
    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'])
       
    68         
       
    69         # First: check if this is a real organisation_number
       
    70         if valid_organisation(gd):
       
    71             return format_organisation_number(gd)
       
    72 
       
    73         # Is this a single properitor (enskild firma)?
       
    74         try:
       
    75             birth_day = validate_id_birthday(gd, False)
       
    76             return format_personal_id_number(birth_day, gd)
       
    77         except ValueError:
       
    78             raise forms.ValidationError(self.error_messages['invalid'])
       
    79 
       
    80 
       
    81 class SEPersonalIdentityNumberField(forms.CharField):
       
    82     """
       
    83     A form field that validates input as a Swedish personal identity number
       
    84     (personnummer).
       
    85 
       
    86     The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
       
    87     YYMMDDXXXX and YYMMDD+XXXX.
       
    88 
       
    89     A + indicates that the person is older than 100 years, which will be taken
       
    90     into consideration when the date is validated.
       
    91     
       
    92     The checksum will be calculated and checked. The birth date is checked to
       
    93     be a valid date.
       
    94 
       
    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.
       
    98 
       
    99     The cleaned value will always have the format YYYYMMDDXXXX.
       
   100     """
       
   101 
       
   102     def __init__(self, coordination_number=True, *args, **kwargs):
       
   103         self.coordination_number = coordination_number
       
   104         super(SEPersonalIdentityNumberField, self).__init__(*args, **kwargs)
       
   105 
       
   106     default_error_messages = {
       
   107         'invalid': _('Enter a valid Swedish personal identity number.'),
       
   108         'coordination_number': _('Co-ordination numbers are not allowed.'),
       
   109     }
       
   110 
       
   111     def clean(self, value):
       
   112         value = super(SEPersonalIdentityNumberField, self).clean(value)
       
   113 
       
   114         if value in EMPTY_VALUES:
       
   115             return u''
       
   116  
       
   117         match = SWEDISH_ID_NUMBER.match(value)
       
   118         if match is None:
       
   119             raise forms.ValidationError(self.error_messages['invalid'])
       
   120 
       
   121         gd = match.groupdict()
       
   122  
       
   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'])
       
   126 
       
   127         # check for valid birthday
       
   128         try:
       
   129             birth_day = validate_id_birthday(gd)
       
   130         except ValueError:
       
   131             raise forms.ValidationError(self.error_messages['invalid'])
       
   132 
       
   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'])
       
   136   
       
   137         return format_personal_id_number(birth_day, gd)
       
   138 
       
   139 
       
   140 class SEPostalCodeField(forms.RegexField):
       
   141     """
       
   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).
       
   145 
       
   146     The cleaned value will never contain the space. 
       
   147     """
       
   148 
       
   149     default_error_messages = {
       
   150         'invalid': _('Enter a Swedish postal code in the format XXXXX.'),
       
   151     }
       
   152 
       
   153     def __init__(self, *args, **kwargs):
       
   154         super(SEPostalCodeField, self).__init__(SE_POSTAL_CODE, *args, **kwargs)
       
   155 
       
   156     def clean(self, value):
       
   157         return super(SEPostalCodeField, self).clean(value).replace(' ', '')