From 5b770733ae6b34f7adb79cf02418618d1d0c8ed1 Mon Sep 17 00:00:00 2001 From: benjamin <benjamin@le-filament.com> Date: Thu, 25 Aug 2022 16:47:57 +0200 Subject: [PATCH] [add] wizard multi partner & retrieve module list from server --- __init__.py | 2 +- __manifest__.py | 4 +- controllers/__init__.py | 2 +- controllers/banner_release.py | 13 ++++ models/res_partner.py | 64 ++++++++++++++++- models/res_partner_module.py | 22 ++++-- security/ir.model.access.csv | 2 + templates/header_template.xml | 29 ++++++++ views/menus.xml | 2 +- views/res_partner.xml | 24 +++++-- views/res_partner_module.xml | 13 +++- views/res_partner_release.xml | 6 +- wizard/__init__.py | 4 ++ wizard/multi_partner_release_wizard.py | 93 +++++++++++++++++++++++++ wizard/multi_partner_release_wizard.xml | 66 ++++++++++++++++++ 15 files changed, 325 insertions(+), 21 deletions(-) create mode 100644 controllers/banner_release.py create mode 100644 templates/header_template.xml create mode 100644 wizard/__init__.py create mode 100644 wizard/multi_partner_release_wizard.py create mode 100644 wizard/multi_partner_release_wizard.xml diff --git a/__init__.py b/__init__.py index 48a63e5..0ae31d9 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ # Copyright 2021 Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from . import controllers, models +from . import controllers, models, wizard diff --git a/__manifest__.py b/__manifest__.py index 29acf73..a3c08a2 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -15,8 +15,11 @@ "data": [ # security "security/ir.model.access.csv", + # wizard + "wizard/multi_partner_release_wizard.xml", # templates "templates/assets.xml", + "templates/header_template.xml", "templates/partner_release_detail.xml", "templates/partner_release_list.xml", # views @@ -26,7 +29,6 @@ "views/res_partner_release_module.xml", # views menu "views/menus.xml", - # wizard ], "qweb": [ "static/src/xml/*.xml", diff --git a/controllers/__init__.py b/controllers/__init__.py index bf688a2..75f6a06 100644 --- a/controllers/__init__.py +++ b/controllers/__init__.py @@ -1,4 +1,4 @@ # Copyright 2022 Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from . import portal +from . import banner_release, portal diff --git a/controllers/banner_release.py b/controllers/banner_release.py new file mode 100644 index 0000000..948ff54 --- /dev/null +++ b/controllers/banner_release.py @@ -0,0 +1,13 @@ +# © 2022 Le Filament (<http://www.le-filament.com>) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import http +from odoo.http import request + + +class BannerController(http.Controller): + @http.route(["/lefilament_release/header"], type="json", auth="user") + def release_header(self): + return { + "html": request.env.ref("lefilament_release.header_template")._render({}) + } diff --git a/models/res_partner.py b/models/res_partner.py index 44b61a4..c94fd7d 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -1,7 +1,9 @@ # © 2022 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import fields, models +import requests + +from odoo import _, exceptions, fields, models class ResPartner(models.Model): @@ -14,13 +16,18 @@ class ResPartner(models.Model): release_ids = fields.One2many( comodel_name="res.partner.release", inverse_name="partner_id", string="Releases" ) - module_ids = fields.One2many( - comodel_name="res.partner.module", inverse_name="partner_id", string="Modules" + module_ids = fields.Many2many( + comodel_name="res.partner.module", + column1="partner_id", + column2="module_id", + string="Modules", ) release_share_link = fields.Char( string="Lien de partage client", compute="_compute_release_share_link", ) + server_url = fields.Char("URL du serveur") + database_name = fields.Char("Nom de la base de données") # ------------------------------------------------------ # Computed fields / Search Fields @@ -46,6 +53,57 @@ class ResPartner(models.Model): if partner.is_company: partner._portal_ensure_token() + def retrieve_modules(self): + existing_modules = self.env["res.partner.module"].search([]) + for partner in self: + if not partner.server_url or not partner.database_name: + raise exceptions.UserError( + _("L'url et la base de données doivent être renseignés.") + ) + + try: + response = requests.get( + partner.server_url + "/get-modules", + headers={"DbName": partner.database_name}, + ).json() + except Exception as e: + raise exceptions.UserError(e.__str__()) + + for module in response: + if module.get("author_type") != "odoo": + # Mise à jour du module + if module.get("name") in existing_modules.mapped("name"): + existing_modules.filtered( + lambda m: m.name == module.get("name") + ).update( + { + "author": module.get("author"), + "author_type": module.get("author_type"), + "partner_ids": [(4, partner.id, 0)], + } + ) + else: + # Création du module + partner.update({"module_ids": [(0, 0, module)]}) + # Check modules désinstallés + module_uninstalled = list( + set(partner.module_ids.mapped("name")) + - set( + map( + lambda m: m.get("name") + if m.get("author_type") != "odoo" + else None, + response, + ) + ) + ) + if module_uninstalled: + module_ids = partner.module_ids.filtered( + lambda m: m.name in module_uninstalled + ) + for module in module_ids: + partner.update({"module_ids": [(3, module.id, 0)]}) + # ------------------------------------------------------ # Common function # ------------------------------------------------------ diff --git a/models/res_partner_module.py b/models/res_partner_module.py index d9dca5b..83dacac 100644 --- a/models/res_partner_module.py +++ b/models/res_partner_module.py @@ -12,12 +12,22 @@ class PartnerModule(models.Model): # Fields declaration # ------------------------------------------------------ name = fields.Char("Nom du module", required=True) - partner_id = fields.Many2one( + partner_ids = fields.Many2many( comodel_name="res.partner", - string="Client", - ondelete="restrict", + column1="module_id", + column2="partner_id", + string="Clients", domain=[("is_company", "=", True)], ) + author = fields.Char("Auteur") + author_type = fields.Selection( + [ + ("lefilament", "Le Filament"), + ("oca", "OCA"), + ("other", "Autre"), + ], + string="Type", + ) active = fields.Boolean("Actif", default=True) # ------------------------------------------------------ @@ -25,9 +35,9 @@ class PartnerModule(models.Model): # ------------------------------------------------------ _sql_constraints = [ ( - "uniq_partner_module_name", - "unique(partnaer_id, name)", - "Module name must be unique for a partner", + "uniq_module_name", + "unique(name)", + "Ce module existe déjà", ), ] # ------------------------------------------------------ diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 38425cc..745fb47 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -2,3 +2,5 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink access_res_partner_release,access_res_partner_release,model_res_partner_release,base.group_user,1,1,1,1 access_res_partner_module,access_res_partner_module,model_res_partner_module,base.group_user,1,1,1,1 access_res_partner_release_module,access_res_partner_release_module,model_res_partner_release_module,base.group_user,1,1,1,1 +access_multi_partner_release_wizard,access_multi_partner_release_wizard,model_multi_partner_release_wizard,base.group_user,1,1,1,1 +access_multi_partner_release_line_wizard,access_multi_partner_release_line_wizard,model_multi_partner_release_line_wizard,base.group_user,1,1,1,1 diff --git a/templates/header_template.xml b/templates/header_template.xml new file mode 100644 index 0000000..a60722d --- /dev/null +++ b/templates/header_template.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <template id="header_template" name="lefilament_release.header_template"> + <div id="release" style="background-color: #fff; padding: 16px 0"> + <div class="container"> + <div class="row"> + <div class="col-12 text-center"> + <div + class="btn-group" + role="group" + aria-label="Releases" + style="padding-top: 5px; padding-bottom: 5px;" + > + <a + type="action" + name="%(lefilament_release.multi_partner_release_wizard_act_window)d" + class="btn btn-outline-info" + style="width: 250px;" + tabindex="4" + > + Saisir pour plusieurs clients + </a> + </div> + </div> + </div> + </div> + </div> + </template> +</odoo> diff --git a/views/menus.xml b/views/menus.xml index 7fd6b52..cb0bcbd 100644 --- a/views/menus.xml +++ b/views/menus.xml @@ -7,10 +7,10 @@ <menuitem name="Gestion de releases" id="res_partner_release_menu" - action="res_partner_release_act_window" sequence="150" web_icon="lefilament_release,static/description/menu_icon.png" /> + <menuitem name="Gestion de releases" id="res_partner_release_all_menu" diff --git a/views/res_partner.xml b/views/res_partner.xml index ab44d28..4263284 100644 --- a/views/res_partner.xml +++ b/views/res_partner.xml @@ -16,12 +16,28 @@ attrs="{'invisible': [('is_company', '!=', True)]}" string="Modules Odoo" > + <group> + <group> + <div class="text-muted" colspan="2"> + Format de l'url : https://exemple.com + </div> + <field name="server_url" /> + <field name="database_name" /> + </group> + <group> + <button + name="retrieve_modules" + type="object" + string="Récupérer la liste depuis le serveur" + class="btn-info" + /> + </group> + </group> <field name="module_ids"> - <tree - editable="top" - context="{'default_partner_id': active_id}" - > + <tree editable="top"> <field name="name" /> + <field name="author_type" /> + <field name="author" /> </tree> </field> </page> diff --git a/views/res_partner_module.xml b/views/res_partner_module.xml index d4c139d..d8449ef 100644 --- a/views/res_partner_module.xml +++ b/views/res_partner_module.xml @@ -8,6 +8,9 @@ <field name="arch" type="xml"> <search string="Modules"> <field name="name" /> + <field name="author_type" /> + <field name="author" /> + <field name="partner_ids" /> </search> </field> </record> @@ -17,9 +20,15 @@ <field name="name">res.partner.module.tree</field> <field name="model">res.partner.module</field> <field name="arch" type="xml"> - <tree string="Modules" editable="top"> + <tree string="Modules"> <field name="name" /> - <field name="partner_id" options="{'no_create': 1,}" /> + <field name="author_type" /> + <field name="author" /> + <field + name="partner_ids" + options="{'no_create': 1,}" + widget="many2many_tags" + /> <field name="active" /> </tree> </field> diff --git a/views/res_partner_release.xml b/views/res_partner_release.xml index 3a7f9b6..fee3b93 100644 --- a/views/res_partner_release.xml +++ b/views/res_partner_release.xml @@ -79,7 +79,6 @@ <field name="release_id" invisible="1" /> <field name="module_id" - domain="['|', ('partner_id', '=', partner_id), ('partner_id', '=', False)]" options="{'no_create': True, 'no_edit': True}" /> <field name="release_type" /> @@ -102,7 +101,10 @@ <field name="name">res.partner.release.tree</field> <field name="model">res.partner.release</field> <field name="arch" type="xml"> - <tree string="Releases Client"> + <tree + string="Releases Client" + banner_route="/lefilament_release/header" + > <field name="release_date" /> <field name="user_id" /> <field name="partner_id" /> diff --git a/wizard/__init__.py b/wizard/__init__.py new file mode 100644 index 0000000..1e2b773 --- /dev/null +++ b/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021 Le Filament (https://le-filament.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from . import multi_partner_release_wizard diff --git a/wizard/multi_partner_release_wizard.py b/wizard/multi_partner_release_wizard.py new file mode 100644 index 0000000..2466cee --- /dev/null +++ b/wizard/multi_partner_release_wizard.py @@ -0,0 +1,93 @@ +# Copyright 2022 Le Filament (https://le-filament.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models, api + + +class MultiPartnerReleaseWizard(models.TransientModel): + _name = "multi.partner.release.wizard" + _description = "Wizard de création de release pour plusieurs clients" + + # ------------------------------------------------------ + # Fields declaration + # ------------------------------------------------------ + user_id = fields.Many2one( + comodel_name="res.users", + string="Utilisateur", + required=True, + domain=[("share", "=", False)], + default=lambda self: self.env.user, + ) + partner_ids = fields.Many2many( + comodel_name="res.partner", + string="Client", + required=True, + domain=[("is_company", "=", True)], + ) + release_date = fields.Datetime( + string="Date de mise en production", + default=fields.Datetime.now(), + ) + release_module_ids = fields.One2many( + comodel_name="multi.partner.release.line.wizard", + inverse_name="release_id", + string="Modules mis à jour", + ) + description = fields.Text("Description") + + # ------------------------------------------------------ + # Button actions + # ------------------------------------------------------ + def create_releases(self): + for partner in self.partner_ids: + detail_module = [] + for module in self.release_module_ids: + detail_module.append( + (0, 0, { + "module_id": module.module_id.id, + "release_type": module.release_type, + "log": module.log, + }) + ) + partner.write({ + "release_ids": [(0, 0, { + "user_id": self.user_id.id, + "release_date": self.release_date, + "description": self.description, + "release_module_ids": detail_module, + })] + }) + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } + + +class MultiPartnerReleaseLineWizard(models.TransientModel): + _name = "multi.partner.release.line.wizard" + _description = "Modules de la release" + + # ------------------------------------------------------ + # Fields declaration + # ------------------------------------------------------ + release_id = fields.Many2one( + comodel_name="multi.partner.release.wizard", + string="Releases", + ondelete="cascade", + copy=False, + ) + module_id = fields.Many2one( + comodel_name="res.partner.module", + string="Module", + required=True, + ) + log = fields.Text("Liste des changements") + release_type = fields.Selection( + selection=[ + ("fix", "[fix]"), + ("update", "[update]"), + ("add", "[add]"), + ("clean", "[clean]"), + ], + string="Type", + ) diff --git a/wizard/multi_partner_release_wizard.xml b/wizard/multi_partner_release_wizard.xml new file mode 100644 index 0000000..7683ce7 --- /dev/null +++ b/wizard/multi_partner_release_wizard.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + <data> + <!-- Form View --> + <record id="multi_partner_release_wizard_form_view" model="ir.ui.view"> + <field name="name">multi.partner.release.wizard.form</field> + <field name="model">multi.partner.release.wizard</field> + <field name="arch" type="xml"> + <form string="Releases Client"> + <sheet> + <group> + <group name="partner_details"> + <field name="release_date" /> + <field + name="partner_ids" + widget="many2many_tags" + options="{'always_reload': True, 'no_create': True, 'no_edit': True}" + /> + </group> + <group name="release_details"> + <field + name="user_id" + options="{'no_create': True, 'no_edit': True}" + /> + <field name="create_date" string="Créé le" /> + </group> + <hr /> + <label for="description" class="font-weight-bold" /> + <field name="description" /> + <hr /> + <label for="release_module_ids" class="font-weight-bold" /> + <field + name="release_module_ids" + context="{'default_release_id': active_id}" + > + <tree editable="top"> + <field name="release_id" invisible="1" /> + <field + name="module_id" + options="{'no_create': True, 'no_edit': True}" + /> + <field name="release_type" /> + <field name="log" /> + </tree> + </field> + </group> + <footer> + <button name="create_releases" type="object" string="Créer les releases" class="btn-primary"/> + <button string="Fermer" class="oe_link" special="cancel" /> + </footer> + </sheet> + </form> + </field> + </record> + + <!-- Action Window --> + <record id="multi_partner_release_wizard_act_window" model="ir.actions.act_window"> + <field name="name">Releases Client</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">multi.partner.release.wizard</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + </data> +</odoo> -- GitLab