diff --git a/__init__.py b/__init__.py index 957992f377d7ce8942a87238397684babc5eb305..44fb71a00bc9b32aafc05249aa6b34e856874549 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,4 @@ # Copyright 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import models +from . import controllers, models diff --git a/__manifest__.py b/__manifest__.py index cb8cfbf5eca51f841d6d26e6d12726a8d86ab581..9b09b8e0558b690b91aa8188ff4c7a17b83cf0c3 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -6,9 +6,9 @@ "summary": "Ajout données spécifiques AFAC", "author": "Le Filament", "website": "https://www.le-filament.com", - "version": "14.0.0.0.0", + "version": "14.0.0.0.1", "license": "AGPL-3", - "depends": ["base", "contacts", "partner_firstname"], + "depends": ["base", "contacts", "partner_firstname", "web"], "data": [ # security "security/security_rules.xml", @@ -24,7 +24,9 @@ "views/res_company.xml", "views/res_partner.xml", ], - "qweb": [], + "qweb": [ + "static/src/xml/base.xml", + ], "installable": True, "auto_install": False, "sequence": 101, diff --git a/controllers/__init__.py b/controllers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..12a7e529b674164f0ad189b131c5d5c8fb9ae0bc --- /dev/null +++ b/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/controllers/main.py b/controllers/main.py new file mode 100644 index 0000000000000000000000000000000000000000..ed77bd77bc9d25be0844c0b2633c1fa689004327 --- /dev/null +++ b/controllers/main.py @@ -0,0 +1,94 @@ +import json +import logging +import operator + +from odoo.http import content_disposition, request, route + +from odoo.addons.web.controllers.main import ( + CSVExport, + ExcelExport, + GroupsTreeNode, + serialize_exception, +) + +_logger = logging.getLogger(__name__) + + +class ExcelExport(ExcelExport): + @route("/web/export/xlsx", type="http", auth="user") + @serialize_exception + def index(self, data, token): + params = json.loads(data) + if params.get("model") == "res.partner" and params.get("afac"): + return afac_base(self, data, token) + else: + return super(ExcelExport, self).index(data, token) + + +class CSVExport(CSVExport): + @route("/web/export/csv", type="http", auth="user") + @serialize_exception + def index(self, data, token): + params = json.loads(data) + if params.get("model") == "res.partner" and params.get("afac"): + return afac_base(self, data, token) + else: + return super(CSVExport, self).index(data, token) + + +def afac_base(object, data, token): + params = json.loads(data) + model, fields, ids, domain, import_compat = operator.itemgetter( + "model", "fields", "ids", "domain", "import_compat" + )(params) + + model_object = request.env[model].with_context( + import_compat=import_compat, **params.get("context", {}) + ) + if not model_object._is_an_ordinary_table(): + fields = [field for field in fields if field["name"] != "id"] + + field_names = [f["name"] for f in fields] + if import_compat: + columns_headers = field_names + else: + columns_headers = [val["label"].strip() for val in fields] + + groupby = params.get("groupby") + if not import_compat and groupby: + groupby_type = [model_object._fields[x.split(":")[0]].type for x in groupby] + domain = [("id", "in", ids)] if ids else domain + groups_data = model_object.read_group( + domain, + [x if x != ".id" else "id" for x in field_names], + groupby, + lazy=False, + ) + + tree = GroupsTreeNode(model_object, field_names, groupby, groupby_type) + for leaf in groups_data: + tree.insert_leaf(leaf) + + response_data = object.from_group_data(fields, tree) + else: + records = ( + model_object.browse(ids) + if ids + else model_object.search(domain, offset=0, limit=False, order=False) + ) + + datas = records.export_data( + fields_to_export=field_names, afac=params.get("afac", False) + ) + response_data = object.from_data( + (columns_headers + datas.get("column", [])), datas.get("datas", []) + ) + + return request.make_response( + response_data, + headers=[ + ("Content-Disposition", content_disposition(object.filename(model))), + ("Content-Type", object.content_type), + ], + cookies={"fileToken": token}, + ) diff --git a/models/afac_project_category.py b/models/afac_project_category.py index 4a607d6f62f261cacc7a13ca608f411b45485e50..a6daf28b69520e92bec8a602190d377eebc9ea4b 100644 --- a/models/afac_project_category.py +++ b/models/afac_project_category.py @@ -10,10 +10,7 @@ class AfacProjectCategory(models.Model): name = fields.Char("Nom", required=True) project_id = fields.Many2one( - comodel_name="afac.project", - string="Projet", - ondelete="cascade", - required=True + comodel_name="afac.project", string="Projet", ondelete="cascade", required=True ) active = fields.Boolean(related="project_id.active") company_id = fields.Many2one( diff --git a/models/afac_project_partner.py b/models/afac_project_partner.py index 85b000dbe212b5b80d3849573ff0850e9c1c58ef..3107f6313cf5f47aef9b90c97d08cc23d3272cd2 100644 --- a/models/afac_project_partner.py +++ b/models/afac_project_partner.py @@ -22,10 +22,7 @@ class AfacProjectPartner(models.Model): comodel_name="res.partner", string="Contact", ondelete="cascade", required=True ) project_id = fields.Many2one( - comodel_name="afac.project", - string="Projet", - ondelete="cascade", - required=True + comodel_name="afac.project", string="Projet", ondelete="cascade", required=True ) active = fields.Boolean(related="project_id.active") project_cat_ids = fields.Many2many( diff --git a/models/res_partner.py b/models/res_partner.py index 9bf4c5ab7b00500357c2f320a8a848f422f22ce4..6c5aa506cfc512e313fe2547ac33bc9068429ed9 100644 --- a/models/res_partner.py +++ b/models/res_partner.py @@ -259,7 +259,7 @@ class ResPartner(models.Model): self.short_name = self.parent_id.short_name # ------------------------------------------------------ - # CRUD methods (ORM overrides) + # ORM overrides # ------------------------------------------------------ def write(self, vals): """ @@ -294,6 +294,54 @@ class ResPartner(models.Model): return res + def export_data(self, fields_to_export, afac=False): + """ + Hérite la fonction de Model pour ajouter les projets et sous projets dans + l'export si la case est cochée + """ + out = super().export_data(fields_to_export) + if afac: + column = [] + datas = out.get("datas", []) + all_projects = ( + self.env["afac.project.partner"] + .search([("partner_id", "in", self.ids)]) + .mapped("project_id") + ) + if datas: + for afac_project in all_projects: + data_index = 0 + column.append(afac_project.name) + afac_project_category = self.env["afac.project.category"].search( + [("project_id", "=", afac_project.id)] + ) + for project_category in afac_project_category: + column.append(f"{afac_project.name}/{project_category.name}") + + for partner in self: + project_partner = self.env["afac.project.partner"].search( + [ + "&", + ("partner_id", "=", partner.id), + ("project_id", "=", afac_project.id), + ] + ) + if project_partner: + datas[data_index].append(1) + else: + datas[data_index].append(0) + + for project_category in afac_project_category: + if project_category in project_partner.project_cat_ids: + datas[data_index].append(1) + else: + datas[data_index].append(0) + data_index += 1 + out["datas"] = datas + out["column"] = column + return out + return out + # ------------------------------------------------------ # Actions # ------------------------------------------------------ diff --git a/static/src/js/data_export.js b/static/src/js/data_export.js new file mode 100644 index 0000000000000000000000000000000000000000..4e30fb593d599d75e54f143189fc008c2f26ff4e --- /dev/null +++ b/static/src/js/data_export.js @@ -0,0 +1,55 @@ +odoo.define("afac_partner.DataExport", function (require) { + "use strict"; + + var core = require("web.core"); + var DataExport = require("web.DataExport"); + var framework = require("web.framework"); + var pyUtils = require("web.py_utils"); + var Dialog = require("web.Dialog"); + var _t = core._t; + + DataExport.include({ + /* + Overwritten Object responsible for the standard export. + A flag (checkbox) afac is checked + */ + _exportData(exportedFields, exportFormat, idsToExport) { + const afac = this.$("#o-export-afac-project"); + if (afac && afac.is(":checked")) { + if (_.isEmpty(exportedFields)) { + Dialog.alert(this, _t("Please select fields to export...")); + return; + } + if (this.isCompatibleMode) { + exportedFields.unshift({name: "id", label: _t("External ID")}); + } + + framework.blockUI(); + this.getSession().get_file({ + url: "/web/export/" + exportFormat, + data: { + data: JSON.stringify({ + model: this.record.model, + fields: exportedFields, + ids: idsToExport, + domain: this.domain, + groupby: this.groupby, + context: pyUtils.eval("contexts", [ + this.record.getContext(), + ]), + import_compat: this.isCompatibleMode, + afac: true, + }), + }, + complete: framework.unblockUI, + error: (error) => this.call("crash_manager", "rpc_error", error), + }); + } else { + /* + Call the standard method if afac is not checked + */ + this._super.apply(this, arguments); + } + }, + }); +}); diff --git a/static/src/xml/base.xml b/static/src/xml/base.xml new file mode 100644 index 0000000000000000000000000000000000000000..c250e8c8c5d47584cdf5a88fffb6ca9db3fc438a --- /dev/null +++ b/static/src/xml/base.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<templates id="template" xml:space="preserve"> + <t t-name="ExportDialog" t-inherit="web.ExportDialog" t-inherit-mode="extension"> + <xpath expr="//div[hasclass('o_import_compat')]" position="after"> + <div t-if="widget.record.model == 'res.partner'" class="mt16 mb16"> + <div class="o_boolean_toggle custom-control custom-checkbox"> + <input + class="custom-control-input" + id="o-export-afac-project" + type="checkbox" + /> + <label class="custom-control-label" for="o-export-afac-project"> + Exporter les projets AFAC + </label> + </div> + </div> + </xpath> + </t> +</templates> diff --git a/views/afac_project.xml b/views/afac_project.xml index acc9bc791f2f10da266e8c9ecfae4dd342cd3e32..008f6da0c2c9cb47780562ad0b0919530a4d7a05 100644 --- a/views/afac_project.xml +++ b/views/afac_project.xml @@ -10,9 +10,17 @@ <field name="model">afac.project</field> <field name="arch" type="xml"> <search string="Projets"> - <field name="name" string="Nom du projet"/> - <filter name="active" string="Archivés" domain="[('active', '=', False)]"/> - <filter name="active" string="Tous" domain="[('active', 'in', (True, False))]"/> + <field name="name" string="Nom du projet" /> + <filter + name="active" + string="Archivés" + domain="[('active', '=', False)]" + /> + <filter + name="active" + string="Tous" + domain="[('active', 'in', (True, False))]" + /> </search> </field> </record> diff --git a/views/afac_project_partner.xml b/views/afac_project_partner.xml index a83e6da17d9fc4b884deecd14030e2c257fc49f7..cabda5330d4dbcc3ab536a33dcb8dfd6bbe9636c 100644 --- a/views/afac_project_partner.xml +++ b/views/afac_project_partner.xml @@ -9,7 +9,7 @@ <field name="model">afac.project.partner</field> <field name="arch" type="xml"> <search string="Projets du contact"> - <field name="partner_id"/> + <field name="partner_id" /> <field name="project_cat_ids" /> <field name="project_id" /> <searchpanel> diff --git a/views/assets.xml b/views/assets.xml index 6e7c7760a17152ac21c00039e18c9453395e95f5..5bbe70edb1175816b7c7c4b8a11db69a86a731dc 100644 --- a/views/assets.xml +++ b/views/assets.xml @@ -10,6 +10,10 @@ rel="stylesheet" href="/afac_partner/static/src/css/afac_partner.css" /> + <script + type="text/javascript" + src="/afac_partner/static/src/js/data_export.js" + /> </xpath> </template> </odoo>