From 94c2e6ed9010122530b2c628ec504fb5b6eaad03 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Wed, 23 Oct 2024 14:41:59 +0800 Subject: [PATCH] [ADD] report_pdf_zip_download --- report_pdf_zip_download/__init__.py | 2 + report_pdf_zip_download/__manifest__.py | 17 ++++ .../controllers/__init__.py | 1 + report_pdf_zip_download/controllers/main.py | 63 +++++++++++++ report_pdf_zip_download/models/__init__.py | 1 + .../models/ir_actions_report.py | 13 +++ report_pdf_zip_download/readme/CONFIGURE.rst | 1 + .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 1 + .../static/src/js/action_manager_report.js | 91 +++++++++++++++++++ .../views/ir_actions_report_views.xml | 13 +++ .../views/web_client_templates.xml | 8 ++ .../odoo/addons/report_pdf_zip_download | 1 + setup/report_pdf_zip_download/setup.py | 6 ++ 14 files changed, 221 insertions(+) create mode 100644 report_pdf_zip_download/__init__.py create mode 100644 report_pdf_zip_download/__manifest__.py create mode 100644 report_pdf_zip_download/controllers/__init__.py create mode 100644 report_pdf_zip_download/controllers/main.py create mode 100644 report_pdf_zip_download/models/__init__.py create mode 100644 report_pdf_zip_download/models/ir_actions_report.py create mode 100644 report_pdf_zip_download/readme/CONFIGURE.rst create mode 100644 report_pdf_zip_download/readme/CONTRIBUTORS.rst create mode 100644 report_pdf_zip_download/readme/DESCRIPTION.rst create mode 100644 report_pdf_zip_download/static/src/js/action_manager_report.js create mode 100644 report_pdf_zip_download/views/ir_actions_report_views.xml create mode 100644 report_pdf_zip_download/views/web_client_templates.xml create mode 120000 setup/report_pdf_zip_download/odoo/addons/report_pdf_zip_download create mode 100644 setup/report_pdf_zip_download/setup.py diff --git a/report_pdf_zip_download/__init__.py b/report_pdf_zip_download/__init__.py new file mode 100644 index 0000000000..91c5580fed --- /dev/null +++ b/report_pdf_zip_download/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/report_pdf_zip_download/__manifest__.py b/report_pdf_zip_download/__manifest__.py new file mode 100644 index 0000000000..21b6a2daff --- /dev/null +++ b/report_pdf_zip_download/__manifest__.py @@ -0,0 +1,17 @@ +# © 2017 Creu Blanca +# Copyright 2024 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Report PDF ZIP Download", + "category": "Report", + "version": "12.0.1.0.0", + "author": "Quartile, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/reporting-engine", + "license": "AGPL-3", + "depends": ["web"], + "data": [ + "views/ir_actions_report_views.xml", + "views/web_client_templates.xml", + ], + "installable": True, +} diff --git a/report_pdf_zip_download/controllers/__init__.py b/report_pdf_zip_download/controllers/__init__.py new file mode 100644 index 0000000000..12a7e529b6 --- /dev/null +++ b/report_pdf_zip_download/controllers/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/report_pdf_zip_download/controllers/main.py b/report_pdf_zip_download/controllers/main.py new file mode 100644 index 0000000000..c45c21219e --- /dev/null +++ b/report_pdf_zip_download/controllers/main.py @@ -0,0 +1,63 @@ +# Copyright 2024 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import zipfile +from odoo.http import content_disposition +from odoo import http +from odoo.http import request +from odoo.addons.web.controllers.main import ReportController +from io import BytesIO +from datetime import datetime +import json +from odoo.tools.safe_eval import safe_eval +import time + + +class ExtendedReportController(ReportController): + + @http.route() + def report_routes(self, reportname, docids=None, converter=None, **data): + report = request.env['ir.actions.report']._get_report_from_name(reportname) + report_name = report.report_file + doc_ids = [] + if converter == "zip": + if docids: + doc_ids = [int(i) for i in docids.split(',')] + context = dict(request.env.context) + if data.get('options'): + data.update(json.loads(data.pop('options'))) + if data.get('context'): + data['context'] = json.loads(data['context']) + if data['context'].get('lang'): + del data['context']['lang'] + context.update(data['context']) + attachments = [] + for doc_id in doc_ids: + pdf_content, _ = report.with_context(context).render_qweb_pdf( + [doc_id], data=data + ) + if report.print_report_name: + obj = request.env[report.model].browse(doc_id) + report_name = safe_eval(report.print_report_name, + {'object': obj, 'time': time}) + report_name = report_name.replace('/', '_') + pdf_name = f'{report_name}.pdf' + attachments.append((pdf_name, pdf_content)) + # Generate the ZIP file + zip_filename = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip" + bitIO = BytesIO() + with zipfile.ZipFile(bitIO, "w", zipfile.ZIP_DEFLATED) as zip_file: + for pdf_name, pdf_content in attachments: + zip_file.writestr(pdf_name, pdf_content) + zip_content = bitIO.getvalue() + headers = [ + ('Content-Type', 'application/zip'), + ('Content-Disposition', content_disposition(zip_filename)) + ] + return request.make_response( + zip_content, + headers=headers + ) + return super(ExtendedReportController, self).report_routes( + reportname, docids, converter, **data + ) diff --git a/report_pdf_zip_download/models/__init__.py b/report_pdf_zip_download/models/__init__.py new file mode 100644 index 0000000000..a248cf2162 --- /dev/null +++ b/report_pdf_zip_download/models/__init__.py @@ -0,0 +1 @@ +from . import ir_actions_report diff --git a/report_pdf_zip_download/models/ir_actions_report.py b/report_pdf_zip_download/models/ir_actions_report.py new file mode 100644 index 0000000000..34a77e4847 --- /dev/null +++ b/report_pdf_zip_download/models/ir_actions_report.py @@ -0,0 +1,13 @@ +# Copyright 2024 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +class IrActionsReport(models.Model): + _inherit = "ir.actions.report" + + zip_download = fields.Boolean( + help="If enabled, the report will be downloaded as a zip " + "file for multiple records." + ) diff --git a/report_pdf_zip_download/readme/CONFIGURE.rst b/report_pdf_zip_download/readme/CONFIGURE.rst new file mode 100644 index 0000000000..5eb1ef4223 --- /dev/null +++ b/report_pdf_zip_download/readme/CONFIGURE.rst @@ -0,0 +1 @@ +To enable the ZIP file feature, please check the 'Zip Download' option under the 'Advanced Properties' tab for the specific report. diff --git a/report_pdf_zip_download/readme/CONTRIBUTORS.rst b/report_pdf_zip_download/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..cd4e44ca98 --- /dev/null +++ b/report_pdf_zip_download/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Quartile `__: + + * Aung Ko Ko Lin diff --git a/report_pdf_zip_download/readme/DESCRIPTION.rst b/report_pdf_zip_download/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..10307fc1b3 --- /dev/null +++ b/report_pdf_zip_download/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module generates one PDF report per record, and the reports are compiled into a ZIP file if the user prints multiple records at once. If the user prints a single record, it will follow Odoo's standard behavior and will generate just a single PDF file. diff --git a/report_pdf_zip_download/static/src/js/action_manager_report.js b/report_pdf_zip_download/static/src/js/action_manager_report.js new file mode 100644 index 0000000000..40b6e6d9ca --- /dev/null +++ b/report_pdf_zip_download/static/src/js/action_manager_report.js @@ -0,0 +1,91 @@ +// © 2017 Creu Blanca +// Copyright 2024 Quartile (https://www.quartile.co) +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +odoo.define("report_pdf_zip_download.report", function (require) { + "use strict"; + + var core = require("web.core"); + var ActionManager = require("web.ActionManager"); + var crash_manager = require("web.crash_manager"); + var framework = require("web.framework"); + var session = require("web.session"); + var _t = core._t; + + ActionManager.include({ + + _downloadReportZIP: function (url, actions) { + framework.blockUI(); + var def = $.Deferred(); + var type = "zip"; + var cloned_action = _.clone(actions); + + if (_.isUndefined(cloned_action.data) || + _.isNull(cloned_action.data) || + (_.isObject(cloned_action.data) && _.isEmpty(cloned_action.data))) + { + if (cloned_action.context.active_ids) { + url += "/" + cloned_action.context.active_ids.join(','); + } + } else { + url += "?options=" + encodeURIComponent(JSON.stringify(cloned_action.data)); + url += "&context=" + encodeURIComponent(JSON.stringify(cloned_action.context)); + } + + var blocked = !session.get_file({ + url: url, + data: { + data: JSON.stringify([url, type]), + }, + success: def.resolve.bind(def), + error: function () { + crash_manager.rpc_error.apply(crash_manager, arguments); + def.reject(); + }, + complete: framework.unblockUI, + }); + if (blocked) { + var message = _t('A popup window with your report was blocked. You ' + + 'may need to change your browser settings to allow ' + + 'popup windows for this page.'); + this.do_warn(_t('Warning'), message, true); + } + return def; + }, + + _triggerDownload: function (action, options, type) { + var self = this; + var reportUrls = this._makeReportUrls(action); + if (type === "zip") { + return this._downloadReportZIP(reportUrls[type], action).then(function () { + if (action.close_on_report_download) { + var closeAction = {type: 'ir.actions.act_window_close'}; + return self.doAction(closeAction, _.pick(options, 'on_close')); + } else { + return options.on_close(); + } + }); + } + return this._super.apply(this, arguments); + }, + + _makeReportUrls: function (action) { + var reportUrls = this._super.apply(this, arguments); + reportUrls.zip = '/report/zip/' + action.report_name; + return reportUrls; + }, + + _executeReportAction: function (action, options) { + var self = this; + + console.log(action.data); + console.log(action.context); + if (action.context.active_ids && action.context.active_ids.length > 1) { + if (action.report_type === 'qweb-pdf' && action.zip_download === true) { + return self._triggerDownload(action, options, 'zip'); + } + } + return this._super.apply(this, arguments); + } + }); + +}); diff --git a/report_pdf_zip_download/views/ir_actions_report_views.xml b/report_pdf_zip_download/views/ir_actions_report_views.xml new file mode 100644 index 0000000000..04ed7a23d4 --- /dev/null +++ b/report_pdf_zip_download/views/ir_actions_report_views.xml @@ -0,0 +1,13 @@ + + + + ir.actions.report + ir.actions.report + + + + + + + + diff --git a/report_pdf_zip_download/views/web_client_templates.xml b/report_pdf_zip_download/views/web_client_templates.xml new file mode 100644 index 0000000000..54cf63b45e --- /dev/null +++ b/report_pdf_zip_download/views/web_client_templates.xml @@ -0,0 +1,8 @@ + + +