OCA-git-bot
4 years ago
23 changed files with 905 additions and 198 deletions
-
74base_comment_template/README.rst
-
13base_comment_template/__manifest__.py
-
135base_comment_template/i18n/base_comment_template.pot
-
181base_comment_template/i18n/es.po
-
29base_comment_template/migrations/14.0.2.0.0/post-migration.py
-
30base_comment_template/migrations/14.0.2.0.0/pre-migration.py
-
5base_comment_template/models/__init__.py
-
88base_comment_template/models/base_comment_template.py
-
48base_comment_template/models/comment.py
-
45base_comment_template/models/comment_template.py
-
33base_comment_template/models/ir_model.py
-
16base_comment_template/models/res_partner.py
-
8base_comment_template/readme/CONFIGURE.rst
-
9base_comment_template/readme/CONTRIBUTORS.rst
-
18base_comment_template/readme/DESCRIPTION.rst
-
31base_comment_template/readme/USAGE.rst
-
1base_comment_template/security/ir.model.access.csv
-
5base_comment_template/security/security.xml
-
92base_comment_template/static/description/index.html
-
34base_comment_template/tests/fake_models.py
-
106base_comment_template/tests/test_base_comment_template.py
-
74base_comment_template/views/base_comment_template_view.xml
-
28base_comment_template/views/res_partner_view.xml
@ -0,0 +1,29 @@ |
|||
# Copyright 2021 Tecnativa - Víctor Martínez |
|||
# Copyright 2021 Tecnativa - Pedro M. Baeza |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
from openupgradelib import openupgrade |
|||
|
|||
from odoo.tools import parse_version |
|||
|
|||
|
|||
@openupgrade.migrate() |
|||
def migrate(env, version): |
|||
if parse_version(version) == parse_version("14.0.1.0.0"): |
|||
openupgrade.logged_query( |
|||
env.cr, |
|||
""" |
|||
INSERT INTO base_comment_template_res_partner_rel |
|||
(res_partner_id, base_comment_template_id) |
|||
SELECT SPLIT_PART(ip.res_id, ',', 2)::int AS res_partner_id, |
|||
SPLIT_PART(ip.value_reference, ',', 2)::int AS base_comment_template_id |
|||
FROM ir_property ip |
|||
JOIN ir_model_fields imf ON ip.fields_id = imf.id |
|||
JOIN res_partner rp ON rp.id = SPLIT_PART(ip.res_id, ',', 2)::int |
|||
JOIN base_comment_template bct |
|||
ON bct.id = SPLIT_PART(ip.value_reference, ',', 2)::int |
|||
WHERE imf.name = 'property_comment_template_id' |
|||
AND imf.model = 'res.partner' |
|||
AND ip.res_id IS NOT NULL |
|||
ON CONFLICT DO NOTHING |
|||
""", |
|||
) |
@ -0,0 +1,30 @@ |
|||
# Copyright 2021 Tecnativa - Víctor Martínez |
|||
# Copyright 2021 Tecnativa - Pedro M: Baeza |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
from openupgradelib import openupgrade |
|||
|
|||
from odoo.tools import parse_version |
|||
|
|||
field_renames = [ |
|||
("base.comment.template", "base_comment_template", "priority", "sequence"), |
|||
] |
|||
|
|||
|
|||
@openupgrade.migrate() |
|||
def migrate(env, version): |
|||
if parse_version(version) == parse_version("14.0.1.0.0"): |
|||
openupgrade.rename_fields(env, field_renames) |
|||
if openupgrade.table_exists(env.cr, "base_comment_template_res_partner_rel"): |
|||
# Swap column names, as they were incorrect |
|||
env.cr.execute( |
|||
"ALTER TABLE base_comment_template_res_partner_rel " |
|||
"RENAME base_comment_template_id TO temp" |
|||
) |
|||
env.cr.execute( |
|||
"ALTER TABLE base_comment_template_res_partner_rel " |
|||
"RENAME res_partner_id TO base_comment_template_id" |
|||
) |
|||
env.cr.execute( |
|||
"ALTER TABLE base_comment_template_res_partner_rel " |
|||
"RENAME temp TO res_partner_id" |
|||
) |
@ -1,3 +1,4 @@ |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from . import comment |
|||
from . import base_comment_template |
|||
from . import comment_template |
|||
from . import res_partner |
|||
from . import ir_model |
@ -0,0 +1,88 @@ |
|||
# Copyright 2014 Guewen Baconnier (Camptocamp SA) |
|||
# Copyright 2013-2014 Nicolas Bessi (Camptocamp SA) |
|||
# Copyright 2020 NextERP Romania SRL |
|||
# Copyright 2021 Tecnativa - Víctor Martínez |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class BaseCommentTemplate(models.Model): |
|||
"""Comment templates printed on reports""" |
|||
|
|||
_name = "base.comment.template" |
|||
_description = "Comments Template" |
|||
_order = "sequence,id" |
|||
|
|||
active = fields.Boolean(default=True) |
|||
position = fields.Selection( |
|||
string="Position on document", |
|||
selection=[("before_lines", "Top"), ("after_lines", "Bottom")], |
|||
required=True, |
|||
default="before_lines", |
|||
help="This field allows to select the position of the comment on reports.", |
|||
) |
|||
name = fields.Char( |
|||
string="Name", |
|||
translate=True, |
|||
required=True, |
|||
help="Name/description of this comment template", |
|||
) |
|||
text = fields.Html( |
|||
string="Template", |
|||
translate=True, |
|||
required=True, |
|||
sanitize=False, |
|||
help="This is the text template that will be inserted into reports.", |
|||
) |
|||
company_id = fields.Many2one( |
|||
comodel_name="res.company", |
|||
string="Company", |
|||
ondelete="cascade", |
|||
index=True, |
|||
help="If set, the comment template will be available only for the selected " |
|||
"company.", |
|||
) |
|||
partner_ids = fields.Many2many( |
|||
comodel_name="res.partner", |
|||
relation="base_comment_template_res_partner_rel", |
|||
column1="base_comment_template_id", |
|||
column2="res_partner_id", |
|||
string="Partner", |
|||
readonly=True, |
|||
help="If set, the comment template will be available only for the selected " |
|||
"partner.", |
|||
) |
|||
model_ids = fields.Many2many( |
|||
comodel_name="ir.model", |
|||
string="IR Model", |
|||
ondelete="cascade", |
|||
domain=[ |
|||
("is_comment_template", "=", True), |
|||
("model", "!=", "comment.template"), |
|||
], |
|||
required=True, |
|||
help="This comment template will be available on this models. " |
|||
"You can see here only models allowed to set the coment template.", |
|||
) |
|||
domain = fields.Char( |
|||
string="Filter Domain", |
|||
required=True, |
|||
default="[]", |
|||
help="This comment template will be available only for objects " |
|||
"that satisfy the condition", |
|||
) |
|||
sequence = fields.Integer( |
|||
required=True, default=10, help="The smaller number = The higher priority" |
|||
) |
|||
|
|||
def name_get(self): |
|||
"""Redefine the name_get method to show the template name with the position.""" |
|||
res = [] |
|||
for item in self: |
|||
name = "{} ({})".format( |
|||
item.name, dict(self._fields["position"].selection).get(item.position) |
|||
) |
|||
if self.env.context.get("comment_template_model_display"): |
|||
name += " (%s)" % ", ".join(item.model_ids.mapped("name")) |
|||
res.append((item.id, name)) |
|||
return res |
@ -1,48 +0,0 @@ |
|||
# Copyright 2014 Guewen Baconnier (Camptocamp SA) |
|||
# Copyright 2013-2014 Nicolas Bessi (Camptocamp SA) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import fields, models |
|||
|
|||
|
|||
class BaseCommentTemplate(models.Model): |
|||
_name = "base.comment.template" |
|||
_description = "Base comment template" |
|||
|
|||
active = fields.Boolean(default=True) |
|||
|
|||
name = fields.Char( |
|||
string="Comment summary", |
|||
required=True, |
|||
) |
|||
|
|||
position = fields.Selection( |
|||
selection=[ |
|||
("before_lines", "Before lines"), |
|||
("after_lines", "After lines"), |
|||
], |
|||
required=True, |
|||
default="before_lines", |
|||
help="Position on document", |
|||
) |
|||
|
|||
text = fields.Html( |
|||
string="Comment", |
|||
translate=True, |
|||
required=True, |
|||
) |
|||
|
|||
company_id = fields.Many2one( |
|||
"res.company", |
|||
string="Company", |
|||
help="If set, it'll only be available for this company", |
|||
ondelete="cascade", |
|||
index=True, |
|||
) |
|||
|
|||
def get_value(self, partner_id=False): |
|||
self.ensure_one() |
|||
lang = None |
|||
if partner_id: |
|||
lang = self.env["res.partner"].browse(partner_id).lang |
|||
return self.with_context(lang=lang).text |
@ -0,0 +1,45 @@ |
|||
# Copyright 2014 Guewen Baconnier (Camptocamp SA) |
|||
# Copyright 2013-2014 Nicolas Bessi (Camptocamp SA) |
|||
# Copyright 2020 NextERP Romania SRL |
|||
# Copyright 2021 Tecnativa - Víctor Martínez |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from odoo import api, fields, models |
|||
from odoo.tools.safe_eval import safe_eval |
|||
|
|||
|
|||
class CommentTemplate(models.AbstractModel): |
|||
_name = "comment.template" |
|||
_description = ( |
|||
"base.comment.template to put header and footer " |
|||
"in reports based on created comment templates" |
|||
) |
|||
# This field allows to set any given field that determines the source partner for |
|||
# the comment templates downstream. |
|||
# E.g.: other models where the partner field is called customer_id. |
|||
_comment_template_partner_field_name = "partner_id" |
|||
|
|||
comment_template_ids = fields.Many2many( |
|||
compute="_compute_comment_template_ids", |
|||
comodel_name="base.comment.template", |
|||
string="Comment Template", |
|||
domain=lambda self: [("model_ids.model", "=", self._name)], |
|||
store=True, |
|||
readonly=False, |
|||
) |
|||
|
|||
@api.depends(_comment_template_partner_field_name) |
|||
def _compute_comment_template_ids(self): |
|||
for record in self: |
|||
partner = record[self._comment_template_partner_field_name] |
|||
record.comment_template_ids = [(5,)] |
|||
templates = self.env["base.comment.template"].search( |
|||
[ |
|||
("id", "in", partner.base_comment_template_ids.ids), |
|||
("model_ids.model", "=", self._name), |
|||
] |
|||
) |
|||
for template in templates: |
|||
if not template.domain or self in self.search( |
|||
safe_eval(template.domain) |
|||
): |
|||
record.comment_template_ids = [(4, template.id)] |
@ -0,0 +1,33 @@ |
|||
# Copyright 2020 NextERP Romania SRL |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class IrModel(models.Model): |
|||
_inherit = "ir.model" |
|||
|
|||
is_comment_template = fields.Boolean( |
|||
string="Comment Template", |
|||
default=False, |
|||
help="Whether this model supports in reports to add comment templates.", |
|||
) |
|||
|
|||
def _reflect_model_params(self, model): |
|||
vals = super(IrModel, self)._reflect_model_params(model) |
|||
vals["is_comment_template"] = issubclass( |
|||
type(model), self.pool["comment.template"] |
|||
) |
|||
return vals |
|||
|
|||
@api.model |
|||
def _instanciate(self, model_data): |
|||
model_class = super(IrModel, self)._instanciate(model_data) |
|||
if ( |
|||
model_data.get("is_comment_template") |
|||
and model_class._name != "comment.template" |
|||
): |
|||
parents = model_class._inherit or [] |
|||
parents = [parents] if isinstance(parents, str) else parents |
|||
model_class._inherit = parents + ["comment.template"] |
|||
return model_class |
@ -0,0 +1,8 @@ |
|||
Go to *Settings > Technical > Reporting > Comment Templates* and start designing you comment templates. |
|||
|
|||
This module is the base module for following modules: |
|||
|
|||
* sale_comment_template |
|||
* purchase_comment_template |
|||
* invoice_comment_template |
|||
* stock_picking_comment_template |
@ -0,0 +1,31 @@ |
|||
#. Go to *Settings* and activate the developer mode. |
|||
#. Go to *Settings > Technical > Reporting > Comment Templates*. |
|||
#. Create a new record. |
|||
#. Define the Company the template is linked or leave default for all companies. |
|||
#. Define the Partner the template is linked or leave default for all partners. |
|||
#. Define the Model, Domain the template is linked. |
|||
#. Define the Position where the template will be printed: |
|||
|
|||
* above document lines |
|||
* below document lines |
|||
|
|||
You should have at least one template with Default field set, if you choose a Partner the template is deselected as a Default one. |
|||
If you create a new template with the same configuration (Model, Domain, Position) and set it as Default, the previous one will be deselected as a default one. |
|||
|
|||
The template is a html field which will be rendered just like a mail template, so you can use variables like ${object}, ${user}, ${ctx} to add dynamic content. |
|||
|
|||
Change the report related to the model from configuration and add a statement like: |
|||
|
|||
<p t-if="o.get_comment_template('before_lines', o.company_id.id, o.partner_id and o.partner_id.id or False)"> |
|||
|
|||
<span t-raw="o.get_comment_template('before_lines', o.company_id.id, o.partner_id and o.partner_id.id or False)"/> |
|||
|
|||
</p> |
|||
|
|||
<p t-if="o.get_comment_template('after_lines', o.company_id.id, o.partner_id and o.partner_id.id or False)"> |
|||
|
|||
<span t-raw="o.get_comment_template('after_lines', o.company_id.id, o.partner_id and o.partner_id.id or False)"/> |
|||
|
|||
</p> |
|||
|
|||
You should always use t-if since the method returns False if no template is found. |
@ -1,2 +1,3 @@ |
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|||
access_base_comment_template_user,access_base_comment_template_user,model_base_comment_template,,1,0,0,0 |
|||
access_base_comment_template,access_base_comment_template no one,model_base_comment_template,base.group_no_one,1,1,1,1 |
@ -0,0 +1,34 @@ |
|||
# Copyright 2017 LasLabs Inc. |
|||
# Copyright 2018 ACSONE |
|||
# Copyright 2018 Camptocamp |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
from odoo import models |
|||
|
|||
|
|||
def setup_test_model(env, model_cls): |
|||
"""Pass a test model class and initialize it. |
|||
|
|||
Courtesy of SBidoul from https://github.com/OCA/mis-builder :) |
|||
""" |
|||
model_cls._build_model(env.registry, env.cr) |
|||
env.registry.setup_models(env.cr) |
|||
env.registry.init_models( |
|||
env.cr, [model_cls._name], dict(env.context, update_custom_fields=True) |
|||
) |
|||
|
|||
|
|||
def teardown_test_model(env, model_cls): |
|||
"""Pass a test model class and deinitialize it. |
|||
|
|||
Courtesy of SBidoul from https://github.com/OCA/mis-builder :) |
|||
""" |
|||
if not getattr(model_cls, "_teardown_no_delete", False): |
|||
del env.registry.models[model_cls._name] |
|||
env.registry.setup_models(env.cr) |
|||
|
|||
|
|||
class ResUsers(models.Model): |
|||
_name = "res.users" |
|||
_inherit = ["res.users", "comment.template"] |
|||
_teardown_no_delete = True |
@ -1,37 +1,89 @@ |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
from odoo.tests.common import TransactionCase |
|||
# Copyright 2020 NextERP Romania SRL |
|||
# Copyright 2021 Tecnativa - Víctor Martínez |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
from odoo.tests import common |
|||
|
|||
from .fake_models import ResUsers, setup_test_model, teardown_test_model |
|||
|
|||
class TestResPartner(TransactionCase): |
|||
def setUp(self): |
|||
super(TestResPartner, self).setUp() |
|||
self.template_id = self.env["base.comment.template"].create( |
|||
|
|||
class TestCommentTemplate(common.SavepointCase): |
|||
@classmethod |
|||
def setUpClass(cls): |
|||
super().setUpClass() |
|||
setup_test_model(cls.env, ResUsers) |
|||
cls.user_obj = cls.env.ref("base.model_res_users") |
|||
cls.user = cls.env.ref("base.user_demo") |
|||
cls.user2 = cls.env.ref("base.demo_user0") |
|||
cls.partner_id = cls.env.ref("base.res_partner_12") |
|||
cls.partner2_id = cls.env.ref("base.res_partner_10") |
|||
cls.main_company = cls.env.ref("base.main_company") |
|||
cls.company = cls.env["res.company"].create({"name": "Test company"}) |
|||
cls.before_template_id = cls.env["base.comment.template"].create( |
|||
{ |
|||
"name": "Comment before lines", |
|||
"position": "before_lines", |
|||
"text": "<p>Text before lines</p>", |
|||
"name": "Top template", |
|||
"text": "Text before lines", |
|||
"model_ids": [(6, 0, cls.user_obj.ids)], |
|||
"company_id": cls.company.id, |
|||
} |
|||
) |
|||
cls.after_template_id = cls.env["base.comment.template"].create( |
|||
{ |
|||
"name": "Bottom template", |
|||
"position": "after_lines", |
|||
"text": "Text after lines", |
|||
"model_ids": [(6, 0, cls.user_obj.ids)], |
|||
"company_id": cls.company.id, |
|||
} |
|||
) |
|||
cls.user.partner_id.base_comment_template_ids = [ |
|||
(4, cls.before_template_id.id), |
|||
(4, cls.after_template_id.id), |
|||
] |
|||
|
|||
def test_commercial_partner_fields(self): |
|||
# Azure Interior |
|||
partner_id = self.env.ref("base.res_partner_12") |
|||
partner_id.property_comment_template_id = self.template_id.id |
|||
# Test childs propagation of commercial partner field |
|||
for child_id in partner_id.child_ids: |
|||
self.assertEqual(child_id.property_comment_template_id, self.template_id) |
|||
|
|||
def test_get_value_without_partner(self): |
|||
self.assertEqual(self.template_id.get_value(), "<p>Text before lines</p>") |
|||
@classmethod |
|||
def tearDownClass(cls): |
|||
teardown_test_model(cls.env, ResUsers) |
|||
super(TestCommentTemplate, cls).tearDownClass() |
|||
|
|||
def test_get_value_with_partner(self): |
|||
self.env["res.lang"]._activate_lang("fr_BE") |
|||
partner = self.env.ref("base.res_partner_12") |
|||
partner.write({"lang": "fr_BE"}) |
|||
self.template_id.with_context(lang="fr_BE").write( |
|||
{"text": "<p>Testing translated fr_BE</p>"} |
|||
def test_template_name_get(self): |
|||
self.assertEqual( |
|||
self.before_template_id.display_name, |
|||
"Top template (Top)", |
|||
) |
|||
self.assertEqual( |
|||
self.template_id.get_value(partner_id=partner.id), |
|||
"<p>Testing translated fr_BE</p>", |
|||
self.after_template_id.display_name, |
|||
"Bottom template (Bottom)", |
|||
) |
|||
|
|||
def test_general_template(self): |
|||
# Need to force _compute because only trigger when partner_id have changed |
|||
self.user._compute_comment_template_ids() |
|||
# Check getting default comment template |
|||
self.assertTrue(self.before_template_id in self.user.comment_template_ids) |
|||
self.assertTrue(self.after_template_id in self.user.comment_template_ids) |
|||
|
|||
def test_partner_template(self): |
|||
self.partner2_id.base_comment_template_ids = [ |
|||
(4, self.before_template_id.id), |
|||
(4, self.after_template_id.id), |
|||
] |
|||
self.assertTrue( |
|||
self.before_template_id in self.partner2_id.base_comment_template_ids |
|||
) |
|||
self.assertTrue( |
|||
self.after_template_id in self.partner2_id.base_comment_template_ids |
|||
) |
|||
|
|||
def test_partner_template_domain(self): |
|||
# Check getting the comment template if domain is set |
|||
self.partner2_id.base_comment_template_ids = [ |
|||
(4, self.before_template_id.id), |
|||
(4, self.after_template_id.id), |
|||
] |
|||
self.before_template_id.domain = "[('id', 'in', %s)]" % self.user.ids |
|||
self.assertTrue( |
|||
self.before_template_id in self.partner2_id.base_comment_template_ids |
|||
) |
|||
self.assertTrue( |
|||
self.before_template_id not in self.partner_id.base_comment_template_ids |
|||
) |
@ -0,0 +1,28 @@ |
|||
<odoo> |
|||
<record id="view_partner_form" model="ir.ui.view"> |
|||
<field name="model">res.partner</field> |
|||
<field name="inherit_id" ref="base.view_partner_form" /> |
|||
<field name="arch" type="xml"> |
|||
<page name='internal_notes' position="after"> |
|||
<page name='base_comment_template_ids' string="Comment Templates"> |
|||
<field |
|||
name="base_comment_template_ids" |
|||
nolabel="1" |
|||
context="{'comment_template_model_display': True}" |
|||
> |
|||
<tree> |
|||
<field name="display_name" string="Name" /> |
|||
<field name="position" /> |
|||
<field |
|||
name="company_id" |
|||
groups="base.group_multi_company" |
|||
/> |
|||
<field name="model_ids" /> |
|||
<field name="domain" /> |
|||
</tree> |
|||
</field> |
|||
</page> |
|||
</page> |
|||
</field> |
|||
</record> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue