From d4fa1102b398442a693fc1ae2f752ce19d292b45 Mon Sep 17 00:00:00 2001 From: mreficent Date: Fri, 23 Mar 2018 11:58:42 +0100 Subject: [PATCH] [IMP] date_range: adapt to multicompany --- date_range/README.rst | 1 + date_range/__manifest__.py | 2 +- .../11.0.2.0.0/noupdate_changes.xml | 9 +++ .../11.0.2.0.0/openupgrade_analysis.txt | 3 + .../11.0.2.0.0/openupgrade_analysis_work.txt | 4 ++ .../migrations/11.0.2.0.0/post-migration.py | 11 ++++ date_range/models/date_range.py | 20 +++++- date_range/models/date_range_type.py | 17 +++++ date_range/security/date_range_security.xml | 4 +- date_range/security/ir.model.access.csv | 2 +- date_range/tests/test_date_range.py | 63 ++++++++++++++----- date_range/tests/test_date_range_generator.py | 44 ++++++++++++- date_range/tests/test_date_range_type.py | 31 ++++++++- date_range/wizard/date_range_generator.py | 22 ++++++- 14 files changed, 207 insertions(+), 26 deletions(-) create mode 100644 date_range/migrations/11.0.2.0.0/noupdate_changes.xml create mode 100644 date_range/migrations/11.0.2.0.0/openupgrade_analysis.txt create mode 100644 date_range/migrations/11.0.2.0.0/openupgrade_analysis_work.txt create mode 100644 date_range/migrations/11.0.2.0.0/post-migration.py diff --git a/date_range/README.rst b/date_range/README.rst index f0aaaa7..64ce918 100644 --- a/date_range/README.rst +++ b/date_range/README.rst @@ -91,6 +91,7 @@ Contributors ------------ * Laurent Mignon +* Miquel Raïch Maintainer ---------- diff --git a/date_range/__manifest__.py b/date_range/__manifest__.py index 89d6b2b..1499633 100644 --- a/date_range/__manifest__.py +++ b/date_range/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Date Range", "summary": "Manage all kind of date range", - "version": "11.0.1.0.1", + "version": "11.0.2.0.0", "category": "Uncategorized", "website": "https://github.com/oca/server-ux", "author": "ACSONE SA/NV, Odoo Community Association (OCA)", diff --git a/date_range/migrations/11.0.2.0.0/noupdate_changes.xml b/date_range/migrations/11.0.2.0.0/noupdate_changes.xml new file mode 100644 index 0000000..db1aff7 --- /dev/null +++ b/date_range/migrations/11.0.2.0.0/noupdate_changes.xml @@ -0,0 +1,9 @@ + + + + ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)] + + + ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)] + + diff --git a/date_range/migrations/11.0.2.0.0/openupgrade_analysis.txt b/date_range/migrations/11.0.2.0.0/openupgrade_analysis.txt new file mode 100644 index 0000000..f5a9ace --- /dev/null +++ b/date_range/migrations/11.0.2.0.0/openupgrade_analysis.txt @@ -0,0 +1,3 @@ +---Fields in module 'date_range'--- +---XML records in module 'date_range'--- +NEW ir.rule: date_range.date_range_generator_comp_rule (noupdate) diff --git a/date_range/migrations/11.0.2.0.0/openupgrade_analysis_work.txt b/date_range/migrations/11.0.2.0.0/openupgrade_analysis_work.txt new file mode 100644 index 0000000..b14d703 --- /dev/null +++ b/date_range/migrations/11.0.2.0.0/openupgrade_analysis_work.txt @@ -0,0 +1,4 @@ +---Fields in module 'date_range'--- +---XML records in module 'date_range'--- +NEW ir.rule: date_range.date_range_generator_comp_rule (noupdate) +# NOTHING TO DO \ No newline at end of file diff --git a/date_range/migrations/11.0.2.0.0/post-migration.py b/date_range/migrations/11.0.2.0.0/post-migration.py new file mode 100644 index 0000000..1145d80 --- /dev/null +++ b/date_range/migrations/11.0.2.0.0/post-migration.py @@ -0,0 +1,11 @@ +# Copyright 2017 Eficent +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.load_data( + env.cr, 'date_range', 'migrations/11.0.2.0.0/noupdate_changes.xml', + ) diff --git a/date_range/models/date_range.py b/date_range/models/date_range.py index 4e60e39..10e7881 100644 --- a/date_range/models/date_range.py +++ b/date_range/models/date_range.py @@ -19,7 +19,8 @@ class DateRange(models.Model): date_end = fields.Date(string='End date', required=True) type_id = fields.Many2one( comodel_name='date.range.type', string='Type', index=1, required=True, - ondelete='restrict') + ondelete='restrict', domain="['|', ('company_id', '=', company_id), " + "('company_id', '=', False)]") type_name = fields.Char( string='Type', related='type_id.name', readonly=True, store=True) company_id = fields.Many2one( @@ -33,6 +34,23 @@ class DateRange(models.Model): ('date_range_uniq', 'unique (name,type_id, company_id)', 'A date range must be unique per company !')] + @api.onchange('company_id') + def _onchange_company_id(self): + if self.company_id and self.type_id.company_id and \ + self.type_id.company_id != self.company_id: + self._cache.update( + self._convert_to_cache({'type_id': False}, update=True)) + + @api.multi + @api.constrains('company_id', 'type_id') + def _check_company_id_type_id(self): + for rec in self.sudo(): + if rec.company_id and rec.type_id.company_id and\ + rec.company_id != rec.type_id.company_id: + raise ValidationError( + _('The Company in the Date Range and in ' + 'Date Range Type must be the same.')) + @api.constrains('type_id', 'date_start', 'date_end', 'company_id') def _validate_range(self): for this in self: diff --git a/date_range/models/date_range_type.py b/date_range/models/date_range_type.py index d8414cf..9ddae3e 100644 --- a/date_range/models/date_range_type.py +++ b/date_range/models/date_range_type.py @@ -2,6 +2,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.tools.translate import _ +from odoo.exceptions import ValidationError class DateRangeType(models.Model): @@ -21,7 +23,22 @@ class DateRangeType(models.Model): company_id = fields.Many2one( comodel_name='res.company', string='Company', index=1, default=_default_company) + date_range_ids = fields.One2many('date.range', 'type_id', string='Ranges') _sql_constraints = [ ('date_range_type_uniq', 'unique (name,company_id)', 'A date range type must be unique per company !')] + + @api.constrains('company_id') + def _check_company_id(self): + if not self.env.context.get('bypass_company_validation', False): + for rec in self.sudo(): + if not rec.company_id: + continue + if bool(self.date_range_ids.filtered( + lambda r: r.company_id and + r.company_id != rec.company_id)): + raise ValidationError( + _('You cannot change the company, as this ' + 'Date Range Type is assigned to Date Range ' + '(%s).') % (self.date_range_ids.name_get()[0][1])) diff --git a/date_range/security/date_range_security.xml b/date_range/security/date_range_security.xml index 87862f8..70f0426 100644 --- a/date_range/security/date_range_security.xml +++ b/date_range/security/date_range_security.xml @@ -3,11 +3,11 @@ Date Range Type multi-company - ['|',('company_id','=',user.company_id.id),('company_id','=',False)] + ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)] Date Range multi-company - ['|',('company_id','=',user.company_id.id),('company_id','=',False)] + ['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)] diff --git a/date_range/security/ir.model.access.csv b/date_range/security/ir.model.access.csv index aabe714..1fe1496 100644 --- a/date_range/security/ir.model.access.csv +++ b/date_range/security/ir.model.access.csv @@ -2,4 +2,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_date_range_date_range,date_range.date_range,model_date_range,base.group_user,1,0,0,0 access_date_range_date_range_type,date_range.date_range_type,model_date_range_type,base.group_user,1,0,0,0 access_date_range_date_range_config,date_range.date_range.config,model_date_range,base.group_system,1,1,1,1 -access_date_range_date_range_type_config,date_range.date_range_type.config,model_date_range_type,base.group_system,1,1,1,1 \ No newline at end of file +access_date_range_date_range_type_config,date_range.date_range_type.config,model_date_range_type,base.group_system,1,1,1,1 diff --git a/date_range/tests/test_date_range.py b/date_range/tests/test_date_range.py index 5431476..f5dc7d3 100644 --- a/date_range/tests/test_date_range.py +++ b/date_range/tests/test_date_range.py @@ -9,45 +9,55 @@ class DateRangeTest(TransactionCase): def setUp(self): super(DateRangeTest, self).setUp() + self.date_range = self.env['date.range'] self.type = self.env['date.range.type'].create( {'name': 'Fiscal year', 'company_id': False, 'allow_overlap': False}) + self.company = self.env['res.company'].create({ + 'name': 'Test company', + }) + self.company_2 = self.env['res.company'].create({ + 'name': 'Test company 2', + 'parent_id': self.company.id, + }) + self.typeB = self.env['date.range.type'].create( + {'name': 'Fiscal year B', + 'company_id': self.company.id, + 'allow_overlap': False}) + def test_default_company(self): - date_range = self.env['date.range'] - dt = date_range.create({ + dr = self.date_range.create({ 'name': 'FS2016', 'date_start': '2015-01-01', 'date_end': '2016-12-31', 'type_id': self.type.id, }) - self.assertTrue(dt.company_id) + self.assertTrue(dr.company_id) # you can specify company_id to False - dt = date_range.create({ + dr = self.date_range.create({ 'name': 'FS2016_NO_COMPANY', 'date_start': '2015-01-01', 'date_end': '2016-12-31', 'type_id': self.type.id, 'company_id': False }) - self.assertFalse(dt.company_id) + self.assertFalse(dr.company_id) def test_empty_company(self): - date_range = self.env['date.range'] - dt = date_range.create({ + dr = self.date_range.create({ 'name': 'FS2016', 'date_start': '2015-01-01', 'date_end': '2016-12-31', 'type_id': self.type.id, 'company_id': None, }) - self.assertEqual(dt.name, 'FS2016') + self.assertEqual(dr.name, 'FS2016') def test_invalid(self): - date_range = self.env['date.range'] with self.assertRaises(ValidationError) as cm: - date_range.create({ + self.date_range.create({ 'name': 'FS2016', 'date_end': '2015-01-01', 'date_start': '2016-12-31', @@ -58,15 +68,14 @@ class DateRangeTest(TransactionCase): 'FS2016 is not a valid range (2016-12-31 > 2015-01-01)') def test_overlap(self): - date_range = self.env['date.range'] - date_range.create({ + self.date_range.create({ 'name': 'FS2015', 'date_start': '2015-01-01', 'date_end': '2015-12-31', 'type_id': self.type.id, }) with self.assertRaises(ValidationError) as cm, self.env.cr.savepoint(): - date_range.create({ + self.date_range.create({ 'name': 'FS2016', 'date_start': '2015-01-01', 'date_end': '2016-12-31', @@ -75,7 +84,7 @@ class DateRangeTest(TransactionCase): self.assertEqual(cm.exception.name, 'FS2016 overlaps FS2015') # check it's possible to overlap if it's allowed by the date range type self.type.allow_overlap = True - dr = date_range.create({ + dr = self.date_range.create({ 'name': 'FS2016', 'date_start': '2015-01-01', 'date_end': '2016-12-31', @@ -84,8 +93,7 @@ class DateRangeTest(TransactionCase): self.assertEqual(dr.name, 'FS2016') def test_domain(self): - date_range = self.env['date.range'] - dr = date_range.create({ + dr = self.date_range.create({ 'name': 'FS2015', 'date_start': '2015-01-01', 'date_end': '2015-12-31', @@ -97,3 +105,26 @@ class DateRangeTest(TransactionCase): domain, [('my_field', '>=', '2015-01-01'), ('my_field', '<=', '2015-12-31')]) + + def test_date_range_multicompany_1(self): + dr = self.date_range.new({ + 'name': 'FS2016', + 'date_start': '2015-01-01', + 'date_end': '2016-12-31', + 'type_id': self.typeB.id, + 'company_id': self.company.id, + }) + dr._cache.update(dr._convert_to_cache( + {'company_id': self.company_2.id}, update=True)) + dr._onchange_company_id() + self.assertFalse(dr.type_id) + + def test_date_range_multicompany_2(self): + with self.assertRaises(ValidationError): + self.date_range.create({ + 'name': 'FS2016', + 'date_start': '2015-01-01', + 'date_end': '2016-12-31', + 'type_id': self.typeB.id, + 'company_id': self.company_2.id, + }) diff --git a/date_range/tests/test_date_range_generator.py b/date_range/tests/test_date_range_generator.py index 04f7318..d7a0b55 100644 --- a/date_range/tests/test_date_range_generator.py +++ b/date_range/tests/test_date_range_generator.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)nses/agpl). from odoo.tests.common import TransactionCase +from odoo.exceptions import ValidationError from dateutil.rrule import MONTHLY @@ -9,14 +10,26 @@ class DateRangeGeneratorTest(TransactionCase): def setUp(self): super(DateRangeGeneratorTest, self).setUp() + self.generator = self.env['date.range.generator'] self.type = self.env['date.range.type'].create( {'name': 'Fiscal year', 'company_id': False, 'allow_overlap': False}) + self.company = self.env['res.company'].create({ + 'name': 'Test company', + }) + self.company_2 = self.env['res.company'].create({ + 'name': 'Test company 2', + 'parent_id': self.company.id, + }) + self.typeB = self.env['date.range.type'].create( + {'name': 'Fiscal year B', + 'company_id': self.company.id, + 'allow_overlap': False}) + def test_generate(self): - generator = self.env['date.range.generator'] - generator = generator.create({ + generator = self.generator.create({ 'date_start': '1943-01-01', 'name_prefix': '1943-', 'type_id': self.type.id, @@ -31,3 +44,30 @@ class DateRangeGeneratorTest(TransactionCase): self.assertEqual(range4.date_start, '1943-10-01') self.assertEqual(range4.date_end, '1943-12-31') self.assertEqual(range4.type_id, self.type) + + def test_generator_multicompany_1(self): + generator = self.generator.new({ + 'date_start': '1943-01-01', + 'name_prefix': '1943-', + 'type_id': self.typeB.id, + 'duration_count': 3, + 'unit_of_time': MONTHLY, + 'count': 4, + 'company_id': self.company.id, + }) + generator._cache.update(generator._convert_to_cache( + {'company_id': self.company_2.id}, update=True)) + generator._onchange_company_id() + self.assertFalse(generator.type_id) + + def test_generator_multicompany_2(self): + with self.assertRaises(ValidationError): + self.generator.create({ + 'date_start': '1943-01-01', + 'name_prefix': '1943-', + 'type_id': self.typeB.id, + 'duration_count': 3, + 'unit_of_time': MONTHLY, + 'count': 4, + 'company_id': self.company_2.id, + }) diff --git a/date_range/tests/test_date_range_type.py b/date_range/tests/test_date_range_type.py index 37a03b3..2ae1e97 100644 --- a/date_range/tests/test_date_range_type.py +++ b/date_range/tests/test_date_range_type.py @@ -4,17 +4,29 @@ from odoo.tests.common import TransactionCase from odoo.tools import mute_logger from psycopg2 import IntegrityError +from odoo.exceptions import ValidationError class DateRangeTypeTest(TransactionCase): + def setUp(self): + super(DateRangeTypeTest, self).setUp() + self.type = self.env['date.range.type'] + self.company = self.env['res.company'].create({ + 'name': 'Test company', + }) + self.company_2 = self.env['res.company'].create({ + 'name': 'Test company 2', + 'parent_id': self.company.id, + }) + def test_default_company(self): - drt = self.env['date.range.type'].create( + drt = self.type.create( {'name': 'Fiscal year', 'allow_overlap': False}) self.assertTrue(drt.company_id) # you can specify company_id to False - drt = self.env['date.range.type'].create( + drt = self.type.create( {'name': 'Fiscal year', 'company_id': False, 'allow_overlap': False}) @@ -33,3 +45,18 @@ class DateRangeTypeTest(TransactionCase): }) with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'): drt.unlink() + + def test_type_multicompany(self): + drt = self.type.create( + {'name': 'Fiscal year', + 'company_id': False, + 'allow_overlap': False}) + self.env['date.range'].create({ + 'name': 'FS2016', + 'date_start': '2015-01-01', + 'date_end': '2016-12-31', + 'type_id': drt.id, + 'company_id': self.company.id, + }) + with self.assertRaises(ValidationError): + drt.company_id = self.company_2 diff --git a/date_range/wizard/date_range_generator.py b/date_range/wizard/date_range_generator.py index b4d3045..8887735 100644 --- a/date_range/wizard/date_range_generator.py +++ b/date_range/wizard/date_range_generator.py @@ -2,6 +2,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import api, fields, models +from odoo.tools.translate import _ +from odoo.exceptions import ValidationError from dateutil.rrule import (rrule, YEARLY, MONTHLY, @@ -21,7 +23,8 @@ class DateRangeGenerator(models.TransientModel): date_start = fields.Date(strint='Start date', required=True) type_id = fields.Many2one( comodel_name='date.range.type', string='Type', required=True, - ondelete='cascade') + domain="['|', ('company_id', '=', company_id), " + "('company_id', '=', False)]", ondelete='cascade') company_id = fields.Many2one( comodel_name='res.company', string='Company', default=_default_company) @@ -58,6 +61,23 @@ class DateRangeGenerator(models.TransientModel): 'company_id': self.company_id.id}) return date_ranges + @api.onchange('company_id') + def _onchange_company_id(self): + if self.company_id and self.type_id.company_id and \ + self.type_id.company_id != self.company_id: + self._cache.update( + self._convert_to_cache({'type_id': False}, update=True)) + + @api.multi + @api.constrains('company_id', 'type_id') + def _check_company_id_type_id(self): + for rec in self.sudo(): + if rec.company_id and rec.type_id.company_id and\ + rec.company_id != rec.type_id.company_id: + raise ValidationError( + _('The Company in the Date Range Generator and in ' + 'Date Range Type must be the same.')) + @api.multi def action_apply(self): date_ranges = self._compute_date_ranges()