Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions website_sale_product_document_variant/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=====================================
Website Sale Product Document Variant
=====================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:f9dd2a5cd1aebeaefe68c984e0130bad8e8991a75c4c032647f48e4fc03f6c96
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fe--commerce-lightgray.png?logo=github
:target: https://github.com/OCA/e-commerce/tree/19.0/website_sale_product_document_variant
:alt: OCA/e-commerce
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/e-commerce-19-0/e-commerce-19-0-website_sale_product_document_variant
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/e-commerce&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Standard Odoo blocks publishing a product document that is attached to a
specific variant instead of the whole product: the "Publish on website"
option is hidden and cannot be enabled for it.

This module lifts that limitation. A document attached to a variant can
be published, is shown on the product page alongside the product's own
documents for whichever variant the customer currently has selected, and
the list refreshes automatically -- without reloading the page -- when
the customer switches variant. The section disappears entirely when the
selected variant has no published document. Downloading such a document
is only allowed while it is published.

**Table of contents**

.. contents::
:local:

Usage
=====

1. On a product's variant (*Sales > Products > Variants*, or the
"Variants" smart button on a product), open the *Documents* tab (or
the linked *Attachments*) and enable "Publish on website" on the
document, exactly as already possible for a document attached to the
whole product.
2. On the eCommerce product page, the document appears in the
"Documents" section together with the product's own published
documents, for as long as that variant stays selected. Selecting a
different variant refreshes the list to that variant's own documents,
without a page reload.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/e-commerce/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/e-commerce/issues/new?body=module:%20website_sale_product_document_variant%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Camptocamp

Contributors
------------

- `Camptocamp <https://www.camptocamp.com>`__:

- Ricardoalso <ricardo.almeidasoares@camptocamp.com>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/e-commerce <https://github.com/OCA/e-commerce/tree/19.0/website_sale_product_document_variant>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions website_sale_product_document_variant/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import controllers
from . import models
26 changes: 26 additions & 0 deletions website_sale_product_document_variant/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2026 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Website Sale Product Document Variant",
"summary": "Publish and refresh variant-specific documents on the product page",
"version": "19.0.1.0.0",
"development_status": "Beta",
"category": "Website",
"website": "https://github.com/OCA/e-commerce",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"website_sale",
],
"data": [
"views/product_document_views.xml",
"views/templates.xml",
],
"assets": {
"web.assets_frontend": [
"website_sale_product_document_variant/static/src/interactions/**/*",
],
},
}
2 changes: 2 additions & 0 deletions website_sale_product_document_variant/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import main
from . import variant
45 changes: 45 additions & 0 deletions website_sale_product_document_variant/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2026 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo.http import request, route

from odoo.addons.website_sale.controllers.main import WebsiteSale


class WebsiteSaleProductDocumentVariant(WebsiteSale):
@route(
'/shop/<model("product.template"):product_template>/document/<int:document_id>',
type="http",
auth="public",
website=True,
sitemap=False,
readonly=True,
)
def product_document(self, product_template, document_id):
# Full override: the base method only accepts template documents
# (`res_model == 'product.template'`); this also accepts a
# document scoped to one of the template's own variants.
product_template.check_access("read")

document = request.env["product.document"].browse(document_id).sudo().exists()
if not document or not document.active or not document.shown_on_product_page:
return request.redirect(self._get_shop_path())

is_template_document = (
document.res_model == "product.template"
and document.res_id == product_template.id
)
is_variant_document = (
document.res_model == "product.product"
and document.res_id in product_template.product_variant_ids.ids
)
if not (is_template_document or is_variant_document):
return request.redirect(self._get_shop_path())

return (
request.env["ir.binary"]
._get_stream_from(
document.ir_attachment_id,
)
.get_response(as_attachment=True)
)
44 changes: 44 additions & 0 deletions website_sale_product_document_variant/controllers/variant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo.http import request, route

from odoo.addons.website_sale.controllers.variant import WebsiteSaleVariantController


