Skip to content

[v2 review] [19.0][MIG] resource_booking: full commit history rebuild#6

Closed
dnplkndll wants to merge 133 commits into
19.0from
19.0-mig-resource_booking-v2
Closed

[v2 review] [19.0][MIG] resource_booking: full commit history rebuild#6
dnplkndll wants to merge 133 commits into
19.0from
19.0-mig-resource_booking-v2

Conversation

@dnplkndll
Copy link
Copy Markdown

@dnplkndll dnplkndll commented May 20, 2026

Port of resource_booking from 18.0 to 19.0. Follows the OCA migration guide.

Supersedes OCA#187 (open since 2025-10-19, original author unreachable).

Non-mechanical adaptations worth flagging:

  • Intervals moved from odoo.addons.resource.models.utilsodoo.tools.intervals.
  • _sql_constraints list rewritten as models.Constraint(...) declarations (Odoo 19 SQL constraint API).
  • Portal controller drops datetime.utcfromtimestamp (deprecated in Python 3.12) for datetime.fromtimestamp(..., tz=timezone.utc).replace(tzinfo=None).
  • Portal template's t-field="meeting.display_time" switched to t-out calling a new booking._get_portal_display_time() helper — Odoo 19 dropped the computed calendar.event.display_time field.
  • Tour step in resource_booking_tour.esm.js annotated with expectUnloadPage: true per Odoo 19's tour framework's new contract for click-triggers that navigate.

Jairo Llopis and others added 30 commits May 20, 2026 16:22
This module adds a new app to allow you to book resource combinations in given
schedules.

Example use cases:

* Management of consultations in a clinic.
* Salesman appointments.
* Classroom and projector reservations.
* Hotel room booking.

Among the things you can do:

* Specify the type of booking, which includes a calendar of availability.
* Specify which resources can be booked together. All of them must be free to be booked.
* Place pending bookings, effectively giving permissions to someone to see the availability calendar and choose one slot.
* Partners can do that from their portals.
* If a partner has no user, he can still do the same via a tokenized URL.
* Backend users can also do that from the backend.
* Booking lifecycle with computed states.
* Automatic meeting creation and deletion.
* Automatic conflict detection.
* Deadline to block modifications.

@Tecnativa TT28201
Currently translated at 100.0% (190 of 190 strings)

Translation: calendar-12.0/calendar-12.0-resource_booking
Translate-URL: https://translation.odoo-community.org/projects/calendar-12-0/calendar-12-0-resource_booking/es/
…ating calendars

Without this patch, users couldn't change a calendar schedule if there were past or unconfirmed bookings that wouldn't fit in it.

Excluding those bookings from the check fixes the situation.

We also check that, to confirm a booking, it must fit in the calendar (because now it can happen that, in the time that has passed since the booking was scheduled until it is confirmed, the calendar changes).

@Tecnativa TT29509
The notifications emitted to the resource booking requester must always be in the same TZ as the resource booking itself.

For example, if you book one hotel room in the other side of the world, a notification in your own TZ is confusing.

Besides, res.partner created from website_sale are created with `tz=False`, making it even more confusing.

@Tecnativa TT30331
The constraint that checks the schedule of a resource booking is currently being applied to all the bookings, including past ones.
As the resource combination or associated calendars might change regularly and trigger a recomputation of this, such change might take a very long time.
Plus, the calendar restrictions might change, trigger a recompute of the constraint and detect bookings that can't be assigned, which makes no sense when they already happened.

This applies it only to future bookings, ignoring past ones.

TT30478
It's very unlikely that you need to book resources in a seconds-based precision.

This simplifies portal UI. Still, declared as a variable in case you need some customizations downstream.

@Tecnativa TT31063
- Standard v13 changes.
- Some onchanges removed in favor of computes.

@Tecnativa TT30987
From now on, `resource.booking.type` duration represents the default duration that will be applied to new `resource.booking` of that type, instead of the hardcoded duration for every new booking.

This means that you can create a new booking in pending state and define a custom duration before sending the portal link to customer.

