From 36662c92ff9055668f6f3665aaabf4b4bd0c9541 Mon Sep 17 00:00:00 2001 From: Agustin Scaramuzza Date: Mon, 18 Jan 2016 11:37:11 -0300 Subject: [PATCH] Add CBUField for Argentina --- docs/authors.rst | 3 ++- localflavor/ar/forms.py | 59 +++++++++++++++++++++++++++++++++++++++++ tests/test_ar.py | 29 +++++++++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/docs/authors.rst b/docs/authors.rst index 2ec7ef2a0..c06d1d4ee 100644 --- a/docs/authors.rst +++ b/docs/authors.rst @@ -4,6 +4,7 @@ Authors * Aaron Boman * Adam Taylor * Adrian Holovaty +* Agustín Scaramuzza * Alex Butum * Alex Gaynor * Alex Hill @@ -63,4 +64,4 @@ Authors * d.merc * luyikei * tadeo -* Łukasz Langa +* Łukasz Langa \ No newline at end of file diff --git a/localflavor/ar/forms.py b/localflavor/ar/forms.py index de418fbfb..0f096cad6 100644 --- a/localflavor/ar/forms.py +++ b/localflavor/ar/forms.py @@ -137,3 +137,62 @@ def _format(self, cuit, check_digit=None): check_digit = cuit[-1] cuit = cuit[:-1] return '%s-%s-%s' % (cuit[:2], cuit[2:], check_digit) + + +class ARCBUField(CharField): + """ + This field validates a CBU (Clave Bancaria Uniforme). A CBU is a 22-digits long + number. The first 8 digits denote bank and branch number, plus a verifying digit. + The remaining 14 digits denote an account number, plus a verifying digit. + + More info: + http://www.clientebancario.gov.ar/default.asp + """ + default_error_messages = { + 'invalid': _('Enter a valid CBU in XXXXXXXXXXXXXXXXXXXXXX format.'), + 'max_length': _('CBU must be exactly 22 digits long.'), + 'min_length': _('CBU must be exactly 22 digits long.'), + 'checksum': _("Invalid CBU."), + } + + def __init__(self, *args, **kwargs): + kwargs['min_length'] = kwargs['max_length'] = 22 + super(ARCBUField, self).__init__(*args, **kwargs) + + def _valid_block(self, block, ponderator): + number = block[:-1] + v_digit = int(block[-1]) + + block_sum = sum(x * int(y) for x, y in zip(ponderator, number)) + remainder = block_sum % 10 + + # The verification digit and the result of the calculation must be the same. + # In the edge case that the remainder is 0, the verification digit must be 0 too. + if remainder == 0: + return v_digit == remainder + + return v_digit == (10 - remainder) + + def _checksum(self, value): + block_1 = value[0:8] + block_2 = value[8:22] + + PONDERATOR_1 = (9, 7, 1, 3, 9, 7, 1, 3) + PONDERATOR_2 = (3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3) + + is_valid_1 = self._valid_block(block_1, PONDERATOR_1) + is_valid_2 = self._valid_block(block_2, PONDERATOR_2) + return is_valid_1 and is_valid_2 + + def clean(self, value): + """ + Value must be a 22 digits long number. + """ + value = super(ARCBUField, self).clean(value) + if value in EMPTY_VALUES: + return '' + if not value.isdigit(): + raise ValidationError(self.error_messages['invalid']) + if not self._checksum(value): + raise ValidationError(self.error_messages['checksum']) + return value diff --git a/tests/test_ar.py b/tests/test_ar.py index 9f732de05..d7f8cd126 100644 --- a/tests/test_ar.py +++ b/tests/test_ar.py @@ -3,7 +3,7 @@ from django.test import SimpleTestCase from localflavor.ar.forms import (ARProvinceSelect, ARPostalCodeField, - ARDNIField, ARCUITField) + ARDNIField, ARCUITField, ARCBUField) class ARLocalFlavorTests(SimpleTestCase): @@ -104,3 +104,30 @@ def test_ARCUITField(self): '11211111110': error_legal_type, } self.assertFieldOutput(ARCUITField, valid, invalid) + + def test_ARCBUField(self): + error_format = ['Enter a valid CBU in XXXXXXXXXXXXXXXXXXXXXX format.'] + error_length = ['CBU must be exactly 22 digits long.'] + error_checksum = ['Invalid CBU.'] + valid = { + '2237628810898098715378': '2237628810898098715378', + '5433758936130717465023': '5433758936130717465023', + '5729195067928761667584': '5729195067928761667584', + '9498175528566296510521': '9498175528566296510521', + '7362966507842824472644': '7362966507842824472644', + '8693513393883886497274': '8693513393883886497274', + '1542952861593836535608': '1542952861593836535608', + '5833008953419074707467': '5833008953419074707467', + '9687027721961737239525': '9687027721961737239525', + '8048819274216931992586': '8048819274216931992586' + } + + invalid = { + 'abc123def456-9024-2313': error_format, + '142512591859898123123': error_length, + '12312452521512526125566': error_length, + '1234567891234567891234': error_checksum, + '1234562374545894589234': error_checksum, + '0987653759257883891234': error_checksum, + } + self.assertFieldOutput(ARCBUField, valid, invalid)