diff --git a/.eslintrc.yml b/.eslintrc.yml index fed88d70..3d63b68a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -4,7 +4,8 @@ env: # See https://github.com/OCA/odoo-community.org/issues/37#issuecomment-470686449 parserOptions: - ecmaVersion: 2019 + ecmaVersion: 11 + sourceType: module overrides: - files: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..f61de956 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# generated from manifests external_dependencies +cssselect diff --git a/resource_booking/README.rst b/resource_booking/README.rst new file mode 100644 index 00000000..ca5aabe8 --- /dev/null +++ b/resource_booking/README.rst @@ -0,0 +1,217 @@ +================ +Resource booking +================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1a00b2b5ad7b830fbe275ea34e4efe05037f94243149329f9a6b5fdbb6b02749 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |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%2Fcalendar-lightgray.png?logo=github + :target: https://github.com/OCA/calendar/tree/16.0/resource_booking + :alt: OCA/calendar +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/calendar-16-0/calendar-16-0-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/calendar&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +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. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +To install this module, you need to install these dependencies: + +#. `freezegun `__ +#. `web_calendar_slot_duration `__ + +When someone is a manager, he will have access to *Resource Bookings > +Configuration*, where he will be able to configure resources, leaves and +schedules. This menu is just provided as a shortcut. However, if you want to +manage that stuff more comfortably: + +* To manage human resources, install `hr `__. +* To manage their leaves, install `hr_holidays `__. +* To manage work centers, install `mrp `__. + +Configuration +============= + +To let some backend user to book resources: + +#. Go to *Settings > Users & Companies > Users*. +#. Pick or create one. +#. Assign *Resource Booking > User*. + +To let some backend user to configure types and combinations, and to be able to +modify overdue bookings: + +#. Go to *Settings > Users & Companies > Users*. +#. Pick or create one. +#. Assign *Resource Booking > Manager*. + +To configure one booking type: + +#. Go to *Resource Bookings > Types*. +#. Create one. +#. Give it a *name*. +#. Set the *Duration*, to know the time assigned to each calendar slot. It will + also be the default duration for each booking, although that can be changed + later if necessary. +#. Set the *Modifications Deadline*, to forbid non-managers to alter dates of + a booking when it's too late. +#. Choose one *Availability Calendar*. No bookings will exist outside of it. +#. Under *Meeting defaults*, you will be able to fill some values that will + be used by default on calendar meetings. These will appear in the global + calendar when some booking is reserved. +#. Choose some *Available resource combinations*. All combinations in the same + line must be free to be booked together; otherwise the booking will not be + able to be scheduled. You can sort them. +#. Pick up one *Combination Assignment*. If you choose *Sorted*, then the order + of the combinations you chose will indicate the one that is selected first. + Of course, it must be free to be selected. +#. Save. + +Usage +===== + +This module installs a new app, "Resource bookings". + +Bookings may involve you: + +* Maybe because you requested to book something. +* Maybe because you are one of the booked resources, if a booking represents + some kind of appointment. + +To see which bookings involve you: + +#. Go to *Resource Bookings > Bookings*. +#. You can switch to the list view if you need to see also the pending ones. +#. You can remove the "Involving me" filter if you want to see others' bookings. + +To book some resources: + +#. Go to *Resource Bookings > Types*. +#. Pick the type of booking you want. +#. Click on *Booking Count*. +#. Click on a free slot. +#. Fill the *Requester*, which may or not be yourself. +#. Uncheck *Auto assign* and pick one *Resources combination*, in case the one + assigned automatically isn't the one you want. + +To invite someone to book a resource combination from the portal: + +#. Go to *Resource Bookings > Types*. +#. Pick the type of booking you want. +#. Click on *Booking Count*. +#. Click on the list view icon. +#. Click on *Create*. +#. Fill the *Requester*. +#. Uncheck *Auto assign* and pick one *Resources combination*, if you want that + the requester is assigned to that combination. Otherwise, leave it empty, + and some free combination will be assigned automatically when the requester + picks a free slot. +#. Choose the *duration*, in case it is different from the one specified in the + resource booking type. +#. Click on *Share > Send*. +#. The requester will receive an email to select a calendar slot from his portal. + +Known issues / Roadmap +====================== + +* Allow combination auto-assignment based on least used combination. +* Allow customer to choose combination. +* Some error messages would be a bit more helpful if they specify the schedule + impossibility reason, but that should be done without affecting performance. +* Optimize ``_calendar_event_busy_intervals()`` to make it work in batch. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* Jairo Llopis (https://www.tecnativa.com/) +* Henrik Norlin (https://ows.cloud) + +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-pedrobaeza| image:: https://github.com/pedrobaeza.png?size=40px + :target: https://github.com/pedrobaeza + :alt: pedrobaeza +.. |maintainer-ows-cloud| image:: https://github.com/ows-cloud.png?size=40px + :target: https://github.com/ows-cloud + :alt: ows-cloud + +Current `maintainers `__: + +|maintainer-pedrobaeza| |maintainer-ows-cloud| + +This module is part of the `OCA/calendar `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/resource_booking/__init__.py b/resource_booking/__init__.py new file mode 100644 index 00000000..4c9f27ed --- /dev/null +++ b/resource_booking/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import controllers +from .hooks import uninstall_hook diff --git a/resource_booking/__manifest__.py b/resource_booking/__manifest__.py new file mode 100644 index 00000000..50b2a655 --- /dev/null +++ b/resource_booking/__manifest__.py @@ -0,0 +1,56 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2022 Tecnativa - Pedro M. Baeza +# Copyright 2024 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Resource booking", + "summary": "Manage appointments and resource booking", + "version": "16.0.1.0.0", + "development_status": "Production/Stable", + "category": "Appointments", + "website": "https://github.com/OCA/calendar", + "author": "Tecnativa, Odoo Community Association (OCA)", + "maintainers": ["pedrobaeza", "ows-cloud"], + "license": "AGPL-3", + "application": True, + "installable": True, + "uninstall_hook": "uninstall_hook", + "external_dependencies": { + "python": [ + # Used implicitly + "cssselect", + ], + }, + "depends": [ + "calendar", + "mail", + "portal", + "resource", + "web_calendar_slot_duration", + ], + "data": [ + "data/mail.xml", + "data/mail_data.xml", + "security/resource_booking_security.xml", + "security/ir.model.access.csv", + "templates/portal.xml", + "views/calendar_event_views.xml", + "views/mail_activity_views.xml", + "views/res_partner_views.xml", + "views/resource_booking_combination_views.xml", + "views/resource_booking_type_views.xml", + "views/resource_booking_views.xml", + "views/menus.xml", + ], + "assets": { + "web.assets_frontend": [ + "resource_booking/static/src/js/booking_portal.js", + "resource_booking/static/src/scss/portal.scss", + ], + "web.assets_tests": [ + "resource_booking/static/src/js/tours/resource_booking_tour.js" + ], + }, + "demo": ["demo/res_users_demo.xml"], +} diff --git a/resource_booking/controllers/__init__.py b/resource_booking/controllers/__init__.py new file mode 100644 index 00000000..8c3feb6f --- /dev/null +++ b/resource_booking/controllers/__init__.py @@ -0,0 +1 @@ +from . import portal diff --git a/resource_booking/controllers/portal.py b/resource_booking/controllers/portal.py new file mode 100644 index 00000000..b8338cdd --- /dev/null +++ b/resource_booking/controllers/portal.py @@ -0,0 +1,142 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2022 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from datetime import datetime + +from dateutil.parser import isoparse + +from odoo.exceptions import AccessError, MissingError, ValidationError +from odoo.http import request, route +from odoo.tests.common import Form + +from odoo.addons.portal.controllers import portal + + +class CustomerPortal(portal.CustomerPortal): + def _get_booking_sudo(self, booking_id, access_token): + """Get sudoed booking record from its ID.""" + booking_sudo = self._document_check_access( + "resource.booking", booking_id, access_token + ) + return booking_sudo.with_context( + using_portal=True, tz=booking_sudo.type_id.resource_calendar_id.tz + ) + + def _prepare_home_portal_values(self, counters): + """Compute values for multi-booking portal views.""" + values = super()._prepare_home_portal_values(counters) + booking_count = request.env["resource.booking"].search_count([]) + values.update({"booking_count": booking_count}) + return values + + def _booking_get_page_view_values(self, booking_sudo, access_token, **kwargs): + """Compute values for single-booking portal views.""" + return self._get_page_view_values( + booking_sudo, + access_token, + {"page_name": "booking", "booking_sudo": booking_sudo}, + "my_bookings_history", + False, + **kwargs + ) + + @route( + ["/my/bookings", "/my/bookings/page/"], + auth="user", + type="http", + website=True, + ) + def portal_my_bookings(self, page=1, **kwargs): + """List bookings that I can access.""" + Booking = request.env["resource.booking"].with_context(using_portal=True) + values = self._prepare_portal_layout_values() + booking_count = Booking.search_count([]) + pager = portal.pager( + url="/my/bookings", + total=booking_count, + page=page, + step=self._items_per_page, + ) + bookings = Booking.search( + [], limit=self._items_per_page, offset=pager["offset"] + ) + request.session["my_bookings_history"] = bookings.ids + values.update({"bookings": bookings, "pager": pager, "page_name": "bookings"}) + return request.render("resource_booking.portal_my_bookings", values) + + @route(["/my/bookings/"], type="http", auth="public", website=True) + def portal_booking_page(self, booking_id, access_token=None, **kwargs): + """Portal booking form.""" + try: + booking_sudo = self._get_booking_sudo(booking_id, access_token) + except (AccessError, MissingError): + return request.redirect("/my") + # ensure attachment are accessible with access token inside template + for attachment in booking_sudo.mapped("message_ids.attachment_ids"): + attachment.generate_access_token() + values = self._booking_get_page_view_values( + booking_sudo, access_token, **kwargs + ) + return request.render("resource_booking.resource_booking_portal_form", values) + + @route( + [ + "/my/bookings//schedule", + "/my/bookings//schedule//", + ], + auth="public", + type="http", + website=True, + ) + def portal_booking_schedule( + self, booking_id, access_token=None, year=None, month=None, error=None, **kwargs + ): + """Portal booking scheduling.""" + try: + booking_sudo = self._get_booking_sudo(booking_id, access_token) + except (AccessError, MissingError): + return request.redirect("/my") + values = self._booking_get_page_view_values( + booking_sudo, access_token, **kwargs + ) + values.update(booking_sudo._get_calendar_context(year, month)) + values.update({"error": error, "page_name": "booking_schedule"}) + return request.render( + "resource_booking.resource_booking_portal_schedule", values + ) + + @route( + ["/my/bookings//cancel"], + auth="public", + type="http", + website=True, + ) + def portal_booking_cancel(self, booking_id, access_token=None, **kwargs): + """Cancel the booking.""" + booking_sudo = self._get_booking_sudo(booking_id, access_token) + booking_sudo.action_cancel() + return request.redirect("/my") + + @route( + ["/my/bookings//confirm"], + auth="public", + type="http", + website=True, + ) + def portal_booking_confirm(self, booking_id, access_token, when, **kwargs): + """Confirm a booking in a given datetime.""" + booking_sudo = self._get_booking_sudo(booking_id, access_token) + when_tz_aware = isoparse(when) + when_naive = datetime.utcfromtimestamp(when_tz_aware.timestamp()) + try: + with Form(booking_sudo) as booking_form: + booking_form.start = when_naive + except ValidationError as error: + url = booking_sudo.get_portal_url( + suffix="/schedule/{:%Y/%m}".format(when_tz_aware), + query_string="&error={}".format(error.args[0]), + ) + return request.redirect(url) + booking_sudo.action_confirm() + return request.redirect(booking_sudo.get_portal_url()) diff --git a/resource_booking/data/mail.xml b/resource_booking/data/mail.xml new file mode 100644 index 00000000..ad955866 --- /dev/null +++ b/resource_booking/data/mail.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/resource_booking/data/mail_data.xml b/resource_booking/data/mail_data.xml new file mode 100644 index 00000000..4600f8d4 --- /dev/null +++ b/resource_booking/data/mail_data.xml @@ -0,0 +1,9 @@ + + + + resource_booking + Resource Booking + fa-users + 11 + + diff --git a/resource_booking/demo/res_users_demo.xml b/resource_booking/demo/res_users_demo.xml new file mode 100644 index 00000000..338a929f --- /dev/null +++ b/resource_booking/demo/res_users_demo.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/resource_booking/hooks.py b/resource_booking/hooks.py new file mode 100644 index 00000000..20e44831 --- /dev/null +++ b/resource_booking/hooks.py @@ -0,0 +1,15 @@ +# Copyright 2024 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import SUPERUSER_ID, api + + +def uninstall_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + # Remove new activity type resource booking and all activities associated' + activiy_resource_booking = env.ref( + "resource_booking.mail_activity_data_resource_booking", raise_if_not_found=False + ) + if activiy_resource_booking: + booking_activiy_ids = env["mail.activity"].search([("booking_id", "!=", False)]) + booking_activiy_ids.unlink() + activiy_resource_booking.unlink() diff --git a/resource_booking/i18n/ca.po b/resource_booking/i18n/ca.po new file mode 100644 index 00000000..785b75c9 --- /dev/null +++ b/resource_booking/i18n/ca.po @@ -0,0 +1,1297 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * resource_booking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-03-02 19:22+0000\n" +"Last-Translator: Eugeni Chafer \n" +"Language-Team: none\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.1\n" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, fuzzy, python-format +msgid "# %(id)d" +msgstr "# %(id)d" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, fuzzy, python-format +msgid "# %(id)d - %(name)s" +msgstr "# %(id)d - %(name)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, fuzzy, python-format +msgid "%(partner)s - %(type)s" +msgstr "%(partner)s - %(type)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, fuzzy, python-format +msgid "%(partner)s - %(type)s - %(time)s" +msgstr "%(partner)s - %(type)s - %(time)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, fuzzy, python-format +msgid "%(resources)s" +msgstr "%(resources)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s (using calendar %(calendar)s)" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "" +"
\n" +" Error details:" +msgstr "" +"
\n" +" detalls de l'error:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Reschedule" +msgstr "" +"\n" +" Reagendar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Schedule" +msgstr "" +"\n" +" Agendar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Feedback" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel this booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "State:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Preview" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Advice:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Booked resources:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Dates:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Location:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Meeting URL:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Requested by:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "The chosen schedule is no longer available." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Type:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_warning +msgid "Access warning" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity_type__category +msgid "Action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_mail_activity_type__category +msgid "" +"Actions may trigger specific behavior like opening calendar view or " +"automatically mark as done when a document is uploaded" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__active +msgid "Active" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__booking_activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_ids +msgid "Activities" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity +msgid "Activity" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_state +msgid "Activity State" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity_type +msgid "Activity Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "All times are displayed using this timezone:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__duration +msgid "" +"Amount of time that the resources will be booked and unavailable for others." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_combination_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Archived" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Are you sure?" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_attachment_count +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_ids +msgid "Attendees" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_auto_assign +msgid "Auto assigned" +msgstr "Auto assignat" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Auto-assigned:" +msgstr "Auto-assignat" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Availability Calendar" +msgstr "Calendari de disponibilitat" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Available resource combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_combination +msgid "Bookable resource combinations" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_count +msgid "Booking Count" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_count +msgid "Booking count" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Booking ref." +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_count +#, python-format +msgid "Booking types" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: code:addons/resource_booking/models/resource_booking_type.py:0 +#: model:ir.actions.act_window,name:resource_booking.resource_booking_action +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_ids +#: model:ir.ui.menu,name:resource_booking.resource_booking_menu +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_home +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_calendar +#, python-format +msgid "Bookings" +msgstr "Reserves" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__booking_ids +msgid "Bookings available for this type" +msgstr "Reserves disponibles d'aquest tipus" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_calendar_event +msgid "Calendar Event" +msgstr "Esdeveniment de calendari" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Cancel" +msgstr "Cancel·lar" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__canceled +msgid "Canceled" +msgstr "Cancel·lat" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because no resources are selected for them:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because they do not fit in their type or " +"resources calendars, or because all resources are busy:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_assignment +msgid "" +"Choose how to auto-assign resource combinations. It has no effect if assiged " +"manually." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Close" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__combination_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Combination" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_assignment +msgid "Combination Assignment" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_combination_menu +msgid "Combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__company_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +msgid "Company" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__company_id +msgid "Company where this booking type is available." +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_configuration_menu +msgid "Configuration" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Confirm booking" +msgstr "Confirmar reserva" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"Confirm that the requesting partner and yourself will attend the scheduled " +"meeting." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__confirmed +msgid "Confirmed" +msgstr "Confirmat" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_uid +msgid "Created by" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_date +msgid "Created on" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Current state of this booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__access_url +msgid "Customer Portal URL" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Date" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "" +"Dear user,\n" +"
\n" +" You have been assigned to the" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__alarm_ids +msgid "Default reminders" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__categ_ids +msgid "Default tags" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "Define bookable resource combinations." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "Define resource booking types." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "Define resource bookings." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__description +msgid "Description" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__display_name +msgid "Display Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__duration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__duration +msgid "Duration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_type_duration_positive +msgid "Duration must be positive." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/mail_activity.py:0 +#, python-format +msgid "Feedback: %(feedback)s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_follower_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_partner_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Force a specific calendar, instead of combining the resources'." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Forced calendar" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Go back" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Group By" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__has_message +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__has_message +msgid "Has Message" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__id +msgid "ID" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "If you cancel this booking:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__duration +msgid "" +"Interval offered to start each resource booking. Also used as booking " +"default duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Invite requesting partner to portal." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Involving me" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_is_follower +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_modifiable +msgid "Is Modifiable" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_overdue +msgid "Is Overdue" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will be unscheduled." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will disappear from your bookings list." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel____last_update +msgid "Last Modified on" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_date +msgid "Last Updated on" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__name +msgid "Leave empty to autogenerate a booking name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__location +msgid "Location" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_main_attachment_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_manager +msgid "Manager" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Manually assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__meeting_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Meeting" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__videocall_location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__videocall_location +msgid "Meeting URL" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__meeting_id +msgid "Meeting confirmed for this booking." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Meeting defaults" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__alarm_ids +msgid "Meetings will be created with these reminders by default." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__categ_ids +msgid "Meetings will be created with these tags by default." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Message and communication history" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_ids +msgid "Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_combination_required_if_event +msgid "Missing resource booking combination." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__modifications_deadline +msgid "Modifications Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__my_activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_name +msgid "Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_calendar_event_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_summary +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Next month" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "No free slots found this month." +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "No resource combinations available on %s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_unique_meeting_id +msgid "Only one event per resource booking can exist." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.mail_activity_view_form_popup +msgid "Open Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Open a calendar to schedule a meeting for this booking request." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__user_id +msgid "Organizer" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__pending +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Pending" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__state +msgid "" +"Pending: No meeting scheduled.\n" +"Scheduled: The requester has not confirmed attendance yet.\n" +"Confirmed: Meeting scheduled, and requester attendance confirmed.\n" +"Canceled: Meeting removed, booking archived." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Please confirm this is really what you want." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_url +msgid "Portal Access URL" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Previous month" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__random +msgid "Randomly: order is not important" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_id +msgid "Requester" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__requester_advice +msgid "Requester Advice" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking +#: model:ir.module.category,name:resource_booking.category_resource_booking +#: model:mail.activity.type,name:resource_booking.mail_activity_data_resource_booking +msgid "Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Resource Booking Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type +msgid "Resource Booking Type" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_main_menu +msgid "Resource Bookings" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_view_resource_calendar_leaves_search +msgid "Resource Time Off" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_calendar +msgid "Resource Working Time" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_calendar_event__resource_booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity__booking_id +#: model:ir.model.fields.selection,name:resource_booking.selection__mail_activity_type__category__resource_booking +msgid "Resource booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type_combination_rel +msgid "Resource booking type relation with combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types where this combination is available." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_combination_action +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Resource combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Resource combinations available for this type of bookings." +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_resource +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__resource_ids +#: model:ir.ui.menu,name:resource_booking.menu_resource_resource +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Resources" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_id +msgid "Resources combination" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__resource_ids +msgid "Resources that must be free to be booked together." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_user_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Restrict bookings to this schedule." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Schedule" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "Schedule booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__scheduled +msgid "Scheduled" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Scheduled or confirmed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_token +msgid "Security Token" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__sequence +msgid "Sequence" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set pending" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set to pending" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Settings" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Share" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__sorted +msgid "Sorted: pick the first one that is free" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__start +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start date" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Start:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__state +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "State" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__stop +msgid "Stop" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__categ_ids +msgid "Tags" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__requester_advice +msgid "" +"Text that will appear by default in portal invitation emails and in calendar " +"views for scheduling." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "There are currently no bookings for your account." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "" +"These records categorize resource bookings and apply restrictions to them, " +"such as available resource combinations, availability schedules and interval " +"duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "" +"These records define resource combinations that can be booked together in " +"specified schedules and intervals." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "This booking exceeded its modifications deadline." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"This will remove the associated meeting to unschedule the booking. Are you " +"sure?" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "" +"Try next month\n" +" " +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_id +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_type_action +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_menu +msgid "Types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking and archive it." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking." +msgstr "" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_user +msgid "User" +msgstr "" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_user +msgid "Users allowed to book resources" +msgstr "" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_manager +msgid "Users allowed to manage resource booking configurations." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "View" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__combination_auto_assign +msgid "" +"When checked, resource combinations will be (un)assigned automatically based " +"on their availability during the booking dates." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "" +"When scheduled, resources will be blocked. When pending, it means the " +"requester didn't place the booking yet." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__modifications_deadline +msgid "" +"When this deadline has been exceeded, if a booking was not yet confirmed, it " +"will be canceled automatically. Also, only booking managers will be able to " +"unschedule or reschedule them. The value is expressed in hours." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__user_id +msgid "" +"Who organized this booking? Usually whoever created the record. It will " +"appear as the calendar event organizer, when scheduled, and calendar " +"notifications will be sent in his/her name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__partner_ids +msgid "Who will attend this booking?" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_resource_calendar +msgid "Working Times" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "You are about to confirm this booking:" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/calendar_event.py:0 +#, python-format +msgid "" +"You are not allowed to alter these bookings because they exceeded their " +"modification deadlines:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "because you belong to the chosen resource combination:" +msgstr "" diff --git a/resource_booking/i18n/es.po b/resource_booking/i18n/es.po new file mode 100644 index 00000000..0acd71f6 --- /dev/null +++ b/resource_booking/i18n/es.po @@ -0,0 +1,1351 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * resource_booking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-10-20 15:55+0000\n" +"PO-Revision-Date: 2023-10-20 15:55+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d - %(name)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s" +msgstr "%(partner)s - %(type)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s - %(time)s" +msgstr "%(partner)s - %(type)s - %(time)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s" +msgstr "%(resources)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s (using calendar %(calendar)s)" +msgstr "%(resources)s (usando el calendario %(calendar)s)" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "" +"
\n" +" Error details:" +msgstr "" +"
\n" +" Detalles del error:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Reschedule" +msgstr "" +"\n" +" Reagendar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Schedule" +msgstr "" +"\n" +" Agendar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Feedback" +msgstr "" +"\n" +" Comentarios" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel this booking" +msgstr "" +"\n" +" Cancelar esta reserva/cita" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel" +msgstr "" +"\n" +" Cancelar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "State:" +msgstr "Estado:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Preview" +msgstr "Previsualizar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Advice:" +msgstr "Aviso:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Booked resources:" +msgstr "Recursos reservados:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Dates:" +msgstr "Fechas:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Location:" +msgstr "Ubicación:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Meeting URL:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Requested by:" +msgstr "Solicitado por:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "The chosen schedule is no longer available." +msgstr "El horario escogido ya no está disponible." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Type:" +msgstr "Tipo:" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_warning +msgid "Access warning" +msgstr "Alerta de acceso" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity_type__category +msgid "Action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction +msgid "Action Needed" +msgstr "Acción necesaria" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_mail_activity_type__category +msgid "" +"Actions may trigger specific behavior like opening calendar view or " +"automatically mark as done when a document is uploaded" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__active +msgid "Active" +msgstr "Activo" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__booking_activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_ids +msgid "Activities" +msgstr "Actividades" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity +msgid "Activity" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_state +msgid "Activity State" +msgstr "Estado de la actividad" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity_type +msgid "Activity Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "All times are displayed using this timezone:" +msgstr "Todos los horarios se muestran usando esta zona horaria:" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__duration +msgid "" +"Amount of time that the resources will be booked and unavailable for others." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_combination_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Archived" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Are you sure?" +msgstr "¿Está seguro/a?" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_attachment_count +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_attachment_count +msgid "Attachment Count" +msgstr "Nº adjuntos" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_ids +msgid "Attendees" +msgstr "Asistentes" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_auto_assign +msgid "Auto assigned" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Auto-assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Availability Calendar" +msgstr "Calendario de disponibilidad" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Available resource combinations" +msgstr "Combinaciones de recursos disponibles" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_combination +msgid "Bookable resource combinations" +msgstr "Combinaciones de recursos reservables" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_count +msgid "Booking Count" +msgstr "Cuenta de reservas/citas" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_count +msgid "Booking count" +msgstr "Cuenta de reservas/citas" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Booking ref." +msgstr "Ref. reserva/cita" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_count +#, python-format +msgid "Booking types" +msgstr "Tipos de reserva/cita" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: code:addons/resource_booking/models/resource_booking_type.py:0 +#: model:ir.actions.act_window,name:resource_booking.resource_booking_action +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_ids +#: model:ir.ui.menu,name:resource_booking.resource_booking_menu +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_home +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_calendar +#, python-format +msgid "Bookings" +msgstr "Reservas/citas" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__booking_ids +msgid "Bookings available for this type" +msgstr "Reservas/citas disponibles para este tipo" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_calendar_event +msgid "Calendar Event" +msgstr "Evento de calendario" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Cancel" +msgstr "Cancelar" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__canceled +msgid "Canceled" +msgstr "Cancelado" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because no resources are selected for them:\n" +"\n" +"- %s" +msgstr "" +"No se pueden agendar estas reservas/citas porque no se les han seleccionado " +"recursos:\n" +"\n" +"- %s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because they do not fit in their type or " +"resources calendars, or because all resources are busy:\n" +"\n" +"- %s" +msgstr "" +"No se pueden agendar estas reservas/citas porque no encajan en los " +"calendarios de sus tipos o recursos, o porque todos sus recursos están " +"ocupados:\n" +"\n" +"- %s" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_assignment +msgid "" +"Choose how to auto-assign resource combinations. It has no effect if assiged " +"manually." +msgstr "" +"Escoja cómo autoasignar combinaciones de recursos. No tendrá efecto si se " +"asigna manualmente." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Close" +msgstr "Cerrar" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__combination_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Combination" +msgstr "Combinación" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_assignment +msgid "Combination Assignment" +msgstr "Asignación de combinaciones" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_combination_menu +msgid "Combinations" +msgstr "Combinaciones" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__company_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +msgid "Company" +msgstr "Compañía" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__company_id +msgid "Company where this booking type is available." +msgstr "Compañía en la que este tipo de reservas/citas está disponible." + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_configuration_menu +msgid "Configuration" +msgstr "Configuración" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Confirm booking" +msgstr "Confirmar reserva/cita" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"Confirm that the requesting partner and yourself will attend the scheduled " +"meeting." +msgstr "" +"Confirmar que el solicitante y usted mismo asistirán a la reunión agendada." + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__confirmed +msgid "Confirmed" +msgstr "Confirmado" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_date +msgid "Created on" +msgstr "Creado en" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Current state of this booking" +msgstr "Estado actual de esta reserva/cita" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__access_url +msgid "Customer Portal URL" +msgstr "URL del portal de cliente" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Date" +msgstr "Fecha" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "" +"Dear user,\n" +"
\n" +" You have been assigned to the" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__alarm_ids +msgid "Default reminders" +msgstr "Recordatorios por defecto" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__categ_ids +msgid "Default tags" +msgstr "Etiquetas por defecto" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "Define bookable resource combinations." +msgstr "Defina las combinaciones de recursos reservables." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "Define resource booking types." +msgstr "Defina los tipos de reservas/citas de recursos." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "Define resource bookings." +msgstr "Defina las reservas/citas de recursos." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__description +msgid "Description" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__duration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__duration +msgid "Duration" +msgstr "Duración" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_type_duration_positive +msgid "Duration must be positive." +msgstr "La duración debe ser positiva." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Duration:" +msgstr "Duración:" + +#. module: resource_booking +#: code:addons/resource_booking/models/mail_activity.py:0 +#, python-format +msgid "Feedback: %(feedback)s" +msgstr "Retroalimentación: %(feedback)s" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_follower_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_partner_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores (contactos)" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Force a specific calendar, instead of combining the resources'." +msgstr "" +"Forzar un calendario específico, en lugar de combinar los de los recursos." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Forced calendar" +msgstr "Calendario forzado" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Go back" +msgstr "Volver" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Group By" +msgstr "Agrupar por" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__has_message +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__has_message +msgid "Has Message" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__id +msgid "ID" +msgstr "ID" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread +msgid "If checked, new messages require your attention." +msgstr "Si está marcado, hay nuevos mensajes que requieren de su atención." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Si está marcado, algunos mensajes no se pudieron entregar." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "If you cancel this booking:" +msgstr "Si cancela esta reserva/cita:" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__duration +msgid "" +"Interval offered to start each resource booking. Also used as booking " +"default duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Invite requesting partner to portal." +msgstr "Invitar al solicitante al portal." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Involving me" +msgstr "Me corresponden" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_is_follower +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_is_follower +msgid "Is Follower" +msgstr "Es un seguidor" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_modifiable +msgid "Is Modifiable" +msgstr "Es modificable" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_overdue +msgid "Is Overdue" +msgstr "Está vencida" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will be unscheduled." +msgstr "Se desagendará." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will disappear from your bookings list." +msgstr "Desaparecerá de su lista de reservas/citas." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_uid +msgid "Last Updated by" +msgstr "Última actualización de" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_date +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__name +msgid "Leave empty to autogenerate a booking name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__location +msgid "Location" +msgstr "Ubicación" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_main_attachment_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_main_attachment_id +msgid "Main Attachment" +msgstr "Adjunto principal" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_manager +msgid "Manager" +msgstr "Responsable" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Manually assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__meeting_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Meeting" +msgstr "Reunión" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__videocall_location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__videocall_location +msgid "Meeting URL" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__meeting_id +msgid "Meeting confirmed for this booking." +msgstr "Reunión confirmada para esta reserva/cita." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Meeting defaults" +msgstr "Valores por defecto para la reunión" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__alarm_ids +msgid "Meetings will be created with these reminders by default." +msgstr "Las reuniones se crearán con estos recordatorios por defecto." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__categ_ids +msgid "Meetings will be created with these tags by default." +msgstr "Las reuniones se crearán con estas etiquetas por defecto." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error +msgid "Message Delivery error" +msgstr "Error de entrega de mensaje" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Message and communication history" +msgstr "Historial de mensajes y comunicaciones" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_ids +msgid "Messages" +msgstr "Mensajes" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_combination_required_if_event +msgid "Missing resource booking combination." +msgstr "Falta la combinación de reserva de recursos." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__modifications_deadline +msgid "Modifications Deadline" +msgstr "Fecha límite para modificaciones" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__my_activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_name +msgid "Name" +msgstr "Nombre" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_calendar_event_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Fecha límite de siguiente actividad" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_summary +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_summary +msgid "Next Activity Summary" +msgstr "Resumen de siguiente actividad" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_id +msgid "Next Activity Type" +msgstr "Tipo de siguiente actividad" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Next month" +msgstr "Siguiente mes" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "No free slots found this month." +msgstr "No quedan huecos libres este mes." + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "No resource combinations available on %s" +msgstr "No hay combinaciones de recursos disponibles en %s" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of Actions" +msgstr "Número de acciones" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "Número de mensajes que requieren una acción" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Número de mensajes con error de entrega" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Number of unread messages" +msgstr "Número de mensajes no leídos" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_unique_meeting_id +msgid "Only one event per resource booking can exist." +msgstr "Solo puede existir un evento por reserva de recursos." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.mail_activity_view_form_popup +msgid "Open Resource Booking" +msgstr "Abrir una reserva de recursos" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Open a calendar to schedule a meeting for this booking request." +msgstr "" +"Abrir un calendario para agendar una reunión para esta solicitud de reserva/" +"cita." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__user_id +msgid "Organizer" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__pending +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Pending" +msgstr "Pendiente" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__state +msgid "" +"Pending: No meeting scheduled.\n" +"Scheduled: The requester has not confirmed attendance yet.\n" +"Confirmed: Meeting scheduled, and requester attendance confirmed.\n" +"Canceled: Meeting removed, booking archived." +msgstr "" +"Pendiente: No se ha agendado ninguna reunión.\n" +"Agendada: El solicitante todavía no ha confirmado su asistencia.\n" +"Confirmada: La reunión ha sido agendada, y el solicitante ha confirmado su " +"asistencia.\n" +"Cancelada: La reunión se ha borrado y la reserva/cita se ha archivado." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Please confirm this is really what you want." +msgstr "Por favor, confirme que esto es lo que de verdad quiere hacer." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_url +msgid "Portal Access URL" +msgstr "URL de acceso al portal" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Previous month" +msgstr "Mes anterior" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__random +msgid "Randomly: order is not important" +msgstr "Aleatoria: el orden no es importante" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_id +msgid "Requester" +msgstr "Solicitante" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__requester_advice +msgid "Requester Advice" +msgstr "Aviso al solicitante" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking +#: model:ir.module.category,name:resource_booking.category_resource_booking +#: model:mail.activity.type,name:resource_booking.mail_activity_data_resource_booking +msgid "Resource Booking" +msgstr "Reserva de recursos" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Resource Booking Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type +msgid "Resource Booking Type" +msgstr "Tipo de reserva de recursos" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_main_menu +msgid "Resource Bookings" +msgstr "Reservas de recursos" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_view_resource_calendar_leaves_search +msgid "Resource Time Off" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_calendar +msgid "Resource Working Time" +msgstr "Tiempo de Trabajo de Recursos" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_calendar_event__resource_booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity__booking_id +#: model:ir.model.fields.selection,name:resource_booking.selection__mail_activity_type__category__resource_booking +msgid "Resource booking" +msgstr "Reserva de recursos" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type_combination_rel +msgid "Resource booking type relation with combinations" +msgstr "Relación del tipo de reservas de recursos con sus combinaciones" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types" +msgstr "Tipos de reserva de recursos" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types where this combination is available." +msgstr "Tipos de reserva de recursos donde esta combinación está disponible." + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_combination_action +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Resource combinations" +msgstr "Combinaciones de recursos" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Resource combinations available for this type of bookings." +msgstr "" +"Combinaciones de recursos disponibles para este tipo de reservas/citas." + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_resource +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__resource_ids +#: model:ir.ui.menu,name:resource_booking.menu_resource_resource +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Resources" +msgstr "Recursos" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_id +msgid "Resources combination" +msgstr "Combinación de recursos" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__resource_ids +msgid "Resources that must be free to be booked together." +msgstr "Recursos que deben estar libres para reservarse juntos." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_user_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_user_id +msgid "Responsible User" +msgstr "Usuario responsable" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Restrict bookings to this schedule." +msgstr "Restringir reservas/citas a este horario." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Schedule" +msgstr "Horario" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "Schedule booking" +msgstr "Agendar reserva/cita" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__scheduled +msgid "Scheduled" +msgstr "Planificado" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Scheduled or confirmed" +msgstr "Agendada o confirmada" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_token +msgid "Security Token" +msgstr "Token de seguridad" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set pending" +msgstr "Hacer pendiente" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set to pending" +msgstr "Establecer como pendiente" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Settings" +msgstr "Ajustes" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Share" +msgstr "Compartir" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__sorted +msgid "Sorted: pick the first one that is free" +msgstr "Ordenada: escoger el primero que esté libre" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__start +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start" +msgstr "Inicio" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start date" +msgstr "Fecha de inicio" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Start:" +msgstr "Inicio:" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__state +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "State" +msgstr "Estado" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"Estado basado en actividades\n" +"Vencida: la fecha tope ya ha pasado\n" +"Hoy: La fecha tope es hoy\n" +"Planificada: futuras actividades." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__stop +msgid "Stop" +msgstr "Fin" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__categ_ids +msgid "Tags" +msgstr "Etiquetas" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__requester_advice +msgid "" +"Text that will appear by default in portal invitation emails and in calendar " +"views for scheduling." +msgstr "" +"Texto que aparecerá por defecto en los correos electrónicos de invitación al " +"portal y en las vistas de calendario para agendar." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "There are currently no bookings for your account." +msgstr "Actualmente no hay reservas/citas en su cuenta." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "" +"These records categorize resource bookings and apply restrictions to them, " +"such as available resource combinations, availability schedules and interval " +"duration." +msgstr "" +"Estos registros categorizan las reservas/citas de recursos y les aplican " +"restricciones, como combinaciones de recursos disponibles, horarios de " +"disponibilidad y duración de los intervalos." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "" +"These records define resource combinations that can be booked together in " +"specified schedules and intervals." +msgstr "" +"Estos registros definen combinaciones de recursos que se pueden reservar " +"juntas en horarios e intervalos especificados." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "This booking exceeded its modifications deadline." +msgstr "Esta reserva/cita ha sobrepasado su fecha límite para modificaciones." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"This will remove the associated meeting to unschedule the booking. Are you " +"sure?" +msgstr "" +"Esto eliminará la reunión asociada para desagendar esta reserva/cita. ¿Está " +"seguro?" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "" +"Try next month\n" +" " +msgstr "" +"Intentar el mes siguiente\n" +" " + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_id +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Type" +msgstr "Tipo" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_type_action +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_menu +msgid "Types" +msgstr "Tipos" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread +msgid "Unread Messages" +msgstr "Mensajes por leer" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Unread Messages Counter" +msgstr "Contador de mensajes sin leer" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule" +msgstr "Desagendar" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking and archive it." +msgstr "Desagendar esta reserva/cita y archivarla." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking." +msgstr "Desagendar esta reserva/cita." + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_user +msgid "User" +msgstr "Usuario" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_user +msgid "Users allowed to book resources" +msgstr "Usuarios que pueden reservar recursos" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_manager +msgid "Users allowed to manage resource booking configurations." +msgstr "" +"Usuarios que pueden gestionar la configuración de las reservas de recursos." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "View" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website Messages" +msgstr "Mensajes del sitio web" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website communication history" +msgstr "Historial de comunicaciones del sitio web" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__combination_auto_assign +msgid "" +"When checked, resource combinations will be (un)assigned automatically based " +"on their availability during the booking dates." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "" +"When scheduled, resources will be blocked. When pending, it means the " +"requester didn't place the booking yet." +msgstr "" +"Cuando esté agendada, los recursos estarán bloqueados. Cuando esté " +"pendiente, significa que el solicitante todavía no ha agendado su reserva/" +"cita." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__modifications_deadline +msgid "" +"When this deadline has been exceeded, if a booking was not yet confirmed, it " +"will be canceled automatically. Also, only booking managers will be able to " +"unschedule or reschedule them. The value is expressed in hours." +msgstr "" +"Cuando esta fecha límite se haya sobrepasado, si una reserva/cita todavía no " +"se había confirmado, será cancelada automáticamente. También, solo los " +"responsables de reservas/citas podrán desagendarla o reagendarla. El valor " +"se expresa en horas." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__user_id +msgid "" +"Who organized this booking? Usually whoever created the record. It will " +"appear as the calendar event organizer, when scheduled, and calendar " +"notifications will be sent in his/her name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__partner_ids +msgid "Who will attend this booking?" +msgstr "¿Quién asistirá a esta reserva/cita?" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_resource_calendar +msgid "Working Times" +msgstr "Tiempos de Trabajo" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "You are about to confirm this booking:" +msgstr "Está a punto de confirmar esta reserva/cita:" + +#. module: resource_booking +#: code:addons/resource_booking/models/calendar_event.py:0 +#, python-format +msgid "" +"You are not allowed to alter these bookings because they exceeded their " +"modification deadlines:\n" +"\n" +"- %s" +msgstr "" +"No puede alterar estas reservas/citas porque han sobrepasado su fecha límite " +"de modificaciones:\n" +"\n" +"- %s" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "because you belong to the chosen resource combination:" +msgstr "" diff --git a/resource_booking/i18n/fr.po b/resource_booking/i18n/fr.po new file mode 100644 index 00000000..5fef84db --- /dev/null +++ b/resource_booking/i18n/fr.po @@ -0,0 +1,1291 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * resource_booking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-09-25 23:34+0000\n" +"Last-Translator: Abdourahmane Wone \n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d" +msgstr "# %(id)d" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d - %(name)s" +msgstr "# %(id)d - %(name)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s" +msgstr "%(partner)s - %(type)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s - %(time)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s (using calendar %(calendar)s)" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "" +"
\n" +" Error details:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Reschedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Schedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Feedback" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel this booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "State:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Preview" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Advice:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Booked resources:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Dates:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Location:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Meeting URL:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Requested by:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "The chosen schedule is no longer available." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Type:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_warning +msgid "Access warning" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity_type__category +msgid "Action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_mail_activity_type__category +msgid "" +"Actions may trigger specific behavior like opening calendar view or " +"automatically mark as done when a document is uploaded" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__active +msgid "Active" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__booking_activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_ids +msgid "Activities" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity +msgid "Activity" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_state +msgid "Activity State" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity_type +msgid "Activity Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "All times are displayed using this timezone:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__duration +msgid "" +"Amount of time that the resources will be booked and unavailable for others." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_combination_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Archived" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Are you sure?" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_attachment_count +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_ids +msgid "Attendees" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_auto_assign +msgid "Auto assigned" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Auto-assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Availability Calendar" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Available resource combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_combination +msgid "Bookable resource combinations" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_count +msgid "Booking Count" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_count +msgid "Booking count" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Booking ref." +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_count +#, python-format +msgid "Booking types" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: code:addons/resource_booking/models/resource_booking_type.py:0 +#: model:ir.actions.act_window,name:resource_booking.resource_booking_action +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_ids +#: model:ir.ui.menu,name:resource_booking.resource_booking_menu +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_home +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_calendar +#, python-format +msgid "Bookings" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__booking_ids +msgid "Bookings available for this type" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_calendar_event +msgid "Calendar Event" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Cancel" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__canceled +msgid "Canceled" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because no resources are selected for them:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because they do not fit in their type or " +"resources calendars, or because all resources are busy:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_assignment +msgid "" +"Choose how to auto-assign resource combinations. It has no effect if assiged " +"manually." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Close" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__combination_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Combination" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_assignment +msgid "Combination Assignment" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_combination_menu +msgid "Combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__company_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +msgid "Company" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__company_id +msgid "Company where this booking type is available." +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_configuration_menu +msgid "Configuration" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Confirm" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Confirm booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"Confirm that the requesting partner and yourself will attend the scheduled " +"meeting." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__confirmed +msgid "Confirmed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_uid +msgid "Created by" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_date +msgid "Created on" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Current state of this booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__access_url +msgid "Customer Portal URL" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Date" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "" +"Dear user,\n" +"
\n" +" You have been assigned to the" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__alarm_ids +msgid "Default reminders" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__categ_ids +msgid "Default tags" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "Define bookable resource combinations." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "Define resource booking types." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "Define resource bookings." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__description +msgid "Description" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__display_name +msgid "Display Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__duration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__duration +msgid "Duration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_type_duration_positive +msgid "Duration must be positive." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/mail_activity.py:0 +#, python-format +msgid "Feedback: %(feedback)s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_follower_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_partner_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Force a specific calendar, instead of combining the resources'." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Forced calendar" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Go back" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Group By" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__has_message +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__has_message +msgid "Has Message" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__id +msgid "ID" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "If you cancel this booking:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__duration +msgid "" +"Interval offered to start each resource booking. Also used as booking " +"default duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Invite requesting partner to portal." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Involving me" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_is_follower +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_modifiable +msgid "Is Modifiable" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_overdue +msgid "Is Overdue" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will be unscheduled." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will disappear from your bookings list." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel____last_update +msgid "Last Modified on" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_date +msgid "Last Updated on" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__name +msgid "Leave empty to autogenerate a booking name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__location +msgid "Location" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_main_attachment_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_manager +msgid "Manager" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Manually assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__meeting_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Meeting" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__videocall_location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__videocall_location +msgid "Meeting URL" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__meeting_id +msgid "Meeting confirmed for this booking." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Meeting defaults" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__alarm_ids +msgid "Meetings will be created with these reminders by default." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__categ_ids +msgid "Meetings will be created with these tags by default." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Message and communication history" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_ids +msgid "Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_combination_required_if_event +msgid "Missing resource booking combination." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__modifications_deadline +msgid "Modifications Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__my_activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_name +msgid "Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_calendar_event_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_summary +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Next month" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "No free slots found this month." +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "No resource combinations available on %s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_unique_meeting_id +msgid "Only one event per resource booking can exist." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.mail_activity_view_form_popup +msgid "Open Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Open a calendar to schedule a meeting for this booking request." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__user_id +msgid "Organizer" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__pending +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Pending" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__state +msgid "" +"Pending: No meeting scheduled.\n" +"Scheduled: The requester has not confirmed attendance yet.\n" +"Confirmed: Meeting scheduled, and requester attendance confirmed.\n" +"Canceled: Meeting removed, booking archived." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Please confirm this is really what you want." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_url +msgid "Portal Access URL" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Previous month" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__random +msgid "Randomly: order is not important" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_id +msgid "Requester" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__requester_advice +msgid "Requester Advice" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking +#: model:ir.module.category,name:resource_booking.category_resource_booking +#: model:mail.activity.type,name:resource_booking.mail_activity_data_resource_booking +msgid "Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Resource Booking Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type +msgid "Resource Booking Type" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_main_menu +msgid "Resource Bookings" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_view_resource_calendar_leaves_search +msgid "Resource Time Off" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_calendar +msgid "Resource Working Time" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_calendar_event__resource_booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity__booking_id +#: model:ir.model.fields.selection,name:resource_booking.selection__mail_activity_type__category__resource_booking +msgid "Resource booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type_combination_rel +msgid "Resource booking type relation with combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types where this combination is available." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_combination_action +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Resource combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Resource combinations available for this type of bookings." +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_resource +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__resource_ids +#: model:ir.ui.menu,name:resource_booking.menu_resource_resource +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Resources" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_id +msgid "Resources combination" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__resource_ids +msgid "Resources that must be free to be booked together." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_user_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Restrict bookings to this schedule." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Schedule" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "Schedule booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__scheduled +msgid "Scheduled" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Scheduled or confirmed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_token +msgid "Security Token" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__sequence +msgid "Sequence" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set pending" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set to pending" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Settings" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Share" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__sorted +msgid "Sorted: pick the first one that is free" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__start +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start date" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Start:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__state +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "State" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__stop +msgid "Stop" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__categ_ids +msgid "Tags" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__requester_advice +msgid "" +"Text that will appear by default in portal invitation emails and in calendar " +"views for scheduling." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "There are currently no bookings for your account." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "" +"These records categorize resource bookings and apply restrictions to them, " +"such as available resource combinations, availability schedules and interval " +"duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "" +"These records define resource combinations that can be booked together in " +"specified schedules and intervals." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "This booking exceeded its modifications deadline." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"This will remove the associated meeting to unschedule the booking. Are you " +"sure?" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "" +"Try next month\n" +" " +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_id +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_type_action +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_menu +msgid "Types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking and archive it." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking." +msgstr "" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_user +msgid "User" +msgstr "" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_user +msgid "Users allowed to book resources" +msgstr "" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_manager +msgid "Users allowed to manage resource booking configurations." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "View" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__combination_auto_assign +msgid "" +"When checked, resource combinations will be (un)assigned automatically based " +"on their availability during the booking dates." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "" +"When scheduled, resources will be blocked. When pending, it means the " +"requester didn't place the booking yet." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__modifications_deadline +msgid "" +"When this deadline has been exceeded, if a booking was not yet confirmed, it " +"will be canceled automatically. Also, only booking managers will be able to " +"unschedule or reschedule them. The value is expressed in hours." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__user_id +msgid "" +"Who organized this booking? Usually whoever created the record. It will " +"appear as the calendar event organizer, when scheduled, and calendar " +"notifications will be sent in his/her name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__partner_ids +msgid "Who will attend this booking?" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_resource_calendar +msgid "Working Times" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "You are about to confirm this booking:" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/calendar_event.py:0 +#, python-format +msgid "" +"You are not allowed to alter these bookings because they exceeded their " +"modification deadlines:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "because you belong to the chosen resource combination:" +msgstr "" diff --git a/resource_booking/i18n/fr_FR.po b/resource_booking/i18n/fr_FR.po new file mode 100644 index 00000000..d1986ac7 --- /dev/null +++ b/resource_booking/i18n/fr_FR.po @@ -0,0 +1,1362 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * resource_booking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-09-25 03:34+0000\n" +"Last-Translator: Abdourahmane Wone \n" +"Language-Team: none\n" +"Language: fr_FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, fuzzy, python-format +msgid "# %(id)d" +msgstr "# %(id)d" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d - %(name)s" +msgstr "# %(id)d - %(name)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s" +msgstr "%(partner)s - %(type)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s - %(time)s" +msgstr "%(partner)s - %(type)s - %(time)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s" +msgstr "%(resources)s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s (using calendar %(calendar)s)" +msgstr "%(resources)s (en utilisant le calendrier %(calendar)s)" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "" +"
\n" +" Error details:" +msgstr "" +"
\n" +" Details de l'erreur:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Reschedule" +msgstr "" +"\n" +" Replanifier" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Schedule" +msgstr "" +"\n" +" Calendrier" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Feedback" +msgstr "" +"\n" +" Retour d'information" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel this booking" +msgstr "" +"\n" +" Annuler cette réservation" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel" +msgstr "" +"\n" +" Annuler" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "State:" +msgstr "État :" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Preview" +msgstr "Aperçu" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Advice:" +msgstr "Conseil :" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Booked resources:" +msgstr "Ressources réservées:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Dates:" +msgstr "Dates:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Duration:" +msgstr "Durée:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Location:" +msgstr "Emplacement:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Meeting URL:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Requested by:" +msgstr "Demandé par:" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "The chosen schedule is no longer available." +msgstr "L'horaire choisi n'est plus disponible." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Type:" +msgstr "Type:" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_warning +msgid "Access warning" +msgstr "Avertissement d'accès" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity_type__category +msgid "Action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction +msgid "Action Needed" +msgstr "Action nécessaire" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_mail_activity_type__category +msgid "" +"Actions may trigger specific behavior like opening calendar view or " +"automatically mark as done when a document is uploaded" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__active +msgid "Active" +msgstr "Actif" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__booking_activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_ids +msgid "Activities" +msgstr "Activités" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity +msgid "Activity" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_state +msgid "Activity State" +msgstr "Statut de l'activité" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity_type +msgid "Activity Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "All times are displayed using this timezone:" +msgstr "Toutes les heures sont affichées en utilisant ce fuseau horaire:" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__duration +msgid "" +"Amount of time that the resources will be booked and unavailable for others." +msgstr "" +"Durée pendant laquelle les ressources seront réservées et indisponibles pour " +"les autres." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_combination_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Archived" +msgstr "Archivé" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Are you sure?" +msgstr "Êtes-vous sûr?" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_attachment_count +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_attachment_count +msgid "Attachment Count" +msgstr "Nombre de pièces jointes" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_ids +msgid "Attendees" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_auto_assign +msgid "Auto assigned" +msgstr "Attribué automatiquement" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Auto-assigned:" +msgstr "Attribué automatiquement:" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Availability Calendar" +msgstr "Calendrier de disponibilité" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Available resource combinations" +msgstr "Combinaisons de ressources disponibles" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_combination +msgid "Bookable resource combinations" +msgstr "Combinaisons de ressources réservables" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Booking" +msgstr "Réservation" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_count +msgid "Booking Count" +msgstr "Nombre de réservations" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_count +msgid "Booking count" +msgstr "Nombre de réservations" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Booking ref." +msgstr "Référence de réservation." + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_count +#, python-format +msgid "Booking types" +msgstr "Types de réservation" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: code:addons/resource_booking/models/resource_booking_type.py:0 +#: model:ir.actions.act_window,name:resource_booking.resource_booking_action +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_ids +#: model:ir.ui.menu,name:resource_booking.resource_booking_menu +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_home +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_calendar +#, python-format +msgid "Bookings" +msgstr "Réservations" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__booking_ids +msgid "Bookings available for this type" +msgstr "Réservations disponibles pour ce type" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_calendar_event +msgid "Calendar Event" +msgstr "Événement du calendrier" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Cancel" +msgstr "Annuler" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__canceled +msgid "Canceled" +msgstr "Annulé" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because no resources are selected for them:\n" +"\n" +"- %s" +msgstr "" +"Impossible de programmer ces réservations car aucune ressource n'est " +"sélectionnée pour elles:\n" +"\n" +"- %s" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because they do not fit in their type or " +"resources calendars, or because all resources are busy:\n" +"\n" +"- %s" +msgstr "" +"Impossible de planifier ces réservations car elles ne rentrent pas dans " +"leurs calendriers de type ou de ressources, ou parce que toutes les " +"ressources sont occupées:\n" +"\n" +"- %s" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_assignment +msgid "" +"Choose how to auto-assign resource combinations. It has no effect if assiged " +"manually." +msgstr "" +"Choisissez comment attribuer automatiquement les combinaisons de ressources. " +"Il n'a aucun effet s'il est affecté manuellement." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Close" +msgstr "Fermer" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__combination_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Combination" +msgstr "Combinaison" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_assignment +msgid "Combination Assignment" +msgstr "Affectation de combinaison" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_combination_menu +msgid "Combinations" +msgstr "Combinaisons" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__company_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +msgid "Company" +msgstr "Société" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__company_id +msgid "Company where this booking type is available." +msgstr "Entreprise où ce type de réservation est disponible." + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_configuration_menu +msgid "Configuration" +msgstr "Configuration" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Confirm" +msgstr "Confirmer" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Confirm booking" +msgstr "Confirmer la réservation" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"Confirm that the requesting partner and yourself will attend the scheduled " +"meeting." +msgstr "" +"Confirmez que le partenaire demandeur et vous-même assisterez à la réunion " +"prévue." + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__confirmed +msgid "Confirmed" +msgstr "Confirmé" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_date +msgid "Created on" +msgstr "Créé sur" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Current state of this booking" +msgstr "État actuel de cette réservation" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__access_url +msgid "Customer Portal URL" +msgstr "URL du portail client" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Date" +msgstr "Date" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "" +"Dear user,\n" +"
\n" +" You have been assigned to the" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__alarm_ids +msgid "Default reminders" +msgstr "Rappels par défaut" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__categ_ids +msgid "Default tags" +msgstr "Étiquettes par défaut" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "Define bookable resource combinations." +msgstr "Définissez des combinaisons de ressources réservables." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "Define resource booking types." +msgstr "Définir les types de réservation de ressources." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "Define resource bookings." +msgstr "Définir les réservations de ressources." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__description +msgid "Description" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__display_name +msgid "Display Name" +msgstr "Afficher un nom" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__duration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__duration +msgid "Duration" +msgstr "Durée" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_type_duration_positive +msgid "Duration must be positive." +msgstr "La durée doit être positive." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Duration:" +msgstr "Durée:" + +#. module: resource_booking +#: code:addons/resource_booking/models/mail_activity.py:0 +#, python-format +msgid "Feedback: %(feedback)s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_follower_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_follower_ids +msgid "Followers" +msgstr "Suiveurs" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_partner_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_partner_ids +msgid "Followers (Partners)" +msgstr "Abonnés (Partenaires)" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Force a specific calendar, instead of combining the resources'." +msgstr "Forcer un calendrier spécifique, au lieu de combiner les ressources '." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Forced calendar" +msgstr "Calendrier forcé" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Go back" +msgstr "Retourner" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Group By" +msgstr "Par groupe" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__has_message +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__has_message +msgid "Has Message" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__id +msgid "ID" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon" +msgstr "Icône" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "Icône pour indiquer une activité d'exception." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Si coché, certains messages ont une erreur de livraison." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "If you cancel this booking:" +msgstr "Si vous annulez cette réservation:" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__duration +msgid "" +"Interval offered to start each resource booking. Also used as booking " +"default duration." +msgstr "" +"Intervalle offert pour démarrer chaque réservation de ressource. Également " +"utilisé comme durée de réservation par défaut." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Invite requesting partner to portal." +msgstr "Invitez le partenaire demandeur sur le portail." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Involving me" +msgstr "M'impliquer" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_is_follower +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_is_follower +msgid "Is Follower" +msgstr "Est suiveur" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_modifiable +msgid "Is Modifiable" +msgstr "Est modifiable" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_overdue +msgid "Is Overdue" +msgstr "Est en retard" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will be unscheduled." +msgstr "Ce sera imprévu." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will disappear from your bookings list." +msgstr "Il disparaîtra de votre liste de réservations." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel____last_update +msgid "Last Modified on" +msgstr "Dernière modification le" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__name +msgid "Leave empty to autogenerate a booking name." +msgstr "Laissez vide pour générer automatiquement un nom de réservation." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__location +msgid "Location" +msgstr "Emplacement" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_main_attachment_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_main_attachment_id +msgid "Main Attachment" +msgstr "Pièce jointe principale" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_manager +msgid "Manager" +msgstr "Responsable" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Manually assigned:" +msgstr "Attribué manuellement :" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__meeting_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Meeting" +msgstr "Rencontre" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__videocall_location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__videocall_location +msgid "Meeting URL" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__meeting_id +msgid "Meeting confirmed for this booking." +msgstr "Rendez-vous confirmé pour cette réservation." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Meeting defaults" +msgstr "Paramètres par défaut de la réunion" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__alarm_ids +msgid "Meetings will be created with these reminders by default." +msgstr "Les réunions seront créées avec ces rappels par défaut." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__categ_ids +msgid "Meetings will be created with these tags by default." +msgstr "Les réunions seront créées avec ces étiquettes par défaut." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error +msgid "Message Delivery error" +msgstr "Erreur de livraison du message" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Message and communication history" +msgstr "Historique des messages et des communications" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_ids +msgid "Messages" +msgstr "Messages" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_combination_required_if_event +msgid "Missing resource booking combination." +msgstr "Combinaison de réservation de ressources manquante." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__modifications_deadline +msgid "Modifications Deadline" +msgstr "Date limite de modification" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__my_activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_name +msgid "Name" +msgstr "Nom" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_calendar_event_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "Date limite de la prochaine activité" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_summary +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_summary +msgid "Next Activity Summary" +msgstr "Résumé de l'activité suivante" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_id +msgid "Next Activity Type" +msgstr "Type d'activité suivante" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Next month" +msgstr "Le mois prochain" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "No free slots found this month." +msgstr "Aucun créneau libre trouvé ce mois-ci." + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "No resource combinations available on %s" +msgstr "Aucune combinaison de ressources disponible sur %s" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of Actions" +msgstr "Nombre d'actions" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of errors" +msgstr "Nombre d'erreurs" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "Nombre de messages nécessitant une action" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Nombre de messages avec erreur de livraison" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Number of unread messages" +msgstr "Nombre de messages non lus" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_unique_meeting_id +msgid "Only one event per resource booking can exist." +msgstr "Un seul événement par réservation de ressource peut exister." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.mail_activity_view_form_popup +msgid "Open Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Open a calendar to schedule a meeting for this booking request." +msgstr "" +"Ouvrez un calendrier pour planifier une réunion pour cette demande de " +"réservation." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__user_id +msgid "Organizer" +msgstr "Organisateur" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__pending +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Pending" +msgstr "En attente" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__state +msgid "" +"Pending: No meeting scheduled.\n" +"Scheduled: The requester has not confirmed attendance yet.\n" +"Confirmed: Meeting scheduled, and requester attendance confirmed.\n" +"Canceled: Meeting removed, booking archived." +msgstr "" +"En attente: aucune réunion n'est prévue.\\n\n" +"Prévu: le demandeur n'a pas encore confirmé sa participation.\\n\n" +"Confirmé: réunion prévue et participation du demandeur confirmée.\\n\n" +"Annulé: réunion supprimée, réservation archivée." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Please confirm this is really what you want." +msgstr "Veuillez confirmer que c'est vraiment ce que vous voulez." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_url +msgid "Portal Access URL" +msgstr "URL d'accès au portail" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Previous month" +msgstr "Le mois précédent" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__random +msgid "Randomly: order is not important" +msgstr "Aléatoirement: l'ordre n'est pas important" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_id +msgid "Requester" +msgstr "Demandeur" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__requester_advice +msgid "Requester Advice" +msgstr "Avis au demandeur" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking +#: model:ir.module.category,name:resource_booking.category_resource_booking +#: model:mail.activity.type,name:resource_booking.mail_activity_data_resource_booking +msgid "Resource Booking" +msgstr "Réservation de ressources" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Resource Booking Name" +msgstr "Nom de réservation de ressource" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type +msgid "Resource Booking Type" +msgstr "Type de réservation de ressource" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_main_menu +msgid "Resource Bookings" +msgstr "Réservations de ressources" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_view_resource_calendar_leaves_search +msgid "Resource Time Off" +msgstr "Temps libre des ressources" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_calendar +msgid "Resource Working Time" +msgstr "Temps de travail des ressources" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_calendar_event__resource_booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity__booking_id +#: model:ir.model.fields.selection,name:resource_booking.selection__mail_activity_type__category__resource_booking +msgid "Resource booking" +msgstr "Réservation de ressources" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type_combination_rel +msgid "Resource booking type relation with combinations" +msgstr "Relation de type de réservation de ressources avec des combinaisons" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types" +msgstr "Types de réservation de ressources" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types where this combination is available." +msgstr "" +"Types de réservation de ressources pour lesquels cette combinaison est " +"disponible." + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_combination_action +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Resource combinations" +msgstr "Combinaisons de ressources" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Resource combinations available for this type of bookings." +msgstr "Combinaisons de ressources disponibles pour ce type de réservations." + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_resource +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__resource_ids +#: model:ir.ui.menu,name:resource_booking.menu_resource_resource +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Resources" +msgstr "Ressources" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_id +msgid "Resources combination" +msgstr "Combinaison de ressources" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__resource_ids +msgid "Resources that must be free to be booked together." +msgstr "Des ressources qui doivent être libre pour être réservées ensemble." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_user_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_user_id +msgid "Responsible User" +msgstr "Utilisateur responsable" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Restrict bookings to this schedule." +msgstr "Limitez les réservations à cet horaire." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Schedule" +msgstr "Calendrier" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "Schedule booking" +msgstr "Planifier la réservation" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__scheduled +msgid "Scheduled" +msgstr "Programmé" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Scheduled or confirmed" +msgstr "Programmé ou confirmé" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_token +msgid "Security Token" +msgstr "Jeton de sécurité" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__sequence +msgid "Sequence" +msgstr "Séquence" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set pending" +msgstr "Définir en attente" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set to pending" +msgstr "Mettre en attente" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Settings" +msgstr "Paramètres" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Share" +msgstr "Partager" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__sorted +msgid "Sorted: pick the first one that is free" +msgstr "Trié : choisissez le premier qui est libre" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__start +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start" +msgstr "Début" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start date" +msgstr "Date de début" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Start:" +msgstr "Début:" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__state +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "State" +msgstr "État" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" +"Statut basé sur les activités\\n\n" +"En retard: la date d'échéance est déjà dépassée\\n\n" +"Aujourd'hui: la date de l'activité est aujourd'hui\\n\n" +"Prévu: Activités futures." + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__stop +msgid "Stop" +msgstr "Arrêter" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__categ_ids +msgid "Tags" +msgstr "Mots clés" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__requester_advice +msgid "" +"Text that will appear by default in portal invitation emails and in calendar " +"views for scheduling." +msgstr "" +"Texte qui apparaîtra par défaut dans les e-mails d'invitation au portail et " +"dans les vues du calendrier pour la planification." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "There are currently no bookings for your account." +msgstr "Il n'y a actuellement aucune réservation pour votre compte." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "" +"These records categorize resource bookings and apply restrictions to them, " +"such as available resource combinations, availability schedules and interval " +"duration." +msgstr "" +"Ces enregistrements catégorisent les réservations de ressources et leur " +"appliquent des restrictions, telles que les combinaisons de ressources " +"disponibles, les calendriers de disponibilité et la durée des intervalles." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "" +"These records define resource combinations that can be booked together in " +"specified schedules and intervals." +msgstr "" +"Ces enregistrements définissent des combinaisons de ressources qui peuvent " +"être réservées ensemble selon des calendriers et des intervalles spécifiés." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "This booking exceeded its modifications deadline." +msgstr "Cette réservation a dépassé son délai de modification." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"This will remove the associated meeting to unschedule the booking. Are you " +"sure?" +msgstr "" +"Cela supprimera la rencontre associée pour annuler la réservation. Etes-vous " +"sûr?" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "" +"Try next month\n" +" " +msgstr "" +"Essayez le mois prochain\\n\n" +" " + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_id +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "Type de l'activité d'exception enregistrée." + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_type_action +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_menu +msgid "Types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread +msgid "Unread Messages" +msgstr "Messages non lus" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Unread Messages Counter" +msgstr "Compteur de messages non lus" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule" +msgstr "Déprogrammer" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking and archive it." +msgstr "Déprogrammez cette réservation et archivez-la." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking." +msgstr "Déprogrammer cette réservation." + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_user +msgid "User" +msgstr "Utilisateur" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_user +msgid "Users allowed to book resources" +msgstr "Utilisateurs autorisés à réserver des ressources" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_manager +msgid "Users allowed to manage resource booking configurations." +msgstr "" +"Utilisateurs autorisés à gérer les configurations de réservation de " +"ressources." + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "View" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website Messages" +msgstr "Messages du site Web" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website communication history" +msgstr "Historique des communications du site Web" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__combination_auto_assign +msgid "" +"When checked, resource combinations will be (un)assigned automatically based " +"on their availability during the booking dates." +msgstr "" +"Lorsqu'elles sont cochées, les combinaisons de ressources seront " +"(dé)attribuées automatiquement en fonction de leur disponibilité pendant les " +"dates de réservation." + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "" +"When scheduled, resources will be blocked. When pending, it means the " +"requester didn't place the booking yet." +msgstr "" +"Lorsqu'elles sont planifiées, les ressources seront bloquées. Lorsqu'il est " +"en attente, cela signifie que le demandeur n'a pas encore effectué la " +"réservation." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__modifications_deadline +msgid "" +"When this deadline has been exceeded, if a booking was not yet confirmed, it " +"will be canceled automatically. Also, only booking managers will be able to " +"unschedule or reschedule them. The value is expressed in hours." +msgstr "" +"Passé ce délai, si une réservation n'a pas encore été confirmée, elle sera " +"automatiquement annulée. De plus, seuls les gestionnaires de réservation " +"pourront les déprogrammer ou les reprogrammer. La valeur est exprimée en " +"heures." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__user_id +msgid "" +"Who organized this booking? Usually whoever created the record. It will " +"appear as the calendar event organizer, when scheduled, and calendar " +"notifications will be sent in his/her name." +msgstr "" +"Qui a organisé cette réservation ? Généralement celui qui a créé le dossier. " +"Il apparaîtra comme l'organisateur de l'événement du calendrier, lorsqu'il " +"est programmé, et les notifications du calendrier seront envoyées en son nom." + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__partner_ids +msgid "Who will attend this booking?" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_resource_calendar +msgid "Working Times" +msgstr "Temps de travail" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "You are about to confirm this booking:" +msgstr "Vous êtes sur le point de confirmer cette réservation:" + +#. module: resource_booking +#: code:addons/resource_booking/models/calendar_event.py:0 +#, python-format +msgid "" +"You are not allowed to alter these bookings because they exceeded their " +"modification deadlines:\n" +"\n" +"- %s" +msgstr "" +"Vous n'êtes pas autorisé à modifier ces réservations car elles ont dépassé " +"leurs délais de modification:\\n\n" +"\"\\n\"\n" +"\"- %s\"" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "because you belong to the chosen resource combination:" +msgstr "" diff --git a/resource_booking/i18n/resource_booking.pot b/resource_booking/i18n/resource_booking.pot new file mode 100644 index 00000000..7b23bd7f --- /dev/null +++ b/resource_booking/i18n/resource_booking.pot @@ -0,0 +1,1286 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * resource_booking +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "# %(id)d - %(name)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "%(partner)s - %(type)s - %(time)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#, python-format +msgid "%(resources)s (using calendar %(calendar)s)" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "" +"
\n" +" Error details:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Reschedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Schedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Feedback" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel this booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "" +"\n" +" Cancel" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "State:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Preview" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Advice:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Booked resources:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Dates:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Location:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Meeting URL:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Requested by:" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +msgid "The chosen schedule is no longer available." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Type:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_warning +msgid "Access warning" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity_type__category +msgid "Action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_mail_activity_type__category +msgid "" +"Actions may trigger specific behavior like opening calendar view or " +"automatically mark as done when a document is uploaded" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__active +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__active +msgid "Active" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__booking_activity_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_ids +msgid "Activities" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity +msgid "Activity" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_state +msgid "Activity State" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_mail_activity_type +msgid "Activity Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "All times are displayed using this timezone:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__duration +msgid "" +"Amount of time that the resources will be booked and unavailable for others." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_combination_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Archived" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Are you sure?" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_attachment_count +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_ids +msgid "Attendees" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_auto_assign +msgid "Auto assigned" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Auto-assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Availability Calendar" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Available resource combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_combination +msgid "Bookable resource combinations" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_count +msgid "Booking Count" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_count +msgid "Booking count" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Booking ref." +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_count +#, python-format +msgid "Booking types" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking_combination.py:0 +#: code:addons/resource_booking/models/resource_booking_type.py:0 +#: model:ir.actions.act_window,name:resource_booking.resource_booking_action +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__booking_ids +#: model:ir.ui.menu,name:resource_booking.resource_booking_menu +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_home +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_calendar +#, python-format +msgid "Bookings" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__booking_ids +msgid "Bookings available for this type" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_calendar_event +msgid "Calendar Event" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Cancel" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__canceled +msgid "Canceled" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because no resources are selected for them:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "" +"Cannot schedule these bookings because they do not fit in their type or resources calendars, or because all resources are busy:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_assignment +msgid "" +"Choose how to auto-assign resource combinations. It has no effect if assiged" +" manually." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.alert_availability_lost +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Close" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__combination_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Combination" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__combination_assignment +msgid "Combination Assignment" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_combination_menu +msgid "Combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__company_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +msgid "Company" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__company_id +msgid "Company where this booking type is available." +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_configuration_menu +msgid "Configuration" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Confirm" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Confirm booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"Confirm that the requesting partner and yourself will attend the scheduled " +"meeting." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__confirmed +msgid "Confirmed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_uid +msgid "Created by" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__create_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__create_date +msgid "Created on" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_header +msgid "Current state of this booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__access_url +msgid "Customer Portal URL" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Date" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "" +"Dear user,\n" +"
\n" +" You have been assigned to the" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__alarm_ids +msgid "Default reminders" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__categ_ids +msgid "Default tags" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "Define bookable resource combinations." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "Define resource booking types." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "Define resource bookings." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__description +msgid "Description" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__display_name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__display_name +msgid "Display Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__duration +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__duration +msgid "Duration" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_type_duration_positive +msgid "Duration must be positive." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Duration:" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/mail_activity.py:0 +#, python-format +msgid "Feedback: %(feedback)s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_follower_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_partner_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_type_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Force a specific calendar, instead of combining the resources'." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__forced_calendar_id +msgid "Forced calendar" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Go back" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_view_search +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Group By" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__has_message +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__has_message +msgid "Has Message" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__id +msgid "ID" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_icon +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "If you cancel this booking:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__duration +msgid "" +"Interval offered to start each resource booking. Also used as booking " +"default duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Invite requesting partner to portal." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Involving me" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_is_follower +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_modifiable +msgid "Is Modifiable" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__is_overdue +msgid "Is Overdue" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will be unscheduled." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "It will disappear from your bookings list." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type____last_update +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel____last_update +msgid "Last Modified on" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_uid +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__write_date +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__write_date +msgid "Last Updated on" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__name +msgid "Leave empty to autogenerate a booking name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__location +msgid "Location" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_main_attachment_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_manager +msgid "Manager" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Manually assigned:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__meeting_id +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Meeting" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__videocall_location +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__videocall_location +msgid "Meeting URL" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__meeting_id +msgid "Meeting confirmed for this booking." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Meeting defaults" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__alarm_ids +msgid "Meetings will be created with these reminders by default." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__categ_ids +msgid "Meetings will be created with these tags by default." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Message and communication history" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_ids +msgid "Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_combination_required_if_event +msgid "Missing resource booking combination." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__modifications_deadline +msgid "Modifications Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__my_activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__name +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_name +msgid "Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_calendar_event_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_calendar_event_id +msgid "Next Activity Calendar Event" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_date_deadline +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_summary +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Next month" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "No free slots found this month." +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "No resource combinations available on %s" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_needaction_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_has_error_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.constraint,message:resource_booking.constraint_resource_booking_unique_meeting_id +msgid "Only one event per resource booking can exist." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.mail_activity_view_form_popup +msgid "Open Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Open a calendar to schedule a meeting for this booking request." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__user_id +msgid "Organizer" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__pending +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Pending" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__state +msgid "" +"Pending: No meeting scheduled.\n" +"Scheduled: The requester has not confirmed attendance yet.\n" +"Confirmed: Meeting scheduled, and requester attendance confirmed.\n" +"Canceled: Meeting removed, booking archived." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "Please confirm this is really what you want." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_url +msgid "Portal Access URL" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Previous month" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__random +msgid "Randomly: order is not important" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__partner_id +msgid "Requester" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__requester_advice +msgid "Requester Advice" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking +#: model:ir.module.category,name:resource_booking.category_resource_booking +#: model:mail.activity.type,name:resource_booking.mail_activity_data_resource_booking +msgid "Resource Booking" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Resource Booking Name" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type +msgid "Resource Booking Type" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.resource_booking_main_menu +msgid "Resource Bookings" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_view_resource_calendar_leaves_search +msgid "Resource Time Off" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_calendar +msgid "Resource Working Time" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_calendar_event__resource_booking_ids +#: model:ir.model.fields,field_description:resource_booking.field_mail_activity__booking_id +#: model:ir.model.fields.selection,name:resource_booking.selection__mail_activity_type__category__resource_booking +msgid "Resource booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_booking_type_combination_rel +msgid "Resource booking type relation with combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__type_rel_ids +msgid "Resource booking types where this combination is available." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_combination_action +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Resource combinations" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__combination_rel_ids +msgid "Resource combinations available for this type of bookings." +msgstr "" + +#. module: resource_booking +#: model:ir.model,name:resource_booking.model_resource_resource +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_combination__resource_ids +#: model:ir.ui.menu,name:resource_booking.menu_resource_resource +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "Resources" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__combination_id +msgid "Resources combination" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_combination__resource_ids +msgid "Resources that must be free to be booked together." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__activity_user_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__resource_calendar_id +msgid "Restrict bookings to this schedule." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_breadcrumbs +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Schedule" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/resource_booking.py:0 +#, python-format +msgid "Schedule booking" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking__state__scheduled +msgid "Scheduled" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Scheduled or confirmed" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__access_token +msgid "Security Token" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__sequence +msgid "Sequence" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set pending" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Set to pending" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_type_form +msgid "Settings" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Share" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields.selection,name:resource_booking.selection__resource_booking_type__combination_assignment__sorted +msgid "Sorted: pick the first one that is free" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__start +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Start date" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "Start:" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__state +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "State" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_state +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_state +msgid "" +"Status based on activities\n" +"Overdue: Due date is already passed\n" +"Today: Activity date is today\n" +"Planned: Future activities." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__stop +msgid "Stop" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__categ_ids +msgid "Tags" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__requester_advice +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__requester_advice +msgid "" +"Text that will appear by default in portal invitation emails and in calendar" +" views for scheduling." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +msgid "There are currently no bookings for your account." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_type_action +msgid "" +"These records categorize resource bookings and apply restrictions to them, " +"such as available resource combinations, availability schedules and interval" +" duration." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_combination_action +msgid "" +"These records define resource combinations that can be booked together in " +"specified schedules and intervals." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_portal_form +msgid "This booking exceeded its modifications deadline." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "" +"This will remove the associated meeting to unschedule the booking. Are you " +"sure?" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "" +"Try next month\n" +" " +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__type_id +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type_combination_rel__type_id +#: model_terms:ir.ui.view,arch_db:resource_booking.portal_my_bookings +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_view_search +msgid "Type" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__activity_exception_decoration +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: resource_booking +#: model:ir.actions.act_window,name:resource_booking.resource_booking_type_action +#: model:ir.ui.menu,name:resource_booking.resource_booking_type_menu +msgid "Types" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__message_unread_counter +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking and archive it." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.resource_booking_form +msgid "Unschedule this booking." +msgstr "" + +#. module: resource_booking +#: model:res.groups,name:resource_booking.group_user +msgid "User" +msgstr "" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_user +msgid "Users allowed to book resources" +msgstr "" + +#. module: resource_booking +#: model:res.groups,comment:resource_booking.group_manager +msgid "Users allowed to manage resource booking configurations." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "View" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,field_description:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__website_message_ids +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__combination_auto_assign +msgid "" +"When checked, resource combinations will be (un)assigned automatically based" +" on their availability during the booking dates." +msgstr "" + +#. module: resource_booking +#: model_terms:ir.actions.act_window,help:resource_booking.resource_booking_action +msgid "" +"When scheduled, resources will be blocked. When pending, it means the " +"requester didn't place the booking yet." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking_type__modifications_deadline +msgid "" +"When this deadline has been exceeded, if a booking was not yet confirmed, it" +" will be canceled automatically. Also, only booking managers will be able to" +" unschedule or reschedule them. The value is expressed in hours." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__user_id +msgid "" +"Who organized this booking? Usually whoever created the record. It will " +"appear as the calendar event organizer, when scheduled, and calendar " +"notifications will be sent in his/her name." +msgstr "" + +#. module: resource_booking +#: model:ir.model.fields,help:resource_booking.field_resource_booking__partner_ids +msgid "Who will attend this booking?" +msgstr "" + +#. module: resource_booking +#: model:ir.ui.menu,name:resource_booking.menu_resource_calendar +msgid "Working Times" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.scheduling_calendar +msgid "You are about to confirm this booking:" +msgstr "" + +#. module: resource_booking +#: code:addons/resource_booking/models/calendar_event.py:0 +#, python-format +msgid "" +"You are not allowed to alter these bookings because they exceeded their modification deadlines:\n" +"\n" +"- %s" +msgstr "" + +#. module: resource_booking +#: model_terms:ir.ui.view,arch_db:resource_booking.message_combination_assigned +msgid "because you belong to the chosen resource combination:" +msgstr "" diff --git a/resource_booking/migrations/16.0.1.0.0/post-migration.py b/resource_booking/migrations/16.0.1.0.0/post-migration.py new file mode 100644 index 00000000..63b565e6 --- /dev/null +++ b/resource_booking/migrations/16.0.1.0.0/post-migration.py @@ -0,0 +1,18 @@ +from openupgradelib import openupgrade + + +def _update_attendance_hour_to(env): + """According to the refactoring of _merge_intervals() a booking from 23:00 to 01:00 + of the next day must be completely available, having defined hour_to=23.59 is not, + so it is changed to 24.0. + This behavior is confirmed to be correct because in the tests calendars are set + with full days (hour_from=0, hour_to=24).""" + lines = env["resource.calendar.attendance"].search( + [("hour_to", ">=", 23 + 59 / 60)] + ) + lines.hour_to = 24.0 + + +@openupgrade.migrate() +def migrate(env, version): + _update_attendance_hour_to(env) diff --git a/resource_booking/models/__init__.py b/resource_booking/models/__init__.py new file mode 100644 index 00000000..b57e4650 --- /dev/null +++ b/resource_booking/models/__init__.py @@ -0,0 +1,9 @@ +from . import calendar_event +from . import res_partner +from . import resource_booking +from . import resource_booking_combination +from . import resource_booking_type +from . import resource_booking_type_combination_rel +from . import resource_calendar +from . import resource_resource +from . import mail_activity diff --git a/resource_booking/models/calendar_event.py b/resource_booking/models/calendar_event.py new file mode 100644 index 00000000..12a4c7b2 --- /dev/null +++ b/resource_booking/models/calendar_event.py @@ -0,0 +1,114 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2022 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class CalendarEvent(models.Model): + _inherit = "calendar.event" + + # One2one field, actually + resource_booking_ids = fields.One2many( + comodel_name="resource.booking", + inverse_name="meeting_id", + string="Resource booking", + ) + + @api.constrains("resource_booking_ids", "start", "stop") + def _check_bookings_scheduling(self): + """Scheduled bookings must have no conflicts.""" + bookings = self.sudo().resource_booking_ids + return bookings._check_scheduling() + + def _validate_booking_modifications(self): + """Make sure you can cancel a booking meeting.""" + bookings = self.sudo().resource_booking_ids + modifiable = bookings.filtered("is_modifiable") + frozen = bookings - modifiable + if frozen: + raise ValidationError( + _( + "You are not allowed to alter these bookings because " + "they exceeded their modification deadlines:\n\n- %s" + ) + % "\n- ".join(frozen.mapped("display_name")) + ) + + def unlink(self): + """Check you're allowed to unschedule it.""" + self._validate_booking_modifications() + return super().unlink() + + def write(self, vals): + """Check you're allowed to reschedule it.""" + before = [(one.start, one.stop) for one in self] + result = super().write(vals) + rescheduled = self + for (old_start, old_stop), new in zip(before, self): + if old_start == new.start and old_stop == new.stop: + rescheduled -= new + rescheduled._validate_booking_modifications() + return result + + @api.model_create_multi + def create(self, vals_list): + """Transfer resource booking to _attendees_values by context. + + We need to serialize the creation in that case. + mail_notify_author key from context is necessary to force the notification + to be sent to author. + """ + vals_list2 = [] + records = self.env["calendar.event"] + for vals in vals_list: + if "resource_booking_ids" in vals: + records += super( + CalendarEvent, + self.with_context( + resource_booking_ids=vals["resource_booking_ids"], + mail_notify_author=True, + ), + ).create(vals) + else: + vals_list2.append(vals) + records += super().create(vals_list2) + return records + + def get_interval(self, interval, tz=None): + """Autofix tz from related resource booking. + + This function is called to render calendar.event notification mails. + Any notification related to a resource.booking must be emitted in the + same TZ as the resource.booking. Otherwise it's confusing to the user. + """ + tz = self.resource_booking_ids.type_id.resource_calendar_id.tz or tz + return super().get_interval(interval=interval, tz=tz) + + def _attendees_values(self, partner_commands): + """Autoconfirm resource attendees if preselected and hand-picked on RB. + + NOTE: There's no support for changing `resource_booking_ids` once the meeting + is created nor having more than one Rb attached to the same meeting, but that's + not a real case for now. + """ + attendee_commands = super()._attendees_values(partner_commands) + partner_ids = False + for cmd in self.env.context.get("resource_booking_ids", []): + if cmd[0] == 0 and not cmd[2].get("combination_auto_assign", True): + partner_ids = [cmd[2]["partner_id"]] + elif cmd[0] == 6: + rb = self.env["resource.booking"].browse(cmd[2]) + if rb.combination_auto_assign: + continue # only auto-confirm if handpicked combination + partner_ids = rb.combination_id.resource_ids.user_id.partner_id.ids + for command in attendee_commands: + if command[0] != 0: + continue + if not partner_ids: + rb = self.sudo().resource_booking_ids + partner_ids = rb.combination_id.resource_ids.user_id.partner_id.ids + if command[2]["partner_id"] in partner_ids: + command[2]["state"] = "accepted" + return attendee_commands diff --git a/resource_booking/models/mail_activity.py b/resource_booking/models/mail_activity.py new file mode 100644 index 00000000..cb2cb9e1 --- /dev/null +++ b/resource_booking/models/mail_activity.py @@ -0,0 +1,60 @@ +# Copyright 2024 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, fields, models, tools +from odoo.tools import is_html_empty + + +class MailActivityType(models.Model): + _inherit = "mail.activity.type" + + category = fields.Selection( + selection_add=[("resource_booking", "Resource booking")] + ) + + +class MailActivity(models.Model): + _inherit = "mail.activity" + + booking_id = fields.Many2one( + "resource.booking", string="Resource booking", ondelete="cascade" + ) + + def action_open_resource_booking(self): + self.ensure_one() + action = self.env["ir.actions.actions"]._for_xml_id( + "resource_booking.resource_booking_action" + ) + action["view_mode"] = "form" + del action["views"] + action["res_id"] = self.booking_id.id + ctx = dict(self.env.context) + ctx.update( + { + "default_activity_type_id": self.activity_type_id.id, + "default_name": self.summary or self.res_name, + "default_description": self.note + if not is_html_empty(self.note) + else "", + "default_booking_activity_ids": [(6, 0, self.ids)], + } + ) + action["context"] = ctx + return action + + def _action_done(self, feedback=False, attachment_ids=False): + bookings = self.mapped("booking_id") + messages, activities = super(MailActivity, self)._action_done( + feedback=feedback, attachment_ids=attachment_ids + ) + if feedback: + for booking in bookings: + description = booking.description + description = "%s
%s" % ( + description if not tools.is_html_empty(description) else "", + _("Feedback: %(feedback)s", feedback=tools.plaintext2html(feedback)) + if feedback + else "", + ) + booking.write({"description": description}) + return messages, activities diff --git a/resource_booking/models/res_partner.py b/resource_booking/models/res_partner.py new file mode 100644 index 00000000..3aefcf5b --- /dev/null +++ b/resource_booking/models/res_partner.py @@ -0,0 +1,31 @@ +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + resource_booking_count = fields.Integer( + compute="_compute_resource_booking_count", string="Resource booking count" + ) + resource_booking_ids = fields.Many2many( + comodel_name="resource.booking", + relation="res_partner_resource_booking_rel", + column1="res_partner_id", + column2="resource_booking_id", + string="Bookings", + copy=False, + ) + + def _compute_resource_booking_count(self): + for p in self: + p.resource_booking_count = len(p.resource_booking_ids) + + def action_view_resource_booking(self): + self.ensure_one() + action = self.env["ir.actions.actions"]._for_xml_id( + "resource_booking.resource_booking_action" + ) + action["context"] = { + "default_partner_ids": self.ids, + } + return action diff --git a/resource_booking/models/resource_booking.py b/resource_booking/models/resource_booking.py new file mode 100644 index 00000000..ae70195e --- /dev/null +++ b/resource_booking/models/resource_booking.py @@ -0,0 +1,796 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2022 Tecnativa - Pedro M. Baeza +# Copyright 2023 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import calendar +from datetime import datetime, timedelta + +from dateutil.relativedelta import relativedelta + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + +from odoo.addons.resource.models.resource import Intervals + + +def _merge_intervals(intervals): + # Merge intervals where start of current interval == stop of previous interval, + # assuming that the intervals are ordererd. + intervals = [list(tup) for tup in intervals._items] + # Handle 23:59:59:99999 + for i in range(len(intervals)): + stop = intervals[i][1] + if ( + stop.hour == 23 + and stop.minute == 59 + and stop.second == 59 + and stop.microsecond == 999999 + ): + intervals[i][1] += timedelta(microseconds=1) + # Begin with the last interval, to safely delete it if needed. + for i in range(len(intervals) - 1, 0, -1): + current_start = intervals[i][0] + current_stop = intervals[i][1] + previous_stop = intervals[i - 1][1] + if current_start == previous_stop: + intervals[i - 1][1] = current_stop + del intervals[i] + return Intervals([tuple(interval) for interval in intervals]) + + +def _availability_is_fitting(available_intervals, start_dt, stop_dt): + available_intervals = _merge_intervals(available_intervals) + for item in available_intervals._items: + available_start, available_stop = item[0], item[1] + if start_dt >= available_start and stop_dt <= available_stop: + return True + return False + + +def _availability_is_fitting_legacy(available_intervals, start_dt, end_dt): + """I keep the old method, since part of it may be needed in the new method.""" + # Test whether the stretch between start_dt and end_dt is an uninterrupted + # stretch of time as determined by `available_intervals`. + # + # `available_intervals` is typically created by `_get_intervals()`, which in + # turn uses `calendar._work_intervals()`. It appears to be default upstream + # behaviour of `_work_intervals()` to create a (start_dt, end_dt, record) + # tuple for every day, where end_dt is at 23:59, and the next tuple's + # start_dt is at 00:00. + # + # Changing this upstream behaviour of `_work_intervals()` to return a + # _single_ tuple for any multi-day uninterrupted stretch of time would + # probably be preferable, but (1.) the code in `_work_intervals()` is + # unbelievably arcane, and (2.) changing this behaviour is extremely likely + # to cause bugs elsewhere. So instead, we account for the upstream behaviour + # here. + start_date = start_dt.date() + end_date = end_dt.date() + # Booking is uninterrupted on the same calendar day. + if ( + len(available_intervals) == 1 + and available_intervals._items[0][0] <= start_dt + and available_intervals._items[0][1] >= end_dt + ): + return True + # Booking spans more than one calendar day, e.g. from 23:00 to 1:00 + # the next day. + elif available_intervals and start_date != end_date: + tally_date = start_date + for item in available_intervals: + item0_date = item[0].date() + item1_date = item[1].date() + # FIXME: Really weird workaround for when available_intervals has + # nonsensical items in it where item1_date is before item0_date. + # Just ignore those items and pretend they don't exist; all the + # other items appear to make sense. + if item1_date < item0_date: + continue + # Intervals that aren't on the running tally date break the streak. + # This check is for malformed data in `available_intervals` where a + # day is skipped. + if item0_date != tally_date or item1_date != tally_date: + break + # Intervals that aren't on the end date should end at 23:59 (and any + # number of seconds). + if item1_date != end_date and (item[1].hour != 23 or item[1].minute != 59): + break + # Intervals that aren't on the start date should start at 00:00 (and + # any number of seconds). + if item0_date != start_date and (item[0].hour != 0 or item[0].minute != 0): + break + # The next interval should be on the next day. + tally_date += timedelta(days=1) + else: + return True + return False + + +class ResourceBooking(models.Model): + _name = "resource.booking" + _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] + _description = "Resource Booking" + _order = "start DESC" + _sql_constraints = [ + ( + "combination_required_if_event", + "CHECK(meeting_id IS NULL OR combination_id IS NOT NULL)", + "Missing resource booking combination.", + ), + ( + "unique_meeting_id", + "UNIQUE(meeting_id)", + "Only one event per resource booking can exist.", + ), + ] + + active = fields.Boolean(default=True) + meeting_id = fields.Many2one( + comodel_name="calendar.event", + string="Meeting", + context={"default_res_id": False, "default_res_model": False}, + copy=False, + index=True, + ondelete="set null", + help="Meeting confirmed for this booking.", + ) + categ_ids = fields.Many2many(string="Tags", comodel_name="calendar.event.type") + combination_id = fields.Many2one( + comodel_name="resource.booking.combination", + string="Resources combination", + compute="_compute_combination_id", + copy=False, + domain="[('type_rel_ids.type_id', 'in', [type_id])]", + index=True, + readonly=False, + states={"scheduled": [("required", True)], "confirmed": [("required", True)]}, + store=True, + tracking=True, + ) + combination_auto_assign = fields.Boolean( + string="Auto assigned", + default=True, + help=( + "When checked, resource combinations will be (un)assigned automatically " + "based on their availability during the booking dates." + ), + ) + name = fields.Char(index=True, help="Leave empty to autogenerate a booking name.") + description = fields.Html() + partner_id = fields.Many2one( + comodel_name="res.partner", + compute="_compute_partner_id", + inverse="_inverse_partner_id", + readonly=False, + string="Requester", + ) + partner_ids = fields.Many2many( + comodel_name="res.partner", + relation="res_partner_resource_booking_rel", + string="Attendees", + required=True, + tracking=True, + help="Who will attend this booking?", + ) + user_id = fields.Many2one( + comodel_name="res.users", + default=lambda self: self._default_user_id(), + store=True, + readonly=False, + compute="_compute_user_id", + string="Organizer", + index=True, + tracking=True, + help=( + "Who organized this booking? Usually whoever created the record. " + "It will appear as the calendar event organizer, when scheduled, " + "and calendar notifications will be sent in his/her name." + ), + ) + location = fields.Char( + compute="_compute_location", + readonly=False, + store=True, + ) + videocall_location = fields.Char( + compute="_compute_videocall_location", + string="Meeting URL", + readonly=False, + store=True, + ) + requester_advice = fields.Text(related="type_id.requester_advice", readonly=True) + is_modifiable = fields.Boolean(compute="_compute_is_modifiable") + is_overdue = fields.Boolean(compute="_compute_is_overdue") + state = fields.Selection( + [ + ("pending", "Pending"), + ("scheduled", "Scheduled"), + ("confirmed", "Confirmed"), + ("canceled", "Canceled"), + ], + compute="_compute_state", + store=True, + default="pending", + index=True, + tracking=True, + help=( + "Pending: No meeting scheduled.\n" + "Scheduled: The requester has not confirmed attendance yet.\n" + "Confirmed: Meeting scheduled, and requester attendance confirmed.\n" + "Canceled: Meeting removed, booking archived." + ), + ) + start = fields.Datetime( + compute="_compute_start", + copy=False, + index=True, + readonly=False, + store=True, + tracking=True, + ) + duration = fields.Float( + compute="_compute_duration", + readonly=False, + store=True, + tracking=True, + help="Amount of time that the resources will be booked and unavailable for others.", + ) + stop = fields.Datetime( + compute="_compute_stop", + copy=False, + index=True, + store=True, + tracking=True, + ) + type_id = fields.Many2one( + comodel_name="resource.booking.type", + string="Type", + index=True, + ondelete="cascade", + required=True, + tracking=True, + ) + booking_activity_ids = fields.One2many( + "mail.activity", "booking_id", string="Activities" + ) + + @api.depends("partner_ids") + def _compute_partner_id(self): + for one in self: + one.partner_id = one.partner_ids[:1] + + def _inverse_partner_id(self): + for one in self: + one.partner_ids = one.partner_id + + @api.model + def _default_user_id(self): + return self.env.user + + def _compute_access_url(self): + result = super()._compute_access_url() + for one in self: + one.access_url = "/my/bookings/%d" % one.id + return result + + @api.onchange("type_id") + def _onchange_type_set_categ_ids(self): + """Copy default tags from RBT when changing it.""" + for one in self.filtered("type_id"): + one.categ_ids = one.type_id.categ_ids + + @api.depends("start", "type_id", "combination_auto_assign") + def _compute_combination_id(self): + """Select best combination candidate when changing booking dates.""" + # Useless without the interval + for one in self.filtered(lambda x: x.start and x.combination_auto_assign): + one.combination_id = one._get_best_combination() + + @api.depends("start") + def _compute_is_overdue(self): + """Indicate if booking is overdue.""" + now = fields.Datetime.now() + for one in self: + # You can always modify it if there's no meeting yet + if not one.start: + one.is_overdue = False + continue + anticipation = timedelta(hours=one.type_id.modifications_deadline) + deadline = one.start - anticipation + one.is_overdue = now > deadline + + @api.depends("is_overdue") + @api.depends_context("uid", "using_portal") + def _compute_is_modifiable(self): + """Indicate if the booking is modifiable.""" + self.is_modifiable = True + is_manager = not self.env.context.get( + "using_portal" + ) and self.env.user.has_group("resource_booking.group_manager") + # Managers can always modify overdue bookings + if not is_manager: + overdue = self.filtered("is_overdue") + overdue.is_modifiable = False + + @api.depends("name", "partner_ids", "type_id", "meeting_id") + @api.depends_context("uid", "using_portal") + def _compute_display_name(self): + """Overridden just for dependencies; see `name_get()` for implementation.""" + return super()._compute_display_name() + + @api.depends("meeting_id.location", "type_id") + def _compute_location(self): + """Get location from meeting or type.""" + for record in self: + # Get location from type when changing it or creating from ORM + # HACK https://github.com/odoo/odoo/issues/74152 + if not record.location or record._origin.type_id != record.type_id: + record.location = record.type_id.location + # Get it from meeting only when available + elif record.meeting_id: + record.location = record.meeting_id.location + + @api.depends("meeting_id.videocall_location", "type_id") + def _compute_videocall_location(self): + """Get videocall location from meeting or type.""" + for record in self: + # Get videocall_location from type when changing it or creating from ORM + if ( + not record.videocall_location + or record._origin.type_id != record.type_id + ): + record.videocall_location = record.type_id.videocall_location + # Get it from meeting only when available + elif record.meeting_id: + record.videocall_location = record.meeting_id.videocall_location + + @api.depends("active", "meeting_id.attendee_ids.state") + def _compute_state(self): + """Obtain request state.""" + to_check = self.browse() + for one in self: + if not one.active: + one.state = "canceled" + continue + confirmed = False + for attendee in one.meeting_id.attendee_ids: + if attendee.partner_id in one.partner_ids: + confirmed = attendee.state == "accepted" + break + if confirmed: + one.state = "confirmed" + to_check |= one + continue + one.state = "scheduled" if one.meeting_id else "pending" + to_check._check_scheduling() + + @api.depends("meeting_id.start") + def _compute_start(self): + """Get start date from related meeting, if available.""" + for record in self: + if record.id: + record.start = record.meeting_id.start + + @api.depends("meeting_id.duration", "type_id") + def _compute_duration(self): + """Compute duration for each booking.""" + for record in self: + # Special case when creating record from UI + if not record.id: + record.duration = self.default_get(["duration"]).get( + "duration", record.type_id.duration + ) + # Get duration from type only when changing type or when creating from ORM + elif not record.duration or record._origin.type_id != record.type_id: + record.duration = record.type_id.duration + # Get it from meeting only when available + elif record.meeting_id: + record.duration = record.meeting_id.duration + + @api.depends("start", "duration") + def _compute_stop(self): + """Get stop date from start date and duration.""" + for record in self: + try: + record.stop = record.start + timedelta(hours=record.duration) + except TypeError: + # Either value is False: no stop date + record.stop = False + + @api.depends("meeting_id.user_id") + def _compute_user_id(self): + """Get user from related meeting, if available.""" + for record in self.filtered(lambda x: x.meeting_id.user_id): + record.user_id = record.meeting_id.user_id + + def _prepare_meeting_vals(self): + resource_partners = self.combination_id.resource_ids.filtered( + lambda res: res.resource_type == "user" + ).mapped("user_id.partner_id") + return dict( + alarm_ids=[(6, 0, self.type_id.alarm_ids.ids)], + categ_ids=[(6, 0, self.categ_ids.ids)], + description=self.type_id.requester_advice, + duration=self.duration, + location=self.location, + videocall_location=self.videocall_location, + name=self.name + or self._get_name_formatted(self.partner_ids[0], self.type_id), + partner_ids=[ + (4, partner.id, 0) for partner in self.partner_ids | resource_partners + ], + resource_booking_ids=[(6, 0, self.ids)], + start=self.start, + stop=self.stop, + user_id=self.user_id.id, + show_as="busy", + # These 2 avoid creating event as activity + res_model_id=False, + res_id=False, + ) + + def _sync_meeting(self): + """Lazy-create or destroy calendar.event.""" + # Notify changed dates to attendees + _self = self.with_context(syncing_booking_ids=self.ids) + # Avoid sync recursion + _self -= self.browse(self.env.context.get("syncing_booking_ids")) + to_create, to_delete = [], _self.env["calendar.event"] + for one in _self: + if one.start: + meeting_vals = one._prepare_meeting_vals() + if one.meeting_id: + meeting = one.meeting_id + if not all( + ( + one.meeting_id.start == one.start, + one.meeting_id.stop == one.stop, + one.meeting_id.duration == one.duration, + ) + ): + # Context to notify scheduling change + meeting = meeting.with_context(from_ui=True) + meeting.write(meeting_vals) + else: + to_create.append(meeting_vals) + else: + to_delete |= one.meeting_id + if to_delete: + to_delete.unlink() + if to_create: + _self.env["calendar.event"].create(to_create) + + @api.constrains("combination_id", "meeting_id", "type_id") + def _check_scheduling(self): + """Scheduled bookings must have no conflicts.""" + # Nothing to do if no bookings are scheduled + has_meeting = self.filtered("meeting_id") + if not has_meeting: + return + # Ensure all scheduled bookings have booked some resources + has_rbc = self.with_context(active_test=False).filtered( + "combination_id.resource_ids" + ) + missing_rbc = has_meeting - has_rbc + if missing_rbc: + raise ValidationError( + _( + "Cannot schedule these bookings because no resources " + "are selected for them:\n\n- %s" + ) + % ("\n- ".join(missing_rbc.mapped("display_name"))) + ) + # Ensure all bookings fit in their type and resources calendars + unfitting_bookings = has_meeting + now = fields.Datetime.now() + for booking in has_meeting: + # Ignore if the event already happened + already_happened = booking.stop and booking.stop < now + if already_happened: + unfitting_bookings -= booking + continue + start_dt = fields.Datetime.context_timestamp(self, booking["start"]) + end_dt = fields.Datetime.context_timestamp(self, booking["stop"]) + available_intervals = booking._get_intervals(start_dt, end_dt) + if _availability_is_fitting(available_intervals, start_dt, end_dt): + unfitting_bookings -= booking + # Explain which bookings failed validation + if unfitting_bookings: + raise ValidationError( + _( + "Cannot schedule these bookings because they do not fit " + "in their type or resources calendars, or because " + "all resources are busy:\n\n- %s" + ) + % "\n- ".join(unfitting_bookings.mapped("display_name")) + ) + + def _get_calendar_context(self, year=None, month=None, now=None): + """Get the required context for the calendar view in the portal. + + See the `resource_booking.scheduling_calendar` view. + + :param int year: Year of the calendar to be displayed. + :param int month: Month of the calendar to be displayed. + :param datetime now: Represents the current datetime. + """ + month1 = relativedelta(months=1) + now = fields.Datetime.context_timestamp(self, now or fields.Datetime.now()) + year = year or now.year + month = month or now.month + start = now.replace( + year=year, + month=month, + day=1, + hour=0, + minute=0, + second=0, + microsecond=0, + ) + lang = self.env["res.lang"]._lang_get(self.env.lang or self.env.user.lang) + weekday_names = dict(lang.fields_get(["week_start"])["week_start"]["selection"]) + booking_duration = timedelta(hours=self.duration) + slots = self._get_available_slots(start, start + month1 + booking_duration) + return { + "booking": self, + "calendar": calendar.Calendar(int(lang.week_start) - 1), + "now": now, + "res_lang": lang, + "slots": slots, + "start": start, + "weekday_names": weekday_names, + } + + @api.model + def _get_name_formatted(self, partner, type_, meeting=None): + """Produce a beautifully formatted name.""" + values = {"partner": partner.display_name, "type": type_.display_name} + if meeting: + values["time"] = meeting.display_time + return _("%(partner)s - %(type)s - %(time)s") % values + return _("%(partner)s - %(type)s") % values + + def _get_best_combination(self): + """Pick best combination based on current booking state.""" + # No dates? Then return whatever is already selected (can be empty) + if not self.start: + return self.combination_id + # If there's a combination already, put it 1st (highest priority) + sorted_combinations = self.combination_id + ( + self.type_id._get_combinations_priorized() - self.combination_id + ) + start_dt = fields.Datetime.context_timestamp(self, self.start) + end_dt = fields.Datetime.context_timestamp(self, self.stop) + # Get 1st combination available in the desired interval + for combination in sorted_combinations: + available_intervals = self._get_intervals(start_dt, end_dt, combination) + if _availability_is_fitting(available_intervals, start_dt, end_dt): + return combination + # Tell portal user there's no combination available + if self.env.context.get("using_portal"): + hours = (self.stop - self.start).total_seconds() / 3600 + raise ValidationError( + _("No resource combinations available on %s") + % self.env["calendar.event"]._get_display_time( + self.start, self.stop, hours, False + ) + ) + + def _get_available_slots(self, start_dt, end_dt): + """Return available slots for scheduling current booking.""" + result = {} + slot_duration = timedelta(hours=self.type_id.slot_duration) + booking_duration = timedelta(hours=self.duration) + now = fields.Datetime.context_timestamp(self, fields.Datetime.now()) + start_dt = max( + start_dt, now + timedelta(hours=self.type_id.modifications_deadline) + ) + # available_intervals should start with the beginning of the work day, + # to compute each slot based on the beginning of the work day. + workday_min = start_dt.replace(hour=0, minute=0, second=0, microsecond=0) + available_intervals = self._get_intervals(workday_min, end_dt) + available_intervals = _merge_intervals(available_intervals) + # Loop through available times and append tested start/stop to the result. + test_start = False + for item in available_intervals._items: + available_start, available_stop = item[0], item[1] + test_start = available_start + while test_start and test_start < available_stop: + test_stop = test_start + booking_duration + if ( + test_start >= start_dt + and test_start >= available_start + and test_stop <= available_stop + ): + if not result.get(test_start.date()): + result.setdefault(test_start.date(), []) + result[test_start.date()].append(test_start) + test_start += slot_duration + return result + + def _get_intervals(self, start_dt, end_dt, combination=None): + """Get available intervals for this booking, + based on the calendar of the booking type + and the calendar(s) of the relevant resource combination(s).""" + # Get all intervals except those from current booking + try: + booking_id = self.id or self._origin.id or -1 + except AttributeError: + booking_id = -1 + # Detached compatibility with hr_holidays_public + booking = self.with_context( + analyzing_booking=booking_id, exclude_public_holidays=True + ) + # RBT calendar uses no resources to restrict bookings + if booking.type_id: + result = booking.type_id.resource_calendar_id._work_intervals_batch( + start_dt, end_dt + )[False] + else: + result = Intervals([]) + # Restrict with the chosen combination, or to at least one of the + # available ones + combinations = ( + combination + or booking.combination_id + or booking.mapped("type_id.combination_rel_ids.combination_id") + ).with_context(analyzing_booking=booking_id) + result &= combinations._get_intervals(start_dt, end_dt) + return result + + def _sync_booking_activities_date(self): + for rec in self.filtered("booking_activity_ids"): + start = rec.start or (datetime.now() + relativedelta(years=1000)) + rec.booking_activity_ids.date_deadline = start + # Set calendar_event_id to show Re-schedule button in activity block + rec.booking_activity_ids.calendar_event_id = rec.meeting_id + + @api.model_create_multi + def create(self, vals_list): + """Sync booking with meeting if needed.""" + result = super().create(vals_list) + result._sync_meeting() + result._sync_booking_activities_date() + return result + + def write(self, vals): + """Sync booking with meeting if needed.""" + result = super().write(vals) + self._sync_meeting() + if vals.get("start") or "meeting_id" in vals: + self._sync_booking_activities_date() + return result + + def unlink(self): + """Unlink meeting if needed.""" + self.meeting_id.unlink() + self.booking_activity_ids.unlink() + return super().unlink() + + def name_get(self): + """Autogenerate booking name if none is provided.""" + old = super().name_get() + new = [] + for id_, name in old: + record = self.browse(id_) + if self.env.context.get("using_portal"): + # ID optionally suffixed with custom name for portal users + template = _("# %(id)d - %(name)s") if record.name else _("# %(id)d") + name = template % {"id": id_, "name": name} + elif not record.name: + # Automatic name for backend users + name = self._get_name_formatted( + record.partner_ids[0], record.type_id, record.meeting_id + ) + new.append((id_, name)) + return new + + def _message_auto_subscribe_followers(self, updated_values, default_subtype_ids): + """Auto-subscribe and notify resource partners.""" + result = super()._message_auto_subscribe_followers( + updated_values, default_subtype_ids + ) + combination = ( + self.env["resource.booking.combination"] + .sudo() + .browse(updated_values.get("combination_id")) + ) + resource_partners = combination.mapped( + "resource_ids.user_id.partner_id" + ).filtered("active") + for partner in resource_partners: + result.append( + ( + partner.id, + default_subtype_ids, + "resource_booking.message_combination_assigned" + if partner != self.env.user.partner_id + else False, + ) + ) + return result + + def _message_get_suggested_recipients(self): + """Suggest related partners.""" + recipients = super()._message_get_suggested_recipients() + for record in self: + for partner in record.partner_ids: + record._message_add_suggested_recipient( + recipients, + partner=partner, + reason=self._fields["partner_ids"].string, + ) + return recipients + + def action_schedule(self): + """Redirect user to a simpler way to schedule this booking.""" + DurationParser = self.env["ir.qweb.field.duration"] + return { + "context": dict( + self.env.context, + # These 2 avoid creating event as activity + default_res_model_id=False, + default_res_id=False, + # Context used by web_calendar_slot_duration module + calendar_slot_duration=DurationParser.value_to_html( + self.duration, + { + "unit": "hour", + "digital": True, + }, + ), + default_resource_booking_ids=[(6, 0, self.ids)], + default_name=self.name or "", + ), + "name": _("Schedule booking"), + "res_model": "calendar.event", + "target": "self", + "type": "ir.actions.act_window", + "view_mode": "calendar,tree,form", + } + + def action_confirm(self): + """Confirm own and requesting partner's attendance.""" + attendees_to_confirm = self.env["calendar.attendee"] + confirm_always = self.env["res.partner"] + if self.env.context.get("confirm_own_attendance"): + confirm_always |= self.env.user.partner_id + # Avoid wasted state recomputes + with self.env.norecompute(): + for booking in self: + if not booking.meeting_id: + continue + # Make sure requesters and user resources are meeting attendees + booking.meeting_id.partner_ids |= booking.partner_ids | booking.mapped( + "combination_id.resource_ids.user_id.partner_id" + ) + # Find meeting attendees that should be confirmed + partners_to_confirm = confirm_always | booking.partner_ids + for attendee in booking.meeting_id.attendee_ids: + if attendee.partner_id & partners_to_confirm: + # attendee.state='accepted' + attendees_to_confirm |= attendee + attendees_to_confirm.write({"state": "accepted"}) + self.env.flush_all() # booking.meeting_id.partner_ids and attendees_to_confirm + + def action_unschedule(self): + """Remove associated meetings.""" + self.booking_activity_ids.calendar_event_id = False + self.mapped("meeting_id").unlink() + # Force recomputing, in case meeting_id is not visible in the form + self.write({"meeting_id": False}) + + def action_cancel(self): + """Cancel this booking.""" + # Remove related meeting + self.action_unschedule() + # Archive and reset access token + self.write({"active": False, "access_token": False}) + + def action_open_portal(self): + return { + "target": "self", + "type": "ir.actions.act_url", + "url": self.get_portal_url(), + } diff --git a/resource_booking/models/resource_booking_combination.py b/resource_booking/models/resource_booking_combination.py new file mode 100644 index 00000000..f8b3d390 --- /dev/null +++ b/resource_booking/models/resource_booking_combination.py @@ -0,0 +1,115 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models + +from odoo.addons.resource.models.resource import Intervals + + +class ResourceBookingCombination(models.Model): + _name = "resource.booking.combination" + _description = "Bookable resource combinations" + + active = fields.Boolean(default=True) + booking_count = fields.Integer( + compute="_compute_booking_count", string="Booking count" + ) + booking_ids = fields.One2many( + comodel_name="resource.booking", + inverse_name="combination_id", + string="Bookings", + ) + forced_calendar_id = fields.Many2one( + comodel_name="resource.calendar", + string="Forced calendar", + index=True, + help="Force a specific calendar, instead of combining the resources'.", + ) + name = fields.Char(compute="_compute_name", store=True) + type_count = fields.Integer(compute="_compute_type_count", string="Booking types") + type_rel_ids = fields.One2many( + comodel_name="resource.booking.type.combination.rel", + inverse_name="combination_id", + string="Resource booking types", + help="Resource booking types where this combination is available.", + ) + resource_ids = fields.Many2many( + string="Resources", + comodel_name="resource.resource", + required=True, + help="Resources that must be free to be booked together.", + ) + + @api.depends("booking_ids") + def _compute_booking_count(self): + data = self.env["resource.booking"].read_group( + [("combination_id", "in", self.ids)], ["combination_id"], ["combination_id"] + ) + mapping = {x["combination_id"][0]: x["combination_id_count"] for x in data} + for one in self: + one.booking_count = mapping.get(one.id, 0) + + @api.depends("resource_ids.name", "forced_calendar_id.name") + def _compute_name(self): + for one in self: + data = { + "resources": " + ".join(sorted(one.resource_ids.mapped("name"))), + "calendar": one.forced_calendar_id.name, + } + if one.forced_calendar_id: + one.name = _("%(resources)s (using calendar %(calendar)s)") % data + else: + one.name = _("%(resources)s") % data + + @api.depends("type_rel_ids") + def _compute_type_count(self): + data = self.env["resource.booking.type.combination.rel"].read_group( + [("combination_id", "in", self.ids)], ["combination_id"], ["combination_id"] + ) + mapping = {x["combination_id"][0]: x["combination_id_count"] for x in data} + for one in self: + one.type_count = mapping.get(one.id, 0) + + @api.constrains("booking_ids", "forced_calendar_id", "resource_ids") + def _check_bookings_scheduling(self): + """Scheduled bookings must have no conflicts.""" + bookings = self.mapped("booking_ids") + return bookings._check_scheduling() + + def _get_intervals(self, start_dt, end_dt): + """Get available intervals for this booking combination.""" + base = Intervals([(start_dt, end_dt, self)]) + result = Intervals([]) + # Detached compatibility with hr_holidays_public + for combination in self.with_context(exclude_public_holidays=True): + combination_intervals = base + for res in combination.resource_ids: + if not combination_intervals: + break # Can't restrict more + calendar = combination.forced_calendar_id or res.calendar_id + # combination_intervals &= calendar._work_intervals(start_dt, end_dt, res) + combination_intervals &= calendar._work_intervals_batch( + start_dt, end_dt, res + )[res.id] + result |= combination_intervals + return result + + def action_open_bookings(self): + return { + "domain": [("combination_id", "in", self.ids)], + "name": _("Bookings"), + "res_model": "resource.booking", + "type": "ir.actions.act_window", + "view_mode": "calendar,tree,form", + "context": {"default_combination_id": self.id}, + } + + def action_open_resource_booking_types(self): + return { + "context": self.env.context, + "domain": [("combination_rel_ids.combination_id", "in", self.ids)], + "name": _("Booking types"), + "res_model": "resource.booking.type", + "type": "ir.actions.act_window", + "view_mode": "tree,form", + } diff --git a/resource_booking/models/resource_booking_type.py b/resource_booking/models/resource_booking_type.py new file mode 100644 index 00000000..d7d51585 --- /dev/null +++ b/resource_booking/models/resource_booking_type.py @@ -0,0 +1,154 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from random import random + +from odoo import _, api, fields, models + + +class ResourceBookingType(models.Model): + _name = "resource.booking.type" + _inherit = ["mail.thread", "mail.activity.mixin"] + _description = "Resource Booking Type" + _sql_constraints = [ + ("duration_positive", "CHECK(duration > 0)", "Duration must be positive."), + ] + + active = fields.Boolean(default=True) + alarm_ids = fields.Many2many( + string="Default reminders", + comodel_name="calendar.alarm", + help="Meetings will be created with these reminders by default.", + ) + booking_count = fields.Integer(compute="_compute_booking_count") + categ_ids = fields.Many2many( + string="Default tags", + comodel_name="calendar.event.type", + help="Meetings will be created with these tags by default.", + ) + combination_assignment = fields.Selection( + [ + ("sorted", "Sorted: pick the first one that is free"), + ("random", "Randomly: order is not important"), + ], + required=True, + default="random", + help=( + "Choose how to auto-assign resource combinations. " + "It has no effect if assigned manually." + ), + ) + combination_rel_ids = fields.One2many( + comodel_name="resource.booking.type.combination.rel", + inverse_name="type_id", + string="Available resource combinations", + copy=True, + help="Resource combinations available for this type of bookings.", + ) + company_id = fields.Many2one( + comodel_name="res.company", + default=lambda self: self.env.company, + index=True, + readonly=False, + store=True, + string="Company", + help="Company where this booking type is available.", + ) + duration = fields.Float( + required=True, + default=0.5, # 30 minutes + help=("Booking default duration."), + ) + slot_duration = fields.Float( + required=True, + default=0.5, # 30 minutes + help=("Interval offered to start each resource booking."), + ) + location = fields.Char() + videocall_location = fields.Char(string="Meeting URL") + modifications_deadline = fields.Float( + required=True, + default=24, + help=( + "When this deadline has been exceeded, if a booking was not yet " + "confirmed, it will be canceled automatically. Also, only booking " + "managers will be able to unschedule or reschedule them. " + "The value is expressed in hours." + ), + ) + name = fields.Char(index=True, translate=True, required=True) + booking_ids = fields.One2many( + comodel_name="resource.booking", + inverse_name="type_id", + string="Bookings", + help="Bookings available for this type", + ) + resource_calendar_id = fields.Many2one( + comodel_name="resource.calendar", + default=lambda self: self._default_resource_calendar(), + index=True, + required=True, + ondelete="restrict", + string="Availability Calendar", + help="Restrict bookings to this schedule.", + ) + requester_advice = fields.Text( + translate=True, + help=( + "Text that will appear by default in portal invitation emails " + "and in calendar views for scheduling." + ), + ) + + @api.model + def _default_resource_calendar(self): + return self.env.company.resource_calendar_id + + @api.depends("booking_ids") + def _compute_booking_count(self): + data = self.env["resource.booking"].read_group( + [("type_id", "in", self.ids)], ["type_id"], ["type_id"] + ) + mapping = {x["type_id"][0]: x["type_id_count"] for x in data} + for one in self: + one.booking_count = mapping.get(one.id, 0) + + @api.constrains("booking_ids", "resource_calendar_id", "combination_rel_ids") + def _check_bookings_scheduling(self): + """Scheduled bookings must have no conflicts.""" + bookings = self.mapped("booking_ids") + return bookings._check_scheduling() + + def _get_combinations_priorized(self): + """Gets all combinations sorted by the chosen assignment method.""" + if not self.combination_assignment: + return self.combination_rel_ids.mapped("combination_id") + keys = {"sorted": "sequence", "random": lambda *a: random()} + rels = self.combination_rel_ids.sorted(keys[self.combination_assignment]) + combinations = rels.mapped("combination_id") + return combinations + + def action_open_bookings(self): + DurationParser = self.env["ir.qweb.field.duration"] + return { + "context": dict( + self.env.context, + default_alarm_ids=[(6, 0, self.alarm_ids.ids)], + default_description=self.requester_advice, + default_duration=self.duration, + default_type_id=self.id, + # Context used by web_calendar_slot_duration module + calendar_slot_duration=DurationParser.value_to_html( + self.slot_duration, + { + "unit": "hour", + "digital": True, + }, + ), + ), + "domain": [("type_id", "=", self.id)], + "name": _("Bookings"), + "res_model": "resource.booking", + "type": "ir.actions.act_window", + "view_mode": "calendar,tree,form", + } diff --git a/resource_booking/models/resource_booking_type_combination_rel.py b/resource_booking/models/resource_booking_type_combination_rel.py new file mode 100644 index 00000000..53ace59e --- /dev/null +++ b/resource_booking/models/resource_booking_type_combination_rel.py @@ -0,0 +1,28 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResourceBookingCombinationRel(models.Model): + _name = "resource.booking.type.combination.rel" + _description = "Resource booking type relation with combinations" + _order = "sequence" + _rec_name = "combination_id" + + sequence = fields.Integer(index=True, required=True, default=100) + combination_id = fields.Many2one( + comodel_name="resource.booking.combination", + string="Combination", + index=True, + required=True, + ondelete="cascade", + ) + type_id = fields.Many2one( + comodel_name="resource.booking.type", + string="Type", + index=True, + required=True, + ondelete="cascade", + ) + type_name = fields.Char(related="type_id.name") diff --git a/resource_booking/models/resource_calendar.py b/resource_booking/models/resource_calendar.py new file mode 100644 index 00000000..7ab270f0 --- /dev/null +++ b/resource_booking/models/resource_calendar.py @@ -0,0 +1,116 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2022 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from pytz import UTC + +from odoo import api, fields, models + +from odoo.addons.resource.models.resource import Intervals + + +class Busy(Exception): + pass # This is not a real exception, just a helper + + +class ResourceCalendar(models.Model): + _inherit = "resource.calendar" + + @api.constrains("attendance_ids", "global_leave_ids", "leave_ids", "tz") + def _check_bookings_scheduling(self): + """Scheduled bookings must have no conflicts.""" + bookings = self.env["resource.booking"].search( + [ + ("state", "=", "confirmed"), + ("stop", ">=", fields.Datetime.now()), + "|", + ("combination_id.forced_calendar_id", "in", self.ids), + ("combination_id.resource_ids.calendar_id", "in", self.ids), + ] + ) + return bookings._check_scheduling() + + @api.model + def _calendar_event_busy_intervals( + self, start_dt, end_dt, resource, analyzed_booking_id + ): + """Get busy meeting intervals.""" + assert start_dt.tzinfo + assert end_dt.tzinfo + start_dt, end_dt = ( + fields.Datetime.to_string(dt.astimezone(UTC)) for dt in (start_dt, end_dt) + ) + intervals = [] + resource_user = ( + resource.resource_type == "user" + and resource.user_id.active + and resource.user_id + ) + # We want to avoid unnecessary queries, which can be quite unperformant when + # there are lots of recurrent events. + if not resource and not resource_user: + return Intervals(intervals) + # Simple domain to get all possibly conflicting events in a single + # query; this reduces DB calls and helps the underlying recurring + # system (in calendar.event) to work smoothly + domain = [("start", "<=", end_dt), ("stop", ">=", start_dt)] + # Anyway up to this version, is more performant to restrict as much as possible + # the events to avoid recurrent events. + # TODO: in v14 we should test which approach remains the most performant + if resource_user: + domain += [("partner_ids", "=", resource_user.partner_id.id)] + all_events = ( + self.env["calendar.event"].with_context(active_test=True).search(domain) + ) + for event in all_events: + # Is the event the same one we're currently checking? + if event.resource_booking_ids.id == analyzed_booking_id: + continue + try: + # Is the event not booking our resource? + if resource & event.mapped( + "resource_booking_ids.combination_id.resource_ids" + ): + raise Busy + # Special cases when the booked resource is a person + if resource_user: + # Is it a busy event belonging to the resource? + if event.user_id == resource_user and event.show_as == "busy": + raise Busy + # ... or is he invited to this event? + for attendee in event.attendee_ids: + if ( + attendee.partner_id == resource_user.partner_id + and attendee.state != "declined" + ): + raise Busy + except Busy: + # Add the matched event as a busy interval + intervals.append( + ( + fields.Datetime.context_timestamp( + event, fields.Datetime.to_datetime(event.start) + ), + fields.Datetime.context_timestamp( + event, fields.Datetime.to_datetime(event.stop) + ), + self.env["resource.calendar.leaves"], + ) + ) + return Intervals(intervals) + + def _leave_intervals_batch( + self, start_dt, end_dt, resources=None, domain=None, tz=None + ): + """Count busy meetings as leaves if required by context.""" + result = super()._leave_intervals_batch(start_dt, end_dt, resources, domain, tz) + if self.env.context.get("analyzing_booking"): + for resource_id in result: + # TODO Make this work in batch too + result[resource_id] |= self._calendar_event_busy_intervals( + start_dt, + end_dt, + self.env["resource.resource"].browse(resource_id), + self.env.context["analyzing_booking"], + ) + return result diff --git a/resource_booking/models/resource_resource.py b/resource_booking/models/resource_resource.py new file mode 100644 index 00000000..3f81d356 --- /dev/null +++ b/resource_booking/models/resource_resource.py @@ -0,0 +1,35 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models + +from .resource_booking import _availability_is_fitting + + +class ResourceResource(models.Model): + _inherit = "resource.resource" + + @api.constrains("calendar_id", "resource_type", "tz", "user_id") + def _check_bookings_scheduling(self): + """Scheduled bookings must have no conflicts.""" + bookings = self.env["resource.booking"].search( + [("combination_id.resource_ids", "in", self.ids)] + ) + return bookings._check_scheduling() + + def is_available(self, start_dt, end_dt, domain=None, tz=None): + """Convenience method to check whether a resource is available within a + time span. + """ + self.ensure_one() + # the `analyzing_booking` context needs to be added here, or bookings + # are not marked as busy. Because we do not actually have a booking_id + # available here, we set the value to -1. + result = self.calendar_id.with_context( + analyzing_booking=-1 + )._work_intervals_batch( + start_dt, end_dt, resources=[self], domain=domain, tz=tz + )[ + self.id + ] + return _availability_is_fitting(result, start_dt, end_dt) diff --git a/resource_booking/readme/CONFIGURE.rst b/resource_booking/readme/CONFIGURE.rst new file mode 100644 index 00000000..d558f06c --- /dev/null +++ b/resource_booking/readme/CONFIGURE.rst @@ -0,0 +1,34 @@ +To let some backend user to book resources: + +#. Go to *Settings > Users & Companies > Users*. +#. Pick or create one. +#. Assign *Resource Booking > User*. + +To let some backend user to configure types and combinations, and to be able to +modify overdue bookings: + +#. Go to *Settings > Users & Companies > Users*. +#. Pick or create one. +#. Assign *Resource Booking > Manager*. + +To configure one booking type: + +#. Go to *Resource Bookings > Types*. +#. Create one. +#. Give it a *name*. +#. Set the *Duration*, to know the time assigned to each calendar slot. It will + also be the default duration for each booking, although that can be changed + later if necessary. +#. Set the *Modifications Deadline*, to forbid non-managers to alter dates of + a booking when it's too late. +#. Choose one *Availability Calendar*. No bookings will exist outside of it. +#. Under *Meeting defaults*, you will be able to fill some values that will + be used by default on calendar meetings. These will appear in the global + calendar when some booking is reserved. +#. Choose some *Available resource combinations*. All combinations in the same + line must be free to be booked together; otherwise the booking will not be + able to be scheduled. You can sort them. +#. Pick up one *Combination Assignment*. If you choose *Sorted*, then the order + of the combinations you chose will indicate the one that is selected first. + Of course, it must be free to be selected. +#. Save. diff --git a/resource_booking/readme/CONTRIBUTORS.rst b/resource_booking/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..f7145432 --- /dev/null +++ b/resource_booking/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Jairo Llopis (https://www.tecnativa.com/) +* Henrik Norlin (https://ows.cloud) diff --git a/resource_booking/readme/DESCRIPTION.rst b/resource_booking/readme/DESCRIPTION.rst new file mode 100644 index 00000000..1ac3b12a --- /dev/null +++ b/resource_booking/readme/DESCRIPTION.rst @@ -0,0 +1,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. diff --git a/resource_booking/readme/INSTALL.rst b/resource_booking/readme/INSTALL.rst new file mode 100644 index 00000000..699186cb --- /dev/null +++ b/resource_booking/readme/INSTALL.rst @@ -0,0 +1,13 @@ +To install this module, you need to install these dependencies: + +#. `freezegun `__ +#. `web_calendar_slot_duration `__ + +When someone is a manager, he will have access to *Resource Bookings > +Configuration*, where he will be able to configure resources, leaves and +schedules. This menu is just provided as a shortcut. However, if you want to +manage that stuff more comfortably: + +* To manage human resources, install `hr `__. +* To manage their leaves, install `hr_holidays `__. +* To manage work centers, install `mrp `__. diff --git a/resource_booking/readme/ROADMAP.rst b/resource_booking/readme/ROADMAP.rst new file mode 100644 index 00000000..ef4d9779 --- /dev/null +++ b/resource_booking/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +* Allow combination auto-assignment based on least used combination. +* Allow customer to choose combination. +* Some error messages would be a bit more helpful if they specify the schedule + impossibility reason, but that should be done without affecting performance. +* Optimize ``_calendar_event_busy_intervals()`` to make it work in batch. diff --git a/resource_booking/readme/USAGE.rst b/resource_booking/readme/USAGE.rst new file mode 100644 index 00000000..aa7c6625 --- /dev/null +++ b/resource_booking/readme/USAGE.rst @@ -0,0 +1,40 @@ +This module installs a new app, "Resource bookings". + +Bookings may involve you: + +* Maybe because you requested to book something. +* Maybe because you are one of the booked resources, if a booking represents + some kind of appointment. + +To see which bookings involve you: + +#. Go to *Resource Bookings > Bookings*. +#. You can switch to the list view if you need to see also the pending ones. +#. You can remove the "Involving me" filter if you want to see others' bookings. + +To book some resources: + +#. Go to *Resource Bookings > Types*. +#. Pick the type of booking you want. +#. Click on *Booking Count*. +#. Click on a free slot. +#. Fill the *Requester*, which may or not be yourself. +#. Uncheck *Auto assign* and pick one *Resources combination*, in case the one + assigned automatically isn't the one you want. + +To invite someone to book a resource combination from the portal: + +#. Go to *Resource Bookings > Types*. +#. Pick the type of booking you want. +#. Click on *Booking Count*. +#. Click on the list view icon. +#. Click on *Create*. +#. Fill the *Requester*. +#. Uncheck *Auto assign* and pick one *Resources combination*, if you want that + the requester is assigned to that combination. Otherwise, leave it empty, + and some free combination will be assigned automatically when the requester + picks a free slot. +#. Choose the *duration*, in case it is different from the one specified in the + resource booking type. +#. Click on *Share > Send*. +#. The requester will receive an email to select a calendar slot from his portal. diff --git a/resource_booking/security/ir.model.access.csv b/resource_booking/security/ir.model.access.csv new file mode 100644 index 00000000..3f55dc84 --- /dev/null +++ b/resource_booking/security/ir.model.access.csv @@ -0,0 +1,11 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +resource_booking_combination_user,Permission to read resource booking combinations,model_resource_booking_combination,group_user,1,0,0,0 +resource_booking_combination_manager,Permission to write resource booking combinations,model_resource_booking_combination,group_manager,1,1,1,1 +resource_booking_type_user,Permission to read resource booking types,model_resource_booking_type,group_user,1,0,0,0 +resource_booking_type_manager,Permission to write resource booking types,model_resource_booking_type,group_manager,1,1,1,1 +resource_booking_portal,Resource bookings for portal,model_resource_booking,base.group_portal,1,0,0,0 +resource_booking_user,Resource bookings for users,model_resource_booking,group_user,1,1,1,0 +resource_booking_manager,Resource bookings for managers,model_resource_booking,group_manager,1,1,1,1 +resource_resource_manager,Permission to write resources,resource.model_resource_resource,group_manager,1,1,1,1 +resource_booking_type_combination_rel_user,Permission to read resource booking type combination relations for users,model_resource_booking_type_combination_rel,group_user,1,0,0,0 +resource_booking_type_combination_rel_manager,Permission to read resource booking type combination relations for managers,model_resource_booking_type_combination_rel,group_manager,1,1,1,1 diff --git a/resource_booking/security/resource_booking_security.xml b/resource_booking/security/resource_booking_security.xml new file mode 100644 index 00000000..761db05d --- /dev/null +++ b/resource_booking/security/resource_booking_security.xml @@ -0,0 +1,68 @@ + + + + + Resource Booking + + + User + + Users allowed to book resources + + + Manager + + + Users allowed to manage resource booking configurations. + + + + + Resource booking type multi company rule + + + ['|', ('company_id','=',False), ('company_id','in',company_ids)] + + + + Resource booking multi company rule + + + ['|', ('type_id.company_id', '=', False), ('type_id.company_id', 'in', company_ids)] + + + + Resource booking portal rule + + + ['|', ('partner_ids', 'child_of', user.partner_id.ids), ('message_partner_ids', 'child_of', user.partner_id.ids)] + + + Resource booking user rule + + + ['|', '|', ('partner_ids', 'child_of', user.partner_id.ids), ('message_partner_ids', 'child_of', user.partner_id.ids), ('combination_id.resource_ids.user_id', 'in', user.ids)] + + + Resource booking manager rule + + + [(1, '=', 1)] + + + diff --git a/resource_booking/static/description/icon.png b/resource_booking/static/description/icon.png new file mode 100644 index 00000000..f27a772e Binary files /dev/null and b/resource_booking/static/description/icon.png differ diff --git a/resource_booking/static/description/icon.svg b/resource_booking/static/description/icon.svg new file mode 100644 index 00000000..f0361814 --- /dev/null +++ b/resource_booking/static/description/icon.svg @@ -0,0 +1,22828 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resource_booking/static/description/index.html b/resource_booking/static/description/index.html new file mode 100644 index 00000000..82f60f2e --- /dev/null +++ b/resource_booking/static/description/index.html @@ -0,0 +1,557 @@ + + + + + + +Resource booking + + + +
+

Resource booking

+ + +

Production/Stable License: AGPL-3 OCA/calendar Translate me on Weblate Try me on Runboat

+

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.
  • +
+

Table of contents

+ +
+

Installation

+

To install this module, you need to install these dependencies:

+
    +
  1. freezegun
  2. +
  3. web_calendar_slot_duration
  4. +
+

When someone is a manager, he will have access to Resource Bookings > +Configuration, where he will be able to configure resources, leaves and +schedules. This menu is just provided as a shortcut. However, if you want to +manage that stuff more comfortably:

+
    +
  • To manage human resources, install hr.
  • +
  • To manage their leaves, install hr_holidays.
  • +
  • To manage work centers, install mrp.
  • +
+
+
+

Configuration

+

To let some backend user to book resources:

+
    +
  1. Go to Settings > Users & Companies > Users.
  2. +
  3. Pick or create one.
  4. +
  5. Assign Resource Booking > User.
  6. +
+

To let some backend user to configure types and combinations, and to be able to +modify overdue bookings:

+
    +
  1. Go to Settings > Users & Companies > Users.
  2. +
  3. Pick or create one.
  4. +
  5. Assign Resource Booking > Manager.
  6. +
+

To configure one booking type:

+
    +
  1. Go to Resource Bookings > Types.
  2. +
  3. Create one.
  4. +
  5. Give it a name.
  6. +
  7. Set the Duration, to know the time assigned to each calendar slot. It will +also be the default duration for each booking, although that can be changed +later if necessary.
  8. +
  9. Set the Modifications Deadline, to forbid non-managers to alter dates of +a booking when it’s too late.
  10. +
  11. Choose one Availability Calendar. No bookings will exist outside of it.
  12. +
  13. Under Meeting defaults, you will be able to fill some values that will +be used by default on calendar meetings. These will appear in the global +calendar when some booking is reserved.
  14. +
  15. Choose some Available resource combinations. All combinations in the same +line must be free to be booked together; otherwise the booking will not be +able to be scheduled. You can sort them.
  16. +
  17. Pick up one Combination Assignment. If you choose Sorted, then the order +of the combinations you chose will indicate the one that is selected first. +Of course, it must be free to be selected.
  18. +
  19. Save.
  20. +
+
+
+

Usage

+

This module installs a new app, “Resource bookings”.

+

Bookings may involve you:

+
    +
  • Maybe because you requested to book something.
  • +
  • Maybe because you are one of the booked resources, if a booking represents +some kind of appointment.
  • +
+

To see which bookings involve you:

+
    +
  1. Go to Resource Bookings > Bookings.
  2. +
  3. You can switch to the list view if you need to see also the pending ones.
  4. +
  5. You can remove the “Involving me” filter if you want to see others’ bookings.
  6. +
+

To book some resources:

+
    +
  1. Go to Resource Bookings > Types.
  2. +
  3. Pick the type of booking you want.
  4. +
  5. Click on Booking Count.
  6. +
  7. Click on a free slot.
  8. +
  9. Fill the Requester, which may or not be yourself.
  10. +
  11. Uncheck Auto assign and pick one Resources combination, in case the one +assigned automatically isn’t the one you want.
  12. +
+

To invite someone to book a resource combination from the portal:

+
    +
  1. Go to Resource Bookings > Types.
  2. +
  3. Pick the type of booking you want.
  4. +
  5. Click on Booking Count.
  6. +
  7. Click on the list view icon.
  8. +
  9. Click on Create.
  10. +
  11. Fill the Requester.
  12. +
  13. Uncheck Auto assign and pick one Resources combination, if you want that +the requester is assigned to that combination. Otherwise, leave it empty, +and some free combination will be assigned automatically when the requester +picks a free slot.
  14. +
  15. Choose the duration, in case it is different from the one specified in the +resource booking type.
  16. +
  17. Click on Share > Send.
  18. +
  19. The requester will receive an email to select a calendar slot from his portal.
  20. +
+
+
+

Known issues / Roadmap

+
    +
  • Allow combination auto-assignment based on least used combination.
  • +
  • Allow customer to choose combination.
  • +
  • Some error messages would be a bit more helpful if they specify the schedule +impossibility reason, but that should be done without affecting performance.
  • +
  • Optimize _calendar_event_busy_intervals() to make it work in batch.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

Current maintainers:

+

pedrobaeza ows-cloud

+

This module is part of the OCA/calendar project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/resource_booking/static/src/js/booking_portal.js b/resource_booking/static/src/js/booking_portal.js new file mode 100644 index 00000000..31213f9b --- /dev/null +++ b/resource_booking/static/src/js/booking_portal.js @@ -0,0 +1,13 @@ +/** @odoo-module */ + +import publicWidget from "web.public.widget"; +import "portal.portal"; // Force dependencies + +publicWidget.registry.PortalHomeCounters.include({ + /** + * @override + */ + _getCountersAlwaysDisplayed() { + return this._super(...arguments).concat(["booking_count"]); + }, +}); diff --git a/resource_booking/static/src/js/tours/resource_booking_tour.js b/resource_booking/static/src/js/tours/resource_booking_tour.js new file mode 100644 index 00000000..972c54a9 --- /dev/null +++ b/resource_booking/static/src/js/tours/resource_booking_tour.js @@ -0,0 +1,42 @@ +odoo.define("resource_booking.tour", function (require) { + var tour = require("web_tour.tour"); + + tour.register( + "resource_booking_ptl_tour", + { + test: true, + url: "/my", + }, + [ + { + content: "Go /my/bookings url", + trigger: 'a[href*="/my/bookings"]', + }, + { + content: "There are currently no bookings for your account.", + trigger: "p", + }, + ] + ); + tour.register( + "resource_booking_ptl2_tour", + { + test: true, + url: "/my", + }, + [ + { + content: "Go /my/bookings url", + trigger: 'a[href*="/my/bookings"]', + }, + { + content: "Go to Booking item", + trigger: ".tr_resource_booking_link:eq(0)", + }, + { + content: "Schedule button", + trigger: ".badge:contains('Pending')", + }, + ] + ); +}); diff --git a/resource_booking/static/src/scss/portal.scss b/resource_booking/static/src/scss/portal.scss new file mode 100644 index 00000000..c7373b0b --- /dev/null +++ b/resource_booking/static/src/scss/portal.scss @@ -0,0 +1,12 @@ +/* Copyright 2021 Tecnativa - Jairo Llopis + * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ + +// Too many available slots? Allow scrolling +.slots-dropdown { + max-height: 40vh; + overflow: auto; +} +// Show full dropdown if opened at the bottom +.resource_booking_portal_schedule { + overflow: initial; +} diff --git a/resource_booking/templates/portal.xml b/resource_booking/templates/portal.xml new file mode 100644 index 00000000..7c6324d6 --- /dev/null +++ b/resource_booking/templates/portal.xml @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + diff --git a/resource_booking/tests/__init__.py b/resource_booking/tests/__init__.py new file mode 100644 index 00000000..60b403f7 --- /dev/null +++ b/resource_booking/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_backend +from . import test_portal diff --git a/resource_booking/tests/common.py b/resource_booking/tests/common.py new file mode 100644 index 00000000..3a57b86c --- /dev/null +++ b/resource_booking/tests/common.py @@ -0,0 +1,137 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + + +def create_test_data(obj): + """Create test data for a case.""" + obj.env = obj.env( + context=dict( + obj.env.context, tracking_disable=True, no_reset_password=True, tz="UTC" + ) + ) + # Create one resource.calendar available on Mondays, another one on + # Tuesdays, and another one on Mondays and Tuesdays; in that order. + # Also create an all-day calendar for Saturday and Sunday. + attendances = [ + ( + 0, + 0, + { + "name": "Mondays", + "dayofweek": "0", + "hour_from": 8, + "hour_to": 17, + "day_period": "morning", + }, + ), + ( + 0, + 0, + { + "name": "Tuesdays", + "dayofweek": "1", + "hour_from": 8, + "hour_to": 17, + "day_period": "morning", + }, + ), + ( + 0, + 0, + { + "name": "Fridays", + "dayofweek": "4", + "hour_from": 0, + "hour_to": 24, + "day_period": "morning", + }, + ), + ( + 0, + 0, + { + "name": "Saturdays", + "dayofweek": "5", + "hour_from": 0, + "hour_to": 24, + "day_period": "morning", + }, + ), + ( + 0, + 0, + { + "name": "Sunday", + "dayofweek": "6", + "hour_from": 0, + "hour_to": 24, + "day_period": "morning", + }, + ), + ] + obj.r_calendars = obj.env["resource.calendar"].create( + [ + {"name": "Mon", "attendance_ids": [attendances[0]], "tz": "UTC"}, + {"name": "Tue", "attendance_ids": [attendances[1]], "tz": "UTC"}, + {"name": "MonTue", "attendance_ids": attendances[0:2], "tz": "UTC"}, + {"name": "FriSun", "attendance_ids": attendances[2:], "tz": "UTC"}, + ] + ) + # Create one material resource for each of those calendars; same order + obj.r_materials = obj.env["resource.resource"].create( + [ + { + "name": "Material resource for %s" % cal.name, + "calendar_id": cal.id, + "resource_type": "material", + "tz": "UTC", + } + for cal in obj.r_calendars + ] + ) + # Create one human resource for each of those calendars; same order + obj.users = obj.env["res.users"].create( + [ + { + "email": "user_%d@example.com" % num, + "login": "user_%d" % num, + "name": "User %d" % num, + } + for num, _ in enumerate(obj.r_calendars) + ] + ) + obj.r_users = obj.env["resource.resource"].create( + [ + { + "calendar_id": cal.id, + "name": "User %s" % user.name, + "resource_type": "user", + "tz": "UTC", + "user_id": user.id, + } + for (user, cal) in zip(obj.users, obj.r_calendars) + ] + ) + # Create one RBC for each of those calendars, which includes the + # corresponding material and human resources simultaneously; same order + obj.rbcs = obj.env["resource.booking.combination"].create( + [ + {"resource_ids": [(6, 0, [user.id, material.id])]} + for (user, material) in zip(obj.r_users, obj.r_materials) + ] + ) + # Create one RBT that includes all RBCs as available combinations + obj.rbt = obj.env["resource.booking.type"].create( + { + "name": "Test resource booking type", + "combination_rel_ids": [ + (0, 0, {"sequence": num, "combination_id": rbc.id}) + for num, rbc in enumerate(obj.rbcs) + ], + "resource_calendar_id": obj.r_calendars[2].id, + "location": "Main office", + "videocall_location": "Videocall Main office", + } + ) + # Create some partner + obj.partner = obj.env["res.partner"].create({"name": "some customer"}) diff --git a/resource_booking/tests/test_backend.py b/resource_booking/tests/test_backend.py new file mode 100644 index 00000000..595db4aa --- /dev/null +++ b/resource_booking/tests/test_backend.py @@ -0,0 +1,1044 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2022 Tecnativa - Pedro M. Baeza +# Copyright 2024 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from datetime import date, datetime +from unittest.mock import patch + +from dateutil.relativedelta import relativedelta +from freezegun import freeze_time +from pytz import utc + +from odoo import fields +from odoo.exceptions import ValidationError +from odoo.tests.common import Form, TransactionCase, new_test_user, users + +from odoo.addons.resource.models.resource import Intervals +from odoo.addons.resource_booking.models.resource_booking import ( + _availability_is_fitting, +) + +from .common import create_test_data + +_2dt = fields.Datetime.to_datetime + + +@freeze_time("2021-02-26 09:00:00", tick=True) # Last Friday of February +class BackendCase(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + create_test_data(cls) + cls.plain_user = new_test_user(cls.env, login="plain", groups="base.group_user") + + @users("plain") + def test_plain_user_calendar_event(self): + """Check that a simple user is able to handle manual calendar events.""" + event = self.env["calendar.event"].create( + { + "name": "Test calendar event", + "start": "2023-01-01 00:00:00", + "stop": "2023-01-01 01:00:00", + } + ) + event.write({"partner_ids": [(4, self.partner.id)]}) + event.unlink() + + def test_scheduling_conflict_constraints(self): + # Combination is available on Mondays and Tuesdays + rbc_montue = self.rbcs[2] + # Type is available on Mondays + cal_mon = self.r_calendars[0] + self.rbt.resource_calendar_id = cal_mon + # Booking cannot be placed next Tuesday + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-02 08:00:00", + "type_id": self.rbt.id, + "combination_id": rbc_montue.id, + "combination_auto_assign": False, + } + ) + # Booking cannot be placed next Monday before 8:00 + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-02 07:45:00", + "type_id": self.rbt.id, + "combination_id": rbc_montue.id, + "combination_auto_assign": False, + } + ) + # Booking cannot be placed next Monday after 17:00 + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-02 16:45:00", + "type_id": self.rbt.id, + "combination_id": rbc_montue.id, + "combination_auto_assign": False, + } + ) + # Booking can be placed next Monday + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:00:00", + "type_id": self.rbt.id, + "combination_id": rbc_montue.id, + "combination_auto_assign": False, + } + ) + # Another event cannot collide with the same RBC + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:29:59", + "type_id": self.rbt.id, + "combination_id": rbc_montue.id, + "combination_auto_assign": False, + } + ) + # Another event can collide with another RBC + rbc_mon = self.rbcs[0] + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:00:00", + "type_id": self.rbt.id, + "combination_id": rbc_mon.id, + "combination_auto_assign": False, + } + ) + + def test_scheduling_constraints_span_two_days(self): + # Booking can span across two calendar days. + cal_frisun = self.r_calendars[3] + rbc_frisun = self.rbcs[3] + self.rbt.resource_calendar_id = cal_frisun + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-06 23:00:00", + "duration": 2, + "type_id": self.rbt.id, + "combination_id": rbc_frisun.id, + "combination_auto_assign": False, + } + ) + # Booking cannot overlap. + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-06 22:00:00", + "duration": 4, + "type_id": self.rbt.id, + "combination_id": rbc_frisun.id, + "combination_auto_assign": False, + } + ) + # Test a case where there is an overlap, but the conflict happens at + # 00:00 exactly. + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-14 00:00:00", + "duration": 1, + "type_id": self.rbt.id, + "combination_id": rbc_frisun.id, + "combination_auto_assign": False, + } + ) + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-13 23:00:00", + "duration": 4, + "type_id": self.rbt.id, + "combination_id": rbc_frisun.id, + "combination_auto_assign": False, + } + ) + # If there are too many minutes between the end and start of the two + # dates, the booking cannot be contiguous. + cal_frisun.attendance_ids.write({"hour_to": 23.96}) # 23:58 + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-20 23:00:00", + "duration": 2, + "type_id": self.rbt.id, + "combination_id": rbc_frisun.id, + "combination_auto_assign": False, + } + ) + + def test_scheduling_constraints_span_three_days(self): + # Booking can span across two calendar days. + cal_frisun = self.r_calendars[3] + rbc_frisun = self.rbcs[3] + self.rbt.resource_calendar_id = cal_frisun + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-05 23:00:00", + "duration": 24 * 2, + "type_id": self.rbt.id, + "combination_id": rbc_frisun.id, + "combination_auto_assign": False, + } + ) + + def test_availability_is_fitting_malformed_date_skip(self): + """Test a case for malformed data where a date is skipped in the + available_intervals list of tuples. + """ + recset = self.env["resource.booking"] + tuples = [ + ( + datetime(2021, 3, 1, 18, 0), + datetime(2021, 3, 1, 23, 59, 59, 999999), + recset, + ), + ( + datetime(2021, 3, 2, 0, 0), + datetime(2021, 3, 2, 23, 59, 59, 999999), + recset, + ), + (datetime(2021, 3, 3, 0, 0), datetime(2021, 3, 3, 18, 0), recset), + ] + available_intervals = Intervals(tuples) + self.assertTrue( + _availability_is_fitting( + available_intervals, + datetime(2021, 3, 1, 18, 0), + datetime(2021, 3, 3, 18, 0), + ) + ) + # Skip a day by removing it. + tuples.pop(1) + available_intervals = Intervals(tuples) + self.assertFalse( + _availability_is_fitting( + available_intervals, + datetime(2021, 3, 1, 18, 0), + datetime(2021, 3, 3, 18, 0), + ) + ) + + def test_rbc_forced_calendar(self): + # Type is available on Mondays + cal_mon = self.r_calendars[0] + self.rbt.resource_calendar_id = cal_mon + # Cannot book an combination with resources that only work on Tuesdays + rbc_tue = self.rbcs[1] + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:00:00", + "type_id": self.rbt.id, + "combination_id": rbc_tue.id, + "combination_auto_assign": False, + } + ) + # However, if the combination is forced to Mondays, you can book it + rbc_tue.forced_calendar_id = cal_mon + rb = self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:00:00", + "type_id": self.rbt.id, + "combination_auto_assign": False, + "combination_id": rbc_tue.id, + } + ) + self.assertEqual(rb.combination_id, rbc_tue) + + def test_booking_from_calendar_view(self): + # The type is configured by default with bookings of 30 minutes + self.assertEqual(self.rbt.duration, 0.5) + # Change it to 45 minutes + self.rbt.duration = 0.75 + # Bookings smart button configures calendar with slots from slot duration field + button_context = self.rbt.action_open_bookings()["context"] + self.assertEqual(button_context["calendar_slot_duration"], "00:30:00") + self.assertEqual(button_context["default_duration"], 0.75) + # When you click & drag on calendar to create an event, it adds the + # start and duration as default; we imitate that here to book a meeting + # with 2 slots next monday + button_context["default_duration"] = 1.5 + booking_form = Form( + self.env["resource.booking"].with_context( + **button_context, + default_start="2021-03-01 08:00:00", + ) + ) + # This might seem redundant, but makes sure onchanges don't mess stuff + self.assertEqual(_2dt(booking_form.start), datetime(2021, 3, 1, 8)) + self.assertEqual(booking_form.duration, 1.5) + self.assertEqual(_2dt(booking_form.stop), datetime(2021, 3, 1, 9, 30)) + # If I change to next week's monday, then the stop date advances 1:30h + booking_form.start = datetime(2021, 3, 8, 8) + booking_form.partner_ids.add(self.partner) + self.assertEqual(_2dt(booking_form.start), datetime(2021, 3, 8, 8)) + self.assertEqual(booking_form.duration, 1.5) + self.assertEqual(_2dt(booking_form.stop), datetime(2021, 3, 8, 9, 30)) + # I can book it (which means type & combination were autofilled) + booking = booking_form.save() + self.assertTrue(booking.meeting_id) + self.assertEqual(booking.state, "scheduled") + + def test_dates_inverse(self): + """Start & stop fields are computed with inverse. Test their workflow.""" + # Set type to be available only on mondays + self.rbt.resource_calendar_id = self.r_calendars[0] + # Create a booking from scratch + booking_form = Form(self.env["resource.booking"]) + booking_form.type_id = self.rbt + booking_form.partner_ids.add(self.partner) + self.assertFalse(booking_form.start) + self.assertFalse(booking_form.stop) + self.assertFalse(booking_form.combination_id) + # I can save it without booking + booking = booking_form.save() + self.assertEqual(booking.state, "pending") + self.assertFalse(booking.meeting_id) + self.assertFalse(booking.start) + self.assertFalse(booking.stop) + self.assertFalse(booking.combination_id) + # I edit it again + with Form(booking) as booking_form: + # Start next Tuesday: updates stop; no combination available + booking_form.start = datetime(2021, 3, 2, 8) + self.assertEqual(_2dt(booking_form.stop), datetime(2021, 3, 2, 8, 30)) + self.assertFalse(booking_form.combination_id) + # Move to Monday: updates stop; found one combination available + booking_form.start = datetime(2021, 3, 1, 8) + self.assertEqual(_2dt(booking_form.stop), datetime(2021, 3, 1, 8, 30)) + self.assertTrue(booking_form.combination_id) + self.assertEqual(booking.state, "scheduled") + self.assertTrue(booking.meeting_id) + self.assertTrue(booking.start) + self.assertTrue(booking.stop) + self.assertTrue(booking.combination_id) + + def test_state(self): + # I create a pending booking + booking = self.env["resource.booking"].create( + {"type_id": self.rbt.id, "partner_ids": [(4, self.partner.id)]} + ) + # Without dates, it's pending + self.assertEqual(booking.state, "pending") + self.assertTrue(booking.active) + self.assertFalse(booking.meeting_id) + self.assertFalse(booking.start) + self.assertFalse(booking.stop) + self.assertFalse(booking.combination_id) + # With a linked meeting, it's scheduled + with Form(booking) as booking_form: + booking_form.start = datetime(2021, 3, 1, 8) + meeting = booking.meeting_id + self.assertEqual(booking.state, "scheduled") + self.assertTrue(booking.active) + self.assertTrue(meeting.exists()) + self.assertTrue(booking.start) + self.assertTrue(booking.stop) + self.assertTrue(booking.combination_id) + # When partner confirms attendance, it's confirmed + booker_attendance = meeting.attendee_ids.filtered( + lambda one: one.partner_id in booking.partner_ids + ) + self.assertTrue(booker_attendance) + booker_attendance.do_accept() + self.assertEqual(booking.state, "confirmed") + self.assertTrue(booking.active) + self.assertTrue(meeting.exists()) + self.assertTrue(booking.start) + self.assertTrue(booking.stop) + self.assertTrue(booking.combination_id) + # Without dates, it's pending again + booking.action_unschedule() + self.assertEqual(booking.state, "pending") + self.assertTrue(booking.active) + self.assertFalse(meeting.exists()) + self.assertFalse(booking.start) + self.assertFalse(booking.stop) + self.assertTrue(booking.combination_id) + # Archived and without dates, it's canceled + booking.action_cancel() + self.assertEqual(booking.state, "canceled") + self.assertFalse(booking.active) + self.assertFalse(meeting.exists()) + self.assertFalse(booking.start) + self.assertFalse(booking.stop) + self.assertTrue(booking.combination_id) + + def test_sorted_assignment(self): + """Set sorted assignment on RBT and test it works correctly.""" + rbc_mon, rbc_tue, rbc_montue, rbc_frisun = self.rbcs + with Form(self.rbt) as rbt_form: + rbt_form.combination_assignment = "sorted" + # Book next monday at 10:00 + rb1_form = Form(self.env["resource.booking"]) + rb1_form.type_id = self.rbt + rb1_form.partner_ids.add(self.partner) + rb1_form.start = datetime(2021, 3, 1, 10) + self.assertEqual(rb1_form.combination_id, rbc_mon) + rb1 = rb1_form.save() + self.assertEqual(rb1.combination_id, rbc_mon) + # Another booking, same time + rb2_form = Form(self.env["resource.booking"]) + rb2_form.type_id = self.rbt + rb2_form.partner_ids.add(self.partner) + rb2_form.start = datetime(2021, 3, 1, 10) + self.assertEqual(rb2_form.combination_id, rbc_montue) + rb2 = rb2_form.save() + self.assertEqual(rb2.combination_id, rbc_montue) + # I'm able to alter rb1 timing + with Form(rb1) as rb1_form: + rb1_form.start = datetime(2021, 3, 2, 10) + self.assertEqual(rb1_form.combination_id, rbc_tue) + self.assertEqual(rb1.combination_id, rbc_tue) + + def test_calendar_meeting_and_leave_combined(self): + """Resource not bookable on calendar leave.""" + cal_mon = self.r_calendars[0] + res_mon = self.r_users[0] + # Add leave next Monday for Mon resource + self.env["resource.calendar.leaves"].create( + { + "date_from": datetime(2021, 3, 1), + "date_to": datetime(2021, 3, 3), + "calendar_id": cal_mon.id, + "resource_id": res_mon.id, + } + ) + # Add meeting same day for all resources, so no combination is available + self.env["calendar.event"].create( + { + "start": datetime(2021, 3, 1, 8), + "stop": datetime(2021, 3, 1, 10, 30), + "name": "some meeting", + "partner_ids": [(6, 0, self.users.partner_id.ids)], + } + ) + # Check it's not bookable + rb_form = Form(self.env["resource.booking"]) + rb_form.type_id = self.rbt + rb_form.partner_ids.add(self.partner) + # No combination found + rb_form.start = datetime(2021, 3, 1, 10) + self.assertFalse(rb_form.combination_id) + # Combination found + rb_form.start = datetime(2021, 3, 8, 10) + self.assertTrue(rb_form.combination_id) + rb_form.save() + + def test_same_slot_twice_not_utc(self): + """Scheduling the same slot twice fails, when not in UTC.""" + for loop in range(2): + rb_f = Form(self.env["resource.booking"].with_context(tz="Europe/Madrid")) + rb_f.partner_ids.add(self.partner) + rb_f.type_id = self.rbt + rb_f.start = datetime(2021, 3, 1, 10) + rb_f.combination_auto_assign = False + rb_f.combination_id = self.rbcs[0] + # 1st one works + if loop == 0: + rb = rb_f.save() + self.assertEqual(rb.state, "scheduled") + else: + with self.assertRaises(ValidationError): + rb_f.save() + + def test_recurring_event(self): + """Recurrent events are considered.""" + # Everyone busy past and next Mondays with a recurring meeting + ce_f = Form(self.env["calendar.event"].with_context(default_mon=True)) + ce_f.name = "recurring event past monday" + for user in self.users: + ce_f.partner_ids.add(user.partner_id) + ce_f.start = datetime(2021, 2, 22, 8) + ce_f.duration = 1 + ce_f.recurrency = True + ce_f.interval = 1 + ce_f.rrule_type = "weekly" + ce_f.end_type = "count" + ce_f.count = 2 + ce_f.save() + # Cannot book next Monday at 8 + rb_f = Form(self.env["resource.booking"]) + rb_f.partner_ids.add(self.partner) + rb_f.type_id = self.rbt + # No RBC when starting + self.assertFalse(rb_f.combination_id) + # No RBC available next Monday at 8 + rb_f.start = datetime(2021, 3, 1, 8) + self.assertFalse(rb_f.combination_id) + # Everyone's free at 9 + rb_f.start = datetime(2021, 3, 1, 9) + self.assertTrue(rb_f.combination_id) + + def test_change_calendar_after_bookings_exist(self): + """Calendar changes can be done only if they introduce no conflicts.""" + rbc_mon = self.rbcs[0] + cal_mon = self.r_calendars[0] + # There's a booking for last monday + past_booking = self.env["resource.booking"].create( + { + "combination_id": rbc_mon.id, + "partner_ids": [(4, self.partner.id)], + "start": "2021-02-22 08:00:00", + "type_id": self.rbt.id, + } + ) + past_booking.action_confirm() + self.assertEqual(past_booking.duration, 0.5) + self.assertEqual(past_booking.state, "confirmed") + # There's another one for next monday, confirmed too + future_booking = self.env["resource.booking"].create( + { + "combination_id": rbc_mon.id, + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:00:00", + "type_id": self.rbt.id, + } + ) + future_booking.action_confirm() + self.assertEqual(future_booking.state, "confirmed") + # Now, it's impossible for me to change the resource calendar + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + with Form(cal_mon) as cal_mon_f: + with cal_mon_f.attendance_ids.edit(0) as att_mon_f: + att_mon_f.hour_from = 9 + # But let's unconfirm future boooking + future_booking.action_unschedule() + with Form(future_booking) as future_booking_f: + future_booking_f.start = "2021-03-01 08:00:00" + self.assertEqual(future_booking.state, "scheduled") + # Now I should be able to change the resource calendar + with Form(cal_mon) as cal_mon_f: + with cal_mon_f.attendance_ids.edit(0) as att_mon_f: + att_mon_f.hour_from = 9 + # However, now I shouldn't be able to confirm future booking + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + future_booking.action_confirm() + + def test_free_slots_with_different_type_and_booking_durations(self): + """Slot and booking duration are different, and all works.""" + # Type and calendar allow one slot each 30 minutes on Mondays and + # Tuesdays from 08:00 to 17:00 UTC. The booking will span for 3 slots. + rb = self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "type_id": self.rbt.id, + "duration": self.rbt.duration * 3, + } + ) + self.assertEqual(rb.duration, 1.5) + slots = rb._get_available_slots( + utc.localize(datetime(2021, 3, 2, 14, 15)), + utc.localize(datetime(2021, 3, 8, 10)), + ) + self.assertEqual( + slots, + { + # Thursday + date(2021, 3, 2): [ + # We start searching at 14:15, so first free slot will + # start at 14:30 + utc.localize(datetime(2021, 3, 2, 14, 30)), + utc.localize(datetime(2021, 3, 2, 15)), + # Booking duration is 1:30, and calendar ends at 17:00, so + # last slot starts at 15:30 + utc.localize(datetime(2021, 3, 2, 15, 30)), + ], + # Next Monday, because calendar only allows Mondays and Tuesdays + date(2021, 3, 8): [ + # Calendar starts at 8:00 + utc.localize(datetime(2021, 3, 8, 8)), + # We are searching until 10:00, so last free slot is at 8:30 + utc.localize(datetime(2021, 3, 8, 8, 30)), + ], + }, + ) + + def test_location(self): + """Location across records works as expected.""" + rbt2 = self.rbt.copy({"location": "Office 2"}) + rb_f = Form(self.env["resource.booking"]) + rb_f.partner_ids.add(self.partner) + rb_f.type_id = self.rbt + rb = rb_f.save() + # Pending booking inherits location from type + self.assertEqual(rb.state, "pending") + self.assertEqual(rb.location, "Main office") + # Booking can change location independently now + with Form(rb) as rb_f: + rb_f.location = "Office 3" + self.assertEqual(self.rbt.location, "Main office") + self.assertEqual(rb.location, "Office 3") + # Changing booking type changes location + with Form(rb) as rb_f: + rb_f.type_id = rbt2 + self.assertEqual(rb.location, "Office 2") + # Still can change it independently + with Form(rb) as rb_f: + rb_f.location = "Office 1" + self.assertEqual(rb.location, "Office 1") + self.assertEqual(rbt2.location, "Office 2") + # Schedule the booking, meeting inherits location from it + with Form(rb) as rb_f: + rb_f.start = "2021-03-01 08:00:00" + self.assertEqual(rb.state, "scheduled") + self.assertEqual(rb.location, "Office 1") + self.assertEqual(rb.meeting_id.location, "Office 1") + # Changing meeting location changes location of booking + with Form(rb.meeting_id) as meeting_f: + meeting_f.location = "Office 2" + self.assertEqual(rb.location, "Office 2") + self.assertEqual(rb.meeting_id.location, "Office 2") + # Changing booking location changes meeting location + with Form(rb) as rb_f: + rb_f.location = "Office 3" + self.assertEqual(rb.meeting_id.location, "Office 3") + self.assertEqual(rb.location, "Office 3") + # When unscheduled, it keeps location untouched + rb.action_unschedule() + self.assertFalse(rb.meeting_id) + self.assertEqual(rb.location, "Office 3") + + def test_videocall_location(self): + """Videocall location across records works as expected. + We need to set dummy urls to prevent the _set_videocall_location() method + of calendar from doing so.""" + rbt2 = self.rbt.copy({"videocall_location": "Videocall Office 2"}) + rb_f = Form(self.env["resource.booking"]) + rb_f.partner_ids.add(self.partner) + rb_f.type_id = self.rbt + rb = rb_f.save() + # Pending booking inherits videocall location from type + self.assertEqual(rb.state, "pending") + self.assertEqual(rb.videocall_location, "Videocall Main office") + # Booking can change videocall location independently now + with Form(rb) as rb_f: + rb_f.videocall_location = "Videocall Office 3" + self.assertEqual(self.rbt.videocall_location, "Videocall Main office") + self.assertEqual(rb.videocall_location, "Videocall Office 3") + # Changing booking type changes videocall location + with Form(rb) as rb_f: + rb_f.type_id = rbt2 + self.assertEqual(rb.videocall_location, "Videocall Office 2") + # Still can change it independently + with Form(rb) as rb_f: + rb_f.videocall_location = "https://Videocall Office 1" + self.assertEqual(rb.videocall_location, "https://Videocall Office 1") + self.assertEqual(rbt2.videocall_location, "Videocall Office 2") + # Schedule the booking, meeting inherits videocall location from it + with Form(rb) as rb_f: + rb_f.start = "2021-03-01 08:00:00" + self.assertEqual(rb.state, "scheduled") + self.assertEqual(rb.videocall_location, "https://Videocall Office 1") + self.assertEqual(rb.meeting_id.videocall_location, "https://Videocall Office 1") + with Form(rb.meeting_id) as meeting_f: + meeting_f.videocall_location = "https://Videocall Office 2" + self.assertEqual(rb.videocall_location, "https://Videocall Office 2") + self.assertEqual(rb.meeting_id.videocall_location, "https://Videocall Office 2") + # Changing booking videocall location changes meeting location + with Form(rb) as rb_f: + rb_f.videocall_location = "https://Videocall Office 3" + self.assertEqual(rb.videocall_location, "https://Videocall Office 3") + self.assertEqual(rb.meeting_id.videocall_location, "https://Videocall Office 3") + # When unscheduled, it keeps videocall location untouched + rb.action_unschedule() + self.assertFalse(rb.meeting_id) + self.assertEqual(rb.videocall_location, "https://Videocall Office 3") + + def test_organizer_sync(self): + """Resource booking and meeting organizers are properly synced.""" + rb = self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "type_id": self.rbt.id, + "start": "2021-03-01 08:00:00", + "duration": 1.5, + } + ) + self.assertEqual(rb.user_id, self.env.user) + self.assertEqual(rb.meeting_id.user_id, self.env.user) + rb.meeting_id.user_id = self.users[1] + self.assertEqual(rb.user_id, self.users[1]) + self.assertEqual(rb.meeting_id.user_id, self.users[1]) + + def test_resource_booking_display_name(self): + # Pending booking with no name + rb = self.env["resource.booking"].create( + {"partner_ids": [(4, self.partner.id)], "type_id": self.rbt.id} + ) + self.assertEqual(rb.display_name, "some customer - Test resource booking type") + self.assertEqual( + rb.with_context(using_portal=True).display_name, "# %d" % rb.id + ) + # Pending booking with name + rb.name = "changed" + self.assertEqual(rb.display_name, "changed") + self.assertEqual( + rb.with_context(using_portal=True).display_name, "# %d - changed" % rb.id + ) + # Scheduled booking with name + rb.start = "2021-03-01 08:00:00" + self.assertEqual(rb.display_name, "changed") + self.assertEqual( + rb.with_context(using_portal=True).display_name, "# %d - changed" % rb.id + ) + # Scheduled booking with no name + rb.name = False + self.assertEqual( + rb.display_name, + "some customer - Test resource booking type " + "- 03/01/2021 at (08:00:00 To 08:30:00) (UTC)", + ) + self.assertEqual( + rb.with_context(using_portal=True).display_name, "# %d" % rb.id + ) + + def test_attendee_autoassigned_not_autoconfirmed(self): + """Meeting attendees are not autoconfirmed when combination is autoassigned.""" + # Create an auto-assigned booking + rb = self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "type_id": self.rbt.id, + "start": "2021-03-01 08:00:00", + } + ) + # Get attendees that belong to the combination human resource + resource_partner = rb.combination_id.resource_ids.user_id.partner_id + resource_attendees = rb.meeting_id.attendee_ids.filtered( + lambda one: one.partner_id == resource_partner + ) + # Combination was auto-assigned, so resource attendees are not confirmed + self.assertEqual(resource_attendees.state, "needsAction") + + def test_attendee_not_autoassigned_autoconfirmed(self): + """Meeting attendees are auto-confirmed when assigned by hand.""" + # Create a booking with handpicked combination assignment + rb = self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "type_id": self.rbt.id, + "start": "2021-03-01 08:00:00", + "combination_auto_assign": False, + "combination_id": self.rbcs[0].id, + } + ) + # Get attendees that belong to the combination human resources + resource_partner = self.users[0].partner_id + resource_attendees = rb.meeting_id.attendee_ids.filtered( + lambda one: one.partner_id == resource_partner + ) + # Combination was handpicked, so resource attendees are auto-confirmed + self.assertEqual(resource_attendees.state, "accepted") + + def test_suggested_and_subscribed_recipients(self): + self.env = self.env(context=dict(self.env.context, tracking_disable=False)) + # Create a booking as a new user + rb_user = new_test_user( + self.env, login="rbu", groups="base.group_user,resource_booking.group_user" + ) + # Enable auto-subscription messaging + with patch.object(self.env.registry, "ready", True): + rb = ( + self.env["resource.booking"] + .with_user(rb_user) + .sudo() + .create( + { + "partner_ids": [(4, self.partner.id)], + "type_id": self.rbt.id, + "combination_auto_assign": False, + "combination_id": self.rbcs[0].id, + "user_id": self.users[1].id, + } + ) + ) + # Organizer, combination and creator must already be following + self.assertEqual( + rb.message_partner_ids, rb_user.partner_id | self.users[:2].partner_id + ) + # Requester and combination must be suggested + self.assertEqual( + rb._message_get_suggested_recipients(), + {rb.id: [(rb.partner_ids.id, "some customer", None, "Attendees")]}, + ) + + def test_creating_rbt_has_tags(self): + """Creating booking works if type has tags.""" + categ = self.env["calendar.event.type"].create({"name": "test tag"}) + self.rbt.categ_ids = categ + rb_f = Form(self.env["resource.booking"]) + rb_f.partner_ids.add(self.partner) + rb_f.type_id = self.rbt + rb = rb_f.save() + self.assertEqual(rb.categ_ids, categ) + + def test_event_show_as_free(self): + """Don't mind about event owner. + + Here I'll create 2 overlapping events. Since I create both, I'll be the + owner of both automatically. However, there are 2 RBC available (one is + me), so I still should be able to create 2 events. + """ + user = self.users[0] + rb_obj = self.env["resource.booking"].with_context(tracking_disable=True) + # I'm the last option + self.rbt.combination_assignment = "sorted" + self.rbt.combination_rel_ids[0].sequence = 10 + # Create one long event on Monday, where there are 2 RBC available (one is me) + rb_f = Form(rb_obj) + rb_f.type_id = self.rbt + rb_f.start = "2021-03-01 09:00:00" + rb_f.duration = 1 + rb_f.partner_ids.add(self.partner) + rb1 = rb_f.save() + # I'm not booked, so I'm free + self.assertEqual(rb1.combination_id, self.rbcs[2]) + self.assertNotIn(user.partner_id, rb1.meeting_id.partner_ids) + # Create another event within the previous one + rb_f = Form(rb_obj) + rb_f.type_id = self.rbt + rb_f.start = "2021-03-01 09:00:00" + rb_f.duration = 1.5 + rb_f.partner_ids.add(self.partner.copy()) + # Saving works because I'm free + rb2 = rb_f.save() + # I'm booked this time, so I'm busy + self.assertEqual(rb2.combination_id, self.rbcs[0]) + self.assertIn(user.partner_id, rb2.meeting_id.partner_ids) + # Thus, it will fail without available resources on a next one + rb_f = Form(rb_obj) + rb_f.type_id = self.rbt + rb_f.start = "2021-03-01 09:30:00" + rb_f.duration = 0.5 + rb_f.partner_ids.add(self.partner.copy()) + with self.assertRaises(AssertionError): + rb_f.save() + + def test_resource_is_available(self): + """If a resource is involved in a booking or is not active at any point + between two datetimes, then it is unavailable. + """ + rbc_montue = self.rbcs[2] + resource = rbc_montue.resource_ids[1] + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-01 08:00:00", + "type_id": self.rbt.id, + "combination_id": rbc_montue.id, + "combination_auto_assign": False, + } + ) + # Resource is available on Monday at an unoccupied time. + self.assertTrue( + resource.is_available( + utc.localize(datetime(2021, 3, 1, 10, 0)), + utc.localize(datetime(2021, 3, 1, 11, 0)), + ) + ) + # Resource is not available on Monday at an occupied time (longer than + # booking). + self.assertFalse( + resource.is_available( + utc.localize(datetime(2021, 3, 1, 7, 45)), + utc.localize(datetime(2021, 3, 1, 8, 45)), + ) + ) + # Resource is not available on Monday at an occupied time (within + # booking time). + self.assertFalse( + resource.is_available( + utc.localize(datetime(2021, 3, 1, 8, 10)), + utc.localize(datetime(2021, 3, 1, 8, 20)), + ) + ) + # Resource is not available on Monday at an occupied time (partially + # overlaps booking). + self.assertFalse( + resource.is_available( + utc.localize(datetime(2021, 3, 1, 8, 15)), + utc.localize(datetime(2021, 3, 1, 8, 45)), + ) + ) + # Resource is not available on Wednesdays. + self.assertFalse( + resource.is_available( + utc.localize(datetime(2021, 3, 3, 10, 0)), + utc.localize(datetime(2021, 3, 3, 11, 0)), + ) + ) + + def test_resource_is_available_span_days(self): + # Correctly handle bookings that span across midnight. + cal_satsun = self.r_calendars[3] + rbc_satsun = self.rbcs[3] + resource = rbc_satsun.resource_ids[1] + self.rbt.resource_calendar_id = cal_satsun + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.partner.id)], + "start": "2021-03-06 23:00:00", + "duration": 2, + "type_id": self.rbt.id, + "combination_id": rbc_satsun.id, + "combination_auto_assign": False, + } + ) + self.assertFalse( + resource.is_available( + utc.localize(datetime(2021, 3, 6, 22, 0)), + utc.localize(datetime(2021, 3, 7, 2, 0)), + ) + ) + # Resource is available on the next weekend. + self.assertTrue( + resource.is_available( + utc.localize(datetime(2021, 3, 13, 22, 0)), + utc.localize(datetime(2021, 3, 14, 2, 0)), + ) + ) + + +class TestMailActivity(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.activity_type = cls.env.ref( + "resource_booking.mail_activity_data_resource_booking" + ) + cls.partner = cls.env["res.partner"].create({"name": "T Partner"}) + create_test_data(cls) + cls.mail_activity = cls.env["mail.activity"].create( + { + "activity_type_id": cls.activity_type.id, + "summary": "Test Summary", + "res_model_id": cls.env["ir.model"]._get_id("res.partner"), + "res_id": cls.partner.id, + } + ) + + def _create_booking_from_mail_activity(self, mail_activity): + action = mail_activity.action_open_resource_booking() + booking_form = Form( + self.env[action["res_model"]].with_context(**action["context"]) + ) + booking_form.start = "2021-03-01 08:00:00" + booking_form.type_id = self.rbt + booking_form.combination_auto_assign = False + booking_form.combination_id = self.rbcs[2] + booking_form.duration = 1 + booking_form.description = "Booking Description" + return booking_form.save() + + def test_action_open_resource_booking_full_process(self): + action = self.mail_activity.action_open_resource_booking() + self.assertEqual(action["res_id"], 0) + self.assertEqual(action["view_mode"], "form") + ctx = action["context"] + self.assertEqual(ctx["default_activity_type_id"], self.activity_type.id) + self.assertEqual(action["context"]["default_name"], "Test Summary") + self.assertEqual( + ctx["default_booking_activity_ids"], + [(6, 0, [self.mail_activity.id])], + ) + booking = self._create_booking_from_mail_activity(self.mail_activity) + self.assertTrue(self.mail_activity.calendar_event_id) + self.assertEqual( + self.mail_activity.date_deadline, fields.Date.from_string("2021-03-01") + ) + feedback = "Test Feedback" + messages, activities = self.mail_activity._action_done(feedback=feedback) + self.assertEqual( + booking.description, + "

