Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f90cfa9
[ADD] website_sale_resource_booking: eCommerce for resource bookings
Apr 15, 2021
87f2f7d
[FIX] website_sale_resource_booking: expire only once
May 11, 2021
a7000c0
[FIX] website_sale_resource_booking: Make sure resource booking partn…
pedrobaeza Dec 10, 2021
199e0ea
[IMP] website_sale_resource_booking: black, isort, prettier
victoralmau Feb 2, 2022
4a8bd70
[MIG] website_sale_resource_booking: Migration to 13.0
Nov 8, 2021
01cc729
[FIX] website_sale_resource_booking: calendar context
chienandalu May 12, 2022
d3eafc5
[FIX] website_sale_resource_booking: Proper license
pedrobaeza May 13, 2022
72af1b2
[IMP] website_sale_resource_booking: black, isort, prettier
stefan-tecnativa Apr 3, 2023
d6c3ca0
[MIG] website_sale_resource_booking: Migration to 15.0
stefan-tecnativa Apr 3, 2023
ba5f8b6
[UPD] Update website_sale_resource_booking.pot
Jul 7, 2023
d0c9d94
[UPD] Update website_sale_resource_booking.pot
Aug 11, 2023
caff370
Update translation files
weblate Aug 11, 2023
6063861
Translated using Weblate (Spanish)
Ivorra78 Oct 4, 2023
1480495
Added translation using Weblate (Portuguese (Brazil))
augustodinizl Apr 26, 2024
77fb805
Translated using Weblate (Portuguese (Brazil))
augustodinizl Apr 26, 2024
5658ed1
Translated using Weblate (Portuguese (Brazil))
augustodinizl Apr 26, 2024
567a2d5
[FIX] website_sale_*: make tests more resilient and compatible with w…
carlos-lopez-tecnativa May 30, 2025
1c4ce78
[IMP] website_sale_resource_booking: pre-commit auto fixes
Jul 21, 2025
3d476ff
[MIG] website_sale_resource_booking: Migration to 18.0
Jul 21, 2025
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
126 changes: 126 additions & 0 deletions website_sale_resource_booking/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
================================================
Sell resource booking products in your eCommerce
================================================

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

.. |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-OCA%2Fe--commerce-lightgray.png?logo=github
:target: https://github.com/OCA/e-commerce/tree/18.0/website_sale_resource_booking
: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-18-0/e-commerce-18-0-website_sale_resource_booking
: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=18.0
:alt: Try me on Runboat

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

This module extends the functionality of ``sale_resource_booking`` to
support the eCommerce use case and to allow your visitors to buy
products that produce a resource booking, and pre-book them before
buying.

You can also set a timeout for those pre-bookings to expire if unpaid.

**Table of contents**

.. contents::
:local:

Installation
============

To install this module, you need these dependencies:

- ``resource_booking`` from https://github.com/OCA/calendar
- ``sale_resource_booking`` from https://github.com/OCA/sale-workflow

Usage
=====

To use this module, you need to know how to use
``sale_resource_booking`` and ``resource_booking``. This document
doesn't explain the details for those related modules.

All products that you link to a resource booking type will allow
pre-bookings if sold from your eCommerce. To configure those
pre-bookings timeout:

1. Go to the product form in the backend.
2. Use the *Resource booking timeout* field, in the *Sales* tab.

When you go to that product's eCommerce page, you'll see a little
message above the *Add to cart* button, telling the user that they will
be able to pre-book it before buying.

When you add to your cart one (or more) bookable products, you will see
in the eCommerce checkout wizard a new step that you will have to follow
to be able to buy. This step will display a calendar with bookable slots
for you to choose.

When you are redirected to payment, make sure to pay before your
pre-bookings expire!

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_resource_booking%0Aversion:%2018.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
-------

* Tecnativa

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

- `Tecnativa <https://www.tecnativa.com>`__:

- Jairo Llopis
- Stefan Ungureanu

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.

.. |maintainer-Yajo| image:: https://github.com/Yajo.png?size=40px
:target: https://github.com/Yajo
:alt: Yajo

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-Yajo|

This module is part of the `OCA/e-commerce <https://github.com/OCA/e-commerce/tree/18.0/website_sale_resource_booking>`_ 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_resource_booking/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import controllers
24 changes: 24 additions & 0 deletions website_sale_resource_booking/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2021 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Sell resource booking products in your eCommerce",
"summary": "Let customers book resources temporarily before buying",
"version": "18.0.1.0.0",
"development_status": "Beta",
"category": "Website",
"website": "https://github.com/OCA/e-commerce",
"author": "Tecnativa, Odoo Community Association (OCA)",
"maintainers": ["Yajo"],
"license": "AGPL-3",
"depends": ["sale_resource_booking", "website_sale"],
"data": [
"data/ir_cron_data.xml",
"templates/website_sale.xml",
"views/product_template_view.xml",
],
"assets": {
"web.assets_tests": [
"/website_sale_resource_booking/static/src/js/tour_checkout.esm.js",
],
},
}
1 change: 1 addition & 0 deletions website_sale_resource_booking/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
133 changes: 133 additions & 0 deletions website_sale_resource_booking/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Copyright 2021 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from datetime import timezone
from urllib.parse import quote_plus

