===================== tax_receipt_in_portal ===================== Let donors download their own tax receipts from the Odoo portal. Description =========== This module publishes the tax receipts managed by ``l10n_fr_tax_receipt`` to the donor they were issued to: * a **Tax Receipts** card on ``/my/home``, with a counter; * a paginated list on ``/my/tax-receipts``; * a detail page on ``/my/tax-receipts/`` with the receipt information, its invoices and the **portal chatter**, where the donor can talk to the association; * a per-receipt PDF download on ``/my/tax-receipts//download``. The invoices section only lists invoices the donor is actually allowed to read — Odoo auto-subscribes a partner linked to a user when the invoice is created, which satisfies the ``account.move`` portal rule. Unreadable invoices are hidden rather than shown as dead links. The PDF is produced by the ``pdf_form`` report configured for the receipt model and is **rendered on demand**: nothing is archived. This is sound because ``l10n_fr_tax_receipt`` freezes a receipt once confirmed — its invoice set can no longer change and its amount is stored — so rendering the same receipt twice yields the same document. Responsibility split -------------------- =========================== ================================================== Module Responsibility =========================== ================================================== ``l10n_fr_tax_receipt`` The receipt itself: donor, year, invoice selection, stored amount, sequential number, draft/confirmed/sent/cancelled workflow, cancellation. ``pdf_form_report`` PDF layout and rendering. ``tax_receipt_in_portal`` Exposes a confirmed receipt to its donor: list, detail page with chatter, PDF download. =========================== ================================================== This module computes no amount, assigns no number, selects no donation and lays out no PDF. Installation ============ Use the normal Odoo module installation procedure to install ``tax_receipt_in_portal``. Dependencies (``portal``, ``l10n_fr_tax_receipt`` and transitively ``pdf_form_report``) are installed automatically. Configuration ============= One system parameter is required (:menuselection:`Settings --> Technical --> System Parameters`): ==================================== ========================================= Key Value ==================================== ========================================= ``tax_receipt_in_portal.report_uuid`` The ``report_uuid`` of the PDF Form report that renders tax receipts. ==================================== ========================================= A UUID is used rather than a database id so the configuration survives a restore on another database. The report must: * be of type ``PDF Form`` (``report_type = pdf_form``); * target the model ``l10n.fr.tax.receipt``. Anything else raises an explicit error at download time instead of serving a wrong document. Bind the PDF fields to receipt field paths, for instance: * ``name`` — receipt number; * ``year`` — civil year; * ``partner_id.name`` — donor; * ``amount_total`` — amount; * ``amount_total_words`` — amount in words; * ``last_payment_date`` — date of the last payment. Prefer field paths over Python expressions: a field path is validated when saved, an expression is not. Usage ===== Donors see only their own receipts, and only once **confirmed**. Confirming a receipt in the back office is therefore what publishes it. Security ======== The model inherits ``portal.mixin``, so every receipt carries an ``access_token`` used for share links and by the portal chatter plumbing. Two independent access layers, both required: * a **record rule** restricting portal users to receipts whose ``partner_id`` is the connected user's commercial entity (or one of its children) and whose state is ``confirmed`` or ``sent``. This is what protects the detail and download routes for a logged-in donor. * an **explicit domain** in the controller, which also applies to internal users browsing ``/my`` — they are not subject to portal record rules. A draft receipt is excluded by the record rule rather than by the controller: a receipt without a legal number must not leak through a route that someone forgets to filter. A valid ``access_token`` grants access to a single receipt — and to its chatter — without login, for instance when the receipt is shared by email. Listing always requires a session. The PDF rendering itself runs with elevated rights, after the access check, so that report field paths may reach data the donor cannot read directly. Portal chatter ============== The detail page embeds the native portal chatter (``portal.message_thread``). A logged-in donor can read the thread and post messages (the composer posts as the donor; messages reach the back-office chatter of the receipt, so accountants see the conversation). Internal notes stay invisible to the donor, as handled by the core mail access rules. For a public visitor using a share token, the thread is not readable: the core chatter endpoint checks record access, which a public user does not have. The section then renders empty; the token remains useful for the PDF and the receipt information. Known issues / Roadmap ====================== * **No archiving.** The PDF is rendered at each download and never stored. A receipt whose rendering becomes impossible (report deleted or reconfigured) can no longer be served, even though it was already sent. * **No email notification** and **no portal account invitation**: a donor without a portal account cannot reach the page. Both are deliberately out of scope. * **Coupling to a private API.** The blank-field guard calls ``_compute_pdf_field_value`` from ``pdf_form_report``, which is private. ``test_compute_api_contract`` fails loudly if that signature changes. The clean fix belongs upstream: ``pdf_form_report`` should expose a strict rendering mode that raises instead of logging a warning and leaving a PDF field blank. * Multi-company is handled through the receipt's own ``company_id``; no additional portal-side company filter is applied. Why a blank-field guard ======================= ``pdf_form_report`` deliberately swallows field resolution errors: a broken field path or Python expression only emits a log warning and leaves the PDF field blank. Serving a donor a tax receipt with a blank amount is worse than serving none, so every mapped field value is resolved before rendering and an empty one aborts with an explicit error. Validation happens before rendering rather than by re-reading the produced PDF, because a report drawing its values as an overlay (``is_acroform = False``) exposes no readable form field afterwards. Bug Tracker =========== Bugs are tracked on `our issues website `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. Credits ======= Contributors ------------ * Stéphan Sainléger Funders ------- The development of this module has been financially supported by: * Elabore (https://elabore.coop) Maintainer ---------- This module is maintained by Elabore.