Skip to content

Commit cb6545a

Browse files
committed
ADD partner_tag_smart_assignation new module
1 parent c54ca0f commit cb6545a

14 files changed

Lines changed: 970 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
============
2+
Smart Tagger
3+
============
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:4cc44e04885361c3f282fc3aa7cfd2136a3df53e41bfc1ed8904a81e767c7b96
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github
20+
:target: https://github.com/OCA/partner-contact/tree/18.0/partner_tag_smart_assignation
21+
:alt: OCA/partner-contact
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/partner-contact-18-0/partner-contact-18-0-partner_tag_smart_assignation
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/partner-contact&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
The Smart Tagger Module update the partner category to add tags in a
32+
dynamic way. Give the possibility to update all the partner linked to
33+
the tags with one button. A cron update all the smart tagger when you
34+
want it.
35+
36+
**Table of contents**
37+
38+
.. contents::
39+
:local:
40+
41+
Bug Tracker
42+
===========
43+
44+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/partner-contact/issues>`_.
45+
In case of trouble, please check there if your issue has already been reported.
46+
If you spotted it first, help us to smash it by providing a detailed and welcomed
47+
`feedback <https://github.com/OCA/partner-contact/issues/new?body=module:%20partner_tag_smart_assignation%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
48+
49+
Do not contact contributors directly about support or help with technical issues.
50+
51+
Credits
52+
=======
53+
54+
Authors
55+
-------
56+
57+
* Compassion CH
58+
59+
Contributors
60+
------------
61+
62+
- Nathan Flückiger <nathan.fluckiger@hotmail.ch>
63+
- Emanuel Cino <ecino@compassion.ch>
64+
- Cyril Favre <cf.cyril@gmail.com>
65+
66+
Maintainers
67+
-----------
68+
69+
This module is maintained by the OCA.
70+
71+
.. image:: https://odoo-community.org/logo.png
72+
:alt: Odoo Community Association
73+
:target: https://odoo-community.org
74+
75+
OCA, or the Odoo Community Association, is a nonprofit organization whose
76+
mission is to support the collaborative development of Odoo features and
77+
promote its widespread use.
78+
79+
This module is part of the `OCA/partner-contact <https://github.com/OCA/partner-contact/tree/18.0/partner_tag_smart_assignation>`_ project on GitHub.
80+
81+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
2+
from . import models
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (C) 2019-2023 Compassion CH (http://www.compassion.ch)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Smart Tagger",
5+
"summary": "Smart tagger, module to have smart tags who " "update themselves alone",
6+
"version": "18.0.1.0.0",
7+
"category": "Other",
8+
"author": "Compassion CH, Odoo Community Association (OCA)",
9+
"license": "AGPL-3",
10+
"website": "https://github.com/OCA/partner-contact",
11+
"depends": ["base", "hr"],
12+
"data": ["cron/update_cron.xml", "views/smart_tagger_view.xml"],
13+
"installable": True,
14+
"auto_install": False,
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<odoo>
2+
<record id="smart_tags_updater" model="ir.cron">
3+
<field name="name">Smart Tags Updater</field>
4+
<field name="active" eval="True" />
5+
<field name="user_id" ref="base.user_root" />
6+
<field name="interval_number">1</field>
7+
<field name="interval_type">weeks</field>
8+
<field name="model_id" ref="model_res_partner_category" />
9+
<field name="state">code</field>
10+
<field name="code">model.update_all_smart_tags()</field>
11+
</record>
12+
</odoo>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
2+
from . import res_partner_category
3+
from . import hr_department
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (C) 2019 Compassion CH (http://www.compassion.ch)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
from odoo import fields, models
4+
5+
6+
class ResPartnerCategoryExtension(models.Model):
7+
_inherit = "hr.department"
8+
9+
tag_ids = fields.Many2many("res.partner.category")
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Copyright (C) 2019 Compassion CH (http://www.compassion.ch)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
from datetime import datetime
4+
5+
from odoo import _, api, fields, models
6+
from odoo.exceptions import ValidationError
7+
from odoo.tools.safe_eval import safe_eval
8+
9+
10+
class ResPartnerCategory(models.Model):
11+
_inherit = "res.partner.category"
12+
13+
tag_filter_condition_id = fields.Many2one("ir.filters", "Domain filter")
14+
smart = fields.Boolean(
15+
help="Enable this to automatically assign the category on partners "
16+
"matching a given filter domain or SQL query."
17+
)
18+
tag_filter_partner_field = fields.Char(
19+
default="partner_id",
20+
help="Relational field used on the filter object to find the partners.",
21+
)
22+
tag_filter_sql_query = fields.Text(
23+
"SQL query",
24+
help="Can be used instead of the filter for finding the relevant "
25+
"partners. The given SQL query should only return partner ids "
26+
"rows.",
27+
)
28+
tag_filter_join_operator = fields.Selection(
29+
[
30+
("and", "AND (must satisfy both SQL and domain filter)"),
31+
("or", "OR (can satisfy either SQL or domain filter)"),
32+
],
33+
"JOIN operator",
34+
default="or",
35+
required=True,
36+
)
37+
partner_ids = fields.Many2many(
38+
"res.partner",
39+
"res_partner_res_partner_category_rel",
40+
"category_id",
41+
"partner_id",
42+
)
43+
44+
tagged_partner_count = fields.Integer(compute="_compute_number_tags", store=True)
45+
46+
author_id = fields.Many2one(
47+
"res.users", string="Author", default=lambda x: x.env.user
48+
)
49+
department_ids = fields.Many2many("hr.department", string="Department")
50+
description = fields.Text()
51+
valid_until = fields.Date()
52+
53+
@api.model_create_multi
54+
def create(self, vals_list):
55+
records = super().create(vals_list)
56+
records.update_partner_tags()
57+
return records
58+
59+
def write(self, vals):
60+
res = super().write(vals)
61+
if "tag_filter_condition_id" in vals or "model" in vals:
62+
self.update_partner_tags()
63+
return res
64+
65+
@api.constrains(
66+
"tag_filter_condition_id",
67+
"tag_filter_condition_id.model_id",
68+
"tag_filter_condition_id.domain",
69+
"tag_filter_partner_field",
70+
)
71+
def check_condition(self):
72+
for me in self.filtered("tag_filter_condition_id"):
73+
if me.tag_filter_condition_id.model_id != "res.partner":
74+
model_link = self.env[me.tag_filter_condition_id.model_id]
75+
if me.tag_filter_partner_field not in model_link:
76+
raise ValidationError(
77+
_("The chosen model has no field %s")
78+
% me.tag_filter_partner_field
79+
)
80+
81+
@api.constrains("tag_filter_sql_query")
82+
def check_sql_query(self):
83+
for me in self.filtered("tag_filter_sql_query"):
84+
self.env.cr.execute(me.tag_filter_sql_query)
85+
rows = self.env.cr.fetchall()
86+
for row in rows:
87+
if (
88+
len(row) > 1
89+
or not isinstance(row[0], int)
90+
or not self.env["res.partner"].browse(row[0]).exists()
91+
):
92+
raise ValidationError(
93+
_("The SQL query should only return partner ids")
94+
)
95+
96+
def update_partner_tags(self):
97+
for tagger in self.filtered("smart"):
98+
sql_partners = tagger.get_partners_from_sql()
99+
filter_partners = tagger.get_partners_from_ir_filter()
100+
if tagger.tag_filter_join_operator == "and":
101+
partners = sql_partners & filter_partners
102+
else:
103+
partners = sql_partners | filter_partners
104+
if partners:
105+
tagger.write({"partner_ids": [(6, 0, partners.ids)]})
106+
else:
107+
tagger.write({"partner_ids": [(5, 0, 0)]})
108+
return True
109+
110+
def get_partners_from_ir_filter(self):
111+
self.ensure_one()
112+
if not self.tag_filter_condition_id.domain:
113+
return self.env["res.partner"]
114+
domain = safe_eval(
115+
self.tag_filter_condition_id.domain,
116+
locals_dict={"datetime": datetime},
117+
locals_builtins=True,
118+
)
119+
model = self.tag_filter_condition_id.model_id
120+
matching_records = self.env[model].search(domain)
121+
if matching_records:
122+
if model == "res.partner":
123+
partners = matching_records
124+
else:
125+
partners = matching_records.mapped(self.tag_filter_partner_field)
126+
return partners
127+
else:
128+
return self.env["res.partner"]
129+
130+
def get_partners_from_sql(self):
131+
self.ensure_one()
132+
partner_obj = self.env["res.partner"]
133+
if not self.tag_filter_sql_query:
134+
return partner_obj
135+
self.env.cr.execute(self.tag_filter_sql_query)
136+
rows = self.env.cr.fetchall()
137+
if rows:
138+
return partner_obj.browse([r[0] for r in rows])
139+
else:
140+
return partner_obj
141+
142+
@api.model
143+
def update_all_smart_tags(self):
144+
self._check_validity_dates()
145+
return self.search([("smart", "=", True)]).update_partner_tags()
146+
147+
@api.model
148+
def _check_validity_dates(self):
149+
"""Scheduled method to deactivate records past their validity date"""
150+
today = fields.Date.today()
151+
records_to_deactivate = self.search(
152+
[("valid_until", "<", today), ("active", "=", True), ("smart", "=", True)]
153+
)
154+
# Archive the tag and unlink the partner
155+
records_to_deactivate.write({"partner_ids": [(5, 0, 0)], "active": False})
156+
157+
@api.depends("partner_ids")
158+
def _compute_number_tags(self):
159+
for category in self:
160+
category.tagged_partner_count = len(category.partner_ids)
161+
162+
def open_tags(self):
163+
return {
164+
"type": "ir.actions.act_window",
165+
"res_model": "res.partner",
166+
"view_mode": "list,form",
167+
"name": "Partners",
168+
"domain": [["id", "in", self.mapped("partner_ids").ids]],
169+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Nathan Flückiger \<<nathan.fluckiger@hotmail.ch>\>
2+
- Emanuel Cino \<<ecino@compassion.ch>\>
3+
- Cyril Favre \<<cf.cyril@gmail.com>\>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The Smart Tagger Module update the partner category to add tags in a
2+
dynamic way. Give the possibility to update all the partner linked to
3+
the tags with one button. A cron update all the smart tagger when you
4+
want it.

0 commit comments

Comments
 (0)