This commit is contained in:
gsinghpal
2026-05-11 03:20:31 -04:00
parent 6b7b44264a
commit eee2dcd615
5 changed files with 127 additions and 7 deletions

View File

@@ -1 +1,2 @@
from . import controllers
from . import models

View File

@@ -3,7 +3,7 @@
# License OPL-1 (Odoo Proprietary License v1.0)
{
'name': 'Fusion Reports — Templates',
'version': '19.0.1.0.0',
'version': '19.0.1.2.0',
'category': 'Tools/Reports',
'summary': 'Branded PDF templates for Quotation, Sales Order, Invoice, Delivery, Purchase Order, and Payment Receipt.',
'description': """

View File

@@ -0,0 +1 @@
from . import portal

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""Swap stock report refs for their Fusion-branded equivalents in customer/vendor
portal routes.
The portal "View Details" link and the portal Download button in
`sale/account/purchase/controllers/portal.py` hard-code the stock report
(`sale.action_report_saleorder`, `account.account_invoices`, ...). All those
routes funnel through `CustomerPortal._show_report` in
`portal/controllers/portal.py`, so a single override on that method is enough
to redirect every customer-facing render to the Fusion templates that
`mail_template_override.xml` already uses for outbound email.
"""
from odoo.addons.portal.controllers import portal
_PORTAL_REPORT_OVERRIDES = {
# Sale: quotation / order
'sale.action_report_saleorder': 'fusion_reports_templates.action_report_fr_sale_portrait',
# Account: customer invoice + credit note (with-payment and without-payment variants)
'account.account_invoices': 'fusion_reports_templates.action_report_fr_invoice_portrait',
'account.account_invoices_without_payment': 'fusion_reports_templates.action_report_fr_invoice_portrait',
# Purchase: PO + RFQ (vendor portal)
'purchase.action_report_purchase_order': 'fusion_reports_templates.action_report_fr_purchase_portrait',
'purchase.report_purchase_quotation': 'fusion_reports_templates.action_report_fr_purchase_portrait',
}
class CustomerPortal(portal.CustomerPortal):
def _show_report(self, model, report_type, report_ref, download=False):
report_ref = _PORTAL_REPORT_OVERRIDES.get(report_ref, report_ref)
return super()._show_report(
model=model,
report_type=report_type,
report_ref=report_ref,
download=download,
)

View File

@@ -1,15 +1,28 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""Sort the Print-menu report bindings by `sequence`.
"""Two orthogonal jobs:
Odoo 19 returns `ir.actions.report` bindings in raw insertion order, which
pushes third-party reports to the bottom even when they are the primary
customer-facing document. Adding a sequence field and sorting inside
`ir.actions.actions._get_bindings` restores the expected ordering.
1. Sequence the Print-menu report bindings so Fusion reports float to the
top — Odoo 19 returns `ir.actions.report` bindings in raw insertion
order, which buries third-party reports.
2. Bridge `sale_pdf_quote_builder` to our Fusion sale-order reports.
Upstream gates its header/footer PDF merge on
`report_name == 'sale.report_saleorder'`, so the merge silently no-ops
when the user prints with our Portrait/Landscape variants. We replay
the same merge here for those report names.
"""
import io
from odoo import api, fields, models
from odoo.tools import frozendict
from odoo.tools import frozendict, pdf, str2bool
_FUSION_SALE_REPORTS = frozenset({
'fusion_reports_templates.report_fr_sale_portrait',
'fusion_reports_templates.report_fr_sale_landscape',
})
class IrActionsReport(models.Model):
@@ -21,6 +34,72 @@ class IrActionsReport(models.Model):
'(lower = higher in the list).',
)
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
result = super()._render_qweb_pdf_prepare_streams(report_ref, data, res_ids=res_ids)
if not res_ids:
return result
report = self._get_report(report_ref)
if report.report_name not in _FUSION_SALE_REPORTS:
return result
# quotation_document_ids is added by sale_pdf_quote_builder; bail if
# that module isn't installed (nothing to merge).
if 'quotation_document_ids' not in self.env['sale.order']._fields:
return result
try:
from odoo.tools.pdf import PdfFileWriter
except ImportError:
return result
always_include = str2bool(
self.env['ir.config_parameter'].sudo().get_param(
'sale.always_include_selected_documents'
)
)
orders = self.env['sale.order'].browse(res_ids)
for order in orders:
initial_stream = result.get(order.id, {}).get('stream')
if not initial_stream:
continue
if order.state == 'sale' and not always_include:
continue
quotation_documents = order.quotation_document_ids
headers = quotation_documents.filtered(lambda d: d.document_type == 'header')
footers = quotation_documents - headers
has_product_document = any(
line.product_document_ids for line in order.order_line
)
if not headers and not has_product_document and not footers:
continue
form_fields_values_mapping = {}
writer = PdfFileWriter()
self_with_order_context = self.with_context(
use_babel=True, lang=order._get_lang() or self.env.user.lang
)
for header in headers:
prefix = f'quotation_document_id_{header.id}__'
self_with_order_context._update_mapping_and_add_pages_to_writer(
writer, header, form_fields_values_mapping, prefix, order
)
if has_product_document:
for line in order.order_line:
for doc in line.product_document_ids:
prefix = f'sol_id_{line.id}_product_document_id_{doc.id}__'
self_with_order_context._update_mapping_and_add_pages_to_writer(
writer, doc, form_fields_values_mapping, prefix, order, line
)
self._add_pages_to_writer(writer, initial_stream.getvalue())
for footer in footers:
prefix = f'quotation_document_id_{footer.id}__'
self_with_order_context._update_mapping_and_add_pages_to_writer(
writer, footer, form_fields_values_mapping, prefix, order
)
pdf.fill_form_fields_pdf(writer, form_fields=form_fields_values_mapping)
buffer = io.BytesIO()
writer.write(buffer)
result[order.id]['stream'] = io.BytesIO(buffer.getvalue())
return result
class IrActionsActions(models.Model):
_inherit = 'ir.actions.actions'