[IMP] tax_receipt_in_portal: pick report per company, hide missing PDFs
Some checks failed
pre-commit / pre-commit (pull_request) Failing after 1m30s
tests / test (pull_request) Failing after 1m41s

Replace the ``tax_receipt_in_portal.report_uuid`` system parameter by a
per-company setting, in Settings > Invoicing > Tax Receipts
(``res.company.tax_receipt_report_id``). Each receipt is rendered with
the report of its issuing company, not the current one: in the portal,
the current company is the website's. The selector only offers PDF Form
reports on ``l10n.fr.tax.receipt``; type and model are still checked at
rendering time since the report can be edited after selection.

The download buttons of the list and detail pages are only shown when
the PDF can actually be rendered: a valid report is configured for the
issuing company and no mapped field resolves to an empty value. The
checks are those of the rendering itself, exposed as a filter
(``_get_pdf_available_receipts``) and run in sudo to avoid false
negatives on field paths the donor cannot read. Otherwise a discreet
"PDF available soon" mention replaces the button, and the download URL
redirects to the receipt page.

The detail page download link now also carries the share token, so a
visitor reaching the page by token can download the PDF.
This commit is contained in:
Stéphan Sainléger
2026-10-23 13:00:01 +02:00
parent 73c0684623
commit 5eb3fe766b
11 changed files with 397 additions and 93 deletions

View File

@@ -1 +1,3 @@
from . import l10n_fr_tax_receipt
from . import res_company
from . import res_config_settings

View File

@@ -10,12 +10,6 @@ amounts, numbering and the invoice selection belong to
from odoo import _, models
from odoo.exceptions import UserError
#: ``ir.config_parameter`` key holding the ``report_uuid`` of the
#: ``pdf_form`` report used to render a tax receipt. A UUID is used rather
#: than a database id so the configuration survives a restore on another
#: database.
REPORT_UUID_PARAM = "tax_receipt_in_portal.report_uuid"
#: Receipt states a donor is allowed to see and download. A draft receipt
#: carries no legal number yet and must never reach the portal.
PORTAL_VISIBLE_STATES = ("confirmed", "sent")
@@ -53,39 +47,32 @@ class L10nFrTaxReceipt(models.Model):
return result
def _get_tax_receipt_report(self):
"""Return the ``pdf_form`` report configured to render receipts.
"""Return the ``pdf_form`` report rendering this receipt.
The report is looked up by its ``report_uuid`` stored in the
``tax_receipt_in_portal.report_uuid`` system parameter.
The report is the one selected on the receipt's **issuing**
company (Settings > Invoicing > Tax Receipts), not on the current
company: in the portal, the current company is the website's,
which may differ.
The type and target model are checked again here although the
settings field restricts the choice: the report can be edited
after being selected.
:return: a singleton ``ir.actions.report`` recordset, sudoed.
:raises UserError: if the parameter is unset, if no report carries
that UUID, if the report is not a ``pdf_form`` report, or if it
does not target ``l10n.fr.tax.receipt``.
:raises UserError: if the company has no report configured, if the
report is not a ``pdf_form`` report, or if it does not target
``l10n.fr.tax.receipt``.
"""
params = self.env["ir.config_parameter"].sudo()
report_uuid = (params.get_param(REPORT_UUID_PARAM) or "").strip()
if not report_uuid:
raise UserError(
_(
"No tax receipt report configured. Set the system "
"parameter %(param)s to the report_uuid of the PDF Form "
"report that renders tax receipts.",
param=REPORT_UUID_PARAM,
)
)
report = (
self.env["ir.actions.report"]
.sudo()
.search([("report_uuid", "=", report_uuid)], limit=1)
)
self.ensure_one()
company = self.sudo().company_id
report = company.tax_receipt_report_id
if not report:
raise UserError(
_(
"No report found with report_uuid %(uuid)s, configured "
"in %(param)s.",
uuid=report_uuid,
param=REPORT_UUID_PARAM,
"No tax receipt report configured for company "
"%(company)s. Select one in Settings > Invoicing > Tax "
"Receipts.",
company=company.name,
)
)
if report.report_type != PDF_FORM_REPORT_TYPE:
@@ -153,6 +140,43 @@ class L10nFrTaxReceipt(models.Model):
)
)
def _get_pdf_available_receipts(self):
"""Return the receipts of ``self`` whose PDF can be rendered.
Applies exactly the checks of ``_render_tax_receipt_pdf`` — visible
state, configured and valid report, no blank field — but as a
filter instead of raising, so the portal only offers a download
button that works.
The checks run in sudo, like the rendering itself: evaluated with
the donor's rights, a field path reaching data they cannot read
would resolve to nothing and hide a downloadable receipt.
The report is resolved once per issuing company.
:return: a subset of ``self``, in the caller's environment.
"""
reports_by_company = {}
available_ids = []
for receipt in self.sudo():
if receipt.state not in PORTAL_VISIBLE_STATES:
continue
company = receipt.company_id
if company not in reports_by_company:
try:
reports_by_company[company] = receipt._get_tax_receipt_report()
except UserError:
reports_by_company[company] = None
report = reports_by_company[company]
if not report:
continue
try:
receipt._check_report_renderable(report)
except UserError:
continue
available_ids.append(receipt.id)
return self.browse(available_ids)
def _render_tax_receipt_pdf(self):
"""Render this tax receipt as a PDF through ``pdf_form_report``.

View File

@@ -0,0 +1,29 @@
# Copyright 2026 Elabore
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
"""Per-company choice of the report rendering tax receipts."""
from odoo import fields, models
#: Domain restricting the selectable reports to PDF Form reports on tax
#: receipts. Shared by the company field and its settings counterpart.
TAX_RECEIPT_REPORT_DOMAIN = (
"[('report_type', '=', 'pdf_form'),"
" ('model', '=', 'l10n.fr.tax.receipt'),"
" ('company_id', 'in', [False, {company}])]"
)
class ResCompany(models.Model):
"""Store the tax receipt report of each issuing company."""
_inherit = "res.company"
tax_receipt_report_id = fields.Many2one(
"ir.actions.report",
string="Tax Receipt Report",
ondelete="set null",
domain=TAX_RECEIPT_REPORT_DOMAIN.format(company="id"),
help="PDF Form report used to render the tax receipts issued by "
"this company in the portal. Without it, donors can see their "
"receipts but not download them.",
)

View File

@@ -0,0 +1,19 @@
# Copyright 2026 Elabore
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
"""Expose the tax receipt report in the Invoicing settings."""
from odoo import fields, models
from .res_company import TAX_RECEIPT_REPORT_DOMAIN
class ResConfigSettings(models.TransientModel):
"""Add the tax receipt report selector to the settings."""
_inherit = "res.config.settings"
tax_receipt_report_id = fields.Many2one(
related="company_id.tax_receipt_report_id",
readonly=False,
domain=TAX_RECEIPT_REPORT_DOMAIN.format(company="company_id"),
)