[IMP] tax_receipt_in_portal: pick report per company, hide missing PDFs
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:
@@ -1 +1,3 @@
|
||||
from . import l10n_fr_tax_receipt
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
|
||||
@@ -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``.
|
||||
|
||||
|
||||
29
tax_receipt_in_portal/models/res_company.py
Normal file
29
tax_receipt_in_portal/models/res_company.py
Normal 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.",
|
||||
)
|
||||
19
tax_receipt_in_portal/models/res_config_settings.py
Normal file
19
tax_receipt_in_portal/models/res_config_settings.py
Normal 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"),
|
||||
)
|
||||
Reference in New Issue
Block a user