class WebsiteSaleProductDocumentVariantController(WebsiteSaleVariantController):
@route(
"/website_sale/get_combination_info",
type="jsonrpc",
auth="public",
methods=["POST"],
website=True,
readonly=True,
)
def get_combination_info_website(
self,
product_template_id,
product_id,
combination,
add_qty,
uom_id=None,
**kwargs,
):
combination_info = super().get_combination_info_website(
product_template_id=product_template_id,
product_id=product_id,
combination=combination,
add_qty=add_qty,
uom_id=uom_id,
**kwargs,
)
product_template = request.env["product.template"].browse(
int(product_template_id)
)
variant = request.env["product.product"].browse(
combination_info.get("product_id")
)
combination_info["product_documents_html"] = (
product_template._get_variant_documents_html(variant)
)
return combination_info
2 changes: 2 additions & 0 deletions website_sale_product_document_variant/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import product_document
from . import product_template
16 changes: 16 additions & 0 deletions website_sale_product_document_variant/models/product_document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2026 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


class ProductDocument(models.Model):
_inherit = "product.document"

@api.constrains("res_model", "shown_on_product_page")
def _unsupported_product_product_document_on_ecommerce(self):
# Full override: this module makes the product page variant-aware,
# so the base method's blanket rejection of product.product
# documents no longer applies.
# Full override because the base loop unconditionally raises for that case.
return
40 changes: 40 additions & 0 deletions website_sale_product_document_variant/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2026 Camptocamp SA (https://www.camptocamp.com).
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class ProductTemplate(models.Model):
_inherit = "product.template"

def _get_variant_documents(self, variant):
"""Return the published documents for this template combined with
`variant`'s own published documents (`variant` may be empty).
"""
self.ensure_one()
documents = self.sudo().product_document_ids.filtered(
lambda doc: doc.shown_on_product_page
)
if variant:
documents |= variant.sudo().product_document_ids.filtered(
lambda doc: doc.shown_on_product_page
)
return documents

def _get_variant_documents_html(self, variant):
"""Render the documents section content for `variant` (a
`product.product` recordset, possibly empty), so it can be
refreshed client-side when the customer changes variant.

Always renders (returns an empty section rather than `None` when
there is nothing to show) so the front-end can also hide the
section on refresh, not just skip updating it.

Called only from the `/website_sale/get_combination_info` controller.
"""
self.ensure_one()
documents = self._get_variant_documents(variant)
return self.env["ir.qweb"]._render(
"website_sale_product_document_variant.product_documents_content",
{"product": self, "product_documents": documents},
)
3 changes: 3 additions & 0 deletions website_sale_product_document_variant/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions website_sale_product_document_variant/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [Camptocamp](https://www.camptocamp.com):
- Ricardoalso \<<ricardo.almeidasoares@camptocamp.com>\>
11 changes: 11 additions & 0 deletions website_sale_product_document_variant/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Standard Odoo blocks publishing a product document that is attached to a
specific variant instead of the whole product: the "Publish on website"
option is hidden and cannot be enabled for it.

This module lifts that limitation. A document attached to a variant can be
published, is shown on the product page alongside the product's own
documents for whichever variant the customer currently has selected, and
the list refreshes automatically -- without reloading the page -- when the
customer switches variant. The section disappears entirely when the
selected variant has no published document. Downloading such a document is
only allowed while it is published.
10 changes: 10 additions & 0 deletions website_sale_product_document_variant/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
1. On a product's variant (*Sales > Products > Variants*, or the
"Variants" smart button on a product), open the *Documents* tab (or
the linked *Attachments*) and enable "Publish on website" on the
document, exactly as already possible for a document attached to the
whole product.
2. On the eCommerce product page, the document appears in the
"Documents" section together with the product's own published
documents, for as long as that variant stays selected. Selecting a
different variant refreshes the list to that variant's own
documents, without a page reload.
Loading
Loading