From 470c056b1faab437531c71a082bfbd3e92bdfd6e Mon Sep 17 00:00:00 2001 From: Matteo Bilotta Date: Tue, 30 Oct 2018 17:08:23 +0100 Subject: [PATCH] [IMP] A better wizard for fiscalcode generation. (#569) --- l10n_it_fiscalcode/README.rst | 1 + l10n_it_fiscalcode/__init__.py | 1 + l10n_it_fiscalcode/__manifest__.py | 9 +- .../migrations/11.0.1.1.0/pre-migrate.py | 9 ++ l10n_it_fiscalcode/model/__init__.py | 1 + l10n_it_fiscalcode/model/res_city_it_code.py | 49 +++++--- .../security/ir.model.access.csv | 2 - l10n_it_fiscalcode/tests/__init__.py | 1 + l10n_it_fiscalcode/tests/test_fiscalcode.py | 5 +- l10n_it_fiscalcode/wizard/__init__.py | 1 + l10n_it_fiscalcode/wizard/compute_fc.py | 107 +++++++++++++----- l10n_it_fiscalcode/wizard/compute_fc_view.xml | 37 +++--- 12 files changed, 155 insertions(+), 68 deletions(-) create mode 100644 l10n_it_fiscalcode/migrations/11.0.1.1.0/pre-migrate.py diff --git a/l10n_it_fiscalcode/README.rst b/l10n_it_fiscalcode/README.rst index f4b94afb3987..15239ae2dd91 100644 --- a/l10n_it_fiscalcode/README.rst +++ b/l10n_it_fiscalcode/README.rst @@ -54,6 +54,7 @@ Contributors * Franco Tampieri * Andrea Cometa * Andrea Gallina +* Matteo Bilotta Maintainer diff --git a/l10n_it_fiscalcode/__init__.py b/l10n_it_fiscalcode/__init__.py index 570221e23300..ce28cad6441c 100644 --- a/l10n_it_fiscalcode/__init__.py +++ b/l10n_it_fiscalcode/__init__.py @@ -1,5 +1,6 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import model diff --git a/l10n_it_fiscalcode/__manifest__.py b/l10n_it_fiscalcode/__manifest__.py index 075a8f85bfe5..7fca66163ac1 100644 --- a/l10n_it_fiscalcode/__manifest__.py +++ b/l10n_it_fiscalcode/__manifest__.py @@ -1,12 +1,17 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# noinspection PyStatementEffect { 'name': 'Italian Localisation - Fiscal Code', - 'version': '11.0.1.0.0', + 'version': '11.0.1.1.0', 'category': 'Localisation/Italy', - 'author': "Odoo Italia Network, Odoo Community Association (OCA)", + 'author': "Link IT s.r.l., " + "Apulia Software, " + "Odoo Italia Network, " + "Odoo Community Association (OCA)", 'website': 'https://odoo-community.org/', 'license': 'AGPL-3', 'depends': ['base_vat'], diff --git a/l10n_it_fiscalcode/migrations/11.0.1.1.0/pre-migrate.py b/l10n_it_fiscalcode/migrations/11.0.1.1.0/pre-migrate.py new file mode 100644 index 000000000000..f32a5decd237 --- /dev/null +++ b/l10n_it_fiscalcode/migrations/11.0.1.1.0/pre-migrate.py @@ -0,0 +1,9 @@ +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +def migrate(cr, version): + if not version: + return + + cr.execute("DROP VIEW IF EXISTS res_city_it_code_province;") diff --git a/l10n_it_fiscalcode/model/__init__.py b/l10n_it_fiscalcode/model/__init__.py index 98762172de8a..167d74b24d69 100644 --- a/l10n_it_fiscalcode/model/__init__.py +++ b/l10n_it_fiscalcode/model/__init__.py @@ -1,5 +1,6 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import res_partner diff --git a/l10n_it_fiscalcode/model/res_city_it_code.py b/l10n_it_fiscalcode/model/res_city_it_code.py index 2085ab176867..b91f06f49c05 100644 --- a/l10n_it_fiscalcode/model/res_city_it_code.py +++ b/l10n_it_fiscalcode/model/res_city_it_code.py @@ -1,11 +1,42 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import models, fields, tools class ResCityItCode(models.Model): + # + # First... + # FIXME: URL in class description is no more useful... + # + # Visit: + # https://www.agenziaentrate.gov.it/wps/content/Nsilib/Nsi/ + # Strumenti/Codici+attivita+e+tributo/Codici+territorio/ + # ... and then click on "Consultazione Archivio Comuni e Stati esteri". + # You will be redirected on: + # https://www.agenziaentrate.gov.it/wps/content/nsilib/nsi/schede/ + # fabbricatiterreni/archivio+comuni+e+stati+esteri/ + # consultazione+archivio+comuni+stati+esteri + # Here, you can download the new and updated file CSV. + # (last update on 30/07/2018) + # + # + # ... BUT then... + # TODO: Find out how to import the new CSV without breaking existing data. + # + # The new CSV as a new structure: + # - some records have been deleted. + # - some columns no longer exist. + # - ... + # - ... and so on... + # - ... + # + # + # Good luck! ;) + # + """ To create res.city.it.code.csv: http://www.agenziaentrate.gov.it/wps/content/Nsilib/Nsi/Strumenti/ @@ -53,21 +84,3 @@ def init(self): SELECT name, MAX(id) AS id FROM res_city_it_code GROUP BY name) """) - - -class ResCityItCodeProvince(models.Model): - _name = 'res.city.it.code.province' - _auto = False - - name = fields.Char('Name', size=100) - town_name = fields.Char('Name', size=100) - - def init(self): - tools.drop_view_if_exists(self.env.cr, self._table) - self.env.cr.execute( - """ - CREATE OR REPLACE VIEW res_city_it_code_province AS ( - SELECT province AS name, name as town_name, MAX(id) AS id - FROM res_city_it_code - GROUP BY province, name) - """) diff --git a/l10n_it_fiscalcode/security/ir.model.access.csv b/l10n_it_fiscalcode/security/ir.model.access.csv index 4bdc3185c960..6eefa7d13e79 100644 --- a/l10n_it_fiscalcode/security/ir.model.access.csv +++ b/l10n_it_fiscalcode/security/ir.model.access.csv @@ -3,5 +3,3 @@ "access_res_city_it_code_group_user","res_city_it_code group_user","model_res_city_it_code","base.group_partner_manager",1,1,1,1 "access_res_city_it_code_distinct_group_all","res_city_it_code_distinct group_user_all","model_res_city_it_code_distinct",,1,0,0,0 "access_res_city_it_code_distinct_group_user","res_city_it_code_distinct group_user","model_res_city_it_code_distinct","base.group_partner_manager",1,1,1,1 -"access_res_city_it_code_province_group_all","res_city_it_code_province group_user_all","model_res_city_it_code_province",,1,0,0,0 -"access_res_city_it_code_province_group_user","res_city_it_code_province group_user","model_res_city_it_code_province","base.group_partner_manager",1,1,1,1 diff --git a/l10n_it_fiscalcode/tests/__init__.py b/l10n_it_fiscalcode/tests/__init__.py index 0a5965707226..6ac9fce109b8 100644 --- a/l10n_it_fiscalcode/tests/__init__.py +++ b/l10n_it_fiscalcode/tests/__init__.py @@ -1,5 +1,6 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import test_fiscalcode diff --git a/l10n_it_fiscalcode/tests/test_fiscalcode.py b/l10n_it_fiscalcode/tests/test_fiscalcode.py index b954a767684d..7595eaa9a07d 100644 --- a/l10n_it_fiscalcode/tests/test_fiscalcode.py +++ b/l10n_it_fiscalcode/tests/test_fiscalcode.py @@ -1,5 +1,6 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo.tests.common import TransactionCase @@ -9,7 +10,9 @@ class TestFiscalCode(TransactionCase): def setUp(self): super(TestFiscalCode, self).setUp() + self.partner = self.env.ref('base.res_partner_2') + self.rome_province = self.env.ref('base.state_it_rm') def test_fiscalcode_compute(self): wizard = self.env['wizard.compute.fc'].with_context( @@ -19,7 +22,7 @@ def test_fiscalcode_compute(self): 'birth_date': '1984-06-04', 'sex': 'M', 'birth_city': 10048, - 'birth_province': 10048, + 'birth_province': self.rome_province.id }) # ---- Compute FiscalCode wizard.compute_fc() diff --git a/l10n_it_fiscalcode/wizard/__init__.py b/l10n_it_fiscalcode/wizard/__init__.py index 66a1047bfd86..c16868b3f7ea 100644 --- a/l10n_it_fiscalcode/wizard/__init__.py +++ b/l10n_it_fiscalcode/wizard/__init__.py @@ -1,5 +1,6 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import compute_fc diff --git a/l10n_it_fiscalcode/wizard/compute_fc.py b/l10n_it_fiscalcode/wizard/compute_fc.py index 82b6a2a87c36..d82f94b647ea 100644 --- a/l10n_it_fiscalcode/wizard/compute_fc.py +++ b/l10n_it_fiscalcode/wizard/compute_fc.py @@ -1,54 +1,101 @@ # Copyright 2014 Associazione Odoo Italia () # Copyright 2016 Andrea Gallina (Apulia Software) +# Copyright © 2018 Matteo Bilotta (Link IT s.r.l.) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import datetime +import logging + from odoo import models, fields, api, _ from odoo.exceptions import UserError -import logging -import datetime +from odoo.osv import expression _logger = logging.getLogger(__name__) try: from codicefiscale import build + except ImportError: _logger.warning( - 'codicefiscale library not found. ' - 'If you plan to use it, please install the codicefiscale library ' - 'from https://pypi.python.org/pypi/codicefiscale') + "codicefiscale library not found. " + "If you plan to use it, please install the codicefiscale library" + " from https://pypi.python.org/pypi/codicefiscale") class WizardComputeFc(models.TransientModel): - - _name = "wizard.compute.fc" + _name = 'wizard.compute.fc' _description = "Compute Fiscal Code" _rec_name = 'fiscalcode_surname' - fiscalcode_surname = fields.Char('Surname', size=64) - fiscalcode_firstname = fields.Char('First name', size=64) - birth_date = fields.Date('Date of birth') - birth_city = fields.Many2one( - 'res.city.it.code.distinct', string='City of birth') - birth_province = fields.Many2one( - 'res.city.it.code.province', string='Province') - sex = fields.Selection([ - ('M', 'Male'), - ('F', 'Female'), - ], "Sex") + fiscalcode_surname = fields.Char("Surname", required=True, size=64) + fiscalcode_firstname = fields.Char("First name", required=True, size=64) + birth_date = fields.Date("Date of birth", required=True) + birth_city = fields.Many2one('res.city.it.code.distinct', + required=True, + string="City of birth") + birth_province = fields.Many2one('res.country.state', + required=True, + string="Province") + sex = fields.Selection([('M', "Male"), ('F', "Female")], + required=True, + string="Sex") @api.multi @api.onchange('birth_city') def onchange_birth_city(self): self.ensure_one() - res = {} + + it = self.env.ref('base.it').id + res = { + 'domain': {'birth_province': [('country_id', '=', it)]}, + 'value': {'birth_province': False} + } + if self.birth_city: - ct = self.birth_city - res['domain'] = { - 'birth_province': [('town_name', '=', ct.name)] - } - else: - res['domain'] = {'birth_province': []} - res['value'] = {'birth_province': ''} + # SMELLS: Add a foreign key in "res_city_it_code" + # instead using the weak link "code" <-> "province". + # + city_ids = self.env['res.city.it.code'] \ + .search([('name', '=', self.birth_city.name)]) + provinces = city_ids.mapped('province') + province_ids = self.env['res.country.state'] \ + .search([('country_id', '=', it), ('code', 'in', provinces)]) + + res['domain']['birth_province'] = \ + expression.AND([ + res['domain']['birth_province'], + [('id', 'in', province_ids.ids)] + ]) + + if len(province_ids) == 1: + res['value']['birth_province'] = province_ids.id + + return res + + @api.multi + @api.onchange('birth_province') + def onchange_birth_province(self): + self.ensure_one() + + res = {'domain': {'birth_city': []}} + + if not self.birth_city: + if self.birth_province: + # SMELLS: Add a foreign key in "res_city_it_code" + # instead using the weak link "code" <-> "province". + # + city_ids = self.env['res.city.it.code'] \ + .search([('province', '=', self.birth_province.code)]) + names = city_ids.mapped('name') + distinct_city_ids = self.env['res.city.it.code.distinct'] \ + .search([('name', 'in', names)]) + + res['domain']['birth_city'] = \ + expression.AND([ + res['domain']['birth_city'], + [('id', 'in', distinct_city_ids.ids)] + ]) + return res def _get_national_code(self, birth_city, birth_prov, birth_date): @@ -88,11 +135,9 @@ def _get_national_code(self, birth_city, birth_prov, birth_date): break if newcts: cities = newcts - return self._check_national_codes( - birth_city, birth_prov, birth_date, cities) + return self._check_national_codes(birth_date, cities) - def _check_national_codes( - self, birth_city, birth_prov, birth_date, cities): + def _check_national_codes(self, birth_date, cities): nc = '' dtcostvar = None for ct in cities: @@ -142,7 +187,7 @@ def compute_fc(self): not f.birth_date or not f.birth_city or not f.sex): raise UserError(_('One or more fields are missing')) nat_code = self._get_national_code( - f.birth_city.name, f.birth_province.name, f.birth_date) + f.birth_city.name, f.birth_province.code, f.birth_date) if not nat_code: raise UserError(_('National code is missing')) birth_date = datetime.datetime.strptime(f.birth_date, "%Y-%m-%d") diff --git a/l10n_it_fiscalcode/wizard/compute_fc_view.xml b/l10n_it_fiscalcode/wizard/compute_fc_view.xml index 8d8b8c37877a..ea4ecf419655 100644 --- a/l10n_it_fiscalcode/wizard/compute_fc_view.xml +++ b/l10n_it_fiscalcode/wizard/compute_fc_view.xml @@ -6,23 +6,32 @@ form
+

Individual Data

- - - - - - - + + + + + + + + + - -
-
+