from dateutil.parser import isoparse

from odoo import _
from odoo.exceptions import ValidationError
from odoo.http import request, route

from ...website_sale.controllers import main


class WebsiteSale(main.WebsiteSale):
def _get_bookings(self):
"""Obtain bookings from current cart."""
order = request.website.sale_get_order()
order = order.with_context(active_test=False)
return order.mapped("order_line.resource_booking_ids")

def _get_indexed_booking(self, index):
"""Get indexed booking from current cart.

:param int index: 1 is the 1st element.
"""
bookings = self._get_bookings().sorted("id")
if index > len(bookings):
raise IndexError()
return bookings[index - 1]

def _booking_redirection(self, booking, index):
"""Call this method in /schedule and /confirm to redirect if
the booking has expired.
"""
if not booking.active:
msg = _("Booking has expired")
url = f"/shop/booking/{index}/schedule?error={quote_plus(msg)}"
booking.sale_order_line_id._sync_resource_bookings() # re-active
return request.redirect(url)

def _check_cart(self, order_sudo):
"""Redirect to scheduling bookings if still not done."""
order_sudo.order_line._sync_resource_bookings()
bookings = order_sudo.mapped("order_line.resource_booking_ids").filtered(
lambda r: r.state == "pending"
)
if bookings:
return request.redirect("/shop/booking/1/schedule")
return super()._check_cart(order_sudo)

@route(
[
"/shop/booking/<int:index>/schedule",
"/shop/booking/<int:index>/schedule/<int:year>/<int:month>",
],
type="http",
auth="public",
website=True,
sitemap=False,
)
def booking_schedule(self, index, year=None, month=None, error=None, **post):
"""Schedule pending bookings."""
# Proceed to checkout if there are no bookings in this cart
bookings = self._get_bookings().with_context(checkout_booking_index=index)
if not bookings:
return request.redirect("/shop/checkout")
# Proceed to checkout if we passed the last booking
try:
booking = self._get_indexed_booking(index).with_context(
checkout_booking_index=index
)
except IndexError:
return request.redirect("/shop/checkout")
redirection = self._booking_redirection(booking, index)
if redirection:
return redirection
count = len(bookings)
values = booking.with_context(
tz=booking.type_id.resource_calendar_id.tz
)._get_calendar_context(year, month)
values.update(
{
"booking_index": index,
"bookings_count": count,
"error": error,
"website_sale_order": request.website.sale_get_order(),
"wizard_title": _("Pre-schedule your booking (%(index)d of %(total)d)")
% {"index": index, "total": count},
}
)
return request.render("website_sale_resource_booking.scheduling", values)

@route(
["/shop/booking/<int:index>/confirm"],
type="http",
auth="public",
website=True,
sitemap=False,
)
def booking_confirm(self, index, partner_name, partner_email, when, **post):
"""Pre-reserve resource booking."""
booking_sudo = (
self._get_indexed_booking(index)
.sudo()
.with_context(
# Avoid calendar notifications now, SO is still draft
dont_notify=True,
no_mail_to_attendees=True,
)
)
if not booking_sudo:
return request.redirect("/shop/checkout")
redirection = self._booking_redirection(booking_sudo, index)
if redirection:
return redirection
when_tz_aware = isoparse(when)
when_naive = when_tz_aware.astimezone(timezone.utc).replace(tzinfo=None)
try:
booking_sudo.start = when_naive
except ValidationError as error:
url = f"/shop/booking/{index}/schedule?error={quote_plus(str(error))}"
return request.redirect(url)
# Store partner info to autocreate and autoconfirm later
product = booking_sudo.sale_order_line_id.product_id
booking_sudo.write(
{
"expiration": product.resource_booking_expiration,
"prereserved_email": partner_email,
"prereserved_name": partner_name,
}
)
return request.redirect(f"/shop/booking/{index + 1}/schedule")
14 changes: 14 additions & 0 deletions website_sale_resource_booking/data/ir_cron_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2021 Tecnativa - Jairo Llopis
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<data noupdate="1">
<record forcecreate="True" id="cron_expire" model="ir.cron">
<field name="name">Auto-cancel expired resource bookings</field>
<field name="model_id" ref="resource_booking.model_resource_booking" />
<field name="state">code</field>
<field name="code">model._cron_cancel_expired()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
</record>
</data>
Loading