From 32441deec7e8e7ca75ef5fb8ac6b0871f307f3e1 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Fri, 4 Dec 2015 19:12:24 +0100 Subject: [PATCH 01/14] [ADD][8.0] base_custom_info: Init develop --- base_custom_info/README.rst | 53 +++++++++++++++++++ base_custom_info/__init__.py | 6 +++ base_custom_info/__openerp__.py | 21 ++++++++ base_custom_info/models/__init__.py | 6 +++ .../models/custom_info_template.py | 4 ++ .../views/custom_info_template_view.xml | 6 +++ 6 files changed, 96 insertions(+) create mode 100644 base_custom_info/README.rst create mode 100644 base_custom_info/__init__.py create mode 100644 base_custom_info/__openerp__.py create mode 100644 base_custom_info/models/__init__.py create mode 100644 base_custom_info/models/custom_info_template.py create mode 100644 base_custom_info/views/custom_info_template_view.xml diff --git a/base_custom_info/README.rst b/base_custom_info/README.rst new file mode 100644 index 000000000..5604e2a88 --- /dev/null +++ b/base_custom_info/README.rst @@ -0,0 +1,53 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================ +Base Custom Info +================ + +This module allow create custom fields in models without alter models +structures. + +Usage +===== + +To use this module, you need to: + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/186 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback +`here `_. + +Credits +======= + +Contributors +------------ + +* Rafael Blasco +* Carlos Dauden +* Sergio Teruel + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/base_custom_info/__init__.py b/base_custom_info/__init__.py new file mode 100644 index 000000000..7fffa6f51 --- /dev/null +++ b/base_custom_info/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel +# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import models \ No newline at end of file diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py new file mode 100644 index 000000000..f9b4f68f4 --- /dev/null +++ b/base_custom_info/__openerp__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel +# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +{ + 'name': "Base Custom Info", + 'summary': "Add custom field in models", + 'category': 'Customize', + 'version': '8.0.1.0.0', + 'depends': [ + 'base', + ], + 'data': [ + ], + 'author': 'Antiun Ingeniería S.L., ' + 'Incaser Informatica S.L., ', + 'website': 'http://www.antiun.com', + 'license': 'AGPL-3', + 'installable': True, +} diff --git a/base_custom_info/models/__init__.py b/base_custom_info/models/__init__.py new file mode 100644 index 000000000..f523517ad --- /dev/null +++ b/base_custom_info/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel +# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from . import custom_info_template diff --git a/base_custom_info/models/custom_info_template.py b/base_custom_info/models/custom_info_template.py new file mode 100644 index 000000000..396df42f4 --- /dev/null +++ b/base_custom_info/models/custom_info_template.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel +# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml new file mode 100644 index 000000000..b3570a73d --- /dev/null +++ b/base_custom_info/views/custom_info_template_view.xml @@ -0,0 +1,6 @@ + + + + + + From 096f9512997ad4cb045e44de414153ebedc884e1 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Wed, 9 Dec 2015 02:54:59 +0100 Subject: [PATCH 02/14] [ADD][8.0] base_custom_info: Initial development --- .../models/custom_info_template.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/base_custom_info/models/custom_info_template.py b/base_custom_info/models/custom_info_template.py index 396df42f4..69a30b822 100644 --- a/base_custom_info/models/custom_info_template.py +++ b/base_custom_info/models/custom_info_template.py @@ -2,3 +2,65 @@ # (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel # (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +from openerp import api, fields, models, _ + + +class CustomInfoTemplate(models.Model): + _name = "custom.info.template" + + name = fields.Char() + model_id = fields.Many2one( + comodel_name='ir.model', + default=lambda self: self._name) #TODO:Fix integer + info_ids = fields.One2many( + comodel_name='custom.info.template.line', + inverse_name='template_id', + string='Info') + + +class CustomInfoTemplateLine(models.Model): + _name = "custom.info.template.line" + + name = fields.Char() + template_id = fields.Many2one( + comodel_name='custom.info.template') + + +class CustomInfoValue(models.Model): + _name = "custom.info.value" + _rec_name = 'value' + + model = fields.Char(select=True) + res_id = fields.Integer(select=True) + custom_info_name_id = fields.Many2one( + comodel_name='custom.info.template.line') + value = fields.Char() + + +class CustomInfo(models.AbstractModel): + _name = "custom.info" + + custom_info_template_id = fields.Many2one( + comodel_name='custom.info.template', + string='Info Template') + custom_info_ids = fields.One2many( + comodel_name='custom.info.value', + inverse_name='res_id', + domain=lambda self: [('model', '=', self._name)], + auto_join=True, + string='Info') + + @api.onchange('custom_info_template_id') + def _onchange_custom_info_template_id(self): + if not self.custom_info_template_id: + self.custom_info_ids = False + else: + info_list = self.custom_info_ids.mapped('custom_info_name_id') + for info_name in self.custom_info_template_id.info_ids: + if info_name not in info_list: + self.custom_info_ids |= self.custom_info_ids.new({ + 'model': self._name, + # 'res_id': self.id, + 'custom_info_name_id': info_name.id, + }) \ No newline at end of file From 93548ccf9641c6c730bc6bd1af69a22c152de9e7 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Wed, 9 Dec 2015 19:42:19 +0100 Subject: [PATCH 03/14] [ADD][8.0] base_custom_info: Views --- .../models/custom_info_template.py | 19 +++--- .../views/custom_info_template_view.xml | 65 +++++++++++++++++++ 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/base_custom_info/models/custom_info_template.py b/base_custom_info/models/custom_info_template.py index 69a30b822..37fb7fc45 100644 --- a/base_custom_info/models/custom_info_template.py +++ b/base_custom_info/models/custom_info_template.py @@ -10,9 +10,7 @@ class CustomInfoTemplate(models.Model): _name = "custom.info.template" name = fields.Char() - model_id = fields.Many2one( - comodel_name='ir.model', - default=lambda self: self._name) #TODO:Fix integer + model_id = fields.Many2one(comodel_name='ir.model', string='Data Model') info_ids = fields.One2many( comodel_name='custom.info.template.line', inverse_name='template_id', @@ -24,7 +22,12 @@ class CustomInfoTemplateLine(models.Model): name = fields.Char() template_id = fields.Many2one( - comodel_name='custom.info.template') + comodel_name='custom.info.template', + string='Template') + info_value_ids = fields.One2many( + comodel_name="custom.info.value", + inverse_name="custom_info_name_id", + string="Info Values") class CustomInfoValue(models.Model): @@ -34,7 +37,8 @@ class CustomInfoValue(models.Model): model = fields.Char(select=True) res_id = fields.Integer(select=True) custom_info_name_id = fields.Many2one( - comodel_name='custom.info.template.line') + comodel_name='custom.info.template.line', + string='Info Name') value = fields.Char() @@ -49,7 +53,7 @@ class CustomInfo(models.AbstractModel): inverse_name='res_id', domain=lambda self: [('model', '=', self._name)], auto_join=True, - string='Info') + string='Custom Info') @api.onchange('custom_info_template_id') def _onchange_custom_info_template_id(self): @@ -61,6 +65,5 @@ class CustomInfo(models.AbstractModel): if info_name not in info_list: self.custom_info_ids |= self.custom_info_ids.new({ 'model': self._name, - # 'res_id': self.id, 'custom_info_name_id': info_name.id, - }) \ No newline at end of file + }) diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml index b3570a73d..660577421 100644 --- a/base_custom_info/views/custom_info_template_view.xml +++ b/base_custom_info/views/custom_info_template_view.xml @@ -2,5 +2,70 @@ + + base.custom.info.template.form + base.custom.info.template + +
+ + + + + + + + + + + + + +
+
+
+ + + base.custom.info.template.tree + base.custom.info.template + + + + + + + + + + + base.custom.info.template.line.form + base.custom.info.template.line + +
+ + + + + + + + + +
+
+
+ + + base.custom.info.value.tree + base.custom.info.value + + + + + + + + + +
From 8724d54fb083f8868db4ae520fb274a7ede97a15 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Fri, 11 Dec 2015 02:41:01 +0100 Subject: [PATCH 04/14] [IMP][8.0] base_custom_info: Actions and menus. Refactor strings Info to Property --- base_custom_info/__openerp__.py | 1 + .../models/custom_info_template.py | 10 +-- .../views/custom_info_template_view.xml | 81 ++++++++++++++++--- 3 files changed, 78 insertions(+), 14 deletions(-) diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py index f9b4f68f4..7bcc581bf 100644 --- a/base_custom_info/__openerp__.py +++ b/base_custom_info/__openerp__.py @@ -12,6 +12,7 @@ 'base', ], 'data': [ + 'views/custom_info_template_view.xml' ], 'author': 'Antiun Ingeniería S.L., ' 'Incaser Informatica S.L., ', diff --git a/base_custom_info/models/custom_info_template.py b/base_custom_info/models/custom_info_template.py index 37fb7fc45..505166abb 100644 --- a/base_custom_info/models/custom_info_template.py +++ b/base_custom_info/models/custom_info_template.py @@ -14,7 +14,7 @@ class CustomInfoTemplate(models.Model): info_ids = fields.One2many( comodel_name='custom.info.template.line', inverse_name='template_id', - string='Info') + string='Properties') class CustomInfoTemplateLine(models.Model): @@ -27,7 +27,7 @@ class CustomInfoTemplateLine(models.Model): info_value_ids = fields.One2many( comodel_name="custom.info.value", inverse_name="custom_info_name_id", - string="Info Values") + string="Property Values") class CustomInfoValue(models.Model): @@ -38,7 +38,7 @@ class CustomInfoValue(models.Model): res_id = fields.Integer(select=True) custom_info_name_id = fields.Many2one( comodel_name='custom.info.template.line', - string='Info Name') + string='Property Name') value = fields.Char() @@ -47,13 +47,13 @@ class CustomInfo(models.AbstractModel): custom_info_template_id = fields.Many2one( comodel_name='custom.info.template', - string='Info Template') + string='Property Template') custom_info_ids = fields.One2many( comodel_name='custom.info.value', inverse_name='res_id', domain=lambda self: [('model', '=', self._name)], auto_join=True, - string='Custom Info') + string='Custom Properties') @api.onchange('custom_info_template_id') def _onchange_custom_info_template_id(self): diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml index 660577421..e432e5336 100644 --- a/base_custom_info/views/custom_info_template_view.xml +++ b/base_custom_info/views/custom_info_template_view.xml @@ -2,15 +2,29 @@ + + + + base.custom.info.template.tree + custom.info.template + + + + + + + + + base.custom.info.template.form - base.custom.info.template + custom.info.template
- + @@ -24,21 +38,46 @@ - - base.custom.info.template.tree - base.custom.info.template + + Templates + ir.actions.act_window + custom.info.template + tree,form + form + + + +

+ Click to define a new custom info template. +

+ You must define a custom info template for every different + product properties group. +