Thus, the available schedule slots in portal will be based on the type duration, whereas only those that have enough time left to complete the booking duration will be displayed.

@Tecnativa TT30987
…cation

Thanks to this patch, you will be able to more predictably block the chosen resource combination, with the added checkbox.

Also, the location field is propagated to `resource.booking` records, and synced from there to `calendar.event` if needed. This way, you can alter the location before sending the portal invitation.

@Tecnativa TT30987
The filter wasn't working fine due to the `auto_join=True` set in `meeting_id`. It was never displaying pending bookings.

Besides, it wasn't necessary to keep it as a field because it was displayed nowhere and the search could be done directly in the view. This way, there's less python code to maintain and we disable the possibility of having a negative domain, which enters doomed scenarios when x2many fields are involved (see odoo/odoo#43957).

@Tecnativa TT30987
Adding `.with_context(exclude_public_holidays=True)` in needed places to let `hr_holidays_public` do its magic and never allow allocating a booking during a public holidays when that module is properly installed and configured.

@Tecnativa TT30987
- Allow users to optionally define a name for the resource booking.
- Display that name in portal if set.
- Sync it with meeting name when creating it.

@Tecnativa TT30987
Without this patch, when entering the Resource Bookings menu, if the current user attended any recurrent meeting, it failed with this error:

```
Server application error
 Error code: 200
 Error message: Odoo Server Error
 Error data message:
 invalid input syntax for integer: "82-20210726143000"
LINE 1: ... AND  ("resource_booking"."meeting_id" in (3,5,81,'82-202107...
                                                             ^

 Error data debug:
 Traceback (most recent call last):
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 624, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 310, in _handle_exception
    raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])
  File "/opt/odoo/custom/src/odoo/odoo/tools/pycompat.py", line 14, in reraise
    raise value
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 669, in dispatch
    result = self._call_function(**self.params)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 350, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/service/model.py", line 94, in wrapper
    return f(dbname, *args, **kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 339, in checked_call
    result = self.endpoint(*a, **kw)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 915, in __call__
    return self.method(*args, **kw)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 515, in response_wrap
    response = f(*args, **kw)
  File "/opt/odoo/auto/addons/web/controllers/main.py", line 1339, in call_kw
    return self._call_kw(model, method, args, kwargs)
  File "/opt/odoo/auto/addons/web/controllers/main.py", line 1331, in _call_kw
    return call_kw(request.env[model], method, args, kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/api.py", line 383, in call_kw
    result = _call_kw_model(method, model, args, kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/api.py", line 356, in _call_kw_model
    result = method(recs, *args, **kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 4936, in search_read
    records = self.search(domain or [], offset=offset, limit=limit, order=order)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 1648, in search
    res = self._search(args, offset=offset, limit=limit, order=order, count=count)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 4499, in _search
    self._cr.execute(query_str, where_clause_params)
  File "/opt/odoo/custom/src/odoo/odoo/sql_db.py", line 173, in wrapper
    return f(self, *args, **kwargs)
  File "/opt/odoo/custom/src/odoo/odoo/sql_db.py", line 250, in execute
    res = self._obj.execute(query, params)
psycopg2.DataError: invalid input syntax for integer: "82-20210726143000"
LINE 1: ... AND  ("resource_booking"."meeting_id" in (3,5,81,'82-202107...
                                                             ^
```

@Tecnativa TT30987
Upstream ``unlink`` method in calendar.event adds this keyword argument.
When a user had no resource_booking permissions and opened a calendar event linked to a resource booking, he got an unnecessary access error.

@Tecnativa TT31238
When `_leave_intervals_batch()` in `resource.calendar` is extended by other modules that add more leaves for any reason, the returned recordset must be of the `resource.calendar.leaves` model. Otherwise, Odoo fails with:

    TypeError: '<' not supported between instances of 'resource.resource' and 'resource.calendar.leaves'

The resource didn't have a meaning here, so changing it to avoid bug.

@Tecnativa TT31249
Missing part from OCA#34:

- Display booking duration and location on portal, even on pending state.
- Improve portal test to contemplate new behaviors.

@Tecnativa TT31250
- If RBC (resource.booking.combination) was auto-assigned, attendees generated from RBC human resources are expected to confirm attendance manually.
- If RBC was handpicked, those attendees are auto-confirmed.
- All those confirmations are done before sending invitations, so invitations contain the correct attendance status.
- If requester books from portal view, he's also confirmed before sending invitation, for the same reason.

@Tecnativa TT31239
This allows better filtering while in the normal calendar view.

By default, the organizer will be whoever created the booking. Calendar invitations will go out in his name.

Also, avoid sending calendar event modification notifications if the modification isn't the schedule.

@Tecnativa TT31240

resource_booking 13.0.2.3.0
Includes those coming from requester and from resource combination.

@Tecnativa TT31901
Currently translated at 97.0% (196 of 202 strings)

Translation: calendar-13.0/calendar-13.0-resource_booking
Translate-URL: https://translation.odoo-community.org/projects/calendar-13-0/calendar-13-0-resource_booking/fr_FR/
Currently translated at 1.4% (3 of 202 strings)

Translation: calendar-13.0/calendar-13.0-resource_booking
Translate-URL: https://translation.odoo-community.org/projects/calendar-13-0/calendar-13-0-resource_booking/fr/
When some partner is a resource assigned to a booking, he should get auto-subscribed.

Inspired in OCA#42, but I'm changing that behavior.

@Tecnativa TT32148
By using the upstream `mail.message_user_assigned` template, we were getting this exception:

```
odoo.addons.base.models.qweb.QWebException: 'resource.booking' object has no attribute 'user_id'
Traceback (most recent call last):
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/qweb.py", line 348, in _compiled_fn
    return compiled(self, append, new, options, log)
  File "<template>", line 1, in template_2601_10519
AttributeError: 'resource.booking' object has no attribute 'user_id'
```

So I provide a specific template for this kind of subscription notification, which doesn't use that `user_id` field. Besides, it explains better why you're getting it.

@Tecnativa TT32190
Forward-port of OCA#51.

Includes a modification for the migration script of 13.0.1.0.0 (if you migrate from v12, you'll need it; if you migrate from v13, it will be automatic).

@Tecnativa TT31901
Without this patch, if you create a resource booking for a type that has categories (tags), it will fail with `CacheMiss` error.

@Tecnativa TT32552
If I'm creating bookings for my colleagues, I'll be the owner of all of the meetings. But that doesn't mean I'll be busy.

With this patch, only when I'm a booked resource will the event be set as busy.

@Tecnativa TT32666
MRomeera and others added 27 commits May 20, 2026 16:22
Currently translated at 100.0% (218 of 218 strings)

Translation: calendar-18.0/calendar-18.0-resource_booking
Translate-URL: https://translation.odoo-community.org/projects/calendar-18-0/calendar-18-0-resource_booking/es/
Currently translated at 6.8% (15 of 218 strings)

Translation: calendar-18.0/calendar-18.0-resource_booking
Translate-URL: https://translation.odoo-community.org/projects/calendar-18-0/calendar-18-0-resource_booking/ca/
Currently translated at 100.0% (217 of 217 strings)

Translation: calendar-18.0/calendar-18.0-resource_booking
Translate-URL: https://translation.odoo-community.org/projects/calendar-18-0/calendar-18-0-resource_booking/it/
Odoo added this new key in commit
odoo/odoo@c840bde.

To prevent the test from failing, we updated the data accordingly.
…ission issues

Steps to reproduce:

- Install `resource_booking` and `hr` modules.
- Create a user with "Employee Officer: Manage all employees" rights
  (without the "Resource Booking User" role).
- Go to Employees and create a record.

Since `hr.employee` inherits from `resource.resource`, the constraint
`_check_bookings_scheduling` is executed.
When the search is performed, an error is raised due to missing permissions.

This commit fixes the issue by using `sudo()` for booking searches.
…ctly

When using a booking type with a resource calendar that has a different time zone than the resource itself, the slots are returned with mixed time zones.
This commit normalizes the behavior and ensures that slots are always returned in the time zone of the booking type.

Steps to reproduce:

Create a resource with the time zone America/Guayaquil, and a calendar that starts from 06:00 to 15:00.

Create a resource booking type with a new calendar that starts from 09:00 to 16:00, and set the time zone to Europe/Madrid.

Before this commit, the slot displayed in the portal was 06:00, but it should have been 12:00.
After this commit, the slot is displayed in the correct time zone (Madrid) at 12:00.

Note: The 06:00 in America/Guayaquil corresponds to 12:00 in Madrid standard time, not during daylight saving time. The tests use a freeze date in February.
The resources parameter must be a recordset, not a list. Before this commit, this was not a problem, but in this commit odoo/odoo@79a559c
, Odoo began expecting a recordset to call the function _get_calendar_att.
This change was introduced during the migration to V15 in this commit OCA@b7a6f5b
, so we updated the parameter to pass it as a record.
When a user had no resource_booking permissions and opened a calendar event linked to a resource booking, he got an unnecessary access error.
…he field is set

For a curious reason, the test_resource_booking_schedule_unschedule test fails after this PR: odoo/odoo#223875
The requester_advice field is of type Text, but in calendar.event the description field is HTML. I tried changing the field type to HTML, but the error still occurs, so another approach is to only pass the field when it is filled.
…t read access

A user landing on /my that lacks resource.booking read access (e.g. an
internal user not in resource_booking.group_user, an internal account
created by another website-facing module) used to hit a hard AccessError:

- _prepare_home_portal_values called search_count([]) unguarded, breaking
  the entire portal home page when "booking_count" was in counters.
- portal_my_bookings called search_count([]) unguarded, breaking the
  /my/bookings listing for the same users.

Both paths now check Booking.has_access("read") and degrade gracefully
(zero counter, empty list page) instead of raising. Portal users with
the dedicated ACL row keep the existing behavior unchanged.

Distinct from upstream 1c59c21 ([FIX] resource_booking: access error on
normal calendar) which fixed a different surface: the calendar event
form's resource_booking_ids field now hidden via groups= for users
without booking permissions. The portal AccessError addressed here is
a separate bug.

Co-Authored-By: Brenden Eshbach <brenden@techsystech.com>
…slots

All-day calendar events store dates in start_date/stop_date and may have
no start/stop datetimes (or values outside the analyzer's UTC window
once timezone-shifted). The previous busy-events query filtered solely
on start/stop, so a user's all-day event (PTO, off-site, etc.) would
not block booking slots on that day.

Extend the conflicting-events search with an OR predicate on
start_date/stop_date for allday events, and render their busy interval
from start_date through stop_date+1 in the analyzer's tz so a single
all-day event blocks the full local day across calendar tz boundaries.

Co-Authored-By: Brenden Eshbach <brenden@techsystech.com>
…ests

Two follow-ups from review of the no-access fix:

- portal_my_bookings: unify the no-access branch with the normal path.
  Previously the no-access branch returned an empty dict for `pager`,
  which is technically harmless because the current template doesn't
  read pager, but is fragile against future template inheritance. Now
  has_access is computed once, search_count is gated, and the rest of
  the function (real portal.pager(total=0), recordset, session, render)
  runs through the single existing code path.
- Tests: replace fragile "no Traceback in response" assertions with
  positive DOM markers. Home test now asserts the .o_portal_my_home /
  .o_portal_wrap wrapper renders; listing test asserts the empty-state
  alert string from the QWeb template appears. Both confirm the
  template actually rendered rather than just "did not crash".
Signed-off-by: Don Kendall <dkendall@ledoweb.com>
Signed-off-by: Don Kendall <dkendall@ledoweb.com>
@dnplkndll dnplkndll force-pushed the 19.0-mig-resource_booking-v2 branch from ec4b7f0 to f8c5ec4 Compare May 20, 2026 20:32
@dnplkndll
Copy link
Copy Markdown
Author

v2 rebuild landed on the original 19.0-mig-resource_booking branch — OCA#217 now reflects the rebuilt history.

@dnplkndll dnplkndll closed this May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.