diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c985deec..a30ad3cf 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -17,6 +17,7 @@ * Si ya existe un docstring, puede sugerirse un estilo básico acorde a PEP8, pero **no será un error** si faltan `return`, tipos o parámetros documentados. 5. No proponer cambios puramente estéticos (espacios, comillas simples vs dobles, orden de imports, etc.). 6. Mantener el feedback **muy conciso** en los PRs: priorizar pocos puntos claros, evitar párrafos largos y no repetir el contexto que ya está explicado en la descripción del PR. +7. Sobre traducciones: usar `_()` o `self.env._()` es indistinto; solo marcar si hay mensajes de error o textos no traducidos que deban serlo. --- @@ -38,13 +39,7 @@ * Confirmar que todos los archivos usados (vistas, seguridad, datos, reportes, wizards) estén referenciados en el manifest. * Verificar dependencias declaradas: que no falten módulos requeridos ni se declaren innecesarios. * **Regla de versión (obligatoria):** - Siempre que el diff incluya **modificaciones en**: - - * definición de campos o modelos (`models/*.py`, `wizards/*.py`), - * vistas o datos XML (`views/*.xml`, `data/*.xml`, `report/*.xml`, `wizards/*.xml`), - * seguridad (`security/*.csv`, `security/*.xml`), - - **y el `__manifest__.py` no incrementa `version`, sugerir el bump de versión** (por ejemplo, `1.0.0 → 1.0.1`). + Solo sugerir bump de versión si el `__manifest__.py` no incrementa `version` y se modificó la estructura de un modelo, una vista, o algún record .xml (ej. cambios en definición de campos, vistas XML, datos XML, seguridad). * Solo hacerlo una vez por revisión, aunque haya múltiples archivos afectados. --- @@ -288,7 +283,7 @@ def migrate(cr, registry): | ------------------ | -------------------------------------------------------------------------------------------------------- | | Modelos | Relaciones válidas; constraints; uso adecuado de `@api.depends`; `super()` correcto | | Vistas XML | Herencias correctas; campos válidos; adaptación a cambios de versión (p.ej. `` vs ``) | -| Manifest | **Bump de versión obligatorio** si hay cambios en modelos/vistas/seguridad/datos; archivos referenciados | +| Manifest | **Bump de versión obligatorio** si hay cambios estructurales en modelos/vistas/records .xml; archivos referenciados | | Seguridad | Accesos mínimos necesarios; reglas revisadas | | Migraciones | **Si hay cambios estructurales, sugerir script en `migrations/` (pre/post/end)** y describir qué hace | | Rendimiento / ORM | Evitar loops costosos; no SQL innecesario; aprovechar las optimizaciones del ORM de la versión | @@ -298,7 +293,7 @@ def migrate(cr, registry): ## Heurística práctica para el bump de versión (general) -* **SI** el diff toca cualquiera de: `models/`, `views/`, `data/`, `report/`, `security/`, `wizards/` +* **SI** el diff modifica la estructura de un modelo, una vista, o algún record .xml (ej. cambios en definición de campos, vistas XML, datos XML, seguridad) **Y** `__manifest__.py` no cambia `version` → **Sugerir bump**. * **SI** hay scripts `migrations/pre_*.py` o `migrations/post_*.py` nuevos → **Sugerir al menos minor bump**. * **SI** hay cambios que rompen compatibilidad (renombres, cambios de tipo con impacto, limpieza masiva de datos) → **Sugerir minor/major** según impacto. @@ -321,12 +316,10 @@ def migrate(cr, registry): ## Resumen operativo para Copilot -1. **Detecta cambios en modelos/vistas/seguridad/datos → exige bump de `version` en `__manifest__.py`.** +1. **Detecta cambios estructurales en modelos, vistas o records .xml → exige bump de `version` en `__manifest__.py` si no está incrementada.** 2. **Si hay cambio estructural (según la lista actualizada) → propone y describe script(s) de migración en `migrations/` (pre/post/end)**, con enfoque idempotente y en lotes. 3. Distingue entre: * **cuestiones generales** (válidas para cualquier versión), * y **matices específicos de Odoo 18** (por ejemplo, uso de ``, passkeys, tours y comportamiento del framework). -4. Mantén el feedback **concreto, breve y accionable**. - -[^odoo18]: Resumen basado en la documentación oficial de Odoo 18 Release Notes y artículos técnicos que analizan sus mejoras de rendimiento y UX. \ No newline at end of file +4. Mantén el feedback **concreto, breve y accionable**. \ No newline at end of file diff --git a/base_ux/__manifest__.py b/base_ux/__manifest__.py index 5eb52e30..69c3d53a 100644 --- a/base_ux/__manifest__.py +++ b/base_ux/__manifest__.py @@ -19,7 +19,7 @@ ############################################################################## { "name": "Base UX", - "version": "18.0.3.0.0", + "version": "18.0.4.0.0", "category": "Base", "sequence": 14, "summary": "", @@ -35,6 +35,7 @@ "views/ir_actions_act_window_view.xml", "views/mail_template_view.xml", "views/res_company_view.xml", + "views/base_partner_merge_view.xml", ], "demo": [], "test": [], diff --git a/base_ux/views/base_partner_merge_view.xml b/base_ux/views/base_partner_merge_view.xml new file mode 100644 index 00000000..59542c09 --- /dev/null +++ b/base_ux/views/base_partner_merge_view.xml @@ -0,0 +1,14 @@ + + + + base.partner.merge.automatic.wizard.form.inherit + base.partner.merge.automatic.wizard + + 99 + + + ¿Desea confirmar la fusión de contactos? + + + + diff --git a/report_copies/README.rst b/export_bg/README.rst similarity index 56% rename from report_copies/README.rst rename to export_bg/README.rst index e570fb37..9d561385 100644 --- a/report_copies/README.rst +++ b/export_bg/README.rst @@ -2,7 +2,7 @@ .. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png :alt: ADHOC SA - :target: https://www.adhoc.com.ar + :target: https://www.adhoc.inc .. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png @@ -10,36 +10,39 @@ :target: https://www.gnu.org/licenses/agpl :alt: License: AGPL-3 -============= -Report Copies -============= +================= +Export Background +================= -* Add the posibility to print more than 1 copy for the qweb reports. +Automatically exports large datasets (>500 records) in background to avoid timeouts. Installation ============ -To install this module, you need to: - -#. Just install +Install the module and its dependency: ``base_bg`` Configuration ============= -To configure this module, you need to: +Optional: Configure the record threshold in **Settings > Technical > System Parameters**: -#. Don't need any configuration +* Key: ``export_bg.record_threshold`` +* Default: ``500`` Usage ===== -To use this module, you need to: +1. Go to any list view and select records to export +2. Click **Export** and choose your format (CSV or XLSX) +3. If records exceed the threshold: + - You'll receive a notification: "Export sent to background" + - You'll receive a message with download link when ready -#. Just use it. +For exports under the threshold, it works as normal (instant download). .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: http://runbot.adhoc.com.ar/ + :target: https://runbot.dev-adhoc.com/ Bug Tracker =========== @@ -67,4 +70,4 @@ Maintainer This module is maintained by the |company|. -To contribute to this module, please visit https://www.adhoc.com.ar. +To contribute to this module, please visit https://www.adhoc.inc. diff --git a/export_bg/__init__.py b/export_bg/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/export_bg/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/report_copies/__manifest__.py b/export_bg/__manifest__.py similarity index 69% rename from report_copies/__manifest__.py rename to export_bg/__manifest__.py index 4a39e26b..e5fcc051 100644 --- a/report_copies/__manifest__.py +++ b/export_bg/__manifest__.py @@ -1,6 +1,6 @@ ############################################################################## # -# Copyright (C) 2020 ADHOC SA (http://www.adhoc.com.ar) +# Copyright (C) 2026 ADHOC SA (http://www.adhoc.com.ar) # All Rights Reserved. # # This program is free software: you can redistribute it and/or modify @@ -17,25 +17,25 @@ # along with this program. If not, see . # ############################################################################## - { - "name": "Report Copies", - "version": "15.0.1.0.0", - "category": "Web & Reports", - "sequence": 14, - "summary": "", + "name": "Export Background", + "version": "18.0.1.0.0", + "category": "Technical", "author": "ADHOC SA", - "website": "www.adhoc.com.ar", + "website": "https://www.adhoc.com.ar", "license": "AGPL-3", - "images": [], + "summary": "Export large datasets in background to avoid timeouts", "depends": [ + "base_bg", "web", ], - "data": [ - "views/report_templates.xml", - "views/ir_actions_views.xml", - ], - "installable": False, + "data": [], + "assets": { + "web.assets_backend": [ + "export_bg/static/src/views/list_controller.js", + ], + }, + "installable": True, "auto_install": False, "application": False, } diff --git a/export_bg/models/__init__.py b/export_bg/models/__init__.py new file mode 100644 index 00000000..a1b6c144 --- /dev/null +++ b/export_bg/models/__init__.py @@ -0,0 +1 @@ +from . import export_bg_mixin diff --git a/export_bg/models/export_bg_mixin.py b/export_bg/models/export_bg_mixin.py new file mode 100644 index 00000000..1872cdb9 --- /dev/null +++ b/export_bg/models/export_bg_mixin.py @@ -0,0 +1,57 @@ +import base64 +import io +import json + +from markupsafe import Markup +from odoo import _, api, models +from odoo.addons.web.controllers.export import CSVExport +from odoo.tools.misc import xlsxwriter + + +class IrModel(models.Model): + _name = "ir.model" + _inherit = ["ir.model", "base.bg"] + + @api.model + def get_export_threshold(self): + """Get the threshold for background export without requiring admin permissions.""" + return int(self.env["ir.config_parameter"].sudo().get_param("export_bg.record_threshold", "500")) + + def _prepare_export_data(self, data): + params = json.loads(data) + Model = self.env[params["model"]].with_context(**params.get("context", {})) + records = Model.browse(params["ids"]) if params.get("ids") else Model.search(params.get("domain", [])) + return ( + params, + [f["string"] for f in params["fields"]], + records.export_data([f["value"] for f in params["fields"]]).get("datas", []), + ) + + def web_export_csv(self, data): + params, headers, export_data = self._prepare_export_data(data) + content = CSVExport().from_data(params["fields"], headers, export_data).encode() + return self._save_attachment(params["model"], content, ".csv", "text/csv;charset=utf8") + + def web_export_xlsx(self, data): + params, headers, export_data = self._prepare_export_data(data) + buf = io.BytesIO() + wb = xlsxwriter.Workbook(buf, {"in_memory": True}) + ws = wb.add_worksheet() + ws.write_row(0, 0, headers) + for i, row in enumerate(export_data, 1): + ws.write_row(i, 0, row) + wb.close() + return self._save_attachment( + params["model"], + buf.getvalue(), + ".xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + + def _save_attachment(self, model, content, ext, mime): + att = self.env["ir.attachment"].create( + {"name": f"{model}{ext}", "datas": base64.b64encode(content), "mimetype": mime} + ) + return Markup( + f'