+
+
+ + + + + + + + + base.custom.info.template.line.tree + custom.info.template.line - - + base.custom.info.template.line.form - base.custom.info.template.line + custom.info.template.line @@ -54,9 +93,22 @@ + + Properties + ir.actions.act_window + custom.info.template.line + tree,form + form + + + + + + base.custom.info.value.tree - base.custom.info.value + custom.info.value @@ -67,5 +119,16 @@ + + Values + ir.actions.act_window + custom.info.value + tree,form + form + + + + From a694cfac0a2e1dba08b5593ff3995ef9b4dcb6c3 Mon Sep 17 00:00:00 2001 From: Carlos Incaser Date: Fri, 11 Dec 2015 18:18:53 +0100 Subject: [PATCH 05/14] [FIX][8.0] base_custom_info: Minor fix and security --- base_custom_info/__openerp__.py | 6 +- base_custom_info/models/__init__.py | 2 +- ...custom_info_template.py => custom_info.py} | 18 ++++- base_custom_info/security/ir.model.access.csv | 7 ++ .../views/custom_info_template_line_view.xml | 43 +++++++++++ .../views/custom_info_template_view.xml | 76 ------------------- .../views/custom_info_value_view.xml | 27 +++++++ base_custom_info/views/menu.xml | 22 ++++++ 8 files changed, 121 insertions(+), 80 deletions(-) rename base_custom_info/models/{custom_info_template.py => custom_info.py} (78%) create mode 100644 base_custom_info/security/ir.model.access.csv create mode 100644 base_custom_info/views/custom_info_template_line_view.xml create mode 100644 base_custom_info/views/custom_info_value_view.xml create mode 100644 base_custom_info/views/menu.xml diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py index 7bcc581bf..9b4521191 100644 --- a/base_custom_info/__openerp__.py +++ b/base_custom_info/__openerp__.py @@ -12,7 +12,11 @@ 'base', ], 'data': [ - 'views/custom_info_template_view.xml' + 'views/custom_info_template_view.xml', + 'views/custom_info_template_line_view.xml', + 'views/custom_info_value_view.xml', + 'views/menu.xml', + 'security/ir.model.access.csv', ], 'author': 'Antiun Ingeniería S.L., ' 'Incaser Informatica S.L., ', diff --git a/base_custom_info/models/__init__.py b/base_custom_info/models/__init__.py index f523517ad..c7f3fd66c 100644 --- a/base_custom_info/models/__init__.py +++ b/base_custom_info/models/__init__.py @@ -3,4 +3,4 @@ # (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from . import custom_info_template +from . import custom_info diff --git a/base_custom_info/models/custom_info_template.py b/base_custom_info/models/custom_info.py similarity index 78% rename from base_custom_info/models/custom_info_template.py rename to base_custom_info/models/custom_info.py index 505166abb..3001ae6bd 100644 --- a/base_custom_info/models/custom_info_template.py +++ b/base_custom_info/models/custom_info.py @@ -8,6 +8,7 @@ from openerp import api, fields, models, _ class CustomInfoTemplate(models.Model): _name = "custom.info.template" + _description = "Template of properties" name = fields.Char() model_id = fields.Many2one(comodel_name='ir.model', string='Data Model') @@ -19,6 +20,7 @@ class CustomInfoTemplate(models.Model): class CustomInfoTemplateLine(models.Model): _name = "custom.info.template.line" + _description = "Properties" name = fields.Char() template_id = fields.Many2one( @@ -32,18 +34,22 @@ class CustomInfoTemplateLine(models.Model): class CustomInfoValue(models.Model): _name = "custom.info.value" + _description = "Values of properties" _rec_name = 'value' - model = fields.Char(select=True) - res_id = fields.Integer(select=True) + model = fields.Char(index=True, required=True) + res_id = fields.Integer(index=True, required=True) custom_info_name_id = fields.Many2one( comodel_name='custom.info.template.line', + required=True, string='Property Name') + name = fields.Char(related='custom_info_name_id.name') value = fields.Char() class CustomInfo(models.AbstractModel): _name = "custom.info" + _description = "Abstract model from inherit to add info in any model" custom_info_template_id = fields.Many2one( comodel_name='custom.info.template', @@ -67,3 +73,11 @@ class CustomInfo(models.AbstractModel): 'model': self._name, 'custom_info_name_id': info_name.id, }) + + @api.multi + def unlink(self): + info_values = self.mapped('custom_info_ids') + res = super(CustomInfo, self).unlink() + if res: + info_values.unlink() + return res diff --git a/base_custom_info/security/ir.model.access.csv b/base_custom_info/security/ir.model.access.csv new file mode 100644 index 000000000..388445929 --- /dev/null +++ b/base_custom_info/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_custom_info_template_user,custom.info.template.user,model_custom_info_template,base.group_user,1,0,0,0 +access_custom_info_template_line_user,custom.info.template.line.user,model_custom_info_template_line,base.group_user,1,0,0,0 +access_custom_info_value_user,custom.info.value.user,model_custom_info_value,base.group_user,1,0,0,0 +access_custom_info_template_sale_manager,custom.info.template.salemanager,model_custom_info_template,base.group_sale_manager,1,1,1,1 +access_custom_info_template_line_sale_manager,custom.info.template.line.salemanager,model_custom_info_template_line,base.group_sale_manager,1,1,1,1 +access_custom_info_value_sale_manager,custom.info.value.salemanager,model_custom_info_value,base.group_sale_manager,1,1,1,1 diff --git a/base_custom_info/views/custom_info_template_line_view.xml b/base_custom_info/views/custom_info_template_line_view.xml new file mode 100644 index 000000000..d682499fe --- /dev/null +++ b/base_custom_info/views/custom_info_template_line_view.xml @@ -0,0 +1,43 @@ + + + + + + base.custom.info.template.line.tree + custom.info.template.line + + + + + + + + + + base.custom.info.template.line.form + custom.info.template.line + + + + + + + + + + + + + + + + + Properties + ir.actions.act_window + custom.info.template.line + tree,form + form + + + + diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml index e432e5336..60f4e8e40 100644 --- a/base_custom_info/views/custom_info_template_view.xml +++ b/base_custom_info/views/custom_info_template_view.xml @@ -2,8 +2,6 @@ - - base.custom.info.template.tree custom.info.template @@ -56,79 +54,5 @@
- - - - - - - - base.custom.info.template.line.tree - custom.info.template.line - - - - - - - - - - base.custom.info.template.line.form - custom.info.template.line - -
- - - - - - - - - -
-
-
- - - Properties - ir.actions.act_window - custom.info.template.line - tree,form - form - - - - - - - - base.custom.info.value.tree - custom.info.value - - - - - - - - - - - - Values - ir.actions.act_window - custom.info.value - tree,form - form - - - -
diff --git a/base_custom_info/views/custom_info_value_view.xml b/base_custom_info/views/custom_info_value_view.xml new file mode 100644 index 000000000..5a08a9c5d --- /dev/null +++ b/base_custom_info/views/custom_info_value_view.xml @@ -0,0 +1,27 @@ + + + + + + base.custom.info.value.tree + custom.info.value + + + + + + + + + + + + Values + ir.actions.act_window + custom.info.value + tree,form + form + + + + diff --git a/base_custom_info/views/menu.xml b/base_custom_info/views/menu.xml new file mode 100644 index 000000000..d23ef5fc8 --- /dev/null +++ b/base_custom_info/views/menu.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + From 821a8fd4f8de331e290d19e53973510255d8ec32 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 15 Dec 2015 17:16:06 +0100 Subject: [PATCH 06/14] Fix many bugs and styling, and prepare for OCA. --- base_custom_info/README.rst | 55 ++++- base_custom_info/__init__.py | 4 +- base_custom_info/__openerp__.py | 7 +- base_custom_info/i18n/es.po | 198 ++++++++++++++++++ base_custom_info/models/__init__.py | 4 +- base_custom_info/models/custom_info.py | 64 ++++-- base_custom_info/security/ir.model.access.csv | 14 +- ...view.xml => custom_info_property_view.xml} | 12 +- .../views/custom_info_template_view.xml | 2 +- .../views/custom_info_value_view.xml | 4 +- base_custom_info/views/menu.xml | 9 +- 11 files changed, 315 insertions(+), 58 deletions(-) create mode 100644 base_custom_info/i18n/es.po rename base_custom_info/views/{custom_info_template_line_view.xml => custom_info_property_view.xml} (73%) diff --git a/base_custom_info/README.rst b/base_custom_info/README.rst index 5604e2a88..24d634ed3 100644 --- a/base_custom_info/README.rst +++ b/base_custom_info/README.rst @@ -6,26 +6,62 @@ Base Custom Info ================ -This module allow create custom fields in models without alter models -structures. +This module allows to create custom fields in models without altering model's +structure. + +Installation +============ + +This module serves as a base for other modules that implement this behavior in +concrete models. + +This means that you should **not** install this module by itself. If you found +it installed, it means probably that another module that needs it installed it. Usage ===== -To use this module, you need to: +This module defines *Custom Info Templates* that define what properties are +expected for a given record. + +To define a template, you need to: + +* Go to *Settings > Custom Info > Templates*. +* Create one. +* Add some *Properties* to it. + +All database records with that template enabled will automatically fill those +properties. +To manage the properties, you need to: + +* Go to *Settings > Custom Info > Properties*. + +To manage their values, you need to: + +* Go to *Settings > Custom Info > Values*. .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/186 + :target: https://runbot.odoo-community.org/runbot/135/8.0 + +Development +=========== + +To create a module that supports custom information, just depend on this module +and inherit from the ``custom.info`` model. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed feedback -`here `_. +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed `feedback +`_. Credits ======= @@ -36,6 +72,7 @@ Contributors * Rafael Blasco * Carlos Dauden * Sergio Teruel +* Jairo Llopis Maintainer ---------- @@ -50,4 +87,4 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit http://odoo-community.org. +To contribute to this module, please visit https://odoo-community.org. diff --git a/base_custom_info/__init__.py b/base_custom_info/__init__.py index 7fffa6f51..16c96a10c 100644 --- a/base_custom_info/__init__.py +++ b/base_custom_info/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel -# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# © 2015 Antiun Ingeniería S.L. - Sergio Teruel +# © 2015 Antiun Ingeniería S.L. - Carlos Dauden # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import models \ No newline at end of file diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py index 9b4521191..08eeb91f9 100644 --- a/base_custom_info/__openerp__.py +++ b/base_custom_info/__openerp__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel -# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# © 2015 Antiun Ingeniería S.L. - Sergio Teruel +# © 2015 Antiun Ingeniería S.L. - Carlos Dauden +# © 2015 Antiun Ingeniería S.L. - Jairo Llopis # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html { @@ -13,7 +14,7 @@ ], 'data': [ 'views/custom_info_template_view.xml', - 'views/custom_info_template_line_view.xml', + 'views/custom_info_property_view.xml', 'views/custom_info_value_view.xml', 'views/menu.xml', 'security/ir.model.access.csv', diff --git a/base_custom_info/i18n/es.po b/base_custom_info/i18n/es.po new file mode 100644 index 000000000..4e14ddb37 --- /dev/null +++ b/base_custom_info/i18n/es.po @@ -0,0 +1,198 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * base_custom_info +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-12-15 17:10+0100\n" +"PO-Revision-Date: 2015-12-15 17:12+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 1.8.5\n" + +#. module: base_custom_info +#: model:ir.actions.act_window,help:base_custom_info.custom_info_template_action +msgid "" +"

\n" +" Click to define a new custom info template.\n" +"

\n" +" You must define a custom info template for each\n" +" product properties group.\n" +"

\n" +" " +msgstr "" +"

\n" +"Pulsa para definir una nueva plantilla de información personalizada.\n" +"

\n" +"Debe definir una plantilla de información personalizada para cada diferente grupo de propiedades.\n" +"

