From 1bb968e6957e6088bcee8302474983476e04d158 Mon Sep 17 00:00:00 2001 From: benjamin <benjamin@le-filament.com> Date: Wed, 21 Aug 2024 11:17:51 +0200 Subject: [PATCH] [IMP] add groups, sum and domain --- controllers/main.py | 79 ++++++++++++++++++++++++++-------- datas/export_journal_datas.xml | 4 -- models/__init__.py | 1 + models/export_journal_field.py | 3 +- models/export_journal_group.py | 28 ++++++++++++ models/export_journal_type.py | 8 ++++ security/ir.model.access.csv | 1 + views/export_journal_type.xml | 64 ++++++++++++++++++++------- 8 files changed, 150 insertions(+), 38 deletions(-) create mode 100644 models/export_journal_group.py diff --git a/controllers/main.py b/controllers/main.py index b748ace..193a218 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -33,44 +33,87 @@ class JournalDatasExport(http.Controller): :return: file """ export_id = request.env["export.journal.type"].browse(int(export)) + # Set domain + domain = self._get_domain(export_id, export_type, date_start, date_end) + + # Retrieve lines & set datas + lines_to_export = [] + if not export_id.group_ids: + aml_ids = request.env["account.move.line"].search(domain) + lines_to_export = self._get_lines(aml_ids, export_id) + else: + group_fields = export_id.group_ids.mapped("field_id.name") + group_agg = export_id.group_ids.mapped( + lambda f: f"{f.field_id.name}:{f.field_agg}" if f.field_agg else f.field_id.name) + grouped_lines = request.env["account.move.line"].read_group( + domain, fields=group_fields, groupby=group_agg, lazy=False) + for group in grouped_lines: + if export_id.is_group_header: + header = [self._get_group_name(export_id, group)] + lines_to_export.append(header) + aml_ids = request.env["account.move.line"].search(group.get("__domain")) + lines_to_export += self._get_lines(aml_ids, export_id) + + filename_ = ( + export_id.company_id.name.title().replace(" ", "") + + date_start.replace("-", "") + + "_" + + date_end.replace("-", "") + ) + + if export_format == "csv": + return self.export_csv(export_id, lines_to_export, filename_) + elif export_format == "xls": + return self.export_xls(export_id, lines_to_export, filename_) + + # ------------------------------------------------------ + # Common function + # ------------------------------------------------------ + def _get_domain(self, export_id, export_type, date_start, date_end): domain = [ ("date", ">=", date_start), ("date", "<=", date_end), ("journal_id", "in", export_id.journal_ids.ids), ("company_id", "=", export_id.company_id.id), ("move_id.state", "=", "posted"), - ("display_type", "not in", ("line_section", "line_note")), + ("display_type", "=", False), ] if export_type == "empty": domain += [("date_export", "=", False)] - export_line_ids = request.env["account.move.line"].search(domain) + if export_id.export_domain: + domain += safe_eval(export_id.export_domain) + return domain + + def _get_lines(self, aml_ids, export_id): lines_to_export = [] - for line in export_line_ids: + sum_row = [None for f in range(0, len(export_id.fields_ids))] + for line in aml_ids: row = [] - for field in export_id.fields_ids: + for index, field in enumerate(export_id.fields_ids): if field.is_python: value = safe_eval(field["field_name"], {"line": line}, mode="eval") else: value = safe_eval(field["field_name"]) row.append(value) + if field.is_sum: + sum_row[index] = sum_row[index] + value if sum_row[index] else value lines_to_export.append(row) line.write({"date_export": datetime.now()}) + if any(sum_row): + lines_to_export.append(sum_row) + return lines_to_export - filename_ = ( - export_id.company_id.name.title().replace(" ", "") - + date_start.replace("-", "") - + "_" - + date_end.replace("-", "") - ) - - if export_format == "csv": - return self.export_csv(export_id, lines_to_export, filename_) - elif export_format == "xls": - return self.export_xls(export_id, lines_to_export, filename_) + def _get_group_name(self, export_id, group): + header_list = [] + for f in export_id.group_ids: + if f.field_agg: + header_list.append(group[f"{f.field_id.name}:{f.field_agg}"]) + elif f.field_id.ttype == "many2one": + header_list.append(group[f.field_id.name][1]._value) + else: + header_list.append(group[f.field_id.name]) + return " - ".join(header_list) - # ------------------------------------------------------ - # Common function - # ------------------------------------------------------ def export_csv(self, export_id, lines_to_export, filename_): fp = StringIO() export_file = csv.writer( diff --git a/datas/export_journal_datas.xml b/datas/export_journal_datas.xml index db2e489..1aa0f09 100644 --- a/datas/export_journal_datas.xml +++ b/datas/export_journal_datas.xml @@ -10,7 +10,6 @@ <field name="active" eval="True" /> <field name="sequence">1</field> <field name="is_header" eval="False" /> - <field name="journal_ids" eval="[(6, 0, [1])]" /> <field name="company_id" eval="1" /> <field name="csv_datestyle">%d/%m/%y</field> <field name="xls_datestyle">dd/mm/yy</field> @@ -20,7 +19,6 @@ <field name="active" eval="True" /> <field name="sequence">2</field> <field name="is_header" eval="False" /> - <field name="journal_ids" eval="[(6, 0, [2])]" /> <field name="company_id" eval="1" /> <field name="csv_datestyle">%d/%m/%y</field> <field name="xls_datestyle">dd/mm/yy</field> @@ -32,7 +30,6 @@ <field name="active" eval="True" /> <field name="sequence">4</field> <field name="is_header" eval="True" /> - <field name="journal_ids" eval="[(6, 0, [1])]" /> <field name="company_id" eval="1" /> <field name="csv_datestyle">%d/%m/%Y</field> <field name="xls_datestyle">dd/mm/yyyy</field> @@ -42,7 +39,6 @@ <field name="active" eval="True" /> <field name="sequence">5</field> <field name="is_header" eval="True" /> - <field name="journal_ids" eval="[(6, 0, [2])]" /> <field name="company_id" eval="1" /> <field name="csv_datestyle">%d/%m/%Y</field> <field name="xls_datestyle">dd/mm/yyyy</field> diff --git a/models/__init__.py b/models/__init__.py index 5ab804d..33171c9 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,3 +1,4 @@ from . import account_move_line from . import export_journal_field +from . import export_journal_group from . import export_journal_type diff --git a/models/export_journal_field.py b/models/export_journal_field.py index 652f408..7467d71 100644 --- a/models/export_journal_field.py +++ b/models/export_journal_field.py @@ -11,6 +11,7 @@ class ExportJournalField(models.Model): name = fields.Char("Header") sequence = fields.Integer(default=10) - export_id = fields.Many2one(comodel_name="export.journal.type", name="Parent") + export_id = fields.Many2one(comodel_name="export.journal.type", string="Parent") field_name = fields.Char("Champ") is_python = fields.Boolean("Expression python", default=True) + is_sum = fields.Boolean() diff --git a/models/export_journal_group.py b/models/export_journal_group.py new file mode 100644 index 0000000..b29c6d2 --- /dev/null +++ b/models/export_journal_group.py @@ -0,0 +1,28 @@ +# Copyright 2020-2022 Le Filament (<https://le-filament.com>) +# License AGPL-3 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class ExportJournalGroup(models.Model): + _name = "export.journal.group" + _description = "Export journal - champs à exporter" + _order = "sequence" + + sequence = fields.Integer(default=10) + export_id = fields.Many2one(comodel_name="export.journal.type", string="Parent") + field_id = fields.Many2one( + comodel_name="ir.model.fields", + string="Champ", + domain="[('model_id', '=', 'account.move.line'), ('store', '=', True)," + "('ttype', 'not in', " + "['many2many', 'one2many', 'binary', 'integer', 'float'])" + "]", + ) + field_type = fields.Selection(related="field_id.ttype") + field_agg = fields.Char("Aggregation") + + @api.onchange("field_id") + def _onchange_field_id(self): + if self.field_type not in ["date", "datetime"]: + self.field_agg = None diff --git a/models/export_journal_type.py b/models/export_journal_type.py index 6f2d9a2..d83991e 100644 --- a/models/export_journal_type.py +++ b/models/export_journal_type.py @@ -18,7 +18,14 @@ class ExportJournalType(models.Model): string="Champs", copy=True, ) + group_ids = fields.One2many( + comodel_name="export.journal.group", + inverse_name="export_id", + string="Groupes", + copy=True, + ) is_header = fields.Boolean("Header", default=True) + is_group_header = fields.Boolean("Header Group", default=False) company_id = fields.Many2one( comodel_name="res.company", string="Company", @@ -33,3 +40,4 @@ class ExportJournalType(models.Model): delimiter = fields.Char("Séparateur", default=";") csv_datestyle = fields.Char("Format Date CSV", default="%Y-%m-%d") xls_datestyle = fields.Char("Format Date XLS", default="YYYY-MM-DD") + export_domain = fields.Char(string="Export filter") diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 1596a2c..a1227a1 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -3,4 +3,5 @@ access_export_journal_type_user,access_export_journal_type_user,model_export_jou access_export_journal_type,access_export_journal_type,model_export_journal_type,base.group_erp_manager,1,1,1,1 access_export_journal_field_user,access_export_journal_field_user,model_export_journal_field,account.group_account_invoice,1,0,0,0 access_export_journal_fields,access_export_journal_fields,model_export_journal_field,base.group_erp_manager,1,1,1,1 +access_export_journal_group,access_export_journal_group,model_export_journal_group,base.group_erp_manager,1,1,1,1 access_export_journal_wizard,access_export_journal_wizard,model_export_journal_wizard,account.group_account_invoice,1,1,1,1 diff --git a/views/export_journal_type.xml b/views/export_journal_type.xml index 0ee90a4..3ba2d02 100644 --- a/views/export_journal_type.xml +++ b/views/export_journal_type.xml @@ -36,23 +36,57 @@ options="{'no_create': 1}" required="1" /> + <field + name="export_domain" + widget="domain" + options='{"model": "account.move.line"}' + /> </group> </group> - <div class="alert alert-info" role="alert"> - La première colonne correspond au nom de l'entête du fichier de sortie.<br - /> - La deuxième colonne est la valeur de sortie : code python où l'objet <code - >line</code> correspond à un <code>account.move.line</code>.<br - /> - </div> - <field name="fields_ids"> - <tree editable="bottom"> - <field name="sequence" widget="handle" /> - <field name="name" required="1" /> - <field name="field_name" required="1" /> - <field name="is_python" widget="boolean_toggle" /> - </tree> - </field> + <notebook> + <page name="fields" string="Lignes d'export"> + <div class="alert alert-info" role="alert"> + La première colonne correspond au nom de l'entête du fichier de sortie.<br + /> + La deuxième colonne est la valeur de sortie : code python où l'objet <code + >line</code> correspond à un <code>account.move.line</code>.<br + /> + </div> + <field name="fields_ids" context="{'default_export_id': active_id}"> + <tree editable="bottom"> + <field name="sequence" widget="handle" /> + <field name="name" required="1" /> + <field name="field_name" required="1" /> + <field name="is_python" widget="boolean_toggle" /> + <field name="is_sum" /> + <field name="export_id" invisible="1" /> + </tree> + </field> + </page> + <page name="groups" string="Groupes"> + <group> + <group> + <field name="is_group_header" /> + </group> + </group> + <field name="group_ids" context="{'default_export_id': active_id}"> + <tree editable="bottom"> + <field name="sequence" widget="handle" /> + <field + name="field_id" + options="{'no_create': 1, 'no_edit': 1}" + required="1" + /> + <field name="field_type" /> + <field + name="field_agg" + attrs="{'readonly': [('field_type', 'not in', ['date', 'datetime'])]}" + /> + <field name="export_id" invisible="1" /> + </tree> + </field> + </page> + </notebook> </sheet> </form> </field> -- GitLab