Booking Description


Feedback:

Test Feedback

", + ) + self.assertNotEqual(messages, []) + self.assertNotEqual(activities, []) + + def test_action_done_without_feedback(self): + booking = self._create_booking_from_mail_activity(self.mail_activity) + messages, activities = self.mail_activity._action_done() + self.assertEqual(booking.description, "

Booking Description

") + self.assertNotEqual(messages, []) + self.assertNotEqual(activities, []) + + @freeze_time("2021-03-01 06:00:00") + def test_sync_booking_activities(self): + booking = self._create_booking_from_mail_activity(self.mail_activity) + self.assertEqual(self.mail_activity.date_deadline, booking.start.date()) + booking.start = "2021-03-02 08:00:00" + self.assertEqual(self.mail_activity.date_deadline, booking.start.date()) + + @freeze_time("2021-03-01 06:00:00") + def test_resource_booking_activity_without_date(self): + booking = self.env["resource.booking"].create( + { + "name": "Test Booking", + "description": "Booking Description", + "type_id": self.rbt.id, + "duration": 1, + "booking_activity_ids": [ + ( + 0, + 0, + { + "activity_type_id": self.activity_type.id, + "summary": "Test Summary", + "note": "Test Note", + "res_model_id": self.env["ir.model"]._get_id("res.partner"), + "res_id": self.partner.id, + }, + ) + ], + } + ) + mail_activity = booking.booking_activity_ids[0] + future_date = datetime.now() + relativedelta(years=1000) + self.assertEqual(mail_activity.date_deadline, future_date.date()) + booking.combination_id = self.rbcs[2].id + booking.combination_auto_assign = False + booking.start = "2021-03-02 08:00:00" + self.assertEqual(mail_activity.date_deadline, booking.start.date()) + + def test_unlink_resource_booking_activity(self): + booking = self._create_booking_from_mail_activity(self.mail_activity) + booking.action_cancel() + self.assertNotEqual( + self.mail_activity.date_deadline, fields.Date.from_string("2021-03-01") + ) + self.assertFalse(self.mail_activity.calendar_event_id) + + def test_resource_booking_schedule_unschedule(self): + booking = self._create_booking_from_mail_activity(self.mail_activity) + res = booking.action_schedule() + self.assertTrue(self.mail_activity.calendar_event_id) + self.env[res["res_model"]].with_context(**res["context"]).create( + {"start": "2024-01-01 08:00:00", "stop": "2024-01-01 09:00:00"} + ) + self.assertEqual( + self.mail_activity.date_deadline, fields.Date.from_string("2024-01-01") + ) + booking.action_unschedule() + self.assertFalse(self.mail_activity.calendar_event_id) + self.assertNotEqual( + self.mail_activity.date_deadline, fields.Date.from_string("2024-01-01") + ) diff --git a/resource_booking/tests/test_portal.py b/resource_booking/tests/test_portal.py new file mode 100644 index 00000000..273e4d02 --- /dev/null +++ b/resource_booking/tests/test_portal.py @@ -0,0 +1,262 @@ +# Copyright 2021 Tecnativa - Jairo Llopis +# Copyright 2023 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from datetime import datetime + +from freezegun import freeze_time +from lxml.html import fromstring + +from odoo.tests import new_test_user, tagged +from odoo.tests.common import HttpCase + +from .common import create_test_data + + +@freeze_time("2021-02-26 09:00:00", tick=True) +@tagged("post_install", "-at_install") +class PortalCase(HttpCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + create_test_data(cls) + + cls.user_portal = new_test_user( + cls.env, login="ptl", password="ptl", groups="base.group_portal" + ) + cls.user_manager = new_test_user( + cls.env, + login="mgr", + password="mgr", + groups="resource_booking.group_manager", + ) + + def _url_xml(self, url, data=None, timeout=10): + """Open an URL and return the lxml etree object resulting from its content.""" + response = self.url_open(url, data, timeout=timeout) + return fromstring(response.content) + + def test_portal_no_bookings(self): + self.start_tour("/", "resource_booking_ptl_tour", login="ptl") + + def test_portal_list_with_bookings(self): + # Create one pending booking + self.env["resource.booking"].create( + { + "partner_ids": [(4, self.user_portal.partner_id.id)], + "type_id": self.rbt.id, + } + ) + self.start_tour("/", "resource_booking_ptl2_tour", login="ptl") + + def test_portal_scheduling_conflict(self): + """Produce a scheduling conflict and see how UI behaves. + + This test would be better as a tour, but since there are a few back and + forth actions among backend and frontend, and among distinct portal + users, it seemed easier to do it completely on python. + """ + # Set RBT to have only 1 combination available: the one for Mondays + self.rbt.combination_rel_ids[1:].unlink() + # One booking for portal user, another for a partner without user + bookings = self.env["resource.booking"].create( + [ + { + "partner_ids": [(4, self.user_portal.partner_id.id)], + "type_id": self.rbt.id, + "duration": 1, + }, + { + "partner_ids": [(4, self.partner.id)], + "type_id": self.rbt.id, + "location": "Office 2", + "videocall_location": "Videocall Office 2", + }, + ] + ) + booking_public = bookings[1] + # We assume they were invited by email and clicked on their links + portal_url, public_url = (one.get_portal_url() for one in bookings) + # Portal guy goes to scheduling page + portal_page = self._url_xml(portal_url) + self.assertTrue(portal_page.cssselect('.badge:contains("Pending")')) + self.assertTrue( + portal_page.cssselect(':contains("Duration:") + :contains("01:00")') + ) + self.assertTrue( + portal_page.cssselect(':contains("Location:") + :contains("Main office")') + ) + self.assertTrue( + portal_page.cssselect( + ':contains("Meeting URL:") + :contains("Videocall Main office")' + ) + ) + link = portal_page.cssselect('a:contains("Schedule")')[0] + portal_url = link.get("href") + portal_page = self._url_xml(portal_url) + # Nothing free on February, he goes to March + self.assertTrue( + portal_page.cssselect(".o_booking_calendar:contains('February 2021')") + ) + self.assertTrue( + portal_page.cssselect( + ".o_booking_calendar td" + ":contains('All times are displayed using this timezone:')" + ":contains('UTC')" + ) + ) + self.assertFalse(portal_page.cssselect(".o_booking_calendar .dropdown")) + self.assertFalse(portal_page.cssselect(".o_booking_calendar form")) + link = portal_page.cssselect('a[title="Next month"]')[0] + portal_url = link.get("href") + portal_page = self._url_xml(portal_url) + self.assertTrue( + portal_page.cssselect(".o_booking_calendar:contains('March 2021')") + ) + self.assertTrue(portal_page.cssselect(".o_booking_calendar .dropdown")) + self.assertTrue(portal_page.cssselect(".o_booking_calendar form")) + # Public guy does the same + public_page = self._url_xml(public_url) + self.assertTrue( + public_page.cssselect(':contains("Duration:") + :contains("00:30")') + ) + self.assertTrue( + public_page.cssselect(':contains("Location:") + :contains("Office 2")') + ) + self.assertTrue( + public_page.cssselect( + ':contains("Meeting URL:") + :contains("Videocall Office 2")' + ) + ) + self.assertTrue(public_page.cssselect('.badge:contains("Pending")')) + link = public_page.cssselect('a:contains("Schedule")')[0] + public_url = link.get("href") + public_page = self._url_xml(public_url) + self.assertTrue( + public_page.cssselect(".o_booking_calendar:contains('February 2021')") + ) + self.assertTrue( + public_page.cssselect( + ".o_booking_calendar td" + ":contains('All times are displayed using this timezone:')" + ":contains('UTC')" + ) + ) + self.assertFalse(public_page.cssselect(".o_booking_calendar .dropdown")) + self.assertFalse(public_page.cssselect(".o_booking_calendar form")) + link = public_page.cssselect('a[title="Next month"]')[0] + public_url = link.get("href") + public_page = self._url_xml(public_url) + self.assertTrue( + public_page.cssselect(".o_booking_calendar:contains('March 2021')") + ) + self.assertTrue(public_page.cssselect(".o_booking_calendar .dropdown")) + self.assertTrue(public_page.cssselect(".o_booking_calendar form")) + # Public guy makes reservation next Monday at 10:00 + slot = datetime(2021, 3, 1, 10).timestamp() + selector_10am = ( + "#dropdown-trigger-2021-03-01 " + "+ .slots-dropdown .dropdown-item:contains('10:00')" + ) + selector_1030am = ( + "#dropdown-trigger-2021-03-01 " + "+ .slots-dropdown .dropdown-item:contains('10:30')" + ) + self.assertTrue(public_page.cssselect(selector_10am)) + self.assertTrue(public_page.cssselect(selector_1030am)) + form = public_page.cssselect("form#modal-confirm-%d" % slot)[0] + public_url = form.get("action") + data = { + element.get("name"): element.get("value") + for element in form.cssselect("input") + } + public_page = self._url_xml(public_url, data) + # Public guy's reservation succeeded + self.assertTrue(public_page.cssselect('.badge:contains("Confirmed")')) + self.assertTrue( + public_page.cssselect( + 'div:contains("Booked resources:")' + ':contains("Material resource for Mon")' + ':contains("User User 0")' + ) + ) + self.assertTrue( + public_page.cssselect('div:contains("Location:"):contains("Office 2")') + ) + self.assertTrue( + public_page.cssselect( + 'div:contains("Dates:")' + ':contains("03/01/2021 at (10:00:00 To 10:30:00) (UTC)")' + ) + ) + # Public guy's booking and related meeting are OK in backend + booking_public.invalidate_model() + self.assertEqual(booking_public.state, "confirmed") + self.assertEqual(len(booking_public.meeting_id.attendee_ids), 2) + for attendee in booking_public.meeting_id.attendee_ids: + self.assertTrue(attendee.partner_id) + self.assertIn( + attendee.partner_id, + self.partner | self.users[0].partner_id, + ) + self.assertEqual( + attendee.state, + "accepted" if attendee.partner_id == self.partner else "needsAction", + ) + # At the same time, portal guy tries to reserve the same slot, which + # appears as free to him due to the race condition we just created + self.assertTrue(portal_page.cssselect(selector_10am)) + self.assertTrue(portal_page.cssselect(selector_1030am)) + form = portal_page.cssselect("form#modal-confirm-%d" % slot)[0] + portal_url = form.get("action") + data = { + element.get("name"): element.get("value") + for element in form.cssselect("input") + } + portal_page = self._url_xml(portal_url, data) + # He's back on the March calendar view, with an error message + self.assertTrue( + portal_page.cssselect( + ".alert-danger:contains('The chosen schedule is no longer available.')" + ) + ) + self.assertTrue( + portal_page.cssselect(".o_booking_calendar:contains('March 2021')") + ) + self.assertTrue(portal_page.cssselect(".o_booking_calendar .dropdown")) + self.assertTrue(portal_page.cssselect(".o_booking_calendar form")) + # He can't select that slot anymore, so he books it 30 minutes later + self.assertFalse(portal_page.cssselect(selector_10am)) + self.assertTrue(portal_page.cssselect(selector_1030am)) + slot = datetime(2021, 3, 1, 10, 30).timestamp() + self.assertTrue(portal_page.cssselect("#dropdown-trigger-2021-03-08")) + form = portal_page.cssselect("form#modal-confirm-%d" % slot)[0] + portal_url = form.get("action") + data = { + element.get("name"): element.get("value") + for element in form.cssselect("input") + } + portal_page = self._url_xml(portal_url, data) + # Portal guy's reservation succeeded + self.assertTrue(portal_page.cssselect('.badge:contains("Confirmed")')) + self.assertTrue( + portal_page.cssselect( + 'div:contains("Booked resources:")' + ':contains("Material resource for Mon")' + ':contains("User User 0")' + ) + ) + self.assertTrue( + portal_page.cssselect('div:contains("Location:"):contains("Main office")') + ) + self.assertTrue( + portal_page.cssselect( + 'div:contains("Dates:")' + ':contains("03/01/2021 at (10:30:00 To 11:30:00) (UTC)")' + ) + ) + # Portal guy cancels + link = portal_page.cssselect('a:contains("Cancel this booking")')[0] + portal_url = link.get("href") + portal_page = self._url_xml(portal_url) + self.assertTrue(portal_page.cssselect(".oe_login_form")) diff --git a/resource_booking/views/calendar_event_views.xml b/resource_booking/views/calendar_event_views.xml new file mode 100644 index 00000000..d3a9021a --- /dev/null +++ b/resource_booking/views/calendar_event_views.xml @@ -0,0 +1,15 @@ + + + + + calendar.event.view.form.inherit + calendar.event + + +
+ + +
+
+
diff --git a/resource_booking/views/mail_activity_views.xml b/resource_booking/views/mail_activity_views.xml new file mode 100644 index 00000000..b5e43fa0 --- /dev/null +++ b/resource_booking/views/mail_activity_views.xml @@ -0,0 +1,45 @@ + + + + mail.activity.form.inherit.booking + mail.activity + + + + {'invisible': [('activity_category', 'in', ['resource_booking','meeting'])]} + + + {'invisible': [('activity_category', 'in', ['resource_booking','meeting'])]} + + + {'invisible': ['|', ('activity_category', 'in', ['resource_booking','meeting', 'phonecall']), ('id', '!=', False)]} + + + {'invisible': ['|', ('activity_category', 'in', ['resource_booking','meeting']), ('chaining_type', '=', 'trigger')]} + + + {'invisible': [('activity_category', 'in', ['resource_booking','meeting'])]} + + + + + + + + diff --git a/resource_booking/views/resource_booking_combination_views.xml b/resource_booking/views/resource_booking_combination_views.xml new file mode 100644 index 00000000..ca778086 --- /dev/null +++ b/resource_booking/views/resource_booking_combination_views.xml @@ -0,0 +1,86 @@ + + + + + + Resource booking combination form + resource.booking.combination + +
+
+ +
+ + + + +
+
+

+ +

+
+ + + + +
+ + + + + Resource booking combination tree + resource.booking.combination + + + + + + + + + + resource.booking.combination.view.search + resource.booking.combination + + + + + + + + + Resource combinations + resource.booking.combination + tree,form + [] + {} + +

Define bookable resource combinations.

+

These records define resource combinations that can be booked together in specified schedules and intervals.

+
+
+ diff --git a/resource_booking/views/resource_booking_type_views.xml b/resource_booking/views/resource_booking_type_views.xml new file mode 100644 index 00000000..7b9d505d --- /dev/null +++ b/resource_booking/views/resource_booking_type_views.xml @@ -0,0 +1,127 @@ + + + + + + Resource booking type form + resource.booking.type + +
+
+ +
+ + + +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + + + Resource booking type tree + resource.booking.type + + + + + + + + + + resource.booking.type.view.search + resource.booking.type + + + + + + + + + + + + + Types + resource.booking.type + tree,form + [] + {} + +

Define resource booking types.

+

These records categorize resource bookings and apply restrictions to them, such as available resource combinations, availability schedules and interval duration.

+
+
+ diff --git a/resource_booking/views/resource_booking_views.xml b/resource_booking/views/resource_booking_views.xml new file mode 100644 index 00000000..f9e5d4a8 --- /dev/null +++ b/resource_booking/views/resource_booking_views.xml @@ -0,0 +1,265 @@ + + + + + + Resource booking calendar + resource.booking + + + + + + + + + + + + Resource booking tree + resource.booking + + + + + + + + + + + + + + Resource booking form + resource.booking + +
+
+ +
+ + + +
+ +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + resource.booking.view.search + resource.booking + + + + + + + + + + + + + + + + + + + + + Bookings + resource.booking + calendar,tree,form + [] + {'search_default_is_mine': 1} + +

+ Define resource bookings. +

+

+ When scheduled, resources will be blocked. When pending, it means the requester didn't place the booking yet. +

+
+
+
diff --git a/setup/resource_booking/odoo/addons/resource_booking b/setup/resource_booking/odoo/addons/resource_booking new file mode 120000 index 00000000..d9f8c68b --- /dev/null +++ b/setup/resource_booking/odoo/addons/resource_booking @@ -0,0 +1 @@ +../../../../resource_booking \ No newline at end of file diff --git a/setup/resource_booking/setup.py b/setup/resource_booking/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/resource_booking/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/test_requirements.txt b/test_requirements.txt new file mode 100644 index 00000000..881bd9f2 --- /dev/null +++ b/test_requirements.txt @@ -0,0 +1 @@ +freezegun