" + +#. module: base_custom_info +#: sql_constraint:custom.info.value:0 +msgid "Another property with that name exists for that resource." +msgstr "Ya existe otra propiedad con ese nombre para ese recurso." + +#. module: base_custom_info +#: sql_constraint:custom.info.property:0 +msgid "Another property with that name exists for that template." +msgstr "Ya existe otra propiedad con ese nombre en esa plantilla." + +#. module: base_custom_info +#: sql_constraint:custom.info.template:0 +msgid "Another template with that name exists for that model." +msgstr "Ya existe otra plantilla con ese nombre para ese modelo." + +#. module: base_custom_info +#: field:custom.info.property,create_uid:0 +#: field:custom.info.template,create_uid:0 +#: field:custom.info.value,create_uid:0 +msgid "Created by" +msgstr "Creado por" + +#. module: base_custom_info +#: field:custom.info.property,create_date:0 +#: field:custom.info.template,create_date:0 +#: field:custom.info.value,create_date:0 +msgid "Created on" +msgstr "Creado el" + +#. module: base_custom_info +#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info +msgid "Custom Info" +msgstr "Información personalizada" + +#. module: base_custom_info +#: view:custom.info.template:base_custom_info.base_custom_info_template_form +msgid "Custom Info Template" +msgstr "Plantilla de información personalizada" + +#. module: base_custom_info +#: view:custom.info.property:base_custom_info.base_custom_info_template_line_form +msgid "Custom Info Template Properties" +msgstr "Propiedades de la plantilla de información personalizada" + +#. module: base_custom_info +#: view:custom.info.property:base_custom_info.base_custom_info_template_line_tree +#: view:custom.info.template:base_custom_info.base_custom_info_template_tree +msgid "Custom Info Templates" +msgstr "Plantillas de información personalizada" + +#. module: base_custom_info +#: field:custom.info,custom_info_template_id:0 +msgid "Custom Information Template" +msgstr "Plantilla de información personalizada" + +#. module: base_custom_info +#: field:custom.info,custom_info_ids:0 +msgid "Custom Properties" +msgstr "Propiedades personalizadas" + +#. module: base_custom_info +#: view:custom.info.value:base_custom_info.base_custom_info_value_tree +msgid "Custom Property Values" +msgstr "Valores de las propiedades personalizadas" + +#. module: base_custom_info +#: model:ir.model,name:base_custom_info.model_custom_info_property +msgid "Custom information property" +msgstr "Propiedad de información personalizada" + +#. module: base_custom_info +#: model:ir.model,name:base_custom_info.model_custom_info_template +msgid "Custom information template" +msgstr "Plantilla de información personalizada" + +#. module: base_custom_info +#: model:ir.model,name:base_custom_info.model_custom_info_value +msgid "Custom information value" +msgstr "Valor de información personalizada" + +#. module: base_custom_info +#: field:custom.info,id:0 field:custom.info.property,id:0 +#: field:custom.info.template,id:0 field:custom.info.value,id:0 +msgid "ID" +msgstr "ID" + +#. module: base_custom_info +#: view:custom.info.template:base_custom_info.base_custom_info_template_form +msgid "Info Lines" +msgstr "Líneas de información" + +#. module: base_custom_info +#: model:ir.model,name:base_custom_info.model_custom_info +msgid "Inheritable abstract model to add custom info in any model" +msgstr "Modelo abstracto que se puede heredar para añadir información personalizada a cualquier modelo" + +#. module: base_custom_info +#: field:custom.info.property,write_uid:0 +#: field:custom.info.template,write_uid:0 field:custom.info.value,write_uid:0 +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: base_custom_info +#: field:custom.info.property,write_date:0 +#: field:custom.info.template,write_date:0 +#: field:custom.info.value,write_date:0 +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: base_custom_info +#: field:custom.info.template,model_id:0 field:custom.info.value,model:0 +msgid "Model" +msgstr "Modelo" + +#. module: base_custom_info +#: field:custom.info.property,name:0 field:custom.info.template,name:0 +msgid "Name" +msgstr "Nombre" + +#. module: base_custom_info +#: field:custom.info.template,info_ids:0 +#: model:ir.actions.act_window,name:base_custom_info.custom_info_template_line_action +#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info_template_line +msgid "Properties" +msgstr "Propiedades" + +#. module: base_custom_info +#: field:custom.info.value,property_id:0 +msgid "Property" +msgstr "Propiedad" + +#. module: base_custom_info +#: field:custom.info.property,info_value_ids:0 +msgid "Property Values" +msgstr "Valor de la propiedad" + +#. module: base_custom_info +#: field:custom.info.value,res_id:0 +msgid "Resource ID" +msgstr "ID del recurso" + +#. module: base_custom_info +#: field:custom.info.property,template_id:0 +msgid "Template" +msgstr "Plantilla" + +#. module: base_custom_info +#: model:ir.actions.act_window,name:base_custom_info.custom_info_template_action +#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info_template +msgid "Templates" +msgstr "Plantillas" + +#. module: base_custom_info +#: field:custom.info.value,value:0 +msgid "Value" +msgstr "Valor" + +#. module: base_custom_info +#: model:ir.actions.act_window,name:base_custom_info.custom_info_value_action +#: model:ir.ui.menu,name:base_custom_info.menu_base_custom_info_value +msgid "Values" +msgstr "Valores" diff --git a/base_custom_info/models/__init__.py b/base_custom_info/models/__init__.py index c7f3fd66c..278faf2ff 100644 --- a/base_custom_info/models/__init__.py +++ b/base_custom_info/models/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel -# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# © 2015 Antiun Ingeniería S.L. - Sergio Teruel +# © 2015 Antiun Ingeniería S.L. - Carlos Dauden # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import custom_info diff --git a/base_custom_info/models/custom_info.py b/base_custom_info/models/custom_info.py index 3001ae6bd..0f42174a8 100644 --- a/base_custom_info/models/custom_info.py +++ b/base_custom_info/models/custom_info.py @@ -1,59 +1,77 @@ # -*- coding: utf-8 -*- -# (c) 2015 Antiun Ingeniería S.L. - Sergio Teruel -# (c) 2015 Antiun Ingeniería S.L. - Carlos Dauden +# © 2015 Antiun Ingeniería S.L. - Sergio Teruel +# © 2015 Antiun Ingeniería S.L. - Carlos Dauden +# © 2015 Antiun Ingeniería S.L. - Jairo Llopis # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from openerp import api, fields, models, _ +from openerp import api, fields, models class CustomInfoTemplate(models.Model): + """Defines custom properties expected for a given database object.""" _name = "custom.info.template" - _description = "Template of properties" + _description = "Custom information template" + _sql_constraints = [ + ("name_model", + "UNIQUE (name, model_id)", + "Another template with that name exists for that model."), + ] - name = fields.Char() - model_id = fields.Many2one(comodel_name='ir.model', string='Data Model') + name = fields.Char(translate=True) + model_id = fields.Many2one(comodel_name='ir.model', string='Model') info_ids = fields.One2many( - comodel_name='custom.info.template.line', + comodel_name='custom.info.property', inverse_name='template_id', string='Properties') -class CustomInfoTemplateLine(models.Model): - _name = "custom.info.template.line" - _description = "Properties" +class CustomInfoProperty(models.Model): + """Name of the custom information property.""" + _name = "custom.info.property" + _description = "Custom information property" + _sql_constraints = [ + ("name_template", + "UNIQUE (name, template_id)", + "Another property with that name exists for that template."), + ] - name = fields.Char() + name = fields.Char(translate=True) template_id = fields.Many2one( comodel_name='custom.info.template', string='Template') info_value_ids = fields.One2many( comodel_name="custom.info.value", - inverse_name="custom_info_name_id", + inverse_name="property_id", string="Property Values") class CustomInfoValue(models.Model): _name = "custom.info.value" - _description = "Values of properties" + _description = "Custom information value" _rec_name = 'value' + _sql_constraints = [ + ("property_model_res", + "UNIQUE (property_id, model, res_id)", + "Another property with that name exists for that resource."), + ] model = fields.Char(index=True, required=True) - res_id = fields.Integer(index=True, required=True) - custom_info_name_id = fields.Many2one( - comodel_name='custom.info.template.line', + res_id = fields.Integer("Resource ID", index=True, required=True) + property_id = fields.Many2one( + comodel_name='custom.info.property', required=True, - string='Property Name') - name = fields.Char(related='custom_info_name_id.name') - value = fields.Char() + string='Property') + name = fields.Char(related='property_id.name') + value = fields.Char(translate=True) class CustomInfo(models.AbstractModel): _name = "custom.info" - _description = "Abstract model from inherit to add info in any model" + _description = "Inheritable abstract model to add custom info in any model" custom_info_template_id = fields.Many2one( comodel_name='custom.info.template', - string='Property Template') + string='Custom Information Template') custom_info_ids = fields.One2many( comodel_name='custom.info.value', inverse_name='res_id', @@ -66,12 +84,12 @@ class CustomInfo(models.AbstractModel): if not self.custom_info_template_id: self.custom_info_ids = False else: - info_list = self.custom_info_ids.mapped('custom_info_name_id') + info_list = self.custom_info_ids.mapped('property_id') for info_name in self.custom_info_template_id.info_ids: if info_name not in info_list: self.custom_info_ids |= self.custom_info_ids.new({ 'model': self._name, - 'custom_info_name_id': info_name.id, + 'property_id': info_name.id, }) @api.multi diff --git a/base_custom_info/security/ir.model.access.csv b/base_custom_info/security/ir.model.access.csv index 388445929..d285e7021 100644 --- a/base_custom_info/security/ir.model.access.csv +++ b/base_custom_info/security/ir.model.access.csv @@ -1,7 +1,7 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_custom_info_template_user,custom.info.template.user,model_custom_info_template,base.group_user,1,0,0,0 -access_custom_info_template_line_user,custom.info.template.line.user,model_custom_info_template_line,base.group_user,1,0,0,0 -access_custom_info_value_user,custom.info.value.user,model_custom_info_value,base.group_user,1,0,0,0 -access_custom_info_template_sale_manager,custom.info.template.salemanager,model_custom_info_template,base.group_sale_manager,1,1,1,1 -access_custom_info_template_line_sale_manager,custom.info.template.line.salemanager,model_custom_info_template_line,base.group_sale_manager,1,1,1,1 -access_custom_info_value_sale_manager,custom.info.value.salemanager,model_custom_info_value,base.group_sale_manager,1,1,1,1 +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_custom_info_template_user","custom.info.template.user","model_custom_info_template","base.group_user",1,0,0,0 +"access_custom_info_property_user","custom.info.template.line.user","model_custom_info_property","base.group_user",1,0,0,0 +"access_custom_info_value_user","custom.info.value.user","model_custom_info_value","base.group_user",1,0,0,0 +"access_custom_info_template_sale_manager","custom.info.template.salemanager","model_custom_info_template","base.group_system",1,1,1,1 +"access_custom_info_property_sale_manager","custom.info.template.line.salemanager","model_custom_info_property","base.group_system",1,1,1,1 +"access_custom_info_value_sale_manager","custom.info.value.salemanager","model_custom_info_value","base.group_system",1,1,1,1 diff --git a/base_custom_info/views/custom_info_template_line_view.xml b/base_custom_info/views/custom_info_property_view.xml similarity index 73% rename from base_custom_info/views/custom_info_template_line_view.xml rename to base_custom_info/views/custom_info_property_view.xml index d682499fe..81fcf0bc5 100644 --- a/base_custom_info/views/custom_info_template_line_view.xml +++ b/base_custom_info/views/custom_info_property_view.xml @@ -3,8 +3,8 @@ - base.custom.info.template.line.tree - custom.info.template.line + base.custom.info.property.tree + custom.info.property @@ -14,10 +14,10 @@ - base.custom.info.template.line.form - custom.info.template.line + base.custom.info.property.form + custom.info.property -
+ @@ -34,7 +34,7 @@ Properties ir.actions.act_window - custom.info.template.line + custom.info.property tree,form form diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml index 60f4e8e40..3f0349a22 100644 --- a/base_custom_info/views/custom_info_template_view.xml +++ b/base_custom_info/views/custom_info_template_view.xml @@ -48,7 +48,7 @@

Click to define a new custom info template.

- You must define a custom info template for every different + You must define a custom info template for each product properties group.

diff --git a/base_custom_info/views/custom_info_value_view.xml b/base_custom_info/views/custom_info_value_view.xml index 5a08a9c5d..c7a5379c5 100644 --- a/base_custom_info/views/custom_info_value_view.xml +++ b/base_custom_info/views/custom_info_value_view.xml @@ -6,8 +6,8 @@ base.custom.info.value.tree custom.info.value - - + + diff --git a/base_custom_info/views/menu.xml b/base_custom_info/views/menu.xml index d23ef5fc8..5cb61a2f3 100644 --- a/base_custom_info/views/menu.xml +++ b/base_custom_info/views/menu.xml @@ -7,15 +7,18 @@ parent="base.menu_administration" sequence="45"/> - - - From 56243f2e5f75d8a969b69dea55e237065825b831 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 18 Dec 2015 16:05:31 +0100 Subject: [PATCH 07/14] Cosmetic changes. --- base_custom_info/__init__.py | 2 +- base_custom_info/__openerp__.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base_custom_info/__init__.py b/base_custom_info/__init__.py index 16c96a10c..5faa0139b 100644 --- a/base_custom_info/__init__.py +++ b/base_custom_info/__init__.py @@ -3,4 +3,4 @@ # © 2015 Antiun Ingeniería S.L. - Carlos Dauden # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from . import models \ No newline at end of file +from . import models diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py index 08eeb91f9..29793e4c4 100644 --- a/base_custom_info/__openerp__.py +++ b/base_custom_info/__openerp__.py @@ -7,7 +7,7 @@ { 'name': "Base Custom Info", 'summary': "Add custom field in models", - 'category': 'Customize', + 'category': 'Tools', 'version': '8.0.1.0.0', 'depends': [ 'base', @@ -20,7 +20,8 @@ 'security/ir.model.access.csv', ], 'author': 'Antiun Ingeniería S.L., ' - 'Incaser Informatica S.L., ', + 'Incaser Informatica S.L., ' + 'Odoo Community Association (OCA)', 'website': 'http://www.antiun.com', 'license': 'AGPL-3', 'installable': True, From 2e0f544a4a68ab888d6e8e201bc3403e3349fd20 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 18 Dec 2015 16:06:20 +0100 Subject: [PATCH 08/14] Move model to model_id. --- base_custom_info/models/custom_info.py | 5 +++-- base_custom_info/views/custom_info_value_view.xml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/base_custom_info/models/custom_info.py b/base_custom_info/models/custom_info.py index 0f42174a8..cfb06c81d 100644 --- a/base_custom_info/models/custom_info.py +++ b/base_custom_info/models/custom_info.py @@ -55,7 +55,7 @@ class CustomInfoValue(models.Model): "Another property with that name exists for that resource."), ] - model = fields.Char(index=True, required=True) + model_id = fields.Many2one("ir.model", "Model", required=True) res_id = fields.Integer("Resource ID", index=True, required=True) property_id = fields.Many2one( comodel_name='custom.info.property', @@ -75,7 +75,8 @@ class CustomInfo(models.AbstractModel): custom_info_ids = fields.One2many( comodel_name='custom.info.value', inverse_name='res_id', - domain=lambda self: [('model', '=', self._name)], + domain=lambda self: self.env["ir.model"].search( + [("model_id", "=", self._name)]).id, auto_join=True, string='Custom Properties') diff --git a/base_custom_info/views/custom_info_value_view.xml b/base_custom_info/views/custom_info_value_view.xml index c7a5379c5..2c3c6f8a3 100644 --- a/base_custom_info/views/custom_info_value_view.xml +++ b/base_custom_info/views/custom_info_value_view.xml @@ -9,7 +9,7 @@ - + From 74224c27bcf2db15033cc77fbf515691bd89a91c Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 18 Dec 2015 16:08:09 +0100 Subject: [PATCH 09/14] Better string. --- base_custom_info/README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base_custom_info/README.rst b/base_custom_info/README.rst index 24d634ed3..e1f962e79 100644 --- a/base_custom_info/README.rst +++ b/base_custom_info/README.rst @@ -15,8 +15,8 @@ Installation This module serves as a base for other modules that implement this behavior in concrete models. -This means that you should **not** install this module by itself. If you found -it installed, it means probably that another module that needs it installed it. +This module is a technical dependency and is to be installed in parallel to +other modules. Usage ===== From a8dba795edde02be4227622a439b1c5b052ff530 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 18 Dec 2015 16:13:55 +0100 Subject: [PATCH 10/14] Warn about data types. --- base_custom_info/README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/base_custom_info/README.rst b/base_custom_info/README.rst index e1f962e79..14f3b5a90 100644 --- a/base_custom_info/README.rst +++ b/base_custom_info/README.rst @@ -51,6 +51,11 @@ Development To create a module that supports custom information, just depend on this module and inherit from the ``custom.info`` model. +Known issues / Roadmap +====================== + +* All data types of custom information values are text strings. + Bug Tracker =========== From d1b340b9333da599fe5f50eeb322a04df1f8ffe5 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 18 Dec 2015 16:32:01 +0100 Subject: [PATCH 11/14] Fix bug in domain computation. --- base_custom_info/models/custom_info.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/base_custom_info/models/custom_info.py b/base_custom_info/models/custom_info.py index cfb06c81d..08e133647 100644 --- a/base_custom_info/models/custom_info.py +++ b/base_custom_info/models/custom_info.py @@ -75,8 +75,9 @@ class CustomInfo(models.AbstractModel): custom_info_ids = fields.One2many( comodel_name='custom.info.value', inverse_name='res_id', - domain=lambda self: self.env["ir.model"].search( - [("model_id", "=", self._name)]).id, + domain=lambda self: [ + ("model_id", "=", + self.env["ir.model"].search([("model", "=", self._name)]).id)], auto_join=True, string='Custom Properties') From b00ae9a1023bb649171776f9cfdbf8675621272d Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 13 Jan 2016 15:40:55 +0100 Subject: [PATCH 12/14] Use of `@api.multi`. --- base_custom_info/models/custom_info.py | 1 + 1 file changed, 1 insertion(+) diff --git a/base_custom_info/models/custom_info.py b/base_custom_info/models/custom_info.py index 08e133647..28df25c5e 100644 --- a/base_custom_info/models/custom_info.py +++ b/base_custom_info/models/custom_info.py @@ -81,6 +81,7 @@ class CustomInfo(models.AbstractModel): auto_join=True, string='Custom Properties') + @api.multi @api.onchange('custom_info_template_id') def _onchange_custom_info_template_id(self): if not self.custom_info_template_id: From 01fd725751d9a8ed3ff296a4a02c40e370480cb0 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 15 Jan 2016 18:38:28 +0100 Subject: [PATCH 13/14] Use model codename instead and make its linked record a computed field. --- base_custom_info/models/custom_info.py | 49 +++++++++++++------ .../views/custom_info_template_view.xml | 2 + 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/base_custom_info/models/custom_info.py b/base_custom_info/models/custom_info.py index 28df25c5e..883a4055c 100644 --- a/base_custom_info/models/custom_info.py +++ b/base_custom_info/models/custom_info.py @@ -7,28 +7,50 @@ from openerp import api, fields, models +class CustomInfoModelLink(models.AbstractModel): + _description = "A model that gets its ``ir.model`` computed" + _name = "custom.info.model_link" + + model = fields.Char( + index=True, + readonly=True, + required=True) + model_id = fields.Many2one( + 'ir.model', + 'Model', + compute="_compute_model_id", + store=True) + + @api.multi + @api.depends("model") + def _compute_model_id(self): + """Get a related model from its name, for better UI.""" + for s in self: + s.model_id = self.env["ir.model"].search([("model", "=", s.model)]) + + class CustomInfoTemplate(models.Model): """Defines custom properties expected for a given database object.""" - _name = "custom.info.template" _description = "Custom information template" + _name = "custom.info.template" + _inherit = "custom.info.model_link" _sql_constraints = [ ("name_model", - "UNIQUE (name, model_id)", + "UNIQUE (name, model)", "Another template with that name exists for that model."), ] - name = fields.Char(translate=True) - model_id = fields.Many2one(comodel_name='ir.model', string='Model') + name = fields.Char(required=True, translate=True) info_ids = fields.One2many( - comodel_name='custom.info.property', - inverse_name='template_id', - string='Properties') + 'custom.info.property', + 'template_id', + 'Properties') class CustomInfoProperty(models.Model): """Name of the custom information property.""" - _name = "custom.info.property" _description = "Custom information property" + _name = "custom.info.property" _sql_constraints = [ ("name_template", "UNIQUE (name, template_id)", @@ -46,8 +68,9 @@ class CustomInfoProperty(models.Model): class CustomInfoValue(models.Model): - _name = "custom.info.value" _description = "Custom information value" + _name = "custom.info.value" + _inherit = "custom.info.model_link" _rec_name = 'value' _sql_constraints = [ ("property_model_res", @@ -55,7 +78,6 @@ class CustomInfoValue(models.Model): "Another property with that name exists for that resource."), ] - model_id = fields.Many2one("ir.model", "Model", required=True) res_id = fields.Integer("Resource ID", index=True, required=True) property_id = fields.Many2one( comodel_name='custom.info.property', @@ -66,8 +88,8 @@ class CustomInfoValue(models.Model): class CustomInfo(models.AbstractModel): - _name = "custom.info" _description = "Inheritable abstract model to add custom info in any model" + _name = "custom.info" custom_info_template_id = fields.Many2one( comodel_name='custom.info.template', @@ -75,9 +97,7 @@ class CustomInfo(models.AbstractModel): custom_info_ids = fields.One2many( comodel_name='custom.info.value', inverse_name='res_id', - domain=lambda self: [ - ("model_id", "=", - self.env["ir.model"].search([("model", "=", self._name)]).id)], + domain=lambda self: [("model", "=", self._name)], auto_join=True, string='Custom Properties') @@ -93,6 +113,7 @@ class CustomInfo(models.AbstractModel): self.custom_info_ids |= self.custom_info_ids.new({ 'model': self._name, 'property_id': info_name.id, + "res_id": self.id, }) @api.multi diff --git a/base_custom_info/views/custom_info_template_view.xml b/base_custom_info/views/custom_info_template_view.xml index 3f0349a22..ef81b7008 100644 --- a/base_custom_info/views/custom_info_template_view.xml +++ b/base_custom_info/views/custom_info_template_view.xml @@ -8,6 +8,7 @@ + @@ -22,6 +23,7 @@ + From b404d46b949f74bb76607cf5938ca60f74e5e710 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 3 Feb 2016 17:33:45 +0100 Subject: [PATCH 14/14] Add images. --- base_custom_info/__openerp__.py | 6 ++++++ base_custom_info/images/menu.png | Bin 0 -> 3542 bytes base_custom_info/images/properties.png | Bin 0 -> 21669 bytes base_custom_info/images/templates.png | Bin 0 -> 19551 bytes base_custom_info/images/values.png | Bin 0 -> 14824 bytes 5 files changed, 6 insertions(+) create mode 100644 base_custom_info/images/menu.png create mode 100644 base_custom_info/images/properties.png create mode 100644 base_custom_info/images/templates.png create mode 100644 base_custom_info/images/values.png diff --git a/base_custom_info/__openerp__.py b/base_custom_info/__openerp__.py index 29793e4c4..79982b965 100644 --- a/base_custom_info/__openerp__.py +++ b/base_custom_info/__openerp__.py @@ -19,6 +19,12 @@ 'views/menu.xml', 'security/ir.model.access.csv', ], + "images": [ + "images/menu.png", + "images/properties.png", + "images/templates.png", + "images/values.png", + ], 'author': 'Antiun Ingeniería S.L., ' 'Incaser Informatica S.L., ' 'Odoo Community Association (OCA)', diff --git a/base_custom_info/images/menu.png b/base_custom_info/images/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..08fb7905087b8c5c935ee0483d1ea2a63eb4584c GIT binary patch literal 3542 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 literal 0 HcmV?d00001 diff --git a/base_custom_info/images/properties.png b/base_custom_info/images/properties.png new file mode 100644 index 0000000000000000000000000000000000000000..d6ae74d00a9055506c9aa19684d01655db5d19c0 GIT binary patch literal 21669 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 Lu6{1-oD!MHO(f&XX^5XM^v& zy;^7OjoToxxK4((zeh*1FP)dP&id?AnVv`IugMmbN)aVGVA5b(r0@Q8(vcI zY{s;^60!0>LK7^w58rru(Oa(Q4Y}QK-crtMx_5Q^_jbKwBgx%hFPI}8-OC%57&h7! z-N#60z4KvAv@e$yJM;F?uCAPp`s>z}lCAx;Temr3VD;dmaQl(wzQ1n8rqV7Rzr39T z5e4+Dau4~}tuKpbXjdmASw4AgO@58Z&qq!#>?M(2)vi>!-Gb%b_ZeS52XoJbqm~Oo z1iHrWjrp$*sEdhJ!chY2qt)iZm=rB-ZCgT-aiUy+??>v=REMsuZ5}6SzK+rtdQs%* zylJ)bxY>w2ZJ2QZPMpTE#zS8%Z{;t=AGZz-Hp~6+?p~7k93fk6bMxo+vBu6yhar4_ zKcjn)vQzy+@ZjL!)AtkWnd)etk4tsB{0v1&lndwA{Lbtu0=NW6aA7gH23iYFHcft6gm6+c&>vLRtdnT#JZ8<{YNV$hyLVCs=ECBzoEm=yl ztLr#+iT>D2-EGS=<@N)f+R+3)S=k5X-;Q@>!W;3%h|f=`Vg_Ya#jSiZz4-oYFclQ) zk=S@Bzgn`SmU@nI(8h%PR{Q##Q!H|R*rgR6-&?S`JXle1*N!l&t*xCgI5>QFmZ+hh z(30rccPJ}olYK~EO)ZRxnK=fH9+`7tZP?k~gzaoC#l*(;&AAv>IrgUCd84JJ#hKKW z{UAe$^qQ~ecL1R{h-&9R#27b*oFK4o|EAZE&9cPBl3rXed5g#3CzIU<4LRYSb!u!8 zd2>U2T3YV+b&X~3D28OalUTf|6~*}OA1Kb4I6^^vy~$8zENSf1D%XX=LV-f#GTrQ^ z?mHJER;95u16c>0ikHwqV+vu%S#lrP+M*Yin&|6u8vIV!ei|ZMBoaONt4Fd842-S` z60cnNOd(8vD&@Sz2%(pcXaw ztz8olL3DL>EqcrBi6CEkSO;0^=nb{Z^vTd;tFbN^iiTN zKfbUv9fH_e;i|{xYE(GbmsvFaEoV+oh9%BZ1;wq=-p55ude^5Dx9#=G;M@iFZe@1I z-nOdbLUlI|CV8`5SGSNEh_1c5bzE@z6XM9#S)se?uLz%ajQQZ#`1uJmidAKO%?k~+ z0889oAvNN=(-4+lVAmDvbT>LFDQk9ik8!M_y&kqhB2CD{Alxe1EgwlvTxm7MzHKr` z;v~3g$DTe&ln<&j32=x(<_{?Bx>_{^=(0MqDIb4w*$WFOfOc)=6;=}&D?NoP-IBwu|4Q=ckX^Is&DXB|^WzO5 zT)aS#bz57T?^2tDrGBhH?evuq&mjjS64^ZwAz$P$R7nyHT7R>>B)@~Gsi`se@bC#g zuDi&%PSkT&Z*I7z$E$FpZfK{-b8c{Qb2e0@{h`lMh`M9dN8tCT=Z&?Iwk~&3-y%9R z9n_)*4UC!O(*kO~eR>v_pJ%%&onwlbv}qE9VOw zm6$lV_m|Pu??pHhs>`%7BnR0(OsN&A^hYo`C-=0sc8L*Evh0dvRj1HiiN}((bcbe> zrfux)qs&wezv?O-!fax`Yzw`9U202cZ#5aoF=GTPJ2B^8BeU^~Oufce&{e%~af8$* zvfPtoJ)L~bFS&ds8Qi#ieeSlF&icU$3-_yM2>75 zn<(#O>RnXYT)YrMt&@stk77D}Sm-YNRm{*_tQli#>)qA4f+EKwl81%eZC$pO`kTu>&Z}a)0K9iK| zyL%$POHn_5JX)a;P2q5OAgj=bkQzeQ*ds>gy+`jzXZm|=?7L&o51*RJ`mD4vi# z`x`bnIoS%RbkRCFO%S`KDeX0{<>FFuuLqy|AVbU0uwaEuAb)(a4-+0XJZw}e3ULQr zlW`SJ<0+|r`O{ECCwlPsnY~1lFL7<-5_`JLY*jx@3YxH%$@)G@%&v62-s|^9ToueJ zU2O9EIFpc!TB?6(o5ZIsA-akQFT>bg@}{U7Lc}^H-({k-+;NU=uKz*W;Gk7SWv!N; zUeV{zuEx%vNH8}f;pBa%l;A4W3Fmo10ReSg(|`&SUlLctN}pk1)SXz%0|B!3%3x1R z`Ee3?CFV`XO_jgp<)q{T<5g&iH5saM(tYqCGLynB7T-@>1dGOH5}xtKSY1`p@XklB z*RK|^?=AIR>qe9}wF8-$huo*{N1>uN;i%VdPOPtb%pM|1wyQCp_%oB_l#mu3Et!y= zwMHc3C)QZo1AJxJVq*JRt;%e_eMg8{&wI1V^yh{#zh0S)n;T&`aA0x|j~EcV(FQ+{ z7Ops!KF%jznr5rB0~^yB5$FQhrP8oYou1faQrvrQ9DK z$Z4KionAUuS8l2L3U~=&5^B1dn)X+}&6fS47JH1(_Ds6_E-w(PHssjl<;rl1m1c~t zby%|R)oc0q=E!{&jt(V*gSG0_?z3#R_Vx%PBdubJh>(y{ke^ou5}6ni8Tp=K3^U>7 zb!lz>mv!-Kz)Wbrd++DhVZO2mKbr5VhdP)fDMSXv>mQ?!7N}m z=j&>>7IBmAunOn#TZMWhw?MlH+0}SW1n?8a6c{cF-?p^09ISE60p=h&CMFBWq3VVJ zzx!-|iK`?_gRde@Rnev43nqEc5u|$j!;g=>f!$ImG^#fj-J_~MGm;O0% zwIuN?V~lI_BL!xS!7VpJ9eIvCr3%QhMR4DXf7cSlV@KUs3EUJB5m{+u-@Z@5Y!;3B zOtDhuoPw78>RK5jhTq}wT13=4k#3i9(04$O%&u%RG-O4R?)itJfeyZ?njY*tzJRP*=;yT;&0Vxr~1mfyKxETdDPGuKB*T2n=dJN6fg zaP0hp1}$~kLmjviAm_!5fNQ)FT-I| z$p|qU=UX3%Eh(~oqPE?c94VZ?w*!W~C&C8HZKIqXeUTED6iFDd?f7pQ-zrM?fKy|@trg&UOt?G!C;zr zG=VRcn|pP@UOXY2HxAd!^|9Nkti`bk=UBDLeYPIGk1awu+xB3C`nwJ;kM-|2j#1}J zRzqgX%lD5+QDiO#j~i;5C1wvhR`KIdW{iPTe)3+DETseX!p9m3WD-IQJk@A4~Vi)f;FjU+92)hlgw zvsO{mM2?3jde)6l);@os1r%f6J^WH(LiiZRSbReiX~Uvuzn<^r zYy3P$UJDzc;46*cpc=EcSMN1`rdvgO@AHeoVtN{}>6|%3L2&VM-P)^eqzEGSoTrZr zn-sdeai)H^1y6WJ;AYFh&$iKNhxy$AOVZ^IK2ym{>J+WT*w5~Qs@>>{?m6?*TQolY zzaDx`<9Ek9^$N~FyE+-E7q}Y)UOA2aJH`|GC+x#%&{`?nPDlJ5<8l8J_N^bLJ$$L+ z{!fgT`FGeSD7Jm>+QvLh_qKu=7laUx?;~C!N_Wbn^P+g9ot;O3r^U5Y)Q5zYe~E5jCX zetV2Q5fz!n8KLZ1wfQ_AeFc&G5McNUKxsetTbm!4t4mnZwq z_2M}jC!{WOi`jeD{Wx{gWaG{)oMAGFwQWV( z*WBM&JR#2C8RIs_H^W@XZ?5sVdw=l`ytjb6UIoRKu{Fg5)?W6xjo^?~Y zDkL=Mb?@%o_eC|Hk5bjL8H3ZRMu&3~6VH!$tPKcje1P*o3;5W=;SE%1CHC;-4K`ng zt}dqa)SbM(fK9E%ttDhZ?tT2@JxqTx*m*b8LdwaX=q+oEntsUnw)vT+&EkJ!&Wm$@ zOY6~K=kF)v(9aVYAIlYC+bS6f56ivz?5x*J-r&yXqexzX*h%-+tAcTLHTwI4{wVUo z4A|mS_gS@dcMdt}V$B+V+S+T8lAQ8q!z4s!T6lWv-Ev;1`reJTy=K*XQq7?Bc2ZHj z=Jh*wO7AH1`EC1Wb)fk!2It2LXfH%*wA$N|R~TipFP3k9nRK-U8W_Z0u_KOzm!~*D zjKdewm&Yz$Vl0mExtz0Z1J8D#zF`1Dg4GyrddBmJ1F-gx)E>)!fq3o5w?p6+^f{)EmqPsKq)|P;QIJo0&0)NoFZ1 z%RbTb!K?-8=t){S=ci?0AQ*?s0DS;M?hK6BC7{=V)s4~Ad?aS8nceq(q?p+;{{0;2 z!*OI!MP)UJWKQY}Z+)Y&mICLI4Xq)Ub$Ic%%AzD1DeJ7zIA5y@@5ycvJysDNog0Wd zKetV#SQ^C6Qa8!vO>77|_^V1{5VrL^|NYewFFu$EqJrGVyYlp?#WI-(XNB0vip#g% z^sJd7TMo>%{vm1fI`!X}`?FH4z5Hk5ZX!&3U};LVt1CpTPXWOMyf{EfD6G)dHO|5b zVvS;AVg@T6l32o5Yf{^iB9@;&>*-WMDsSrpk)=LD|Kyq^8*I4Sd-MiTGe2)o}at}NCyvcW7bgb@N8dP!K&@r zydsl~Bs_|n0byyGA!l5vDxTD~#;T^Ke%;h0Gk*GNOq>LXJ#{v^tSv*xJlLiq1=*`5 z!MYs;z(4RgBr+l(zo4f>krq3qI3Ii7VJB!uW1rRc`>j4xw$pP}ICq$Kjm4|u^((Q- zZEdgb-qrjh7{u$P_-r^V>~#w==(ks9cD`fdK}(`f&La(JwXhd2?#k}?YX>dd**qOT zZNpH%tU0$e=hUmC!`oicJYbcl2@AtZ2p4wOLp_*V|Fw7(&rHIx7|ojSXyvudFLLaABj`WBw)Pjl z&3LZ2j9VcknVBr2;|kmt9u3H^-&WF!{-GR>e@h86HRo3A4rX?ITTr9Fx=j_EtsJtr zeJ5}7;~w}N?$%&_zShnP;cmm&-WmhLa}tl9d&;cFCo?dVHhtyw1z1hcsy>#oTwdPX zhsDYeO>%gw-cIs3Rv7|k=J4UeNC$@)Gse3rD)#{+IVC6okIYCwx7h9K4B3ziPTgEv zQj>IdUq}|(@c$|RVn0P(9OeGbHdWMXo)Nftsd33H8L_eFEiE{&iIic1CAoHuX{9%= zPFqJO=dvQoqH5@;XHuXl92GSYoy@|K)00#7_uO^9^S8qUin0C5b*UJaztS0(H8@v z1{2e^1u-~qg^>36H0=V>X+L7r+0pANQ`8)sus zR~pCPHqFhi7#cjiF8}J)tH{Rj`^LuTN9N`L2AP?eIeX#4RMOiI{HkmbIl3bZv0M(L zT36t$%_=jl;kJpT7h^Fy&qTliwLTOYr3O?gn!X>*__AG+pKEj9#|PcrV_sq1adU1| zyU=BVp1jntmRp{wUN!0&j*2p0!AIe0{i{YCn7cRetJfypH`{{`)))&&4LkO}H_Ul*!8p^n zLxw~7)>Ehrlh<5#nwXvM$b%oGUvoShfct>@&MIq%0SYLS`wDw(bI`w}x%-(B^&aNt zN!tr8>OS`n6=K(+CEvOg>dIra(E#i93j;7(ZeB~)@G!qvRXO$?x0IB64jOj9&QoWm zCup|w_o=*V{JGIRB%RR#j-P+TCCqy-)b;lFbLW&b*k#HlFV{Jqq!^lf(`d$rv&L*J zvk!T+HjB;?O@}O5XsYIRdgp-`G{41DW<8V1i_$k8!rmvP8i&Lyp2(|6zg3qSWKz_p zQ!_YNPMNC`Tb!>Gn=B5BDa%$8V-~mh-gpAH?2Y&R{cfhxKJpZ6;WN9w=H6joet#kz zb-GM;^pSP3x2JkHq1!a@wVoFIZYM z_x1Oa-wdRupG7C;$1l4N06zCmS{idiY8qN*C5wrbHhsG$&Y}1K4i`qN>u2-&-3v8h zU_i<_D~Q(_FGxw1O0ggZ>vSFHPqYfFT|6H0NE2~R#Q7bIiwnxExu-k5@AVO2;U;Zu zNxh71&-Vuy8RYxv6akDo8W^Qxjc?5u0Tiq0?p`jK`gvlZndH8)T4N+oV^kzC!tmh1 z0}%1WHIDx#2mbb!0q$W`4=H-YQP;>Q8ANZYs&9a50WtjEz*O7B*Wux@Kz4x-WSi&; zL%Y@(fmr@E5MVRLIaNJ-aiZ9D+~Re#t*vb&(C?rVUnFPbwjzuqB019PsL%VG=NF!m=r=C*PRdz&7Nx ztHME42-egzv7jdGT?eu;Z^7kt()9N^&w`SYF2VObbG$7Ngc_KSvjbzkb7dcrN@ zPdFw#(A6U}f}UFN-YeioYSV+Uv(Fmr3SH@#ll=PX__{Mp?8=iW)xScZ%Evg3%+>IN z9cGwrvD13Q;1|9@R!?aUA2;K(5RFM)Cs{A#=)|dZW%Fj>Y$IDxKizBaf^sIP+WPB% zrO(1aCjeZ^y(m)dF(8bdalYDDdL{5tBMNU-miMkwC#Nm(>+{`c>QwtFoAnoFj4*(D zlymUHzuFOI9!jL8L~?Qqs9UQgq`T~_It4~C@!zDy0+$vU{nqAJ{L^naWHqN%yz{(tKc6*j{Hz+xD`s_8*^MK#-UEH>`#*+F6(D>hvPSd{7Je(=ol1|F}hsjI6S((9d@_pgseIZRAU?ESZiLO=g3zLVknxYJJa5Pcp` zG;VGIb9bz9dZNoeWReGeO4~5j15Z#@S3jP#nU?*p&-|O(%F5N;VrxdFRN@IV)5}B3 z-3eVT+$Fn&QTI&qHd&O*S+Sftn81WLGi6t|`WCtWIjZHR+CQIwbb>PKm5bz)w1AkJ zn>hzR8#q}V^%E)UFX?&KPNN}D(!&nL5f1|v^(KE)5Bs-yH(4t^G+57$%u$NWpWR~s z;Qe2pnW%<&0YpaK@vsz_aJW~~K{V4LRap;X_cfm;>Jyl#FD&A^VWs^*DafEf7aHXb z)=$EhJPq`8ejg8enCdkgBqJ@}jDL5$Zo^sn2E^!Fjik;G?=ymMM*y|{{&<+;LzZ+< z4_TSf(gJ%u9gWMH&0lQx`TqOd+z=^OkwY-4&(Z5?H)UC*~z;~!e7sj(jRa3UIX z*JNbW4f;_PSeXI_N+6=$(h%`y4LU9w)xWS5?%^d8{Rxw#&hYOeD=qeIx;1s|>t&6Y ztDZ|P>?+B~Sekl*$yi!r#f38ni{&<^SHAzqdN#J#OxR=5Yqqx0jA zyt%0TH<`iTnfrubGxq_aadvQ(Vum@rQd9VwQBUXnu#|fY$<9yPW)377&&otiQ)?zt z6`BqVX;>fp55oThRA}l@>p7N~*R}pf0nNl3G@obA(La@@kbTf2l`wj-an!;_FSgf# zb=nss9Z-Wm=yGQMt_83@c^!x4$Z%tw-f~Vf%la2XX3rPR58v!^37QHI3->TKHvYII z?fKAse(Vsw>(qPmjHN%fR%BW-4|;szmtGJMIjrS0SdM_i?{O!tO}1BpcicGm^n1Bc z+O}-Jy{Sj#R$iHU%{Kp@r&P0W^}9y3vog&k zT%@M~8;2S)Gc(_@UH~K3do&#nSJ+fs@rUOeymD*m=V*9pNDu#IsKaENTEF9D@{|y? zY5DVN!NFDLjhi}WDC^ME$DIX*g$(^Jr>3u8{Uj_N-n+yJ#DH10p-1w*E|3VQjkEpA zGJjuB*>K5iK<<7EqL$el8xKO%G>I9vsLuj%VsL36=D$^*eubb<4Aw0MoQU-d}@{EL`V7lNFq#4o8YHQNYJRN@aO2VRrJKGdkiyjdD5PG?z(IE zwDCe!)!nohjW{MAHv9d^aE%HuZ}pSP!Uf6~Xc!`k^!Qe?5?G6#CDJ{x;MLi!LLLhm z>h`fj*U*qm{*YT(IJW>O0+WPM*7{B-FiGA#V32y}8%@P8hbeSe1gwXG9#&k68(0~i z%mCSX0+Xz-uuZ%R;!+=A~y3*Mh=(0P{#L`HODk+m6W z<}7@KFm(gk*zEb?wDGpiX+5u5@Jm-moQ5Wz^lfQrF$N50ZSVfQbGk?o>_>7rtm9yC z&+H|rgXHPY3kjish96q-zkxCl-3utm-)L5?~fN{*q%xo4_!yXT7_t4fZgZihD;J2quzkQ{E2l&K= zGJqXR{!%aUcwGDpEs6d|-BhkF5~%6b1g0m#qA)Le+Kb^~0OS>5WdcmujHi2iMg@3y zrYE3bCC#AL;vQ4?!opcEK7onXCouoLdlPN=>G*^$;}Bp%Xx-4sUP3$rG=sh<+#gTQ z8?lj04PQy{_~rfJQ-y)l4Lf|US?NG1hOOP@CdBV}3*?wZ zg`4#&1B1bz&Y>}8w^vr2LHmNpY!!xI#x9D%XmMr1tO+VThWDAp-HN&u*d;v8{lVg!tZvY`h?fY+x_%F3FNjN5FuBt~|Rt&aT{Mljv@_50= zE~ZOU>Jd;92+B=EkG8kgTU%NpIYAOj(+Nwh1j$(_`9&_qnjo_89Uf?a#{SrK1+nkt zrHdCY!l=tVE3JIR&87U<0KtZ36lQBtHH<|{(`~A~cYdU9B}bmxLWwUe6$a^k1QZuS zHIX355`} zu5gZF> zd3r=WzMj){&ueHBG{qR9%7bt^4S5S5(HxmWSOsY{x!6sa+kki}QReOjaCQ*G%uu+s0E_7exemL5418dYW-^>IA_l zXfrm&cSdd`aI-NH=AqNv4p|2nv^NSMi3J0LQsvwv2a}S{^Xt6}VGq)pkHD|{n9Q|Q4uFUo@mR-WKE6W`nPCt^t*K_>Z4?54R6Lwz$l z+Y~9{cGak_raYDO4DgeIN(VIHl>@+O1EyhO(5|=_ptX|=-kfkK^Hym!{A+EgJ#)TtWd?R4x19$!y1l6~YG2lnoSLbpXhy zGxXwm?T^_7gkn7tcs zD7*yf5Omzxbgc0*&b8%6`iAdS(EO!y(~lk9V7F|@igz~sn4oW-;ElDcSY5dfo}T;! zlowkE+t;QXa3h?V#;DwgeLMvSjW&EcnhS{>Z$FbYW*$2gT)$ZeW5K*AdA)4Ef4SI&Yeb$f!@aaGKFdD}| z_vxMjcBNfh`T{xBNq=eVL-MZ*wz=GU;fG1sgbjw4x4-q`9>b;2TDrOgrKOrEQ0vj(lmY1g zy8VCyz*^9m+1dHo*-Ar|4n6N~J_8MMmzUi^fyX*))uXRR8kX9nuU)^c4V2Zd?+F<= zfx3I*hMPv?K#cQW9KDc_!I%KOgtAQxoGSjWH7V)eH7U?lOhS*I1Kq$`;e`xklO3eQ zLzOSDK*BB{paCp#(phqO5hqA~c0%qgr^8@5Ttg1PSIo>}Zolpo>X#{jR6SMtB#byAt$UcbQ8>?!kwfE?>6=tp+S4}$5}ejUZW)Gp!LN` zR3<=tK^35pdJY0qS%4Zbu%g>46q>^t4vezG^NZ4+??F*hg$G#&AQZ^&@j`){4XO|7 zSy_N3xqI)P)}1>qIM*vjmxM(`V&dbcrHRr{5c>LgDAZ}<%3W2}h;d_@ntlcL4k;|Q zmr>TY92i9)?`N5r3ub5Su~eW-L2;4h&6`iM)zSID?1+hqUKJ53!)@F4WP>^hEx;dv zQ>xuP8uEqXjUm7afJT}C%)X4h0PVgc)8CH5?e}~39kc;}zd@a6A-8(+^wFK=L}{-g zonn*1sT4o;0|yQmR5_w4Ztz4|ze?yXWYwXv!*5~C)OPIe&qpgPT29Mu&X%>}a6K1H z{nI{wz9kDvZd%*gWaT7iUfQYfT_etmP(+%-G#@$SPm*vpyJQF z8XAP_Rm17Pd4PB0^AKWAqcYbG%2jr@rsRPs3#*-`}29nk=y?XBw%9J0aK^K(NI(-a4i2?=!b*>^~9sordu5k-MGe7w` zIms(1SlqOmBiu_fyZ`c|J`Mey%)Wwn2D6`W#_dY{b1$00fyj92%C2UjU0;Rt8uYnE zb2sz;J2eJ1x%5BTSc!gpeH}Guea6&h@*FT^!NCWO-P*)Zncu(PgPb2__c`hLYDG}B zVo>kxQvA(Dlt^R5G$7q4%|I-st*_4u)mzR^bV1|N?Y+FamKMAUFzNs_0X5CpH=iBM z`|#mDV52gl3Dr%EslYRJBqaJqC88t?J_OOOlozg6(nvX$t}zlC-IOan^y zgCeyNPzz&F^5})r4)6}H!V@K&QXsh*D6=N74mlCP&0N5A*NtnnQ0_pZklvTIcR$%j z?+%Et(Qb->aPc)kLEyJUTk0EBAY+t58BZ;4LUQ_;;|@gZZ{e&3peoQ|q*fca zA50=vp~OIn=2<6tWhJHH(`U|TKX`B%jwxKL2V2L*&ArjSrv6h)M<+is^NUwQcXzkB zw=P65=STnS(Az;CzW-%%-*5uzUDDuxyW9f}_LLD&1L&(-e%Z)OP@6@*fc^90yns>e z8ZpQea9!R~oqApg<3~MpS=F!`jTKABCY>17+2ooR=VCeXu^+5AeHRg)safl!a4|k(G^}o!zC!o)N2>!oERsdOo-T|i+XfFYm1p4-z{oe&2Ecdbb zRUV$4;t=Q|%ejAl2m+b^6U#gfSwT(cZbts)nuOOtRrA0+ z%#qDlB>w$H7FJy8t-vne3ngT zs3$u0+rR}n#R;lSp%4cY3KwK%auyob>FiL6J0pa~#r59eKrT3tK0?q|Q;-S@D4r}* zPtMwSF7*1?AE37(^$b)R9KLX!Y~$e2yFxH624&gntA2G?0+%z`Npn@iK2Rv|{v4mq zG_d8MG(QholXW|bvFCs#s6Y#xO7}z>>rge4 z4QeVwX1yRVVC>5&;9Pf@crV5K$KhTA zfe%m%l(azsmE51PeONc(;AsI@0n|K_C_y`QPEXU{y;F#g->wF$aUby@@QRD;M93_@ z1XZtRes7utu3N&7)DCCnkg0?qYFwq`NWQ9IgO+(ycrS>FvO)O6n~9Z%$SBFS8@flK zP$+=`CSdxDOh<-?-{++GLY0CWZBBt4|CQqGpM;HOaS)8$!gPv^@}L+H7bh6UIY-2r z0x1Kzr&4~^0-Zuo55Q6DZ>r&2YXVFMNDcs}4T@K%pB@#s@9)25wvz(QBAMm06yg~u zTtoNId4f_HJrv~f@FKFO^9za=rLBOUf;k+VpMMSBJ>8YT1^<^rMU(hWIZ#15aDX7S1ONhjXXn_O=V=NRI9q?d&5xv}qzJ%$L3yy4{~u>5 zv^q%WU%Pzy^4Qu6p!e4=U#2me^HHGI=k+KDQ3poTpFsf>^SB&Bq!Gid6&TQ-ikGQJF^t>+h?^@^_VW$N}F|CSc*~ z1z^1T)=jIesOeVdy@iA z0vT5gyQ;>?gaPZtz#@6yj!@JKyeNyh*-DJE(`;oPGW4z$Hxb_o8*->99=GTGqx7X)PxF@u8!4FHG$WCQi~Nu{Nw%TP^hhEf;`KS~m^>Aa*C zf8!>fZV?`YLCaC8Tekti(hHoKLf{>d`V5`4Jv}QqZQByX49aaHfia5)jwfGH_0udk znCl_KT{9v8{Qv5UqBd`TOiWZf{Ipm0&)BoT%~4Qo4gmxc-@*`9R@rjff6O5QY=8+& zOJ}F2-vWs=C@zA0F$liMNp^j$E;V@;XfU;-=lMGW)<$yB(q1zO)+t?}CKo!Z+kVtP(?Io26G;J4BVpZ zXY?mFSbK_1<)H}z38iiBpx;)$b^&JvmNy3k3j%SfAif!?^`M=|yTniPbicWDCRtur z@KOq6$~t_8dpc-mEr==?5fMQE+mZv$J}|J$83BbQ5Qn40|NLnI7IvVp0pzN=@^G0d zVAFCPp(yy&OMq8?d{75s*%b=FTLM#|0|rnLI$O||OIB7E6qhOcQ$c8=f(%ma=uA_b zbggm!J$CbJQxkh{HCXCna}XWv+SA%NG`bKN1Ts+Y%K^Wf%RAJt!0XR1EYt=&ow+Ij ziV|J)2&teVOK$z=nFcT_Z0Wm5g;g8V++c+&c56~l%yYKCa|BY*SMGuUzCV0CU?1JB zSrCcFjX19LLWes5$CUdlSxx@@X|p`tg~|kFX6&mKWo2a^z+(iu2M)Q#V7$Q9vQ#0+ zFM0gIQZLqWWd+-bNHy~Ka4!td!<02wk%O1FrT7{2J1*~mcJF)Jo^OW_NdQU#K$5f? zuhRSiysSXXEhs=}YId8YP6dp@3<`9e9{Bnu3nDU#irR}g8V$YX#{lZ=?KTvy{h#3o ztN33K#mT^bK@`fsZlvxUm0fAkrpMu@^$cSt$^FyAXcSQUWakQ8 zvuplZRu&h_5)`^COhb#6d<2gF6-!X!IK{*&z`@bia%>MMYRU11LgvC;@F41rTpVXF zUX)y+i=D=qfn6Hg|2KY4_hfERJ1;w1928|{h!wn9Uin3Z&6R(`P?;splgm0P3$h0C zUY#kkG}xY6E5~L82kTtduZR1su|?x1!Oo`tYDd2t=jaBwh{-V()y~|W5F6@7!(Ek^ zU28!%;NVc#f%?&JVP63}&^i0&TYhaVTyv8X0JEDNh8U@LlenYW#v=fj-ES=E_ntDk zO-=KPNn|SSQD=UymbA(%CT6l8H#xI(PP*JqH%k*S;jtc(4UWQOa!`8#^7ifmO>dN_ zF|V~)iMW3{kO5Uh9ypNY3L8OXj@0q3%1Hkt%3EB75v6*2)vod8b-fL>04IHBUQERmq@`&m=<{9^EbeIv zB6gFB9DqsGWdKJ4K6+jPP9PpRstV5NJUhqEp7AQGRC}vR@f!ctp64RL$hMg)@NPi9 z2N}xNx%v4wg92yH5Z7hrDE2=x?Zep6X`d8PS`SQkLK?FNnRe83r)@$U8|I9pSj54x zg%euZ=loHb(n%5(f_ZuQ$opy@QAUms<}=iHnt^S2G~m>kiB52|kQZ0F+}ud<$g{Ic z3ZJe6Y|-iWca%jZdDpNix_M4q+sI!N6j z-$qPAnk%n~iiXacP44jHpX}=em6VYqj!{DnKwD#j8R1nk{0xXbrW^=kr z5gfnDAi4mO%w|Mjx-bDD%VmC6d|X`WSU-DsGo^)@6dBFJ2j3vnS=s&`+KWg z6Bmd5Bm!jTF~@~nZ&o25lx%7)j6P$MyP_sRl<8Spe|>IfrKQQR0vw9sR%0uA`c$}K zXY}CQD7tsD?J5}XyQ3ZD=^uNaG$tkka6pO^9&6ch<0)3DvWn$+(hS*vMy15)!eY#D z;a7@NcYMjk!P<)3n`_!+9*LBUNDg|o6)#8zzwDy23h{&WH4Uof%hSiO9m)~yojmML zy}#Nv-N}Vhq*5IB%o{;Zy~*Zs8GEKYb-{_WLJrDiPmxo{S>81bSSeoKSQ%Pg zCao!%x}8h)U*vD_U$^vKCkXZk2CZBP*nY#FgY{onTmG856A7GtY0^~cPV~@<*A_140q4DYK4SUtbC#fqyGFCDXd=`{VMP!|HKsj?`?8t9$y{w3@bq=Eh#Ubx&c&}bJKTFwp)f;N1^2i#f}MW^#>x50T;$+iu+gnuLGr0qsU-CXGVxME+mUDoU7VDX5hR10<13N_Wyvh{=Yzulx8&3n$z#KgAeJ8O&St+ z`5`$aMV)%e@x?!quq$czW`XOD2m)1r5c~hUK@&p-8SrozWTKYn={8hJdf&nvedtMD1r=$Cjx_cKO9KY9(0NSe-lVDzx+RwE{mfc$Y^FJp9~;xkCTK_J9J^9 zo+JG#K>g6$GX~RknNB+Khjg+!gVenuX|cuNZ+cBi7&5$_rj;F)=JCzU7wbk3Z~?V+ zqxC${;veVUtQtcqAA{g9CAwcY3zqNr?NN!1r_J}NrD#oq8ZyxHj1N)|DM?=jyjzXC z0?HPE{%iG71CiCxNacM)bJxt|0sLeZN)vS4EVjGNB3+J({opJkE|gxXd=P0xF(8bTI8&m#rH>(8N&NkoG|NNkSmTYQ4jn2 z$i-(apOr~gT5O&;TYQ{wW`(U8yjCyKSL{+C9|VfRIjBc5{){#Acop8PiHV75&Dz=K zt8N2%VALMhntRI1%5luhra=KNP8Q(B4d3NyZ%ajU#$b1U|N8C{7gtx5X6-alg|~1r zX$BaWPn_cDQHI91pz3?jypRA&ui4~e4ERpBg|JOtR=G>r;b@lHQI1`DRHEk-Xi|Uz z1+lcB(GZzqje{#&5#OYy6rX@3ib|xL+?VE%^A&1d*~6;=#W~~PtPxIS=cypOdOPz) z^BS4tTnX_RGMFGAn%T05l87pfOXa}^p_j~*(Hzv&7*M54`w2l;1q8Jat7ei-P1#M# zVIt?>^frdqyF75T0DJtCT=*XGOd2A(zkd&Dtbb|7hcenwZX`gk3TRl_SoGQL1^hm> zn4PeL_lc7?FXrv-VD9m)=K-X*s2P2GRpLe~8L+@WP1j&&vlMnmk4M?(ss2<+N7dYZ zt=$#ITKeQ0Iax##Ld+Q38b8#gMRso-Z0c81zI%cBLgNig$pbm;wwzv&37U!J`9|i4 z(cFHIiAO^ubL!SsV76`I?oZ@YB}Jr=B2i~=UGBP$L3HI^)<~2XIgnL9I@90WLCiW? z-oCzGct(Sn>F;?{3`af8GloCfO!{VUWR;fy#{82juMuDjM6_V|dv2Y$xPwusgX^)z z4=D0f!rw#9_t-nUk0W@kkCxl=na|9H0^#xLHu8K}E-u*`7HaHjr#SJa8icsVXegHG}z2Y?(fX|WHL1Q+{s=fLkzbz?Ah&28Qln03V8bW z!m?{L+3AnYm zznKTaC*|}IB5Z^C#GgNpo@ESIH%n!$FogB3Y-}2@Zl<5J(SyuPNicC=3m~c=NIxAJbtJ@Hi!}B( zrRZY3d;(^zl_YP9sr87`erBc0U{g)4Q*l1C8~qc7Yon6}C&0q*`w+sy$Uhw@*un|L zf5>aPm>m|H9AXymEPb@P8acYU-B6~+aBINxa}_T84!R00+J|Tg{+p(oGE1rCdby!k h!-zT8!knEw`SG8;?e>0p3?!Kjense3UK2(rCd7Sn*2?@zb#Dn`v zBqXF?;QBY&5qOqP==6nuNFDAWRLJ1rLH6Vg{7vI1so|(>W8&yyXm3nnYHed>%;{ic zZ)|MsU}odEbf`v*gyb>_;{IKgXVI7u*Jq))nDylj&#(nU#O0vZufF9T;Un|8=KtnI zVT0?S#jO&p>r&duS5nJ5A0o@Uioy4xMUKN=RwkYi{HBvX4<<)a*v*uXcl;!ULpJE z%FK^+@7|rJ0$gz&emBX@z0MSw7*!*r9n8W^YQ@cwf%fNK2x8(ab9*<{(J_OqR~Ii) z<>%)_CZa@k4xgO7vpCTlEM!0NW~u`J{j%YWZr{(l1Gh9OpsP==6PQ$P{-rA+SFV-AHl2SQ@oyt_3{+8=ArhDe|eN5 zy{xQk2t&8~i^4moXBNKgVYT|+H$8vYYbpHXNun+djgiyDgQDXn(hHwzs&!sZ{`%Dd z(fz9Tb^tXEV-9*K<-|o_J1uHH6;tVmY+ z2+{j9ye;l#<;N$l%xl6l-}KRfpI=Z#cO*5>$cPEi)~2AU$}fY=&qp5m(*Cwv2s7v; z7pfXyVeZ^wU1BlO^g!1G@y?_z^7JRo{s=?rtx4|Dk4*KA(({YMk~ig*eGN&A6!06Y zt%uO;Y-xQT=p@rK&4LW6BY6x9sjf#SzIyd3&VfPDUa7)`uBSP;&0a|K9o?Dukcmf; zF0O7ry~>r<)l;UXOp=n4aBFslD>tX2wDO<4ZFo=9GwyZt)TPdgHkI+8KVQ+jP9+~B zqY$Bt_LxVxK77dSXuS? z96sSV=DCh5Q~S%rXiMQl^T&wSdeKPH^YseQbnO3lUU%GHot9Sg%CpM!46CjbRM_lm!a)2=zAwF{kn_SP zWN*{+KU&9EYVz|JAdk0 zIy%A)sfq7q3JnQqnssd9A*Z}>;b{9K6&0pB9u-y9r2c+2M0ciU!Ilc^-nwgFe?~h$ z=;5QJGEp@#@qwkO7#fC7>3`%x2(7qsOnh5?CMh*IiQO!`wN&a+z2?v!6CVHi1WQ_V zyejujhxCe@=hi~cO-{|5n#txzib$PR{k&1x#ey;PR>K;X5s{+8^7+Nq3Z;d%s zs46KXxQq$LQM%}|`PX-jxfJ?IIajET>E%7Nv+ELi7N_IA*(&BLqDOQ#!m2md;u`NY zF0Ovb3%X3G4zy8>7l&GACxzFaLsnzex@djxs$@l!*I-p~aq-+_xPF*SRbtx$+)XSuhkn^0{ z#d65i-~QaruEV2M?tQ(zObD$)vyTd~B5B3N0-So48b}p2wd9r-8RI8UuCcSrAXOed zyd2(|ghZZYW@WYf_T~&bJ3F#t`ot>(b8~ZALHj4Dx&4S(`f#_j5zz*9A_ zKP6?1lAIhw=bTJ6e-=dq8L8+wmiKDr<}qs+j=rIbVvjsM=gp^i$;UHI*lB#@%eRVa z=P&4Oq_*zP%@6T8Kh2!djg8OJ9yATG8uL;aDq&E)|4R29Ys36+S*4JOh|8#)`NO-i zGBSLRzmWx85>ygH9Dv51Tw@_;;ZRzRhwex-qUZt&ASRgwy zvt+oA{afdSAMszm-Xr??G}e5sH!VFUM_o@ZRw4TKnNMfJVq)5^@28FD3J~K$1|@i+ z+R_ohXG2i>@n3z|1E|liAw>Ye!wPlrbHMhx!VSZh9>Up z94Bs*iputtWeDI$zcSYG6nc-nt-ZmqsFm84kT8LbaKpCk?y_el)l-_quWrf}74KYK zy-6;yynN#Yios*%#BmCWYRVqI$>nDgI5$;2J#PQ{&#(Pi%m-E?gq+efx7WhV8b7QJ zC~t0t>iQaBPV6%DT&Vu@+UvCp{`B0SWlmqkQgy~TRs~Y4TjUaN-@bi;()Ze&ywhql zz2zx}GHH)yLs&e0+FNWj#8l*Gz|YT*Obobqs|DlCas9fSa*UF-!NTRBXaz-7Hm{pwAye}& zTMnBJJ5}{tvm-7PO}}i9_94TlE|_HuuNl4=9E`TGupl~YR}Q_mbdA^Vs@m@bX#JrS z{toX{@m;%ccJ{gR=Zw^qx>IX6r2>whirWEyo4{^{goN}i88lC0n|a8IIU~i2mzXEN zll2l{-DyQdsyH0Z4Zk_y>k!K_$ zFArg9#jQe*H`541ryrHfmV?gl=dRW$Fzry&)6?r*o0xdwIV-E*ZJAS*tgi2=np1;! z>+E5*so8a|udhG+wNRF;>^>zWB{K2*cR2_OAFoQYi;0QxT)i$VTrtF|ad2;|b(*$@ zPq7tsSVzW&Qj>i#`7U={i?x+cURO+9LLf@jeLXBStvP;TqN{vy>pC%bpgeTg5rZWq zDh|`~BAA_I^SO5%H=h(y4-%G-@$(n6F*2&5^}>^IHlCTvDUpY^A->nE%d6eHm+8DD zmK@9)^`uCPPs}b6AYJFTx63bVtrv$K188ZIMk)f+l9M&FvbZFr{WxyeB~o4glxR0@ zba2~QMMo!7mb+^8y#zBs=<(QAe@US?0e>fGDu-}!Y{OScSsAxju{5`_Jf1&f{YxUf$C_x*p5*qz3!vSC%b5wJ&2-85q48z(uRp6X-y&Y{{c#=yxMqxV>ohjTxl)|xCizv{ z>JQsmS!$YD{k1(8c6oCv^=vN3&0C14iS}Zb(0!lRohw9bt7E@1``2Ho7=iq_A+Y|%aNgxFa85#g%hOBfFL=(QgWH@9+J8%}ZN1z-@c#dEpl`(TcR@kV_|FyQuc66Y zzyEb2?=<~=Ht#eg(4F0%8Dn6C!TuGQ>#KjR3MKZv`0~V!bL4-&@$}zUIUSgw_IQ@G{TH8Ozx5BD0%%mdljFzhyp_QHzk9CT-o} zvCr=P>gvkZm^prUZ{XX2AR&;2#KvadFlrz-Gs$ zjq~p~vV%sfmzFP`Jb7}k(zQT@UNKIJP-bCi$?vv0lbtg%a%{I*yXym8WN=90$F{b7 zHxb4&K9U2N$cV^vItH)Q0r9Pmay?9cZQX^!?rtRjJfsJ69lpST)+Mbb1&3c7aD~f` zZ8Y5Cv*=9~i)nnEH?zAwM)6$*>&?x{i4h~6XYazY+QORKaJYB!4}z+aBz@5r&oX|h zw@`JIdp{T%HHmY3ncTBqZtA>&QyUr@>ZE`^P@t9dV!!~-s;^-a97-PY09t;TqZIVL z5Gtw3nuLl)wrcR>;&=(bm?8C#KaN;a$XTy!dRSOnFBaqJ6GS1~^2$eWg?m`9=Tg+} z$e|RIdj`C0>OA7T_sseH#ZMcKg-l2=-djDW2}dD!Q9na!BCd7plDyzlHUb8HoJXp4 zh52yx_NM8(P4r1DdJ5+@>0F^$5!+{RvUd^ZR*{v}==^}-(lc#In8T_0QDCw4{fWN_ z-(>g4a7ZdE({x{73~im9bJDNl2?-5N`u_bp7naJKY>KeL(36*S@$8>PdY{(F1 zQNUrg^1ABuWLl zD#t_8oI*VFsyIIHyTXZx7#sXHZBv8+-tnf$FcAo}nnnsadnTV<92yd62UD+trhkY1v!MA<$jUly+j7;|l?#k28B8?O)^>U&WQgpb` zaq>HY;}Hg8Jt64n;I6>a-^Hw>KeNay&Cc~%aIk%xwQo+%3`r?cJ*K~fJWs`vGnBQF zB+LJ6v4ZkrB)g46L5seZCZ+er%j9gWq<#R|J8LC3J;h$Lvdnek=8FhZQGIGEOtKoE zx)*Eojl#LZ$t76FytJ7!)SA(LDd#s_J*%xvOUg#9F(Vb~%*@OHc4ZV5)t#M-p9)0j zc`Qp3iWyA28OkUvy{79rc|Sn${m1&pUEZQqx(U+KbKSDRMV2mU)-|5Rz#XKC zx+^qwO{uA=eU_uS>$Goc$0w1=5U31BvPSRm5R@)KKgci?~J4bLzlBB#J1?*`42kQ9dnw_M%VjJ~l-i~W--AqX%E+`8DE2Z67QCdP0 z_w2@8--_TRTKBq>7J?ociw#l{Y8lAtT`S4Nt<$3CB zxuqW;xdfbDRVeol7bE`t=qZy2?q#P#7kj)ViVr=rE>nOveu1_~^M2fP? z9~I+33)?XCTa|S5f50?4t(paN_cWR<{GcY5=nPLqJ`Y(}@Yx20QH2ZGaCOggJp%Xb z+VG`#L)&*8RMoYknjJozgIpIem8~VJ+-?E)Vmt?aSld%O?am*q>-* zrF&rb18O!+H1(g|U0hia3!UBF85Js#(7H}VC+emGY*O%*D_58h*r~Q2C_PhKxhmw4 zjswQw=EJ3S5Y6vm-qVT81~y)WxER=&@aByzLPD5En3Ua@*h8Bxv;z&ubweELK!cKKcdlKv_vC6B#VsG>-#Zqv<_nr$R}2Qbz6)TSniNz{U%`gSRbILZE^RZ=F<< zkk~G32=FJ;V~>sMh!uT*|ERLgp2ci`5kn?q>uXV2Xlgby`CR;;qw4rIS?`}y9glX- zecTEDD0D=(s>o~$S4P)8( zy=EBcJXa8Xn1O`c(%S)=l^S9w#l6ClFf)4yhv}yTM$tRN zT!vwZJD7=?IY0zMjp|_aH=IM^J0QSvwuoz_0SCF-wf4CF!yBObg4sDP0uht=&R=nM z=A%~ICqX?1FOAHV{3D*GlLrH6DbvQX2cHcVWvNM6muSO$uS!b+(tOkyhz5>D3Wqy& za37d=UiH4#*Wcft!e8IvD9=-8*1@Bsq_i5EQ``r?1A!dJ{ zzrq>k+7Madmm*#U4_+82G|{y zj&n6Wsu>|$xE#yD4zJ`l%j1*QW)DYVMcFs&7$%%=Pw zsQkO8;yuODmjo4Jl1g82#<9ob%?S?BwILRy1T}9 z73fp2nWjEqFk-q8c3$Y*!p$4!Y48CDrEA~J?1_IZ979hhqQQoE7ZA|dn>Oy$heX_z z&+(}Kss8~u|F0Jc3e9`YOit2;_gQqo>qSqRnI!N3aN5nkjv4ioLwPC16KRQ}C;5+! zt~$bp-PU7X3c0N&=9|g~8EQB#{9p?W>E#d=<=W8m6iZ6}(7iIrTj&6ppCP@!(ERO9 zdDyWc#r1ujlBaX$z^4&wBFzW%Uzc%)QRDhPuDo)kT+cau$TBo6EH>=$W=b}Ik6q_4 z2X(_km}DIsP>L3LLzGk(QvT!_83()%S>ALMRFw6P?(4Jf>uaW^Noz`E8UVcBF^QbT zHq+8`xhMx~DMkkos{twB>jF_)ALxRyy=)O;DDo)zUv_rK6(0m~vZwEwhgezJtEe`Q z{J3TVT``N*`foEilapItimiYcfTa7v+|jv0*U-@Lg}E1$8(iTWxouE3V4P85*9S4q zlG4(|dZjIU?gxcskrt3A%sqT?*jONN{`vEVD`y(Jd7g@jC8~`DYI!*D;jPS&a-!Oj z|NOC~@$DK_GS4GzH;*)l3oXdXlBW-phWhTznKLl>i?O^ui_pgLN0yeBD5{cuAQRs@ zM(M)Up$}AJ)7?xp%gY^qrXS*sW8`|skC*P`oU99i0)o^KZw2*}LW(k+p~W?NVvk7N zTPSfL@bz+)LP{m@m3`f3$-Y1Y)bo=V4Wrr=({K8&t;KFBAMhj+67`E+5i2WX2kX+u zs~E+oogHcfvXxn1K4AEiU*?%J7|tk#6p^;J71t_^zb)>?64rfgcyNaBlSpHPKyqDi zs{)QvFXvY0;~x>Q00#$$hk=ndr1I!=GuIz3PpWg$h?#LueH@ zOs$wq7n8%w`5pnJQ2NKBLttvhC=Z!ud*C|mrsk9TJLKLmf=Ip?v%Tj$o3 zC)w@&RDZRa<;S$4thb5J$)hG05R7|Mt9{)vb^u-G|1{3Y>rWhEkgjHZ`Vu@jdIVyE zl(yfq*?3bmFtETrUH{zCb7rqbs(sBRAfN^`aCq18*X)K$7 zKrsOmgg^N8^XCcM3d$+$>~|SjH#r1XiT0VUE@b;o-F^#|jB>moK-t_p?Xi*3A^&y? zK5valecyC$-qP~3pDJ;iWjoXIa&il6pXi4ePu6wzzCPh(FfJvZKXBeWvQCVQmsyaL zY7&jMTXBTxaT|gd>&w-SfC32YHBDtN!L?-Y+8bfl#nW zeYiA$!8ha?HI_$To$|i&IiXDRaE5xulXdGNT<)7)+q3kPNxgy&eB&sBrgKGA$BCT% z0`XP(;GmYOx_Sn52+>DLP6+^QGBaZqz#v}TNFq1KNQxDg&mAw!f2bjxF>b_XB@hMB zEK`kx_v?OKzNZmr>9c7nJ%CxeL&jqMM1s$wniExy;%lB~KY5>etzH|2RJ ze6A4Q^VIS9TBRP{d-Zwstev3;g7M<{*IIwyTEx-%Y@VUrpPxbLT;AVY64~LfFZ(V? zLUJ4ZZ%g`Vib4$S0`2~A&mCH9D@KwNYkL@kz4r$p@x4fppx+&{0%sl%KY!`@nZK%I zgVH~KdUW0b8@)^0@egBq4 zo}v@t3rc~*T515?_80-b{!$nIs?8NJu=D}uJuN$1fyh9W+KoHTbTS6e39GRx#*z=( z%=c%x;ri5}uR|mO%?9#~%a{}uW9?85#nG$M%2E}i^FLWey51tw!1?B$#KID9kT@;V!-vNSXiX1 zWopFJ@R~fZKq10DeM%iDFtvopA|of)E4n_MfYx)DCswVGAAeq~A$S32_uz3R*yJAi zc^j$!`}ZntZsjXjEOSu@X_SDKR;QS&tE=1Zx2(NCexyc6UmAESkfmFpc2VGI5EEvs zMn6L>OUc2ZfQnNqQMbZ54U_=39$sG6^xivm0W`c)C8KT`SFT>g%_Ib-mzF+@vj*!L zG$8=_hp;KHem%iW8W#IUyTBxX*hhqR1*d+sj&vY>pN$tbD$Wxcr79X4sc@fI4T|sl zqJX_lXbxrt3PFRr@{bQwgBGY}3Qw8|Ppr=NgrZQ~kg$Dx4$7m%M9sE^mnyF!%GXET zof_#}W*WuucAeYMB`Ic_bsyV~wRdp9^`P*4GaJz0lts$O%Hl@dm(t41wUW&(w%0I+ zTrYylOSG3%<2Z={6zp(!iCec^g+k=d^J;9&k{Q@Pf8gnhNwyv05|;@0CAW!>(&ZkQ zF;pHE2??3;q5e~B2u)4R!Cb?ar!L*T>{n-}MK)yw6ruXWyi@2)2IQ&_N)8@C%=F~fu)BJ2Y!C-=1nDNHs2j;{6Lohdo(#cZMisH z293Oj%F4kYaZnr*vl$f$4G%whkcZ%maGOg@;j{aFzf6Y@qiCic$p^Sz8!5_e>v|UA3l#87@;+6yGgeTN=yp++I6O=iSDSHrw68 zxlKiwI@VLFgZqee$4mGT1s80>!tR%Cz+(-XnxW(KyWYRZxHYWST|j%=)_SDeDLpGI zwC-0>FaAEFj%UlsYtKbOPR>Lcbc<`z2$$TG>0THpz||h2xW>XFWmCPGJZxP8v@X}= z#KZ&SRC`Q@`%;zqqeqVh3(bQ;*O2|{)vMZN8X6k6KVOda4G(9A>$xFL`AJZSuAY8* zlZ3?fOX*~oHZ->mHEkI0&UjSc(=N$%)#L*Ko4kMGGpr*00G5huF zA+3|Nd*K^;_sONDx04bRqj$^xe&079a=1a>#Bhm(gq-r<#>^R?{ls*6;{X0?;7A3@ zPdSLP(9=#$PL|Tkamj5xKoTbn2%h!^&?3sp#>D0h_@wO@M$)=mRW&u1lpbiGa& z4OQ2BBq{kC=mbaTqMNrz3!1ufz<3f$Iy%viaUK8sybF2(1;0~t-O!_UTA65$+by;p zNhh|S!GCFaP2PubYA1sh>=Bjhc8$9i9F;9BijdjRAjJtGQw+sD^Q=Jwd@1}E z6(Yi`ZwC(XVmr(fV1$ zU;^gZ!qZuml{@d}0m4$x0kkSKtUE%?W2gMa3r~L^qn$-DhJ}PALW@&XRTZK1vw~0x z%7Xfa1^{e`vwI_N9bp3TmFY`S!VXre#5x8t6p`9KQcQ#Zo12?%YXhdCK|yiaz#XBn zAS-F+lP}ZBu8osJHhiGt_>VLaXT2A=BLu7>K@>>T4#G}?xKd-pB`HoDAV zIvq}gyu7@Of`XXUcBLy;MNf~OtafLJdC!R)Pil}A8+MvC09m-=)f(Eff{;X1Ky=7=Z{K!B*;KQY3}sa; zmdgUvMJ9q3=K#=vf-6M8YA|uOCxwHNk7U1zed-4CQFxP)OL`%|GdUpf& zmBmnLK0fCxtg=3uyB)2F1}tv)?hSQ(8f2q9>X*8wpsEa{Ha$S%`|icgP{?WWIyFPX z2+%&|59B|ghP1>w@hMqZVcL$8D$KlvIBRgTXzBL_5NYf0;H_kYnS8Ut!h|%l&QX7g zx$1j{E5qrtmX^hlit{A3Jb(VwQCRk+t3uInglzmeN$tZMLEcfPn&fh$qoaF9q47&B z@efgm#DVPg)Hh>axP1u}hx29Au_r1QuYs8(A|kZ%9=|?&?%dIYZ$T!JTr^S}2H|JA z$?Ld!R>_@dz}WL5)ozbb*B}fs2=~h>u7dSCM z4mcNvE#lQ0mCy2kc1Qz=IA4RGuQ^hY`5d(~!HP@-@#OAM&F;`oD)5rdfCJj+p03Vx zl@svWhjLn5T0)|tI8u7p5QD{5QF^YEl;GmmK#o{4;v6;gJ>n{YDBJZ~uNX_q&3y>1 za|oCHEukFzAaS%BDOb&@-b!z2ZRI!lPEG88-Wq+Da6N2oCmZ`Tb9h(>kV2%&s zfmoN!sa$zS%t`BG-ufW3Dkn-gqN6i~7fU5TjA3Od7q1XF_j6R^co*RI3ESJ}FJ8P@m^J<^CC4Zg zzz4Cc0DJ-oQ}1%;TtLx}0uuyO6|jVTnGUFEhDvPIp@;>o2yp1mj?ZUd*68$kC%idipsZo`Z^7X=FQL@PD?U5x5Vgd&r z0)(Ril)J$Z~Xm(nPNm-SKfb+Ch*_7W!9I@04ib^vxfJrIB&v|H#>{TghBXW zl3E%L4h~_fg@R|xzvIxl0WMoNeOF3uLV-I*6l6MXV@lS|<$9OhVu(}f>gpQIGxDq6 z#n^za=t8*b87^};I)R1gbo<;PDj%n(2*-*MqGTu5V+B!#-4pVYOIL+X-5uaUy-@5a#Pd8@QI5N3EN1q!JdDFOsCg#v?iPL=4 z7Jl73skGFtd~GmiHZ8^LKJZ`E{Q%7vHX0ii9WO1dI+24O;3Bm&0QbYi>&C==)zt)t z9`)L`%F?US0`DdMLav_A$y?Z`HWE>%uEz$Yinxq${;0b%$63AGYb z_@ndGElU?_HY9&eGs}iO=%xvH3wk4>Iv&QEhav?!;=v(E-Lnd~wY0TyUyd@EL8p*tWe~~WGW2)f%@U>&va$(G=HEZ95l;Oc zAJUeNjJkLvi-m*)xT5#*iEESBfe=wqQu++0pNG7V&lJ=3X$C$EsP^;0f}g<__Fl zD$`N$oa9hmtr)X<^!RaPORK-r`iSf*0Y7F4nrBoiQX;H1l!)cltFMw6Xn_3Wm{yNd z9Py_Z00nxWSiv=24V=ku%Tm6 z>|T3T3I1UX9!Ig7Wuu#&nOWA-=INL#dk_d8*ZHiH_SsEuh|Q=TEF7UudoI)Gn`e9& zC+UVs@01xh+P?wpeQ$+Of8O81cFLX$Wu4k01yTv>SQG|fNoE1yTqHwzJrRMpT( z4hy>%k!+dUdQ8yQP+3JKI#~{A2PLTFT^op?Nril3EculIb@6qoaV+7?>28=MITLZv zhxh;e#oWL8*nh5K4I`5Ue@N~v3kE;?H#_t{e^mG1)cyWP{{Q#=KYxJuU$(-Fo0V(2 UE0N!YQw;pSq{98|dj>xL2cSLFlK=n! literal 0 HcmV?d00001