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 000000000..bffa2c63a Binary files /dev/null and b/pms_sale/static/description/agency_logo.png differ diff --git a/pms_sale/static/description/agency_logo1.png b/pms_sale/static/description/agency_logo1.png new file mode 100644 index 000000000..d20ba13b3 Binary files /dev/null and b/pms_sale/static/description/agency_logo1.png differ diff --git a/pms_sale/static/description/avatar.png b/pms_sale/static/description/avatar.png new file mode 100644 index 000000000..dff716c28 Binary files /dev/null and b/pms_sale/static/description/avatar.png differ diff --git a/pms_sale/static/description/icon.png b/pms_sale/static/description/icon.png new file mode 100644 index 000000000..a81dd64c3 Binary files /dev/null and b/pms_sale/static/description/icon.png differ 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 @@ + + +