{_("Your export is ready!")}

{_("Download")} {att.name}

' + ) diff --git a/export_bg/static/src/views/list_controller.js b/export_bg/static/src/views/list_controller.js new file mode 100644 index 00000000..a2f361af --- /dev/null +++ b/export_bg/static/src/views/list_controller.js @@ -0,0 +1,43 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { ListController } from "@web/views/list/list_controller"; + +patch(ListController.prototype, { + async downloadExport(fields, import_compat, format) { + const resIds = this.isDomainSelected ? false : await this.getSelectedResIds(); + const recordCount = resIds ? resIds.length : (this.model.root.count || 0); + + const threshold = await this.model.orm.call( + "ir.model", + "get_export_threshold", + [] + ); + + if (recordCount > threshold) { + const data = { + model: this.props.resModel, + fields: fields, + ids: resIds, + domain: this.model.root.domain, + import_compat: import_compat, + }; + + const method = format === "csv" ? "web_export_csv" : "web_export_xlsx"; + const actionResult = await this.model.orm.call( + "ir.model", + "bg_enqueue", + [method], + { + data: JSON.stringify(data), + } + ); + + if (actionResult && actionResult.type === "ir.actions.client") { + this.env.services.action.doAction(actionResult); + } + } else { + await super.downloadExport(...arguments); + } + }, +}); diff --git a/report_copies/__init__.py b/report_copies/__init__.py deleted file mode 100644 index 83bb583d..00000000 --- a/report_copies/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -############################################################################## -# For copyright and license notices, see __manifest__.py file in module root -# directory -############################################################################## - -from . import models diff --git a/report_copies/i18n/es.po b/report_copies/i18n/es.po deleted file mode 100644 index 2d66f93f..00000000 --- a/report_copies/i18n/es.po +++ /dev/null @@ -1,36 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * report_copies -# -# Translators: -# Juan José Scarafía , 2022 -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 15.0+e\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-04-24 16:45+0000\n" -"PO-Revision-Date: 2022-04-11 18:14+0000\n" -"Last-Translator: Juan José Scarafía , 2022\n" -"Language-Team: Spanish (https://www.transifex.com/adhoc/teams/133229/es/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Language: es\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#. module: report_copies -#: model:ir.model.fields,field_description:report_copies.field_ir_actions_report__ncopies -msgid "Number of print copies" -msgstr "" - -#. module: report_copies -#: model:ir.model,name:report_copies.model_ir_actions_report -msgid "Report Action" -msgstr "Acción de informe" - -#. module: report_copies -#: code:addons/report_copies/models/ir_actions_report.py:0 -#, python-format -msgid "The number of copies must be strict positive and different than zero." -msgstr "" diff --git a/report_copies/i18n/ru.po b/report_copies/i18n/ru.po deleted file mode 100644 index da91385d..00000000 --- a/report_copies/i18n/ru.po +++ /dev/null @@ -1,30 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * report_copies -# -# Translators: -# Ekaterina , 2021 -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 13.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-12 22:27+0000\n" -"PO-Revision-Date: 2020-12-15 10:05+0000\n" -"Last-Translator: Ekaterina , 2021\n" -"Language-Team: Russian (https://www.transifex.com/adhoc/teams/46451/ru/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Language: ru\n" -"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" - -#. module: report_copies -#: model:ir.model.fields,field_description:report_copies.field_ir_actions_report__ncopies -msgid "Number of print copies" -msgstr "Number of print copies" - -#. module: report_copies -#: model:ir.model,name:report_copies.model_ir_actions_report -msgid "Report Action" -msgstr "Report Action" diff --git a/report_copies/models/__init__.py b/report_copies/models/__init__.py deleted file mode 100644 index 906e5e71..00000000 --- a/report_copies/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -############################################################################## -# For copyright and license notices, see __manifest__.py file in module root -# directory -############################################################################## -from . import ir_actions_report diff --git a/report_copies/models/ir_actions_report.py b/report_copies/models/ir_actions_report.py deleted file mode 100644 index 757b7e15..00000000 --- a/report_copies/models/ir_actions_report.py +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################## -# For copyright and license notices, see __manifest__.py file in module root -# directory -############################################################################## - -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError - - -class IrActionsReport(models.Model): - _inherit = "ir.actions.report" - - ncopies = fields.Integer(string="Number of print copies", default=1) - - @api.model - def _get_rendering_context(self, docids, data): - res = super()._get_rendering_context(docids, data) - ncopies = self.ncopies - if self._context.get("force_email", False): - ncopies = 1 - res.update( - { - "ncopies": ncopies, - } - ) - return res - - @api.constrains("ncopies") - def _check_ncopies(self): - if self.filtered(lambda x: x.ncopies < 1): - raise ValidationError(_("The number of copies must be strict positive and different than zero.")) diff --git a/report_copies/views/ir_actions_views.xml b/report_copies/views/ir_actions_views.xml deleted file mode 100644 index f114b54e..00000000 --- a/report_copies/views/ir_actions_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - ir.actions.report - ir.actions.report - - - - - - - - - diff --git a/report_copies/views/report_templates.xml b/report_copies/views/report_templates.xml deleted file mode 100644 index 46b6434a..00000000 --- a/report_copies/views/report_templates.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - -