From 32fb4adf06841bf37009cdbd6cba7c5feb7f9705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= <remytms@tsmail.eu> Date: Thu, 6 Feb 2025 18:47:11 +0100 Subject: [PATCH 1/5] [ADD] website_sale_product_trial --- .../odoo/addons/website_sale_product_trial | 1 + setup/website_sale_product_trial/setup.py | 6 + website_sale_product_trial/README.rst | 70 +++ website_sale_product_trial/__init__.py | 5 + website_sale_product_trial/__manifest__.py | 27 ++ .../controllers/__init__.py | 4 + .../controllers/main.py | 35 ++ .../i18n/website_sale_restrict_sepa_dd.pot | 69 +++ website_sale_product_trial/models/__init__.py | 5 + .../models/product_template.py | 11 + .../models/sale_order.py | 48 ++ .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 1 + .../static/description/index.html | 421 ++++++++++++++++++ .../views/product_views.xml | 15 + .../views/templates.xml | 33 ++ 16 files changed, 754 insertions(+) create mode 120000 setup/website_sale_product_trial/odoo/addons/website_sale_product_trial create mode 100644 setup/website_sale_product_trial/setup.py create mode 100644 website_sale_product_trial/README.rst create mode 100644 website_sale_product_trial/__init__.py create mode 100644 website_sale_product_trial/__manifest__.py create mode 100644 website_sale_product_trial/controllers/__init__.py create mode 100644 website_sale_product_trial/controllers/main.py create mode 100644 website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot create mode 100644 website_sale_product_trial/models/__init__.py create mode 100644 website_sale_product_trial/models/product_template.py create mode 100644 website_sale_product_trial/models/sale_order.py create mode 100644 website_sale_product_trial/readme/CONTRIBUTORS.rst create mode 100644 website_sale_product_trial/readme/DESCRIPTION.rst create mode 100644 website_sale_product_trial/static/description/index.html create mode 100644 website_sale_product_trial/views/product_views.xml create mode 100644 website_sale_product_trial/views/templates.xml diff --git a/setup/website_sale_product_trial/odoo/addons/website_sale_product_trial b/setup/website_sale_product_trial/odoo/addons/website_sale_product_trial new file mode 120000 index 000000000..86acaa4b4 --- /dev/null +++ b/setup/website_sale_product_trial/odoo/addons/website_sale_product_trial @@ -0,0 +1 @@ +../../../../website_sale_product_trial \ No newline at end of file diff --git a/setup/website_sale_product_trial/setup.py b/setup/website_sale_product_trial/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/website_sale_product_trial/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/website_sale_product_trial/README.rst b/website_sale_product_trial/README.rst new file mode 100644 index 000000000..902b0a5bc --- /dev/null +++ b/website_sale_product_trial/README.rst @@ -0,0 +1,70 @@ +========================== +Website Sale Product Trial +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a25ed6054ea1e7db1fe6f90cdb8102edd80859c7504cc1cebdaae1a0123c4819 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-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-coopiteasy%2Faddons-lightgray.png?logo=github + :target: https://github.com/coopiteasy/addons/tree/16.0/website_sale_product_trial + :alt: coopiteasy/addons + +|badge1| |badge2| |badge3| + +Form to order subscription product + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues <https://github.com/coopiteasy/addons/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/coopiteasy/addons/issues/new?body=module:%20website_sale_product_trial%0Aversion:%2016.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 +~~~~~~~ + +* Coop IT Easy SC + +Contributors +~~~~~~~~~~~~ + +* `Coop IT Easy SC <https://coopiteasy.be>`_: + + * Rémy Taymans + +Maintainers +~~~~~~~~~~~ + +.. |maintainer-remytms| image:: https://github.com/remytms.png?size=40px + :target: https://github.com/remytms + :alt: remytms + +Current maintainer: + +|maintainer-remytms| + +This module is part of the `coopiteasy/addons <https://github.com/coopiteasy/addons/tree/16.0/website_sale_product_trial>`_ project on GitHub. + +You are welcome to contribute. diff --git a/website_sale_product_trial/__init__.py b/website_sale_product_trial/__init__.py new file mode 100644 index 000000000..4ece112b6 --- /dev/null +++ b/website_sale_product_trial/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later +from . import models +from . import controllers diff --git a/website_sale_product_trial/__manifest__.py b/website_sale_product_trial/__manifest__.py new file mode 100644 index 000000000..15bd32f3c --- /dev/null +++ b/website_sale_product_trial/__manifest__.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +{ + "name": "Website Sale Product Trial", + "summary": """ + Configure product contract to be a trial subscription.""", + "version": "16.0.1.0.0", + "category": "Website", + "website": "https://github.com/coopiteasy/addons", + "author": "Coop IT Easy SC", + "maintainers": ["remytms"], + "license": "AGPL-3", + "application": False, + "depends": [ + "website_sale_product_compatibility", + "product", + ], + "excludes": [], + "data": [ + "views/product_views.xml", + "views/templates.xml", + ], + "demo": [], + "qweb": [], +} diff --git a/website_sale_product_trial/controllers/__init__.py b/website_sale_product_trial/controllers/__init__.py new file mode 100644 index 000000000..b71b9fcad --- /dev/null +++ b/website_sale_product_trial/controllers/__init__.py @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later +from . import main diff --git a/website_sale_product_trial/controllers/main.py b/website_sale_product_trial/controllers/main.py new file mode 100644 index 000000000..5b66105af --- /dev/null +++ b/website_sale_product_trial/controllers/main.py @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import http +from odoo.http import request + +from odoo.addons.website_sale.controllers.main import WebsiteSale + + +class WebsiteSaleProductTrial(WebsiteSale): + @http.route( + "/shop/payment/validate", + type="http", + auth="public", + website=True, + sitemap=False, + ) + def shop_payment_validate(self, sale_order_id=None, **post): + result = super().shop_payment_validate(sale_order_id=sale_order_id, **post) + if "my/order" in result.location: + result.location = "/shop/main/confirmation" + return result + + @http.route( + "/shop/main/confirmation", + type="http", + auth="public", + website=True, + sitemap=False, + ) + def shop_main_confirmation(self): + """General confirmation for product. Used to skip view of the + invoice.""" + return request.render("website_sale_product_trial.main_confirmation", {}) diff --git a/website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot b/website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot new file mode 100644 index 000000000..54b01d0c8 --- /dev/null +++ b/website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot @@ -0,0 +1,69 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_sale_restrict_sepa_dd +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_product__allow_sepa_dd_payment +#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_template__allow_sepa_dd_payment +msgid "Allow SEPA Direct Debit Payment" +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#. odoo-python +#: code:addons/website_sale_restrict_sepa_dd/models/product_template.py:0 +#, python-format +msgid "" +"Allow SEPA Direct Debit payment for the product in ordre to set Only SEPA " +"Direct Debit payment." +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_sale_order__allow_sepa_dd_payment +msgid "Allow Sepa Dd Payment" +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#. odoo-python +#: code:addons/website_sale_restrict_sepa_dd/models/sale_order.py:0 +#, python-format +msgid "" +"Cannot add product that does not allow SEPA Direct Debit with products that " +"allow only SEPA Direct Debit payment." +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_product__only_sepa_dd_payment +#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_template__only_sepa_dd_payment +msgid "Only SEPA Direct Debit Payment" +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_sale_order__only_sepa_dd_payment +msgid "Only Sepa Dd Payment" +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model,name:website_sale_restrict_sepa_dd.model_payment_provider +msgid "Payment Provider" +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model,name:website_sale_restrict_sepa_dd.model_product_template +msgid "Product" +msgstr "" + +#. module: website_sale_restrict_sepa_dd +#: model:ir.model,name:website_sale_restrict_sepa_dd.model_sale_order +msgid "Sales Order" +msgstr "" diff --git a/website_sale_product_trial/models/__init__.py b/website_sale_product_trial/models/__init__.py new file mode 100644 index 000000000..6ea7c7217 --- /dev/null +++ b/website_sale_product_trial/models/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later +from . import product_template +from . import sale_order diff --git a/website_sale_product_trial/models/product_template.py b/website_sale_product_trial/models/product_template.py new file mode 100644 index 000000000..3bb54388e --- /dev/null +++ b/website_sale_product_trial/models/product_template.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + is_trial = fields.Boolean() diff --git a/website_sale_product_trial/models/sale_order.py b/website_sale_product_trial/models/sale_order.py new file mode 100644 index 000000000..f29c30940 --- /dev/null +++ b/website_sale_product_trial/models/sale_order.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + + +from odoo import _, api, models +from odoo.exceptions import ValidationError + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + @api.constrains("order_line") + def _check_trial_alone(self): + """Ensure trial are not mixed in a sale order.""" + for order in self: + at_least_one_trial = any( + order.order_line.mapped("product_id").mapped("is_trial") + ) + if at_least_one_trial and order.order_line > 1: + raise ValidationError( + _( + "Cannot add product trial in an order that " + "contains other products." + ) + ) + + def check_product_compatibility(self, product_id): + warning = super().check_product_compatibility(product_id) + if not warning: + product = self.env["product.product"].browse(product_id).exists() + non_trials = self.order_line.mapped("product_id").filtered( + lambda p: not p.is_trial + ) + any_trial = any(self.order_line.mapped("product_id").mapped("is_trial")) + if product: + if product.is_trial and non_trials: + warning = _( + f"Product {product.name} cannot be added because " + "it's a trial and trial must be ordered seperately." + ) + elif not product.is_trial and any_trial: + warning = _( + f"Product {product.name} cannot be added because " + "order contains products that are trials and " + "trials must be ordered separately." + ) + return warning diff --git a/website_sale_product_trial/readme/CONTRIBUTORS.rst b/website_sale_product_trial/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..31498d266 --- /dev/null +++ b/website_sale_product_trial/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Coop IT Easy SC <https://coopiteasy.be>`_: + + * Rémy Taymans diff --git a/website_sale_product_trial/readme/DESCRIPTION.rst b/website_sale_product_trial/readme/DESCRIPTION.rst new file mode 100644 index 000000000..9cd7a7503 --- /dev/null +++ b/website_sale_product_trial/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Form to order subscription product diff --git a/website_sale_product_trial/static/description/index.html b/website_sale_product_trial/static/description/index.html new file mode 100644 index 000000000..26588f783 --- /dev/null +++ b/website_sale_product_trial/static/description/index.html @@ -0,0 +1,421 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" /> +<title>Website Sale Product Trial</title> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. + +See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +.subscript { + vertical-align: sub; + font-size: smaller } + +.superscript { + vertical-align: super; + font-size: smaller } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; +} + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title, .code .error { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left, .figure.align-left, object.align-left, table.align-left { + clear: left ; + float: left ; + margin-right: 1em } + +img.align-right, .figure.align-right, object.align-right, table.align-right { + clear: right ; + float: right ; + margin-left: 1em } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left } + +.align-center { + clear: both ; + text-align: center } + +.align-right { + text-align: right } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit } + +/* div.align-center * { */ +/* text-align: left } */ + +.align-top { + vertical-align: top } + +.align-middle { + vertical-align: middle } + +.align-bottom { + vertical-align: bottom } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font: inherit } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em ; + margin-right: 2em } + +pre.code .ln { color: gray; } /* line numbers */ +pre.code, code { background-color: #eeeeee } +pre.code .comment, code .comment { color: #5C6576 } +pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } +pre.code .literal.string, code .literal.string { color: #0C5404 } +pre.code .name.builtin, code .name.builtin { color: #352B84 } +pre.code .deleted, code .deleted { background-color: #DEB0A1} +pre.code .inserted, code .inserted { background-color: #A3D289} + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic, pre.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +/* "booktabs" style (no vertical lines) */ +table.docutils.booktabs { + border: 0px; + border-top: 2px solid; + border-bottom: 2px solid; + border-collapse: collapse; +} +table.docutils.booktabs * { + border: 0px; +} +table.docutils.booktabs th { + border-bottom: thin solid; + text-align: left; +} + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +</head> +<body> +<div class="document" id="website-sale-product-trial"> +<h1 class="title">Website Sale Product Trial</h1> + +<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!! This file is generated by oca-gen-addon-readme !! +!! changes will be overwritten. !! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!! source digest: sha256:a25ed6054ea1e7db1fe6f90cdb8102edd80859c7504cc1cebdaae1a0123c4819 +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> +<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/coopiteasy/addons/tree/16.0/website_sale_product_trial"><img alt="coopiteasy/addons" src="https://img.shields.io/badge/github-coopiteasy%2Faddons-lightgray.png?logo=github" /></a></p> +<p>Form to order subscription product</p> +<p><strong>Table of contents</strong></p> +<div class="contents local topic" id="contents"> +<ul class="simple"> +<li><a class="reference internal" href="#bug-tracker" id="toc-entry-1">Bug Tracker</a></li> +<li><a class="reference internal" href="#credits" id="toc-entry-2">Credits</a><ul> +<li><a class="reference internal" href="#authors" id="toc-entry-3">Authors</a></li> +<li><a class="reference internal" href="#contributors" id="toc-entry-4">Contributors</a></li> +<li><a class="reference internal" href="#maintainers" id="toc-entry-5">Maintainers</a></li> +</ul> +</li> +</ul> +</div> +<div class="section" id="bug-tracker"> +<h1><a class="toc-backref" href="#toc-entry-1">Bug Tracker</a></h1> +<p>Bugs are tracked on <a class="reference external" href="https://github.com/coopiteasy/addons/issues">GitHub Issues</a>. +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 +<a class="reference external" href="https://github.com/coopiteasy/addons/issues/new?body=module:%20website_sale_product_trial%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> +<p>Do not contact contributors directly about support or help with technical issues.</p> +</div> +<div class="section" id="credits"> +<h1><a class="toc-backref" href="#toc-entry-2">Credits</a></h1> +<div class="section" id="authors"> +<h2><a class="toc-backref" href="#toc-entry-3">Authors</a></h2> +<ul class="simple"> +<li>Coop IT Easy SC</li> +</ul> +</div> +<div class="section" id="contributors"> +<h2><a class="toc-backref" href="#toc-entry-4">Contributors</a></h2> +<ul class="simple"> +<li><a class="reference external" href="https://coopiteasy.be">Coop IT Easy SC</a>:<ul> +<li>Rémy Taymans</li> +</ul> +</li> +</ul> +</div> +<div class="section" id="maintainers"> +<h2><a class="toc-backref" href="#toc-entry-5">Maintainers</a></h2> +<p>Current maintainer:</p> +<p><a class="reference external image-reference" href="https://github.com/remytms"><img alt="remytms" src="https://github.com/remytms.png?size=40px" /></a></p> +<p>This module is part of the <a class="reference external" href="https://github.com/coopiteasy/addons/tree/16.0/website_sale_product_trial">coopiteasy/addons</a> project on GitHub.</p> +<p>You are welcome to contribute.</p> +</div> +</div> +</div> +</body> +</html> diff --git a/website_sale_product_trial/views/product_views.xml b/website_sale_product_trial/views/product_views.xml new file mode 100644 index 000000000..b125f5b6e --- /dev/null +++ b/website_sale_product_trial/views/product_views.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + + <record id="product_template_is_gift" model="ir.ui.view"> + <field name="name">product.template.common.is_gift.form</field> + <field name="model">product.template</field> + <field name="inherit_id" ref="product.product_template_form_view" /> + <field name="arch" type="xml"> + <field name="website_ribbon_id" position="after"> + <field name="is_trial" /> + </field> + </field> + </record> + +</odoo> diff --git a/website_sale_product_trial/views/templates.xml b/website_sale_product_trial/views/templates.xml new file mode 100644 index 000000000..9886607e3 --- /dev/null +++ b/website_sale_product_trial/views/templates.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" ?> +<odoo> + + <template id="main_confirmation"> + <t t-call="website.layout"> + <t t-set="additional_title">Shop - Order Confirmed</t> + <div id="wrap"> + <div + class="oe_structure" + id="oe_structure_website_sale_trial_confirmation_1" + /> + <div class="container oe_website_sale py-2"> + <h1>Order Confirmed</h1> + + <div class="row"> + <div class="col-12 col-xl"> + <div class="oe_cart"> + <div class="thanks_msg"> + <h2>Thank you for your order.</h2> + </div> + </div> + </div> + </div> + </div> + <div + class="oe_structure" + id="oe_structure_website_sale_trial_confirmation_2" + /> + </div> + </t> + </template> + +</odoo> From d2ee360db67561df0563627978cca3b176098d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= <remytms@tsmail.eu> Date: Thu, 13 Mar 2025 23:00:20 +0100 Subject: [PATCH 2/5] =?UTF-8?q?[ADD]=20website=5Fsale=5Fproduct=5Ftrial:?= =?UTF-8?q?=E2=80=AF14=20day=20duration=20for=20trial=20products?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hard coding this value is not a good idea. But is a trade of cost and flexibility. --- .../models/sale_order.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/website_sale_product_trial/models/sale_order.py b/website_sale_product_trial/models/sale_order.py index f29c30940..3f08d3cbf 100644 --- a/website_sale_product_trial/models/sale_order.py +++ b/website_sale_product_trial/models/sale_order.py @@ -2,10 +2,13 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later +from datetime import datetime, timedelta from odoo import _, api, models from odoo.exceptions import ValidationError +TRIAL_DURATION_DAYS = 14 + class SaleOrder(models.Model): _inherit = "sale.order" @@ -46,3 +49,25 @@ def check_product_compatibility(self, product_id): "trials must be ordered separately." ) return warning + + def _prepare_order_line_values( + self, + product_id, + quantity, + linked_line_id=False, + no_variant_attribute_values=None, + product_custom_attribute_values=None, + **kwargs, + ): + values = super()._prepare_order_line_values( + product_id=product_id, + quantity=quantity, + linked_line_id=linked_line_id, + no_variant_attirbute_values=no_variant_attribute_values, + ) + # Hard coded 15 days for trial duration + product = self.env["product.product"].browse(product_id) + if product.is_trial: + values["date_start"] = datetime.today() + values["date_end"] = datetime.today() + timedelta(days=TRIAL_DURATION_DAYS) + return values From cfb7dc1ceb834b7b8d1e80c22db3424c914494f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= <remytms@tsmail.eu> Date: Wed, 19 Mar 2025 21:20:14 +0100 Subject: [PATCH 3/5] [IMP] website_sale_product_trial: copyright and description --- website_sale_product_trial/README.rst | 2 +- website_sale_product_trial/__init__.py | 2 +- website_sale_product_trial/__manifest__.py | 2 +- .../controllers/__init__.py | 2 +- .../controllers/main.py | 2 +- .../i18n/website_sale_restrict_sepa_dd.pot | 69 ------------------- website_sale_product_trial/models/__init__.py | 2 +- .../models/product_template.py | 2 +- .../models/sale_order.py | 2 +- .../readme/DESCRIPTION.rst | 2 +- .../static/description/index.html | 2 +- 11 files changed, 10 insertions(+), 79 deletions(-) delete mode 100644 website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot diff --git a/website_sale_product_trial/README.rst b/website_sale_product_trial/README.rst index 902b0a5bc..753f85425 100644 --- a/website_sale_product_trial/README.rst +++ b/website_sale_product_trial/README.rst @@ -22,7 +22,7 @@ Website Sale Product Trial |badge1| |badge2| |badge3| -Form to order subscription product +Configure product to be a trial subscription. **Table of contents** diff --git a/website_sale_product_trial/__init__.py b/website_sale_product_trial/__init__.py index 4ece112b6..f2fcc3b9c 100644 --- a/website_sale_product_trial/__init__.py +++ b/website_sale_product_trial/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later from . import models diff --git a/website_sale_product_trial/__manifest__.py b/website_sale_product_trial/__manifest__.py index 15bd32f3c..45b55a4b8 100644 --- a/website_sale_product_trial/__manifest__.py +++ b/website_sale_product_trial/__manifest__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/website_sale_product_trial/controllers/__init__.py b/website_sale_product_trial/controllers/__init__.py index b71b9fcad..76ac57025 100644 --- a/website_sale_product_trial/controllers/__init__.py +++ b/website_sale_product_trial/controllers/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later from . import main diff --git a/website_sale_product_trial/controllers/main.py b/website_sale_product_trial/controllers/main.py index 5b66105af..891f22fc0 100644 --- a/website_sale_product_trial/controllers/main.py +++ b/website_sale_product_trial/controllers/main.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot b/website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot deleted file mode 100644 index 54b01d0c8..000000000 --- a/website_sale_product_trial/i18n/website_sale_restrict_sepa_dd.pot +++ /dev/null @@ -1,69 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * website_sale_restrict_sepa_dd -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 16.0\n" -"Report-Msgid-Bugs-To: \n" -"Last-Translator: \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_product__allow_sepa_dd_payment -#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_template__allow_sepa_dd_payment -msgid "Allow SEPA Direct Debit Payment" -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#. odoo-python -#: code:addons/website_sale_restrict_sepa_dd/models/product_template.py:0 -#, python-format -msgid "" -"Allow SEPA Direct Debit payment for the product in ordre to set Only SEPA " -"Direct Debit payment." -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_sale_order__allow_sepa_dd_payment -msgid "Allow Sepa Dd Payment" -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#. odoo-python -#: code:addons/website_sale_restrict_sepa_dd/models/sale_order.py:0 -#, python-format -msgid "" -"Cannot add product that does not allow SEPA Direct Debit with products that " -"allow only SEPA Direct Debit payment." -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_product__only_sepa_dd_payment -#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_product_template__only_sepa_dd_payment -msgid "Only SEPA Direct Debit Payment" -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model.fields,field_description:website_sale_restrict_sepa_dd.field_sale_order__only_sepa_dd_payment -msgid "Only Sepa Dd Payment" -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model,name:website_sale_restrict_sepa_dd.model_payment_provider -msgid "Payment Provider" -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model,name:website_sale_restrict_sepa_dd.model_product_template -msgid "Product" -msgstr "" - -#. module: website_sale_restrict_sepa_dd -#: model:ir.model,name:website_sale_restrict_sepa_dd.model_sale_order -msgid "Sales Order" -msgstr "" diff --git a/website_sale_product_trial/models/__init__.py b/website_sale_product_trial/models/__init__.py index 6ea7c7217..3ccd6a8cb 100644 --- a/website_sale_product_trial/models/__init__.py +++ b/website_sale_product_trial/models/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later from . import product_template diff --git a/website_sale_product_trial/models/product_template.py b/website_sale_product_trial/models/product_template.py index 3bb54388e..32ed1804a 100644 --- a/website_sale_product_trial/models/product_template.py +++ b/website_sale_product_trial/models/product_template.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/website_sale_product_trial/models/sale_order.py b/website_sale_product_trial/models/sale_order.py index 3f08d3cbf..b121d3f02 100644 --- a/website_sale_product_trial/models/sale_order.py +++ b/website_sale_product_trial/models/sale_order.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Coop IT Easy SC +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC # # SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/website_sale_product_trial/readme/DESCRIPTION.rst b/website_sale_product_trial/readme/DESCRIPTION.rst index 9cd7a7503..2972d328c 100644 --- a/website_sale_product_trial/readme/DESCRIPTION.rst +++ b/website_sale_product_trial/readme/DESCRIPTION.rst @@ -1 +1 @@ -Form to order subscription product +Configure product to be a trial subscription. diff --git a/website_sale_product_trial/static/description/index.html b/website_sale_product_trial/static/description/index.html index 26588f783..1bd806e38 100644 --- a/website_sale_product_trial/static/description/index.html +++ b/website_sale_product_trial/static/description/index.html @@ -370,7 +370,7 @@ <h1 class="title">Website Sale Product Trial</h1> !! source digest: sha256:a25ed6054ea1e7db1fe6f90cdb8102edd80859c7504cc1cebdaae1a0123c4819 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/coopiteasy/addons/tree/16.0/website_sale_product_trial"><img alt="coopiteasy/addons" src="https://img.shields.io/badge/github-coopiteasy%2Faddons-lightgray.png?logo=github" /></a></p> -<p>Form to order subscription product</p> +<p>Configure product to be a trial subscription.</p> <p><strong>Table of contents</strong></p> <div class="contents local topic" id="contents"> <ul class="simple"> From f6cdfdf779e6fb39cd6feabb4cac54ec7e420d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= <remytms@tsmail.eu> Date: Thu, 20 Mar 2025 16:00:42 +0100 Subject: [PATCH 4/5] [FIX] website_sale_product_trial: fix compatibility with delivery --- website_sale_product_trial/__manifest__.py | 1 + .../models/sale_order.py | 32 ++++++++----- website_sale_product_trial/tests/__init__.py | 4 ++ .../tests/test_trial.py | 45 +++++++++++++++++++ 4 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 website_sale_product_trial/tests/__init__.py create mode 100644 website_sale_product_trial/tests/test_trial.py diff --git a/website_sale_product_trial/__manifest__.py b/website_sale_product_trial/__manifest__.py index 45b55a4b8..a4babfe27 100644 --- a/website_sale_product_trial/__manifest__.py +++ b/website_sale_product_trial/__manifest__.py @@ -16,6 +16,7 @@ "depends": [ "website_sale_product_compatibility", "product", + "delivery", ], "excludes": [], "data": [ diff --git a/website_sale_product_trial/models/sale_order.py b/website_sale_product_trial/models/sale_order.py index b121d3f02..0121991e2 100644 --- a/website_sale_product_trial/models/sale_order.py +++ b/website_sale_product_trial/models/sale_order.py @@ -17,10 +17,8 @@ class SaleOrder(models.Model): def _check_trial_alone(self): """Ensure trial are not mixed in a sale order.""" for order in self: - at_least_one_trial = any( - order.order_line.mapped("product_id").mapped("is_trial") - ) - if at_least_one_trial and order.order_line > 1: + lines_to_check = order._exclude_delivery_order_line() + if not self._is_product_compatible(lines_to_check.product_id): raise ValidationError( _( "Cannot add product trial in an order that " @@ -28,21 +26,31 @@ def _check_trial_alone(self): ) ) + @api.model + def _is_product_compatible(self, product_ids): + at_least_one_trial = any(product_ids.mapped("is_trial")) + return not at_least_one_trial or len(product_ids) <= 1 + + def _exclude_delivery_order_line(self): + """Return the order_lines that are not delivery""" + self.ensure_one() + return self.order_line.filtered(lambda r: not r.is_delivery) + def check_product_compatibility(self, product_id): warning = super().check_product_compatibility(product_id) - if not warning: - product = self.env["product.product"].browse(product_id).exists() - non_trials = self.order_line.mapped("product_id").filtered( - lambda p: not p.is_trial + lines_to_check = self._exclude_delivery_order_line() + product = self.env["product.product"].browse(product_id).exists() + if not warning and lines_to_check and product: + is_product_compatible = self._is_product_compatible( + lines_to_check.product_id | product ) - any_trial = any(self.order_line.mapped("product_id").mapped("is_trial")) - if product: - if product.is_trial and non_trials: + if not is_product_compatible: + if product.is_trial: warning = _( f"Product {product.name} cannot be added because " "it's a trial and trial must be ordered seperately." ) - elif not product.is_trial and any_trial: + else: warning = _( f"Product {product.name} cannot be added because " "order contains products that are trials and " diff --git a/website_sale_product_trial/tests/__init__.py b/website_sale_product_trial/tests/__init__.py new file mode 100644 index 000000000..e20dfec0d --- /dev/null +++ b/website_sale_product_trial/tests/__init__.py @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later +from . import test_trial diff --git a/website_sale_product_trial/tests/test_trial.py b/website_sale_product_trial/tests/test_trial.py new file mode 100644 index 000000000..efbe78233 --- /dev/null +++ b/website_sale_product_trial/tests/test_trial.py @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: 2025 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + + +from odoo.tests import common + + +class TestTrialContractBase(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product_1 = cls.env.ref("product.product_product_1") + cls.product_1.is_trial = True + cls.product_2 = cls.env.ref("product.product_product_2") + cls.product_2.is_trial = False + cls.product_3 = cls.env.ref("product.product_product_3") + cls.product_3.is_trial = True + + +class TestTrialContract(TestTrialContractBase): + def test_is_product_compatible_empty(self): + self.assertTrue( + self.env["sale.order"]._is_product_compatible( + self.env["product.product"], + ) + ) + + def test_is_product_compatible_mixed(self): + self.assertFalse( + self.env["sale.order"]._is_product_compatible( + self.product_1 | self.product_2 + ) + ) + + def test_is_product_compatible_only_trial(self): + self.assertTrue(self.env["sale.order"]._is_product_compatible(self.product_1)) + self.assertFalse( + self.env["sale.order"]._is_product_compatible( + self.product_1 | self.product_3 + ) + ) + + def test_is_product_compatible_only_non_trial(self): + self.assertTrue(self.env["sale.order"]._is_product_compatible(self.product_2)) From 3a26df00cf3633879d152210454f010ff5e84d38 Mon Sep 17 00:00:00 2001 From: hugues de keyzer <odoo@hugues.info> Date: Sat, 22 Mar 2025 16:57:41 +0100 Subject: [PATCH 5/5] [FIX] fix english string --- website_sale_product_trial/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website_sale_product_trial/models/sale_order.py b/website_sale_product_trial/models/sale_order.py index 0121991e2..2afbe6841 100644 --- a/website_sale_product_trial/models/sale_order.py +++ b/website_sale_product_trial/models/sale_order.py @@ -48,7 +48,7 @@ def check_product_compatibility(self, product_id): if product.is_trial: warning = _( f"Product {product.name} cannot be added because " - "it's a trial and trial must be ordered seperately." + "it's a trial and trials must be ordered separately." ) else: warning = _(