From d58c9e676894ca154385a1b8d215d0d0446cd773 Mon Sep 17 00:00:00 2001 From: Murtuza Saleh Date: Mon, 29 Nov 2021 22:58:22 +0530 Subject: [PATCH] [ADD] pms_sale --- oca_dependencies.txt | 2 + pms_sale/README.rst | 101 ++ pms_sale/__init__.py | 3 + pms_sale/__manifest__.py | 34 + pms_sale/data/ir_sequence.xml | 11 + pms_sale/data/pms_stage.xml | 52 + pms_sale/data/product_data.xml | 15 + pms_sale/i18n/es.po | 1014 +++++++++++++++++ pms_sale/i18n/pms_sale.pot | 1010 ++++++++++++++++ pms_sale/models/__init__.py | 15 + pms_sale/models/account_move.py | 29 + pms_sale/models/account_move_line.py | 11 + pms_sale/models/pms_mail.py | 33 + pms_sale/models/pms_property.py | 53 + pms_sale/models/pms_property_reservation.py | 49 + pms_sale/models/pms_reservation.py | 327 ++++++ pms_sale/models/pms_reservation_guest.py | 22 + pms_sale/models/pms_stage.py | 27 + pms_sale/models/pms_team.py | 83 ++ pms_sale/models/product.py | 23 + pms_sale/models/sale_order.py | 43 + pms_sale/models/sale_order_line.py | 193 ++++ pms_sale/readme/CONFIGURE.rst | 1 + pms_sale/readme/CONTRIBUTORS.rst | 4 + pms_sale/readme/DESCRIPTION.rst | 2 + pms_sale/readme/USAGE.rst | 1 + pms_sale/security/ir.model.access.csv | 12 + pms_sale/static/description/agency_logo.png | Bin 0 -> 4515 bytes pms_sale/static/description/agency_logo1.png | Bin 0 -> 58521 bytes pms_sale/static/description/avatar.png | Bin 0 -> 4370 bytes pms_sale/static/description/icon.png | Bin 0 -> 24626 bytes pms_sale/static/description/index.html | 455 ++++++++ .../src/js/pms_configurator_controller.js | 55 + .../static/src/js/pms_configurator_view.js | 20 + .../static/src/js/pms_configurator_widget.js | 189 +++ pms_sale/static/src/js/timeline.js | 300 +++++ pms_sale/static/src/xml/timeline.xml | 25 + pms_sale/tests/__init__.py | 3 + pms_sale/tests/test_account_move.py | 53 + pms_sale/tests/test_pms_property.py | 107 ++ pms_sale/tests/test_pms_reservation.py | 117 ++ pms_sale/views/account_move.xml | 35 + pms_sale/views/assets.xml | 25 + pms_sale/views/menu.xml | 71 ++ pms_sale/views/pms_mail_views.xml | 63 + pms_sale/views/pms_property.xml | 34 + pms_sale/views/pms_property_reservation.xml | 47 + .../views/pms_reservation_guest_views.xml | 41 + pms_sale/views/pms_reservation_views.xml | 396 +++++++ pms_sale/views/pms_team_views.xml | 74 ++ pms_sale/views/product_views.xml | 38 + pms_sale/views/sale_order_views.xml | 86 ++ pms_sale/wizards/__init__.py | 3 + pms_sale/wizards/account_payment_register.py | 16 + pms_sale/wizards/pms_configurator.py | 224 ++++ pms_sale/wizards/pms_configurator_views.xml | 80 ++ setup/pms_sale/odoo/addons/pms_sale | 1 + setup/pms_sale/setup.py | 6 + 58 files changed, 5734 insertions(+) create mode 100644 pms_sale/README.rst create mode 100644 pms_sale/__init__.py create mode 100644 pms_sale/__manifest__.py create mode 100644 pms_sale/data/ir_sequence.xml create mode 100644 pms_sale/data/pms_stage.xml create mode 100644 pms_sale/data/product_data.xml create mode 100644 pms_sale/i18n/es.po create mode 100644 pms_sale/i18n/pms_sale.pot create mode 100644 pms_sale/models/__init__.py create mode 100644 pms_sale/models/account_move.py create mode 100644 pms_sale/models/account_move_line.py create mode 100644 pms_sale/models/pms_mail.py create mode 100644 pms_sale/models/pms_property.py create mode 100644 pms_sale/models/pms_property_reservation.py create mode 100644 pms_sale/models/pms_reservation.py create mode 100644 pms_sale/models/pms_reservation_guest.py create mode 100644 pms_sale/models/pms_stage.py create mode 100644 pms_sale/models/pms_team.py create mode 100644 pms_sale/models/product.py create mode 100644 pms_sale/models/sale_order.py create mode 100644 pms_sale/models/sale_order_line.py create mode 100644 pms_sale/readme/CONFIGURE.rst create mode 100644 pms_sale/readme/CONTRIBUTORS.rst create mode 100644 pms_sale/readme/DESCRIPTION.rst create mode 100644 pms_sale/readme/USAGE.rst create mode 100644 pms_sale/security/ir.model.access.csv create mode 100644 pms_sale/static/description/agency_logo.png create mode 100644 pms_sale/static/description/agency_logo1.png create mode 100644 pms_sale/static/description/avatar.png create mode 100644 pms_sale/static/description/icon.png create mode 100644 pms_sale/static/description/index.html create mode 100644 pms_sale/static/src/js/pms_configurator_controller.js create mode 100644 pms_sale/static/src/js/pms_configurator_view.js create mode 100644 pms_sale/static/src/js/pms_configurator_widget.js create mode 100644 pms_sale/static/src/js/timeline.js create mode 100644 pms_sale/static/src/xml/timeline.xml create mode 100644 pms_sale/tests/__init__.py create mode 100644 pms_sale/tests/test_account_move.py create mode 100644 pms_sale/tests/test_pms_property.py create mode 100644 pms_sale/tests/test_pms_reservation.py create mode 100644 pms_sale/views/account_move.xml create mode 100644 pms_sale/views/assets.xml create mode 100644 pms_sale/views/menu.xml create mode 100644 pms_sale/views/pms_mail_views.xml create mode 100644 pms_sale/views/pms_property.xml create mode 100644 pms_sale/views/pms_property_reservation.xml create mode 100644 pms_sale/views/pms_reservation_guest_views.xml create mode 100644 pms_sale/views/pms_reservation_views.xml create mode 100644 pms_sale/views/pms_team_views.xml create mode 100644 pms_sale/views/product_views.xml create mode 100644 pms_sale/views/sale_order_views.xml create mode 100644 pms_sale/wizards/__init__.py create mode 100644 pms_sale/wizards/account_payment_register.py create mode 100644 pms_sale/wizards/pms_configurator.py create mode 100644 pms_sale/wizards/pms_configurator_views.xml create mode 120000 setup/pms_sale/odoo/addons/pms_sale create mode 100644 setup/pms_sale/setup.py diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 37d443ee2..70f7973d0 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,2 +1,4 @@ account-financial-tools contract +stock-logistics-warehouse +web diff --git a/pms_sale/README.rst b/pms_sale/README.rst new file mode 100644 index 000000000..6e0804bef --- /dev/null +++ b/pms_sale/README.rst @@ -0,0 +1,101 @@ +================================ +PMS (Property Management System) +================================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |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%2Fpms-lightgray.png?logo=github + :target: https://github.com/OCA/pms/tree/14.0/pms + :alt: OCA/pms +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pms-14-0/pms-14-0-pms + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/293/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is an all-in-one property management system (PMS) focused on medium-sized properties +for managing every aspect of your property's daily operations. + +You can manage properties with multi-property and multi-company support, including your rooms inventory, +reservations, check-in, daily reports, board services, rate and availability plans among other property functionalities. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module depends on modules ``base``, ``mail``, ``sale`` and ``multi_pms_properties``. +Ensure yourself to have all them in your addons list. + +Configuration +============= + +You will find the hotel settings in PMS Management > Configuration > Properties > Your Property. + +This module required additional configuration for company, accounting, invoicing and user privileges. + +Usage +===== + +To use this module, please, read the complete user guide at ``_. + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Open Source Integrators + +Contributors +~~~~~~~~~~~~ + +* Maxime Chambreuil +* Serpent Consulting Services Pvt. Ltd. + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/pms `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/pms_sale/__init__.py b/pms_sale/__init__.py new file mode 100644 index 000000000..2bf243070 --- /dev/null +++ b/pms_sale/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import models +from . import wizards diff --git a/pms_sale/__manifest__.py b/pms_sale/__manifest__.py new file mode 100644 index 000000000..3bdc85448 --- /dev/null +++ b/pms_sale/__manifest__.py @@ -0,0 +1,34 @@ +# Copyright 2019 Darío Lodeiros, Alexandre Díaz, Jose Luis Algara, Pablo Quesada +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "PMS - Sale", + "summary": "Manage reservations", + "version": "14.0.1.0.0", + "development_status": "Alpha", + "category": "Generic Modules/Property Management System", + "website": "https://github.com/OCA/pms", + "author": "Commit [Sun], Open Source Integrators, Odoo Community Association (OCA)", + "maintainers": ["eantones"], + "license": "AGPL-3", + "depends": ["pms_account", "sale", "web_timeline", "calendar"], + "data": [ + "security/ir.model.access.csv", + "data/ir_sequence.xml", + "data/product_data.xml", + "data/pms_stage.xml", + "views/assets.xml", + "views/product_views.xml", + "views/pms_property_reservation.xml", + "views/pms_mail_views.xml", + "views/pms_property.xml", + "views/pms_reservation_guest_views.xml", + "views/pms_reservation_views.xml", + "wizards/pms_configurator_views.xml", + "views/sale_order_views.xml", + "views/pms_team_views.xml", + "views/menu.xml", + "views/account_move.xml", + ], + "qweb": ["static/src/xml/timeline.xml"], +} diff --git a/pms_sale/data/ir_sequence.xml b/pms_sale/data/ir_sequence.xml new file mode 100644 index 000000000..3af3a2f0c --- /dev/null +++ b/pms_sale/data/ir_sequence.xml @@ -0,0 +1,11 @@ + + + + + PMS Reservation + pms.reservation + 5 + + + + diff --git a/pms_sale/data/pms_stage.xml b/pms_sale/data/pms_stage.xml new file mode 100644 index 000000000..2a1aef69a --- /dev/null +++ b/pms_sale/data/pms_stage.xml @@ -0,0 +1,52 @@ + + + + New + 10 + 1 + reservation + + + + + Booked + 20 + reservation + #FF6961 + + + + + Confirmed + 30 + reservation + #FFB347 + + + + + Checked In + 40 + reservation + #77DD77 + + + + + Checked Out + 50 + 1 + reservation + + + + + Cancelled + 99 + 1 + 1 + reservation + + + + diff --git a/pms_sale/data/product_data.xml b/pms_sale/data/product_data.xml new file mode 100644 index 000000000..27661cff1 --- /dev/null +++ b/pms_sale/data/product_data.xml @@ -0,0 +1,15 @@ + + + + + Reservations + + + + Reservation + service + + + + + diff --git a/pms_sale/i18n/es.po b/pms_sale/i18n/es.po new file mode 100644 index 000000000..1631fcb77 --- /dev/null +++ b/pms_sale/i18n/es.po @@ -0,0 +1,1014 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pms_sale +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e-20211202\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-12-23 19:41+0000\n" +"PO-Revision-Date: 2021-12-23 19:41+0000\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: pms_sale +#: code:addons/pms_sale/wizards/pms_configurator.py:0 +#, python-format +msgid "%s of guests is lower than the %s of guests of the property." +msgstr "%s huespedes es menor a %s huespedes de la propiedad." + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "" +"
\n" +" RESERVATION(S)" +msgstr "" +"
\n" +" RESERVA(S)" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_needaction +msgid "Action Needed" +msgstr "Accion necesaria" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_ids +msgid "Activities" +msgstr "Actividades" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_state +msgid "Activity State" +msgstr "Estado de actividad" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__adults +msgid "Adults" +msgstr "Adultos" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__after_checkin +msgid "After Checkin" +msgstr "Después del checkin" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__after_checkout +msgid "After Checkout" +msgstr "Después del checkout" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__after_resev +msgid "After the reservation" +msgstr "Después de la reserva" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_attachment_count +msgid "Attachment Count" +msgstr "Numéro de adjuntos" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +msgid "Availabilities" +msgstr "Disponibilidades" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Bedrooms..." +msgstr "Recamaras..." + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__before_checkin +msgid "Before Checkin" +msgstr "Antes del checkin" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__before_checkout +msgid "Before Checkout" +msgstr "Antes del checkout" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Book" +msgstr "Reservar" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__partner_id +msgid "Booked by" +msgstr "Reservado por" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_cancelled +msgid "Cancelled" +msgstr "Cancellado" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Check In" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Check Out" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_checked_in +msgid "Checked In" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_checked_out +msgid "Checked Out" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__checkin +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__start +msgid "Checkin" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__checkout +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__stop +msgid "Checkout" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__children +msgid "Children" +msgstr "Hijos" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__color +msgid "Color Index" +msgstr "Indíce de color" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__pms_mail_ids +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +msgid "Communication" +msgstr "Comunicación" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__company_id +msgid "Company" +msgstr "Empresa" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__configurator_id +msgid "Configurator" +msgstr "Configurador" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.pms_configurator_action +msgid "Configure Reservation" +msgstr "Configurar reserva" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Confirm" +msgstr "Confirmar" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_confirmed +msgid "Confirmed" +msgstr "Confirmado" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__currency_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__currency_id +msgid "Currency" +msgstr "Divisa" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__date +msgid "Date" +msgstr "Fecha" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Date..." +msgstr "Fecha..." + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +msgid "Defaults" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "Delete" +msgstr "Borrar" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_move__display_name +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line__display_name +#: model:ir.model.fields,field_description:pms_sale.field_account_payment_register__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__display_name +#: model:ir.model.fields,field_description:pms_sale.field_product_product__display_name +#: model:ir.model.fields,field_description:pms_sale.field_product_template__display_name +#: model:ir.model.fields,field_description:pms_sale.field_sale_order__display_name +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__display_name +msgid "Display Name" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "Dropdown menu" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "Edit" +msgstr "Editar" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__email +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__email +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__notification_type__email +msgid "Email" +msgstr "Correo" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__template_id +msgid "Email Template" +msgstr "Plantilla de correo" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_property__listing_type__entire_home +msgid "Entire Home" +msgstr "Hogar completo" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_follower_ids +msgid "Followers" +msgstr "Seguidores" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguidores (canales)" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__start +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__start +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "From" +msgstr "Desde" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__guest_ids +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__guest_ids +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__guest_ids +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Guests" +msgstr "Huespedes" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__2 +msgid "High" +msgstr "Alta" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_move__id +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line__id +#: model:ir.model.fields,field_description:pms_sale.field_account_payment_register__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__id +#: model:ir.model.fields,field_description:pms_sale.field_product_product__id +#: model:ir.model.fields,field_description:pms_sale.field_product_template__id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order__id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__id +msgid "ID" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_exception_icon +msgid "Icon" +msgstr "Icono" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_needaction +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_has_error +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__interval +msgid "Interval" +msgstr "Intervalo" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__invoice_count +msgid "Invoice Count" +msgstr "Numéro de factura" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__invoice_status +msgid "Invoice Status" +msgstr "Estado de factura" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Invoices" +msgstr "Facturas" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_is_follower +msgid "Is Follower" +msgstr "Es seguidor" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__reservation_ok +msgid "Is Reservation?" +msgstr "Es reserva?" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_account_move +msgid "Journal Entry" +msgstr "Asiento contable" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_account_move_line +msgid "Journal Item" +msgstr "Apunte contable" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_move____last_update +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line____last_update +#: model:ir.model.fields,field_description:pms_sale.field_account_payment_register____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_property____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_team____last_update +#: model:ir.model.fields,field_description:pms_sale.field_product_product____last_update +#: model:ir.model.fields,field_description:pms_sale.field_product_template____last_update +#: model:ir.model.fields,field_description:pms_sale.field_sale_order____last_update +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line____last_update +msgid "Last Modified on" +msgstr "Ultima modificación el" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__write_uid +msgid "Last Updated by" +msgstr "Ultima modificación por" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__write_date +msgid "Last Updated on" +msgstr "Ultima modificación el" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +msgid "Limits" +msgstr "Limites" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__listing_type +msgid "Listing Type" +msgstr "Típo de anuncio" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__1 +msgid "Low" +msgstr "Baja" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_mail_form +msgid "Mail Scheduler" +msgstr "Planificador de correo" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.action_pms_mail +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_mail_tree +msgid "Mail Schedulers" +msgstr "Planificadores de correo" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__max_nights +msgid "Maximum Nights" +msgstr "Noches maxima" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_ids +msgid "Messages" +msgstr "Mensajes" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__min_nights +msgid "Minimum Nights" +msgstr "Noches minima" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__name +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__name +msgid "Name" +msgstr "Nombre" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: model:pms.stage,name:pms_sale.pms_stage_new +#, python-format +msgid "New" +msgstr "Nueva" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__duration +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__duration +msgid "Nights" +msgstr "Noches" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__0 +msgid "Normal" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_needaction_counter +msgid "Number of Actions" +msgstr "Numéro acciones" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__no_of_guests +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__no_of_guests +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__no_of_guests +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__no_of_guests +msgid "Number of Guests" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_has_error_counter +msgid "Number of errors" +msgstr "Numéro de huespedes" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +msgid "Ok" +msgstr "Ok" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.reservation_action_from_dashboard +msgid "Orders" +msgstr "Pedidos" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_mail +msgid "PMS Automated Mailing" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_configurator +msgid "PMS Configurator" +msgstr "Configurador" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_reservation_guest +#: model:ir.model,name:pms_sale.model_pms_reservation_guest_wizard +msgid "PMS Reservation guest" +msgstr "Huesped de reserva" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_stage +msgid "PMS Stage" +msgstr "Etapa" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_team +msgid "PMS Team" +msgstr "Equipo" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__partner_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__partner_id +msgid "Partner" +msgstr "Contacto" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Payment Status" +msgstr "Estado de pago" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__phone +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__phone +msgid "Phone" +msgstr "Telefono" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__price +msgid "Price" +msgstr "Precio" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__priority +msgid "Priority" +msgstr "Prioridad" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_property__listing_type__private_room +msgid "Private Room" +msgstr "Cuarto privado" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_product_product +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__product_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__product_id +msgid "Product" +msgstr "Producto" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_property +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__property_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__property_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__property_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__property_id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__property_id +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_mail_view_search +msgid "Property" +msgstr "Propiedad" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.pms_property_reservation_action +#: model:ir.model,name:pms_sale.model_pms_property_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_property_reservation_view_form +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_property_reservation_view_tree +msgid "Property Reservation" +msgstr "Reserva de propiedad" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_account_payment_register +msgid "Register Payment" +msgstr "Registrar pago" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_reservation +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line__pms_reservation_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__reservation_ids +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__reservation_id +#: model:ir.model.fields,field_description:pms_sale.field_product_product__reservation_ok +#: model:ir.model.fields,field_description:pms_sale.field_product_template__reservation_ok +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__pms_reservation_id +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_stage__stage_type__reservation +#: model:product.product,name:pms_sale.product_product_reservation +#: model:product.template,name:pms_sale.product_product_reservation_product_template +#: model_terms:ir.ui.view,arch_db:pms_sale.product_template_form_view_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.product_template_search_view_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_tree +msgid "Reservation" +msgstr "Reserva" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__name +msgid "Reservation #" +msgstr "Numéro de reserva" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Reservation Detail" +msgstr "Detalle de reserva" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.pms_reservation_guest_action +msgid "Reservation Guest" +msgstr "Huesped" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__name +msgid "Reservation Number" +msgstr "Numéro de reserva" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_reservation_guest_view_form +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_reservation_guest_view_tree +msgid "Reservation Stage" +msgstr "Etapa" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__reservation_id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__reservation_id +msgid "Reservation Type" +msgstr "Típo de reserva" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__reservation_ids +msgid "Reservation Types" +msgstr "Típos de reserva" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "Reservation(s)" +msgstr "Reserva(s)" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.action_board_reservation +#: model:ir.actions.act_window,name:pms_sale.action_board_reservation_window +#: model:ir.actions.act_window,name:pms_sale.action_operations_reservation +#: model:ir.actions.act_window,name:pms_sale.action_pms_reservation +#: model:ir.actions.act_window,name:pms_sale.action_report_reservation +#: model:ir.ui.menu,name:pms_sale.menu_board_reservation +#: model:ir.ui.menu,name:pms_sale.menu_config_reservation +#: model:ir.ui.menu,name:pms_sale.menu_operations_reservation +#: model:ir.ui.menu,name:pms_sale.menu_report_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.sale_order_view_form_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_move_form_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_calendar +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_graph +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_pivot +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_timeline +msgid "Reservations" +msgstr "Reservas" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_bank_statement_line__reservation_count +#: model:ir.model.fields,field_description:pms_sale.field_account_move__reservation_count +#: model:ir.model.fields,field_description:pms_sale.field_account_payment__reservation_count +#: model:ir.model.fields,field_description:pms_sale.field_sale_order__reservation_count +msgid "Reservations Count" +msgstr "Numéro de reservas" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_booked +msgid "Reserved" +msgstr "Reservado" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__user_id +msgid "Responsible" +msgstr "Responsable" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_user_id +msgid "Responsible User" +msgstr "Responsable" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__order_line_id +msgid "Sale Order" +msgstr "Pedido de venta" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_sale_order +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__sale_order_id +msgid "Sales Order" +msgstr "Pedido de venta" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_sale_order_line +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__sale_order_line_id +msgid "Sales Order Line" +msgstr "Línea de pedido de venta" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Search" +msgstr "Buscar" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Select City" +msgstr "Seleccionar la ciudad" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__notification_type +msgid "Send" +msgstr "Mandar" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_property__listing_type__shared_room +msgid "Shared Room" +msgstr "Cuarto compartido" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__stage_id +msgid "Stage" +msgstr "Etapa" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_configurator__start +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__start +msgid "Start date of the reservation" +msgstr "Fecha de inicio de la reserva" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "State" +msgstr "Estado" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__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: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_configurator__stop +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__stop +msgid "Stop date of the reservation" +msgstr "Fecha final de la resrva" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__tag_ids +msgid "Tags" +msgstr "Etiquetas" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__team_id +msgid "Team" +msgstr "Equipo" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#, python-format +msgid "The number of nights must be between %s and %s." +msgstr "El numéro de noches debe estar entre %s y %s." + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__no_week_reservation +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__total_reservation +msgid "This Week Reservations" +msgstr "Reservas de esta semana" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "This Week:" +msgstr "Esta semana:" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "This week Reservations" +msgstr "Reservas de esta semana" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__timeline_html +msgid "Timeline HTML" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__stop +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__stop +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "To" +msgstr "Hasta" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__no_today_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Today Reservations" +msgstr "Reservas de hoy" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "Today:" +msgstr "Hoy:" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__no_tomorrow_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Tomorrow Reservations" +msgstr "Reservas de mañana" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "Tomorrow:" +msgstr "Mañana:" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#, python-format +msgid "" +"Too many guests (%s) on the reservation: the property\n" +" accepts a maximum of %s guests." +msgstr "" +"Demasiado huespedes (%s) en esta reserva: la propiedad\n" +" solo admite un maximo de %s huespedes." + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Total Reservations" +msgstr "Reservas total" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__interval_trigger +msgid "Trigger" +msgstr "Disparador" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage__stage_type +msgid "Type" +msgstr "Típo" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__interval_unit +msgid "Unit" +msgstr "Unidad" + +#. module: pms_sale +#: model:product.product,uom_name:pms_sale.product_product_reservation +#: model:product.template,uom_name:pms_sale.product_product_reservation_product_template +msgid "Units" +msgstr "Unidades" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__3 +msgid "Urgent" +msgstr "Urgente" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#, python-format +msgid "You cannot have 2 reservations on the same night at the same property." +msgstr "No pueden tener 2 reservas por la misma noche en la misma propiedad." + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "night(s)" +msgstr "noche(s)" diff --git a/pms_sale/i18n/pms_sale.pot b/pms_sale/i18n/pms_sale.pot new file mode 100644 index 000000000..cb44d3084 --- /dev/null +++ b/pms_sale/i18n/pms_sale.pot @@ -0,0 +1,1010 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pms_sale +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e-20211202\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-12-23 19:41+0000\n" +"PO-Revision-Date: 2021-12-23 19:41+0000\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: pms_sale +#: code:addons/pms_sale/wizards/pms_configurator.py:0 +#, python-format +msgid "%s of guests is lower than the %s of guests of the property." +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "" +"
\n" +" RESERVATION(S)" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_ids +msgid "Activities" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_exception_decoration +msgid "Activity Exception Decoration" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_state +msgid "Activity State" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_type_icon +msgid "Activity Type Icon" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__adults +msgid "Adults" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__after_checkin +msgid "After Checkin" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__after_checkout +msgid "After Checkout" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__after_resev +msgid "After the reservation" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +msgid "Availabilities" +msgstr "" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Bedrooms..." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__before_checkin +msgid "Before Checkin" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__interval_trigger__before_checkout +msgid "Before Checkout" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Book" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__partner_id +msgid "Booked by" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +msgid "Cancel" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_cancelled +msgid "Cancelled" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Check In" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Check Out" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_checked_in +msgid "Checked In" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_checked_out +msgid "Checked Out" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__checkin +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__start +msgid "Checkin" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__checkout +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__stop +msgid "Checkout" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__children +msgid "Children" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__color +msgid "Color Index" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__pms_mail_ids +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +msgid "Communication" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__company_id +msgid "Company" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__configurator_id +msgid "Configurator" +msgstr "" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.pms_configurator_action +msgid "Configure Reservation" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Confirm" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_confirmed +msgid "Confirmed" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__create_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__create_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__currency_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__currency_id +msgid "Currency" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__date +msgid "Date" +msgstr "" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Date..." +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +msgid "Defaults" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "Delete" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_move__display_name +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line__display_name +#: model:ir.model.fields,field_description:pms_sale.field_account_payment_register__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage__display_name +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__display_name +#: model:ir.model.fields,field_description:pms_sale.field_product_product__display_name +#: model:ir.model.fields,field_description:pms_sale.field_product_template__display_name +#: model:ir.model.fields,field_description:pms_sale.field_sale_order__display_name +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__display_name +msgid "Display Name" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "Dropdown menu" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "Edit" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__email +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__email +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_mail__notification_type__email +msgid "Email" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__template_id +msgid "Email Template" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_property__listing_type__entire_home +msgid "Entire Home" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_channel_ids +msgid "Followers (Channels)" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__activity_type_icon +msgid "Font awesome icon e.g. fa-tasks" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__start +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__start +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "From" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__guest_ids +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__guest_ids +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__guest_ids +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Guests" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__2 +msgid "High" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_move__id +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line__id +#: model:ir.model.fields,field_description:pms_sale.field_account_payment_register__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage__id +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__id +#: model:ir.model.fields,field_description:pms_sale.field_product_product__id +#: model:ir.model.fields,field_description:pms_sale.field_product_template__id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order__id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__id +msgid "ID" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_exception_icon +msgid "Icon" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__activity_exception_icon +msgid "Icon to indicate an exception activity." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_needaction +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_unread +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_has_error +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__interval +msgid "Interval" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__invoice_count +msgid "Invoice Count" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__invoice_status +msgid "Invoice Status" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "Invoices" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__reservation_ok +msgid "Is Reservation?" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_move____last_update +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line____last_update +#: model:ir.model.fields,field_description:pms_sale.field_account_payment_register____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_property____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage____last_update +#: model:ir.model.fields,field_description:pms_sale.field_pms_team____last_update +#: model:ir.model.fields,field_description:pms_sale.field_product_product____last_update +#: model:ir.model.fields,field_description:pms_sale.field_product_template____last_update +#: model:ir.model.fields,field_description:pms_sale.field_sale_order____last_update +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line____last_update +msgid "Last Modified on" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__write_uid +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__write_date +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +msgid "Limits" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__listing_type +msgid "Listing Type" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__1 +msgid "Low" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_mail_form +msgid "Mail Scheduler" +msgstr "" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.action_pms_mail +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_mail_tree +msgid "Mail Schedulers" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__max_nights +msgid "Maximum Nights" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_ids +msgid "Messages" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__min_nights +msgid "Minimum Nights" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__my_activity_date_deadline +msgid "My Activity Deadline" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__name +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__name +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__name +msgid "Name" +msgstr "" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: code:addons/pms_sale/models/pms_reservation.py:0 +#: model:pms.stage,name:pms_sale.pms_stage_new +#, python-format +msgid "New" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_date_deadline +msgid "Next Activity Deadline" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_summary +msgid "Next Activity Summary" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_type_id +msgid "Next Activity Type" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__duration +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__duration +msgid "Nights" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__0 +msgid "Normal" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__no_of_guests +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__no_of_guests +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__no_of_guests +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__no_of_guests +msgid "Number of Guests" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_needaction_counter +msgid "Number of messages which requires an action" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__message_unread_counter +msgid "Number of unread messages" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_configurator_view_form +msgid "Ok" +msgstr "" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.reservation_action_from_dashboard +msgid "Orders" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_mail +msgid "PMS Automated Mailing" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_configurator +msgid "PMS Configurator" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_reservation_guest +#: model:ir.model,name:pms_sale.model_pms_reservation_guest_wizard +msgid "PMS Reservation guest" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_stage +msgid "PMS Stage" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_team +msgid "PMS Team" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__partner_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__partner_id +msgid "Partner" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Payment Status" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__phone +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest_wizard__phone +msgid "Phone" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__price +msgid "Price" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__priority +msgid "Priority" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_property__listing_type__private_room +msgid "Private Room" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_product_product +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__product_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__product_id +msgid "Product" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_product_template +msgid "Product Template" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_property +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__property_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__property_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_property_reservation__property_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__property_id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__property_id +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_mail_view_search +msgid "Property" +msgstr "" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.pms_property_reservation_action +#: model:ir.model,name:pms_sale.model_pms_property_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_property_reservation_view_form +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_property_reservation_view_tree +msgid "Property Reservation" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_account_payment_register +msgid "Register Payment" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_pms_reservation +#: model:ir.model.fields,field_description:pms_sale.field_account_move_line__pms_reservation_id +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__reservation_ids +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__reservation_id +#: model:ir.model.fields,field_description:pms_sale.field_product_product__reservation_ok +#: model:ir.model.fields,field_description:pms_sale.field_product_template__reservation_ok +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__pms_reservation_id +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_stage__stage_type__reservation +#: model:product.product,name:pms_sale.product_product_reservation +#: model:product.template,name:pms_sale.product_product_reservation_product_template +#: model_terms:ir.ui.view,arch_db:pms_sale.product_template_form_view_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.product_template_search_view_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_property_form_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_tree +msgid "Reservation" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__name +msgid "Reservation #" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Reservation Detail" +msgstr "" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.pms_reservation_guest_action +msgid "Reservation Guest" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__name +msgid "Reservation Number" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_reservation_guest_view_form +#: model_terms:ir.ui.view,arch_db:pms_sale.pms_reservation_guest_view_tree +msgid "Reservation Stage" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__reservation_id +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__reservation_id +msgid "Reservation Type" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_property__reservation_ids +msgid "Reservation Types" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "Reservation(s)" +msgstr "" + +#. module: pms_sale +#: model:ir.actions.act_window,name:pms_sale.action_board_reservation +#: model:ir.actions.act_window,name:pms_sale.action_board_reservation_window +#: model:ir.actions.act_window,name:pms_sale.action_operations_reservation +#: model:ir.actions.act_window,name:pms_sale.action_pms_reservation +#: model:ir.actions.act_window,name:pms_sale.action_report_reservation +#: model:ir.ui.menu,name:pms_sale.menu_board_reservation +#: model:ir.ui.menu,name:pms_sale.menu_config_reservation +#: model:ir.ui.menu,name:pms_sale.menu_operations_reservation +#: model:ir.ui.menu,name:pms_sale.menu_report_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.sale_order_view_form_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_move_form_inherit_pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_calendar +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_graph +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_pivot +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_timeline +msgid "Reservations" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_account_bank_statement_line__reservation_count +#: model:ir.model.fields,field_description:pms_sale.field_account_move__reservation_count +#: model:ir.model.fields,field_description:pms_sale.field_account_payment__reservation_count +#: model:ir.model.fields,field_description:pms_sale.field_sale_order__reservation_count +msgid "Reservations Count" +msgstr "" + +#. module: pms_sale +#: model:pms.stage,name:pms_sale.pms_stage_booked +msgid "Reserved" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__user_id +msgid "Responsible" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__activity_user_id +msgid "Responsible User" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation_guest__order_line_id +msgid "Sale Order" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_sale_order +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__sale_order_id +msgid "Sales Order" +msgstr "" + +#. module: pms_sale +#: model:ir.model,name:pms_sale.model_sale_order_line +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__sale_order_line_id +msgid "Sales Order Line" +msgstr "" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Search" +msgstr "" + +#. module: pms_sale +#. openerp-web +#: code:addons/pms_sale/static/src/xml/timeline.xml:0 +#, python-format +msgid "Select City" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__notification_type +msgid "Send" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_property__listing_type__shared_room +msgid "Shared Room" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__stage_id +msgid "Stage" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_configurator__start +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__start +msgid "Start date of the reservation" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "State" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__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: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_configurator__stop +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__stop +msgid "Stop date of the reservation" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__tag_ids +msgid "Tags" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__team_id +msgid "Team" +msgstr "" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#, python-format +msgid "The number of nights must be between %s and %s." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__no_week_reservation +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__total_reservation +msgid "This Week Reservations" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "This Week:" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "This week Reservations" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__timeline_html +msgid "Timeline HTML" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_configurator__stop +#: model:ir.model.fields,field_description:pms_sale.field_sale_order_line__stop +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_form +msgid "To" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__no_today_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Today Reservations" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "Today:" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_team__no_tomorrow_reservation +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Tomorrow Reservations" +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_pms_team_kanban_inherit_pms_sale +msgid "Tomorrow:" +msgstr "" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#, python-format +msgid "" +"Too many guests (%s) on the reservation: the property\n" +" accepts a maximum of %s guests." +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_search +msgid "Total Reservations" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__interval_trigger +msgid "Trigger" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_stage__stage_type +msgid "Type" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__activity_exception_decoration +msgid "Type of the exception activity on record." +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_mail__interval_unit +msgid "Unit" +msgstr "" + +#. module: pms_sale +#: model:product.product,uom_name:pms_sale.product_product_reservation +#: model:product.template,uom_name:pms_sale.product_product_reservation_product_template +msgid "Units" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_unread +msgid "Unread Messages" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__message_unread_counter +msgid "Unread Messages Counter" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields.selection,name:pms_sale.selection__pms_reservation__priority__3 +msgid "Urgent" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,field_description:pms_sale.field_pms_reservation__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: pms_sale +#: model:ir.model.fields,help:pms_sale.field_pms_reservation__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: pms_sale +#: code:addons/pms_sale/models/pms_reservation.py:0 +#, python-format +msgid "You cannot have 2 reservations on the same night at the same property." +msgstr "" + +#. module: pms_sale +#: model_terms:ir.ui.view,arch_db:pms_sale.view_reservation_kanban +msgid "night(s)" +msgstr "" diff --git a/pms_sale/models/__init__.py b/pms_sale/models/__init__.py new file mode 100644 index 000000000..8c24cc351 --- /dev/null +++ b/pms_sale/models/__init__.py @@ -0,0 +1,15 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import ( + product, + pms_property_reservation, + pms_stage, + pms_mail, + pms_property, + pms_reservation_guest, + pms_reservation, + sale_order, + sale_order_line, + pms_team, + account_move, + account_move_line, +) diff --git a/pms_sale/models/account_move.py b/pms_sale/models/account_move.py new file mode 100644 index 000000000..be091e03b --- /dev/null +++ b/pms_sale/models/account_move.py @@ -0,0 +1,29 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class AccountMove(models.Model): + _inherit = "account.move" + + reservation_count = fields.Integer( + "Reservations Count", compute="_compute_reservation_count" + ) + + @api.depends("line_ids") + def _compute_reservation_count(self): + for invoice in self: + reservation = invoice.line_ids.mapped("pms_reservation_id") + invoice.reservation_count = len(reservation) + + def action_view_reservation_list(self): + for invoice in self: + action = self.env["ir.actions.actions"]._for_xml_id( + "pms_sale.action_pms_reservation" + ) + reservation = self.line_ids.mapped("pms_reservation_id") + action["domain"] = [ + ("id", "in", reservation.ids), + ("partner_id", "=", invoice.partner_id.id), + ] + return action diff --git a/pms_sale/models/account_move_line.py b/pms_sale/models/account_move_line.py new file mode 100644 index 000000000..83b271dd6 --- /dev/null +++ b/pms_sale/models/account_move_line.py @@ -0,0 +1,11 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + pms_reservation_id = fields.Many2one( + "pms.reservation", string="Reservation", readonly=True, copy=False + ) diff --git a/pms_sale/models/pms_mail.py b/pms_sale/models/pms_mail.py new file mode 100644 index 000000000..24ce2e6d5 --- /dev/null +++ b/pms_sale/models/pms_mail.py @@ -0,0 +1,33 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class PMSMailScheduler(models.Model): + _name = "pms.mail" + _description = "PMS Automated Mailing" + + name = fields.Char(string="Name", required=True) + notification_type = fields.Selection( + [("email", "Email")], string="Send", default="Email" + ) + template_id = fields.Many2one("mail.template", string="Email Template") + interval = fields.Integer("Interval", default=1) + interval_unit = fields.Many2one( + "uom.uom", + string="Unit", + domain=lambda self: [ + ("category_id", "=", self.env.ref("uom.uom_categ_wtime").id) + ], + ) + interval_trigger = fields.Selection( + [ + ("after_resev", "After the reservation"), + ("before_checkin", "Before Checkin"), + ("after_checkin", "After Checkin"), + ("before_checkout", "Before Checkout"), + ("after_checkout", "After Checkout"), + ], + string="Trigger", + ) + property_id = fields.Many2one("pms.property", string="Property") diff --git a/pms_sale/models/pms_property.py b/pms_sale/models/pms_property.py new file mode 100644 index 000000000..ad08ebb78 --- /dev/null +++ b/pms_sale/models/pms_property.py @@ -0,0 +1,53 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime + +from odoo import api, fields, models + + +class PmsProperty(models.Model): + _inherit = "pms.property" + + checkin = fields.Float(string="Checkin") + checkout = fields.Float(string="Checkout") + reservation_ids = fields.One2many( + "pms.property.reservation", "property_id", string="Reservation Types" + ) + pms_mail_ids = fields.One2many("pms.mail", "property_id", string="Communication") + no_of_guests = fields.Integer("Number of Guests") + min_nights = fields.Integer("Minimum Nights") + max_nights = fields.Integer("Maximum Nights") + listing_type = fields.Selection( + string="Listing Type", + selection=[ + ("private_room", "Private Room"), + ("entire_home", "Entire Home"), + ("shared_room", "Shared Room"), + ], + ) + + @api.model + def get_property_information(self, vals, domain=False): + domain = domain or [] + domain.append(("property_child_ids", "=", False)) + if vals.get("city_value") and vals.get("city_value") != "Select City": + domain += [("city", "=", vals.get("city_value"))] + if vals.get("bedrooms_value") and vals.get("bedrooms_value") != 0: + domain += [("qty_bedroom", ">=", vals.get("bedrooms_value"))] + if vals.get("datepicker_value"): + date_range = vals.get("datepicker_value").split("-") + start = date_range[0].strip() + " 00:00:00" + end = date_range[1].strip() + " 23:59:59" + start = datetime.strptime(start, "%m/%d/%Y %H:%M:%S") + end = datetime.strptime(end, "%m/%d/%Y %H:%M:%S") + reservation_ids = self.env["pms.reservation"].search( + [ + ("start", "<", end), + ("stop", ">", start), + ] + ) + property_ids = reservation_ids.mapped("property_id") + # Remove all the properties with reservations in the date range to only + # show the ones available + domain += [("id", "not in", property_ids.ids)] + return self.search_read(domain, ["ref", "name"]) diff --git a/pms_sale/models/pms_property_reservation.py b/pms_sale/models/pms_property_reservation.py new file mode 100644 index 000000000..06f6e5d6b --- /dev/null +++ b/pms_sale/models/pms_property_reservation.py @@ -0,0 +1,49 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class PmsPropertyReservation(models.Model): + _name = "pms.property.reservation" + _description = "Property Reservation" + + def _default_product_id(self): + return self.env.ref( + "pms_sale.product_product_reservation", raise_if_not_found=False + ) + + @api.depends("product_id") + def _compute_price(self): + for rec in self: + if rec.product_id and rec.product_id.lst_price: + rec.price = rec.product_id.lst_price or 0 + elif not rec.price: + rec.price = 0 + + name = fields.Char(string="Name", required=True) + product_id = fields.Many2one( + "product.product", + string="Product", + required=True, + domain=[("reservation_ok", "=", True)], + default=_default_product_id, + ) + price = fields.Float( + string="Price", + compute="_compute_price", + digits="Product Price", + readonly=False, + store=True, + ) + property_id = fields.Many2one("pms.property", string="Property") + currency_id = fields.Many2one( + "res.currency", + string="Currency", + default=lambda self: self.env.company.currency_id, + ) + + def _get_reservation_multiline_description(self): + """Compute a multiline description of this ticket. It is used when ticket + description are necessary without having to encode it manually, like sales + information.""" + return "%s\n%s" % (self.display_name, self.property_id.display_name) diff --git a/pms_sale/models/pms_reservation.py b/pms_sale/models/pms_reservation.py new file mode 100644 index 000000000..1d50710f3 --- /dev/null +++ b/pms_sale/models/pms_reservation.py @@ -0,0 +1,327 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime, timedelta + +import pytz + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT + +AVAILABLE_PRIORITIES = [("0", "Normal"), ("1", "Low"), ("2", "High"), ("3", "Urgent")] + + +class PmsReservation(models.Model): + _name = "pms.reservation" + _description = "Reservation" + _order = "start, id" + _inherit = ["mail.thread", "mail.activity.mixin"] + + def _default_stage_id(self): + return self.env.ref("pms_sale.pms_stage_new", raise_if_not_found=False) + + def _get_duration(self, start, stop): + """Get the duration value between the 2 given dates.""" + if not start or not stop: + return 0 + duration = (stop - start).total_seconds() / (24 * 3600) + return round(duration, 0) + + @api.depends("guest_ids") + def _compute_no_of_guests(self): + self.no_of_guests = 0 + if self.guest_ids: + self.no_of_guests = len(self.guest_ids) + + name = fields.Char( + string="Reservation #", + help="Reservation Number", + required=True, + readonly=True, + index=True, + copy=False, + default=lambda self: _("New"), + ) + start = fields.Datetime( + "Checkin", + required=True, + tracking=True, + default=fields.Date.today, + help="Start date of the reservation", + ) + stop = fields.Datetime( + "Checkout", + required=True, + tracking=True, + default=fields.Date.today, + compute="_compute_stop", + readonly=False, + store=True, + help="Stop date of the reservation", + ) + duration = fields.Integer( + "Nights", compute="_compute_duration", store=True, readonly=False + ) + date = fields.Datetime(string="Date", default=lambda self: fields.Datetime.now()) + stage_id = fields.Many2one( + "pms.stage", + string="Stage", + store=True, + tracking=True, + index=True, + default=_default_stage_id, + group_expand="_read_group_stage_ids", + ) + team_id = fields.Many2one( + "pms.team", string="Team", related="property_id.team_id", store=True + ) + property_id = fields.Many2one("pms.property", string="Property") + sale_order_id = fields.Many2one("sale.order", string="Sales Order") + sale_order_line_id = fields.Many2one("sale.order.line", string="Sales Order Line") + invoice_status = fields.Selection( + related="sale_order_id.invoice_status", store=True, index=True + ) + partner_id = fields.Many2one("res.partner", string="Booked by") + user_id = fields.Many2one( + "res.users", string="Responsible", default=lambda self: self.env.user.id + ) + company_id = fields.Many2one( + "res.company", + string="Company", + default=lambda self: self.env.company.id, + ) + adults = fields.Integer(string="Adults") + children = fields.Integer(string="Children") + no_of_guests = fields.Integer( + "Number of Guests", compute="_compute_no_of_guests", store=True + ) + guest_ids = fields.One2many( + "pms.reservation.guest", "reservation_id", string="Guests" + ) + priority = fields.Selection( + AVAILABLE_PRIORITIES, + string="Priority", + index=True, + default=AVAILABLE_PRIORITIES[0][0], + ) + tag_ids = fields.Many2many("pms.tag", string="Tags") + color = fields.Integer("Color Index", default=0) + invoice_count = fields.Integer( + string="Invoice Count", + compute="_compute_invoice_count", + readonly=True, + copy=False, + ) + + @api.depends("stop", "start") + def _compute_duration(self): + for reservation in self.with_context(dont_notify=True): + reservation.duration = self._get_duration( + reservation.start, reservation.stop + ) + + @api.depends("start", "duration") + def _compute_stop(self): + # stop and duration fields both depends on the start field. + # But they also depends on each other. + # When start is updated, we want to update the stop datetime based on + # the *current* duration. + # In other words, we want: change start => keep the duration fixed and + # recompute stop accordingly. + # However, while computing stop, duration is marked to be recomputed. + # Calling `reservation.duration` would trigger its recomputation. + # To avoid this we manually mark the field as computed. + duration_field = self._fields["duration"] + self.env.remove_to_compute(duration_field, self) + for reservation in self: + reservation.stop = reservation.start + timedelta(days=reservation.duration) + + def _compute_invoice_count(self): + for reservation in self: + invoices = ( + self.env["account.move.line"] + .search([("pms_reservation_id", "=", reservation.id)]) + .mapped("move_id") + .filtered(lambda r: r.move_type in ("out_invoice", "out_refund")) + ) + reservation.invoice_count = len(invoices) + + @api.model + def _read_group_stage_ids(self, stages, domain, order): + search_domain = [("stage_type", "=", "reservation")] + if self.env.context.get("default_team_id"): + search_domain = [ + "&", + ("team_ids", "in", self.env.context["default_team_id"]), + ] + search_domain + return stages.search(search_domain, order=order) + + @api.model + def create(self, vals): + if vals.get("name", _("New")) == _("New"): + vals["name"] = self.env["ir.sequence"].next_by_code("pms.reservation") or _( + "New" + ) + return super().create(vals) + + @api.onchange("property_id") + def onchange_property_id(self): + user_tz = self.env.user.tz or "UTC" + utc = pytz.timezone("UTC") + timezone = pytz.timezone(user_tz) + if ( + self.property_id + and self.start + and self.stop + and self.property_id.checkin + and self.property_id.checkout + ): + start_datetime = ( + str(self.start.date()) + + " " + + str(timedelta(hours=self.property_id.checkin)) + ) + with_timezone = timezone.localize( + datetime.strptime(start_datetime, DEFAULT_SERVER_DATETIME_FORMAT) + ) + start_datetime = with_timezone.astimezone(utc) + self.start = start_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + + end_datetime = ( + str(self.stop.date()) + + " " + + str(timedelta(hours=self.property_id.checkout)) + ) + with_timezone = timezone.localize( + datetime.strptime(end_datetime, DEFAULT_SERVER_DATETIME_FORMAT) + ) + end_datetime = with_timezone.astimezone(utc) + self.stop = end_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + else: + self.start = self.start.date() + self.stop = self.stop.date() + + @api.constrains("property_id", "no_of_guests") + def _check_max_no_of_guests(self): + for reservation in self: + if reservation.no_of_guests > reservation.property_id.no_of_guests: + raise ValidationError( + _( + """Too many guests (%s) on the reservation: the property + accepts a maximum of %s guests.""" + % ( + reservation.no_of_guests, + reservation.property_id.no_of_guests, + ) + ) + ) + + @api.constrains("property_id", "stage_id", "start", "stop") + def _check_no_of_reservations(self): + stage_ids = [ + self.env.ref("pms_sale.pms_stage_new", raise_if_not_found=False).id, + self.env.ref("pms_sale.pms_stage_cancelled", raise_if_not_found=False).id, + ] + for rec in self: + if rec.stage_id.id not in stage_ids: + reservation = self.search( + [ + ("property_id", "=", rec.property_id.id), + ("stage_id", "not in", stage_ids), + ("id", "!=", rec.id), + ("start", "<=", rec.stop), + ("stop", ">=", rec.start), + ] + ) + if reservation: + raise ValidationError( + _( + "You cannot have 2 reservations on the same night at the " + "same property." + ) + ) + + @api.constrains("property_id", "duration") + def _check_no_of_nights(self): + for rec in self: + if ( + rec.duration > rec.property_id.min_nights + and rec.property_id.max_nights < rec.duration + ): + raise ValidationError( + _( + "The number of nights must be between %s and %s." + % ( + rec.property_id.min_nights, + rec.property_id.max_nights, + ) + ) + ) + + def action_book(self): + return self.write( + { + "stage_id": self.env.ref( + "pms_sale.pms_stage_booked", raise_if_not_found=False + ).id, + } + ) + + def action_confirm(self): + return self.write( + { + "stage_id": self.env.ref( + "pms_sale.pms_stage_confirmed", raise_if_not_found=False + ).id + } + ) + + def action_check_in(self): + return self.write( + { + "stage_id": self.env.ref( + "pms_sale.pms_stage_checked_in", raise_if_not_found=False + ).id, + "start": fields.Datetime.now(), + } + ) + + def action_check_out(self): + return self.write( + { + "stage_id": self.env.ref( + "pms_sale.pms_stage_checked_out", raise_if_not_found=False + ).id, + "stop": fields.Datetime.now(), + } + ) + + def action_cancel(self): + self.write( + { + "stage_id": self.env.ref( + "pms_sale.pms_stage_cancelled", raise_if_not_found=False + ).id + } + ) + + def action_view_invoices(self): + for reservation in self: + action = self.env.ref("account.action_move_out_invoice_type").read()[0] + invoices = ( + self.env["account.move.line"] + .search([("pms_reservation_id", "=", reservation.id)]) + .mapped("move_id") + .filtered(lambda r: r.move_type in ("out_invoice", "out_refund")) + ) + action["domain"] = [("id", "in", invoices.ids)] + return action + + @api.model + def get_selections(self): + cities = list( + {rec.city for rec in self.env["pms.property"].search([]) if rec.city} + ) + cities.sort() + values = {"city": cities} + return values diff --git a/pms_sale/models/pms_reservation_guest.py b/pms_sale/models/pms_reservation_guest.py new file mode 100644 index 000000000..3311f15b5 --- /dev/null +++ b/pms_sale/models/pms_reservation_guest.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class PMSReservationGuest(models.Model): + _name = "pms.reservation.guest" + _description = "PMS Reservation guest" + + name = fields.Char(string="Name", required=True) + phone = fields.Char(string="Phone") + email = fields.Char(string="Email") + reservation_id = fields.Many2one("pms.reservation", string="Reservation") + order_line_id = fields.Many2one("sale.order.line", string="Sale Order") + partner_id = fields.Many2one("res.partner", string="Partner") + + @api.onchange("partner_id") + def _onchange_partner_id(self): + if self.partner_id: + self.name = self.partner_id.name + self.phone = self.partner_id.phone + self.email = self.partner_id.email diff --git a/pms_sale/models/pms_stage.py b/pms_sale/models/pms_stage.py new file mode 100644 index 000000000..e1bb8691c --- /dev/null +++ b/pms_sale/models/pms_stage.py @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class PMSStage(models.Model): + _inherit = "pms.stage" + + stage_type = fields.Selection( + selection_add=[("reservation", "Reservation")], + ondelete={"reservation": "cascade"}, + ) + + def get_color_information(self): + # get stage ids + stage_ids = self.search([]) + color_information_dict = [] + for stage in stage_ids: + color_information_dict.append( + { + "color": stage.custom_color, + "field": "stage_id", + "opt": "==", + "value": stage.name, + } + ) + return color_information_dict diff --git a/pms_sale/models/pms_team.py b/pms_sale/models/pms_team.py new file mode 100644 index 000000000..b14e55a7e --- /dev/null +++ b/pms_sale/models/pms_team.py @@ -0,0 +1,83 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import timedelta + +from odoo import fields, models + + +class PMSTeam(models.Model): + _inherit = "pms.team" + + no_today_reservation = fields.Integer( + string="Today Reservations", compute="_compute_no_reservations" + ) + no_tomorrow_reservation = fields.Integer( + string="Tomorrow Reservations", compute="_compute_no_reservations" + ) + no_week_reservation = fields.Integer( + string="This Week Reservations", compute="_compute_no_reservations" + ) + total_reservation = fields.Integer( + string="This Week Reservations", compute="_compute_no_reservations" + ) + + def _compute_no_reservations(self): + start = fields.date.today() - timedelta(days=fields.date.today().weekday()) + end = start + timedelta(days=6) + reservation_obj = self.env["pms.reservation"] + for rec in self: + today_reservation_count = reservation_obj.search_count( + [ + ("team_id", "=", rec.id), + ("start", ">=", fields.date.today()), + ("start", "<=", fields.date.today()), + ( + "stage_id", + "!=", + self.env.ref("pms_sale.pms_stage_checked_out").id, + ), + ("stage_id", "!=", self.env.ref("pms_sale.pms_stage_cancelled").id), + ] + ) + tomorrow_reservation_count = reservation_obj.search_count( + [ + ("team_id", "=", rec.id), + ("start", ">=", fields.date.today()), + ("stop", "<=", fields.date.today() + timedelta(1)), + ( + "stage_id", + "!=", + self.env.ref("pms_sale.pms_stage_checked_out").id, + ), + ("stage_id", "!=", self.env.ref("pms_sale.pms_stage_cancelled").id), + ] + ) + this_week_reservation_count = reservation_obj.search_count( + [ + ("team_id", "=", rec.id), + ("start", ">=", start), + ("stop", "<=", end), + ( + "stage_id", + "!=", + self.env.ref("pms_sale.pms_stage_checked_out").id, + ), + ("stage_id", "!=", self.env.ref("pms_sale.pms_stage_cancelled").id), + ] + ) + total_reservation_count = reservation_obj.search_count( + [ + ("team_id", "=", rec.id), + ( + "stage_id", + "!=", + self.env.ref("pms_sale.pms_stage_checked_out").id, + ), + ("stage_id", "!=", self.env.ref("pms_sale.pms_stage_cancelled").id), + ] + ) + + rec.no_today_reservation = today_reservation_count + rec.no_tomorrow_reservation = tomorrow_reservation_count + rec.no_week_reservation = this_week_reservation_count + rec.total_reservation = total_reservation_count diff --git a/pms_sale/models/product.py b/pms_sale/models/product.py new file mode 100644 index 000000000..a4c8ca8aa --- /dev/null +++ b/pms_sale/models/product.py @@ -0,0 +1,23 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + reservation_ok = fields.Boolean(string="Reservation") + + @api.onchange("reservation_ok") + def _onchange_reservation_ok(self): + if self.reservation_ok: + self.type = "service" + + +class Product(models.Model): + _inherit = "product.product" + + @api.onchange("reservation_ok") + def _onchange_reservation_ok(self): + if self.reservation_ok: + self.type = "service" diff --git a/pms_sale/models/sale_order.py b/pms_sale/models/sale_order.py new file mode 100644 index 000000000..fcbfdc7d4 --- /dev/null +++ b/pms_sale/models/sale_order.py @@ -0,0 +1,43 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def _compute_reservation_count(self): + sale_orders_data = self.env["pms.reservation"].read_group( + [("sale_order_id", "in", self.ids)], ["sale_order_id"], ["sale_order_id"] + ) + reservation_count_data = { + sale_order_data["sale_order_id"][0]: sale_order_data["sale_order_id_count"] + for sale_order_data in sale_orders_data + } + for sale_order in self: + sale_order.reservation_count = reservation_count_data.get(sale_order.id, 0) + + reservation_count = fields.Integer( + "Reservations Count", compute="_compute_reservation_count" + ) + + def action_view_reservation_list(self): + action = self.env["ir.actions.actions"]._for_xml_id( + "pms_sale.action_pms_reservation" + ) + action["domain"] = [("sale_order_id", "in", self.ids)] + return action + + def action_confirm(self): + res = super(SaleOrder, self).action_confirm() + for sale in self: + reservation = self.env["pms.reservation"].search( + [("sale_order_id", "=", sale.id)] + ) + if reservation: + reservation.action_book() + # Set reservation confirm when payment is done by Generate a Payment Link + if not sale.has_to_be_paid(): + reservation.action_confirm() + return res diff --git a/pms_sale/models/sale_order_line.py b/pms_sale/models/sale_order_line.py new file mode 100644 index 000000000..1fa71649e --- /dev/null +++ b/pms_sale/models/sale_order_line.py @@ -0,0 +1,193 @@ +# Copyright (c) 2021 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime + +from odoo import api, fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + property_id = fields.Many2one("pms.property", string="Property") + reservation_id = fields.Many2one( + "pms.property.reservation", string="Reservation Type" + ) + reservation_ok = fields.Boolean( + related="product_id.reservation_ok", readonly=True, string="Is Reservation?" + ) + start = fields.Datetime("From") + stop = fields.Datetime("To") + no_of_guests = fields.Integer("Number of Guests") + guest_ids = fields.One2many( + "pms.reservation.guest", "order_line_id", string="Guests" + ) + pms_reservation_id = fields.Many2one("pms.reservation", string="Reservation") + + @api.onchange("reservation_id", "no_of_guests") + def _onchange_reservation_id(self): + # we call this to force update the default name + self.product_id_change() + + @api.onchange("property_id") + def _onchange_property_id(self): + if self.property_id and self.property_id.analytic_id: + self.order_id.analytic_account_id = self.property_id.analytic_id.id + + def get_sale_order_line_multiline_description_sale(self, product): + if self.reservation_id: + return ( + "".join( + [ + self.reservation_id.display_name, + " (", + str(self.no_of_guests), + " Guests)", + ] + ) + + self._get_sale_order_line_multiline_description_variants() + ) + else: + return super( + SaleOrderLine, self + ).get_sale_order_line_multiline_description_sale(product) + + @api.model + def create(self, values): + rec = super(SaleOrderLine, self).create(values) + if ( + values.get("product_id") + and values.get("reservation_id") + and values.get("property_id") + and not values.get("pms_reservation_id", False) + ): + reservation_vals = { + "partner_id": rec.order_id.partner_id.id, + "sale_order_id": rec.order_id.id, + "sale_order_line_id": rec.id, + } + reservation = self._create_pms_reservation(values, reservation_vals) + if reservation: + rec.pms_reservation_id = reservation.id + if values.get("property_id"): + rec.order_id.analytic_account_id = rec.property_id.analytic_id.id + return rec + + def write(self, values): + rec = super(SaleOrderLine, self).write(values) + if self.pms_reservation_id: + reserv_vals = {} + if values.get( + "property_id" + ) and self.pms_reservation_id.property_id.id != values.get("property_id"): + reserv_vals.update({"property_id": values.get("property_id")}) + if values.get("start") and self.pms_reservation_id.start != values.get( + "start" + ): + reserv_vals.update({"start": values.get("start")}) + if values.get("stop") and self.pms_reservation_id.stop != values.get( + "stop" + ): + reserv_vals.update({"stop": values.get("stop")}) + if values.get("guest_ids"): + reserv_vals.update({"guest_ids": values.get("guest_ids")}) + self.pms_reservation_id.sudo().write(reserv_vals) + if ( + ( + values.get("product_id") + or (values.get("reservation_id") and values.get("property_id")) + ) + and self.product_id.reservation_ok + and not self.pms_reservation_id + ): + reservation_vals = { + "partner_id": self.order_id.partner_id.id, + "sale_order_id": self.order_id.id, + "sale_order_line_id": self.id, + } + reservation = self._create_pms_reservation(values, reservation_vals) + if reservation: + self.pms_reservation_id = reservation.id + if values.get("property_id"): + self.order_id.analytic_account_id = self.property_id.analytic_id.id + return rec + + def _create_pms_reservation(self, values, reservation_vals): + reservation = False + if reservation_vals: + reservation_vals.update( + { + "date": datetime.now(), + "property_id": values.get("property_id") or self.property_id.id, + "start": values.get("start") or self.start or datetime.now(), + "stop": values.get("stop") or self.stop or datetime.now(), + "guest_ids": values.get("guest_ids") or False, + } + ) + reservation = self.env["pms.reservation"].sudo().create(reservation_vals) + return reservation + + def unlink(self): + for line in self: + if line.product_id.reservation_ok and line.pms_reservation_id: + line.pms_reservation_id.action_cancel() + return super(SaleOrderLine, self).unlink() + + @api.onchange("product_id") + def product_id_change(self): + super(SaleOrderLine, self).product_id_change() + if self.reservation_id: + self.price_unit = self.reservation_id.price + if self.order_id.pricelist_id: + product = self.product_id.with_context( + lang=self.order_id.partner_id.lang, + partner=self.order_id.partner_id, + quantity=self.product_uom_qty, + date=self.order_id.date_order, + pricelist=self.order_id.pricelist_id.id, + uom=self.product_uom.id, + fiscal_position=self.env.context.get("fiscal_position"), + ) + price = self.env["account.tax"]._fix_tax_included_price_company( + self._get_display_price(product), + product.taxes_id, + self.tax_id, + self.company_id, + ) + if price != product.lst_price: + self.price_unit = price + + @api.onchange("product_uom", "product_uom_qty") + def product_uom_change(self): + super(SaleOrderLine, self).product_uom_change() + if self.reservation_id: + self.price_unit = self.reservation_id.price + if self.order_id.pricelist_id: + product = self.product_id.with_context( + lang=self.order_id.partner_id.lang, + partner=self.order_id.partner_id, + quantity=self.product_uom_qty, + date=self.order_id.date_order, + pricelist=self.order_id.pricelist_id.id, + uom=self.product_uom.id, + fiscal_position=self.env.context.get("fiscal_position"), + ) + price = self.env["account.tax"]._fix_tax_included_price_company( + self._get_display_price(product), + product.taxes_id, + self.tax_id, + self.company_id, + ) + if price != product.lst_price: + self.price_unit = price + + def _prepare_invoice_line(self, **optional_values): + result = super()._prepare_invoice_line(**optional_values) + self.ensure_one() + if self.pms_reservation_id and self.property_id: + result.update( + { + "pms_reservation_id": self.pms_reservation_id.id, + "property_ids": [(6, 0, self.property_id.ids)], + } + ) + return result diff --git a/pms_sale/readme/CONFIGURE.rst b/pms_sale/readme/CONFIGURE.rst new file mode 100644 index 000000000..173b6bdd6 --- /dev/null +++ b/pms_sale/readme/CONFIGURE.rst @@ -0,0 +1 @@ +* Go to Properties > Configuration > Settings. diff --git a/pms_sale/readme/CONTRIBUTORS.rst b/pms_sale/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..2f469efa0 --- /dev/null +++ b/pms_sale/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* `Open Source Integrators `: + + * Maxime Chambreuil + * Serpent Consulting Services Pvt. Ltd. diff --git a/pms_sale/readme/DESCRIPTION.rst b/pms_sale/readme/DESCRIPTION.rst new file mode 100644 index 000000000..c400829be --- /dev/null +++ b/pms_sale/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allows you to manage the sale and financial information of the +properties. diff --git a/pms_sale/readme/USAGE.rst b/pms_sale/readme/USAGE.rst new file mode 100644 index 000000000..acb2b8f3d --- /dev/null +++ b/pms_sale/readme/USAGE.rst @@ -0,0 +1 @@ +To use this module, please read the complete user guide at ``_. diff --git a/pms_sale/security/ir.model.access.csv b/pms_sale/security/ir.model.access.csv new file mode 100644 index 000000000..b3f218395 --- /dev/null +++ b/pms_sale/security/ir.model.access.csv @@ -0,0 +1,12 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_pms_property_reservation,access_pms_property_reservation,model_pms_property_reservation,base.group_user,1,1,1,0 +access_pms_mail,access_pms_mail,model_pms_mail,base.group_user,1,1,1,0 +access_pms_reservation_guest,access_pms_reservation_guest,model_pms_reservation_guest,base.group_user,1,1,1,0 +access_pms_configurator,access_pms_configurator,model_pms_configurator,base.group_user,1,1,1,0 +access_pms_reservation_guest_wizard,access_pms_reservation_guest_wizard,model_pms_reservation_guest_wizard,base.group_user,1,1,1,0 +access_pms_property_reservation_manager,access_pms_property_reservation_manager,model_pms_property_reservation,pms_base.group_pms_manager,1,1,1,1 +access_pms_mail_manager,access_pms_mail_manager,model_pms_mail,pms_base.group_pms_manager,1,1,1,1 +access_pms_reservation_guest_manager,access_pms_reservation_guest_manager,model_pms_reservation_guest,pms_base.group_pms_manager,1,1,1,1 +access_pms_reservation_manager,access_pms_reservation_manager,model_pms_reservation,pms_base.group_pms_manager,1,1,1,1 +access_pms_reservation_user,access_pms_reservation_user,model_pms_reservation,pms_base.group_pms_user,1,1,1,0 +access_pms_reservation,access_pms_reservation,model_pms_reservation,base.group_user,1,0,0,0 diff --git a/pms_sale/static/description/agency_logo.png b/pms_sale/static/description/agency_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bffa2c63abbb0f1b8e71326e909f6343c32e0795 GIT binary patch literal 4515 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 literal 0 HcmV?d00001 diff --git a/pms_sale/static/description/agency_logo1.png b/pms_sale/static/description/agency_logo1.png new file mode 100644 index 0000000000000000000000000000000000000000..d20ba13b37af2d834de40fdd0868cdb56ec182d9 GIT binary patch literal 58521 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

  • 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 RfIm@?uC}38rKUsF{{W1g3}*lU literal 0 HcmV?d00001 diff --git a/pms_sale/static/description/icon.png b/pms_sale/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a81dd64c384b0427f6c7cc4131ebff3e90aee131 GIT binary patch literal 24626 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 literal 0 HcmV?d00001 diff --git a/pms_sale/static/description/index.html b/pms_sale/static/description/index.html new file mode 100644 index 000000000..f2429e666 --- /dev/null +++ b/pms_sale/static/description/index.html @@ -0,0 +1,455 @@ + + + + + + +PMS (Property Management System) + + + +
    +

    PMS (Property Management System)

    + + +

    Alpha License: AGPL-3 OCA/pms Translate me on Weblate Try me on Runbot

    +

    This module is an all-in-one property management system (PMS) focused on medium-sized properties +for managing every aspect of your property’s daily operations.

    +

    You can manage properties with multi-property and multi-company support, including your rooms inventory, +reservations, check-in, daily reports, board services, rate and availability plans among other property functionalities.

    +
    +

    Important

    +

    This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

    +
    +

    Table of contents

    + +
    +

    Installation

    +

    This module depends on modules base, mail, sale and multi_pms_properties. +Ensure yourself to have all them in your addons list.

    +
    +
    +

    Configuration

    +

    You will find the hotel settings in PMS Management > Configuration > Properties > Your Property.

    +

    This module required additional configuration for company, accounting, invoicing and user privileges.

    +
    +
    +

    Usage

    +

    To use this module, please, read the complete user guide at roomdoo.com.

    +
    +
    +

    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 smashing it by providing a detailed and welcomed +feedback.

    +

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

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Commit [Sun]
    • +
    +
    +
    +

    Contributors

    +
      +
    • Alexandre Díaz
    • +
    • Pablo Quesada
    • +
    • Jose Luis Algara
    • +
    • Commit [Sun] <https://www.commitsun.com>:
        +
      • Dario Lodeiros
      • +
      • Eric Antones
      • +
      • Sara Lago
      • +
      • Brais Abeijon
      • +
      • Miguel Padin
      • +
      +
    • +
    +
    +
    +

    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.

    +

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

    +

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

    +
    +
    +
    + + diff --git a/pms_sale/static/src/js/pms_configurator_controller.js b/pms_sale/static/src/js/pms_configurator_controller.js new file mode 100644 index 000000000..b4a8d23cb --- /dev/null +++ b/pms_sale/static/src/js/pms_configurator_controller.js @@ -0,0 +1,55 @@ +odoo.define("pms_sale.PMSConfiguratorFormController", function (require) { + "use strict"; + + var FormController = require("web.FormController"); + + /** + * This controller is overridden to allow configuring sale_order_lines through a + * popup window when a product with 'reservation_ok' is selected. + * + * This allows keeping an editable list view for sales order and remove the noise of + * those 2 fields ('property_id' + 'reservation_id') + */ + var PMSConfiguratorFormController = FormController.extend({ + /** + * We let the regular process take place to allow the validation of the required + * fields to happen. + * + * Then we can manually close the window, providing event information to the + * caller. + * + * @override + */ + saveRecord: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + var state = self.renderer.state.data; + var guest_ids = [[5, 0, 0]]; + _.each(state.guest_ids.data, function (data) { + if (data.data && data.data.name) { + if (data.data.partner_id) { + data.data.partner_id = data.data.partner_id.data.id; + } + guest_ids.push([0, 0, data.data]); + } + }); + self.do_action({ + type: "ir.actions.act_window_close", + infos: { + ReservationConfiguration: { + property_id: {id: state.property_id.data.id}, + reservation_id: {id: state.reservation_id.data.id}, + start: state.start, + stop: state.stop, + no_of_guests: state.no_of_guests, + product_uom_qty: state.duration, + guest_ids: guest_ids, + }, + }, + }); + }); + }, + }); + + return PMSConfiguratorFormController; +}); diff --git a/pms_sale/static/src/js/pms_configurator_view.js b/pms_sale/static/src/js/pms_configurator_view.js new file mode 100644 index 000000000..9590c1fc6 --- /dev/null +++ b/pms_sale/static/src/js/pms_configurator_view.js @@ -0,0 +1,20 @@ +odoo.define("pms_sale.PMSConfiguratorFormView", function (require) { + "use strict"; + + var PMSConfiguratorFormController = require("pms_sale.PMSConfiguratorFormController"); + var FormView = require("web.FormView"); + var viewRegistry = require("web.view_registry"); + + /** + * @see EventConfiguratorFormController for more information + */ + var PMSConfiguratorFormView = FormView.extend({ + config: _.extend({}, FormView.prototype.config, { + Controller: PMSConfiguratorFormController, + }), + }); + + viewRegistry.add("pms_configurator_form", PMSConfiguratorFormView); + + return PMSConfiguratorFormView; +}); diff --git a/pms_sale/static/src/js/pms_configurator_widget.js b/pms_sale/static/src/js/pms_configurator_widget.js new file mode 100644 index 000000000..368a7f5df --- /dev/null +++ b/pms_sale/static/src/js/pms_configurator_widget.js @@ -0,0 +1,189 @@ +odoo.define("pms_sale.product_configurator", function (require) { + "use strict"; + + var ProductConfiguratorWidget = require("sale.product_configurator"); + + /** + * Extension of the ProductConfiguratorWidget to support event product + * configuration. It opens when an event product_product is set. + * + * The event information include: + * - property_id + * - reservation_id + * + */ + ProductConfiguratorWidget.include({ + /** + * @returns {Boolean} + * + * @override + * @private + */ + _isConfigurableLine: function () { + return this.recordData.reservation_ok || this._super.apply(this, arguments); + }, + + /** + * @param {integer} productId + * @param {String} dataPointId + * @returns {Promise} stopPropagation true if a suitable configurator + * has been found. + * + * @override + * @private + */ + _onProductChange: function (productId, dataPointId) { + var self = this; + return this._super.apply(this, arguments).then(function (stopPropagation) { + if (stopPropagation || productId === undefined) { + return Promise.resolve(true); + } + return self._checkForReservation(productId, dataPointId); + }); + }, + + get_parent_partner: function () { + var self = this; + if ( + self.getParent() && + self.getParent().getParent() && + self.getParent().getParent().recordData && + self.getParent().getParent().recordData.partner_id && + self.getParent().getParent().recordData.partner_id.res_id + ) { + return self.getParent().getParent().recordData.partner_id.res_id; + } + return false; + }, + + /** + * This method will check if the productId needs configuration or not: + * + * @param {integer} productId + * @param {String} dataPointId + * @returns {Promise} stopPropagation true if the product is an event + * ticket. + * + * @private + */ + _checkForReservation: function (productId, dataPointId) { + var self = this; + return this._rpc({ + model: "product.product", + method: "read", + args: [productId, ["reservation_ok"]], + }).then(function (result) { + if ( + Array.isArray(result) && + result.length && + result[0].reservation_ok + ) { + var web_partner_id = self.get_parent_partner(); + var result_vals = { + default_product_id: productId, + }; + if (web_partner_id) { + result_vals.web_partner_id = web_partner_id; + } + + if (self.recordData && self.recordData.currency_id) { + result_vals.default_currency_id = + self.recordData.currency_id.data.id; + } + self._openReservationConfigurator(result_vals, dataPointId); + return Promise.resolve(true); + } + return Promise.resolve(false); + }); + }, + + /** + * Opens the event configurator in 'edit' mode. + * + * @override + * @private + */ + _onEditLineConfiguration: function () { + if (this.recordData.reservation_ok) { + var defaultValues = { + default_product_id: this.recordData.product_id.data.id, + }; + + if (this.recordData.property_id) { + defaultValues.default_property_id = this.recordData.property_id.data.id; + } + + if (this.recordData.reservation_id) { + defaultValues.default_reservation_id = this.recordData.reservation_id.data.id; + } + if (this.recordData.start) { + defaultValues.default_start = this.recordData.start; + } + if (this.recordData.stop) { + defaultValues.default_stop = this.recordData.stop; + } + if (this.recordData.currency_id) { + defaultValues.default_currency_id = this.recordData.currency_id.data.id; + } + if (this.recordData.id) { + defaultValues.sale_line_ine = this.recordData.id; + } + var web_partner_id = this.get_parent_partner(); + if (web_partner_id) { + defaultValues.web_partner_id = web_partner_id; + } + + this._openReservationConfigurator(defaultValues, this.dataPointID); + } else { + this._super.apply(this, arguments); + } + }, + + /** + * Opens the event configurator to allow configuring the SO line with events + * information. + * + * When the window is closed, configured values are used to trigger a + * 'field_changed' event to modify the current SO line. + * + * If the window is closed without providing the required values 'property_id' + * and 'reservation_id', the product_id field is cleaned. + * + * @param {Object} data various "default_" values + * @param {String} dataPointId + * + * @private + */ + _openReservationConfigurator: function (data, dataPointId) { + var self = this; + + this.do_action("pms_sale.pms_configurator_action", { + additional_context: data, + on_close: function (result) { + if (result && !result.special) { + self.trigger_up("field_changed", { + dataPointID: dataPointId, + changes: result.ReservationConfiguration, + onSuccess: function () { + // Call post-init function. + self._onLineConfigured(); + }, + }); + } else if ( + !self.recordData.property_id || + !self.recordData.reservation_id + ) { + self.trigger_up("field_changed", { + dataPointID: dataPointId, + changes: { + product_id: false, + name: "", + }, + }); + } + }, + }); + }, + }); + return ProductConfiguratorWidget; +}); diff --git a/pms_sale/static/src/js/timeline.js b/pms_sale/static/src/js/timeline.js new file mode 100644 index 000000000..d107dc82a --- /dev/null +++ b/pms_sale/static/src/js/timeline.js @@ -0,0 +1,300 @@ +odoo.define("pms_sale.timeline", function (require) { + "use strict"; + + const core = require("web.core"); + const time = require("web.time"); + const TimelineRenderer = require("web_timeline.TimelineRenderer"); + const TimelineView = require("web_timeline.TimelineView"); + + const _t = core._t; + TimelineView.prototype.jsLibs.push( + "/web/static/lib/daterangepicker/daterangepicker.js" + ); + TimelineView.prototype.jsLibs.push("/web/static/src/js/libs/daterangepicker.js"); + TimelineView.prototype.cssLibs.push( + "/web/static/lib/daterangepicker/daterangepicker.css" + ); + TimelineRenderer.include({ + willStart: function () { + this.city = []; + this.values = {}; + return Promise.all([ + this._super.apply(this, arguments), + this.get_selections(), + ]); + }, + get_selections: function () { + var self = this; + return this._rpc({ + model: "pms.reservation", + method: "get_selections", + args: [], + }).then(function (rec) { + self.values = rec; + self.city = rec.city; + }); + }, + init: function (parent, state, params) { + var self = this; + this._super.apply(this, arguments); + this.modelName = params.model; + this.date_start = params.date_start; + this.date_stop = params.date_stop; + this.view = params.view; + this.city_value = false; + this.$filter_reservation = false; + this.datepicker_value = false; + this.bedrooms_value = false; + // Find their matches + if (this.modelName === "pms.reservation") { + // Find custom color if mentioned + if (params.arch.attrs.custom_color === "true") { + this._rpc({ + model: "pms.stage", + method: "get_color_information", + args: [[]], + }).then(function (result) { + self.colors = result; + }); + } + } + }, + start: function () { + var self = this; + this._super.apply(this, arguments); + if (this.modelName === "pms.reservation") { + const $filter_reservation = $( + core.qweb.render("TimelineReservationFilter") + ); + self.$filter_reservation = $filter_reservation; + _.each(this.city, function (city) { + const newOption = new Option(city, city); + $filter_reservation + .find(".oe_timeline_select_city") + .append(newOption, undefined); + }); + this.$el.find(".oe_timeline_buttons").append($filter_reservation); + $filter_reservation + .find(".oe_timeline_text_datepicker") + .daterangepicker({ + autoApply: true, + }); + $filter_reservation + .find(".oe_timeline_button_search") + .click(function () { + self._onsearchbutton(); + }); + } + }, + _onsearchbutton: function () { + if (this.$el.find(".oe_timeline_select_city").val() !== "Select City") { + this.city_value = this.$el.find(".oe_timeline_select_city").val(); + } else { + this.city_value = false; + } + if (this.$el.find(".oe_timeline_text_datepicker").val()) { + this.datepicker_value = this.$el + .find(".oe_timeline_text_datepicker") + .val(); + } else { + this.datepicker_value = false; + } + if (this.$el.find(".oe_timeline_text_bedrooms").val()) { + this.bedrooms_value = this.$el.find(".oe_timeline_text_bedrooms").val(); + } else { + this.bedrooms_value = false; + } + this.on_data_loaded(this.view.model.data.data, this.last_group_bys); + }, + split_groups: function (events, group_bys) { + if (group_bys.length === 0) { + return events; + } + const groups = []; + for (const evt of events) { + const group_name = evt[_.first(group_bys)]; + if (group_name) { + if (group_name instanceof Array) { + const group = _.find( + groups, + (existing_group) => existing_group.id === group_name[0] + ); + if (_.isUndefined(group)) { + groups.push({ + id: group_name[0], + content: group_name[1], + }); + } + } + } + } + return groups; + }, + get_vals: function () { + return { + city_value: this.city_value, + datepicker_value: this.datepicker_value, + bedrooms_value: this.bedrooms_value, + }; + }, + on_data_loaded_2: function (events, group_bys) { + var self = this; + if (this.modelName === "pms.reservation") { + var data = []; + var groups = []; + this.grouped_by = group_bys; + _.each(events, function (event) { + if (event[self.date_start]) { + data.push(self.event_data_transform(event)); + } + }); + groups = self.split_groups(events, group_bys); + if (group_bys[0] === "property_id") { + var groups_user_ids = []; + for (var g in groups) { + groups_user_ids.push(groups[g].id); + } + self._rpc({ + model: "pms.property", + method: "get_property_information", + args: [this.get_vals()], + }).then(function (result) { + self.property_ids = []; + self.properties = []; + self.properties.push(result); + for (var r in result) { + self.property_ids.push(result[r].id); + } + var res_user_groups = []; + var res_user_groups_ids = []; + + for (var u in self.property_ids) { + if ( + !(self.property_ids[u] in groups_user_ids) || + self.property_ids[u] !== -1 + ) { + // Get User Name + var user_name = "-"; + for (var n in self.properties[0]) { + if ( + self.properties[0][n].id === + self.property_ids[u] + ) { + user_name = + self.properties[0][n].ref || + self.properties[0][n].name; + } + } + var is_available = false; + for (var i in groups) { + if (groups[i].id === self.property_ids[u]) { + if ( + !res_user_groups_ids.includes( + self.property_ids[u] + ) + ) { + res_user_groups.push({ + id: self.property_ids[u], + content: _t(user_name), + }); + res_user_groups_ids.push( + self.property_ids[u] + ); + } + } + } + if (!is_available) { + if ( + !res_user_groups_ids.includes( + self.property_ids[u] + ) + ) { + res_user_groups.push({ + id: self.property_ids[u], + content: _t(user_name), + }); + res_user_groups_ids.push(self.property_ids[u]); + } + } + } + } + self.timeline.setGroups(res_user_groups); + self.timeline.setItems(data); + self.timeline.setOptions({ + orientation: "top", + }); + if (self.datepicker_value) { + var value = self.datepicker_value.split("-"); + const date_value = new moment(value[0], "MM/DD/YYYY"); + self.timeline.moveTo(date_value); + } + }); + } + } + return this._super.apply(this, arguments); + }, + event_data_transform: function (evt) { + if (this.modelName === "pms.reservation") { + var self = this; + var date_start = new moment(); + var date_stop = null; + date_start = time.auto_str_to_date(evt[this.date_start]); + date_stop = this.date_stop + ? time.auto_str_to_date(evt[this.date_stop]) + : null; + var group = evt[self.last_group_bys[0]]; + if (group && group instanceof Array) { + group = _.first(group); + } else { + group = -1; + } + _.each(self.colors, function (color) { + if ( + Function( + '"use strict";return\'' + + evt[color.field] + + "' " + + color.opt + + " '" + + color.value + + "'" + )() + ) { + self.color = color.color; + } else if ( + Function( + '"use strict";return\'' + + evt[color.field][1] + + "' " + + color.opt + + " '" + + color.value + + "'" + ) + ) { + self.color = color.color; + } + }); + var content = _.isUndefined(evt.__name) ? evt.display_name : evt.__name; + if (this.arch.children.length) { + content = this.render_timeline_item(evt); + } + var r = { + start: date_start, + content: content, + id: evt.id, + group: group, + evt: evt, + style: "background-color: " + self.color + ";", + }; + + if (date_stop && !moment(date_start).isSame(date_stop)) { + r.end = date_stop; + } + self.color = null; + return r; + } + return this._super.apply(this, arguments); + }, + }); +}); diff --git a/pms_sale/static/src/xml/timeline.xml b/pms_sale/static/src/xml/timeline.xml new file mode 100644 index 000000000..18baad106 --- /dev/null +++ b/pms_sale/static/src/xml/timeline.xml @@ -0,0 +1,25 @@ + + diff --git a/pms_sale/tests/__init__.py b/pms_sale/tests/__init__.py new file mode 100644 index 000000000..d8295b67a --- /dev/null +++ b/pms_sale/tests/__init__.py @@ -0,0 +1,3 @@ +from . import test_account_move +from . import test_pms_property +from . import test_pms_reservation diff --git a/pms_sale/tests/test_account_move.py b/pms_sale/tests/test_account_move.py new file mode 100644 index 000000000..2330f4d35 --- /dev/null +++ b/pms_sale/tests/test_account_move.py @@ -0,0 +1,53 @@ +# Copyright (c) 2022 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import date, timedelta + +from odoo.tests import SavepointCase + + +class TestAccountMove(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product = cls.env.ref("pms_sale.product_product_reservation") + cls.partner_owner = cls.env["res.partner"].create({"name": "Property Owner"}) + cls.partner_property = cls.env["res.partner"].create({"name": "Property"}) + cls.property = cls.env["pms.property"].create( + {"owner_id": cls.partner_owner.id, "partner_id": cls.partner_property.id} + ) + cls.reservation = cls.env["pms.reservation"].create( + {"name": "Test Reservation", "property_id": cls.property.id} + ) + cls.sale_order_obj = cls.env["sale.order"] + cls.partner = cls.env["res.partner"].create({"name": "TEST CUSTOMER"}) + cls.sale_pricelist = cls.env["product.pricelist"].create( + {"name": "Test Pricelist", "currency_id": cls.env.ref("base.USD").id} + ) + cls.so = cls.sale_order_obj.create( + { + "partner_id": cls.partner.id, + "date_order": date.today() + timedelta(days=1), + "pricelist_id": cls.sale_pricelist.id, + "order_line": [ + ( + 0, + 0, + { + "name": cls.reservation.name, + "product_id": cls.product.id, + "product_uom_qty": 5.0, + "product_uom": cls.product.uom_po_id.id, + "price_unit": 10.0, + "qty_delivered": 1, + "pms_reservation_id": cls.reservation.id, + "property_id": cls.reservation.property_id.id, + }, + ), + ], + } + ) + + def test_compute_reservation_count(self): + self.so.sudo().action_confirm() + invoice = self.so._create_invoices() + self.assertEqual(invoice.reservation_count, 1) diff --git a/pms_sale/tests/test_pms_property.py b/pms_sale/tests/test_pms_property.py new file mode 100644 index 000000000..2a91a51cf --- /dev/null +++ b/pms_sale/tests/test_pms_property.py @@ -0,0 +1,107 @@ +# Copyright (c) 2022 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests import SavepointCase + + +class TestPMSProperty(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product = cls.env.ref("pms_sale.product_product_reservation") + cls.partner_owner = cls.env["res.partner"].create({"name": "Property Owner"}) + cls.partner_property = cls.env["res.partner"].create({"name": "Property"}) + cls.property = cls.env["pms.property"].create( + {"owner_id": cls.partner_owner.id, "partner_id": cls.partner_property.id} + ) + cls.partner_owner_2 = cls.env["res.partner"].create( + {"name": "Property Owner 2"} + ) + cls.partner_property_2 = cls.env["res.partner"].create({"name": "Property 2"}) + cls.property_2 = cls.env["pms.property"].create( + { + "owner_id": cls.partner_owner.id, + "partner_id": cls.partner_property_2.id, + "max_nights": 21, + } + ) + + cls.my_pms_property_reservation = cls.env["pms.property.reservation"].create( + { + "name": "PMS property reservation 1", + "product_id": cls.product.id, + "property_id": cls.property.id, + } + ) + cls.my_pms_property_reservation_2 = cls.env["pms.property.reservation"].create( + { + "name": "PMS property reservation 2", + "product_id": cls.product.id, + "property_id": cls.property.id, + } + ) + + cls.pms_property2 = cls.env["pms.property"].create( + { + "name": "Property_2", + "ref": "test ref", + "owner_id": cls.partner_owner.id, + "city": "la", + "room_ids": [ + ( + 0, + 0, + { + "name": "Room 101", + "type_id": 7, + }, + ), + ( + 0, + 0, + { + "name": "Room 102", + "type_id": 7, + }, + ), + ], + "property_child_ids": [], + "reservation_ids": [ + ( + 0, + 0, + { + "name": cls.my_pms_property_reservation.name, + "product_id": cls.product.id, + "property_id": cls.property.id, + }, + ), + ( + 0, + 0, + { + "name": cls.my_pms_property_reservation_2.name, + "product_id": cls.product.id, + "property_id": cls.property.id, + }, + ), + ], + } + ) + + cls.reservation = cls.env["pms.reservation"].create( + { + "name": "Test Reservation", + "property_id": cls.property_2.id, + "start": "2022-06-01", + "stop": "2022-06-15", + } + ) + + def test_get_property_information(self): + vals = { + "city_value": "la", + "bedrooms_value": 2, + "datepicker_value": "12/1/2022-12/15/2022", + } + + self.assertEqual(len(self.pms_property2.get_property_information(vals)), 1) diff --git a/pms_sale/tests/test_pms_reservation.py b/pms_sale/tests/test_pms_reservation.py new file mode 100644 index 000000000..6fa0e7bab --- /dev/null +++ b/pms_sale/tests/test_pms_reservation.py @@ -0,0 +1,117 @@ +# Copyright (c) 2022 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests import SavepointCase + + +class TestPMSReservation(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.product = cls.env.ref("pms_sale.product_product_reservation") + cls.partner_owner = cls.env["res.partner"].create({"name": "Property Owner"}) + cls.partner_property = cls.env["res.partner"].create({"name": "Property"}) + cls.property = cls.env["pms.property"].create( + { + "owner_id": cls.partner_owner.id, + "partner_id": cls.partner_property.id, + "checkin": 12.0, + "checkout": 15.0, + } + ) + cls.property_reservation = cls.env["pms.property.reservation"].create( + { + "name": "PMS property reservation 1", + "product_id": cls.product.id, + "property_id": cls.property.id, + } + ) + cls.property.write( + { + "name": "Property", + "ref": "test ref", + "city": "la", + "no_of_guests": 4, + "min_nights": 1, + "max_nights": 30, + "reservation_ids": [ + ( + 0, + 0, + { + "name": cls.property_reservation.name, + "product_id": cls.product.id, + "property_id": cls.property.id, + }, + ) + ], + } + ) + cls.reservation = cls.env["pms.reservation"].create( + { + "name": "Test Reservation", + "property_id": cls.property.id, + "start": "2022-06-01", + "stop": "2022-06-15", + } + ) + + def test_read_group_stage_ids(self): + stages = self.env["pms.stage"] + stages = self.reservation._read_group_stage_ids(stages, [], False) + self.assertEqual(len(stages), 6) + + def test_onchange_property_id(self): + self.reservation.onchange_property_id() + self.assertEqual( + self.reservation.start.strftime("%m/%d/%Y %H:%M"), "06/01/2022 10:00" + ) + self.assertEqual( + self.reservation.stop.strftime("%m/%d/%Y %H:%M"), "06/15/2022 13:00" + ) + + def test_check_max_no_of_guests(self): + self.reservation._check_max_no_of_guests() + + def test_check_no_of_reservations(self): + self.reservation._check_no_of_reservations() + + def test_check_no_of_nights(self): + self.reservation._check_no_of_nights() + + def test_action_book(self): + self.reservation.action_book() + self.assertEqual( + self.reservation.stage_id.id, + self.env.ref("pms_sale.pms_stage_booked", raise_if_not_found=False).id, + ) + + def test_action_confirm(self): + self.reservation.action_confirm() + self.assertEqual( + self.reservation.stage_id.id, + self.env.ref("pms_sale.pms_stage_confirmed", raise_if_not_found=False).id, + ) + + def test_action_check_in(self): + self.reservation.action_check_in() + self.assertEqual( + self.reservation.stage_id.id, + self.env.ref("pms_sale.pms_stage_checked_in", raise_if_not_found=False).id, + ) + + def test_action_check_out(self): + self.reservation.action_check_out() + self.assertEqual( + self.reservation.stage_id.id, + self.env.ref("pms_sale.pms_stage_checked_out", raise_if_not_found=False).id, + ) + + def test_action_cancel(self): + self.reservation.action_cancel() + self.assertEqual( + self.reservation.stage_id.id, + self.env.ref("pms_sale.pms_stage_cancelled", raise_if_not_found=False).id, + ) + + def test_action_view_invoices(self): + self.reservation.action_view_invoices() diff --git a/pms_sale/views/account_move.xml b/pms_sale/views/account_move.xml new file mode 100644 index 000000000..a6366d609 --- /dev/null +++ b/pms_sale/views/account_move.xml @@ -0,0 +1,35 @@ + + + + pms.property.invoice.form.pms + account.move + + + +
    + +
    + + + + + +
    +
    + +
    diff --git a/pms_sale/views/assets.xml b/pms_sale/views/assets.xml new file mode 100644 index 000000000..de155fe32 --- /dev/null +++ b/pms_sale/views/assets.xml @@ -0,0 +1,25 @@ + + +