From 80c43faa252e830a01ca49ee6659e3515933f902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Lodeiros?= Date: Sat, 1 Aug 2020 22:36:39 +0200 Subject: [PATCH] [IMP] Precommit --- .travis.yml | 36 +- pms/__init__.py | 1 - pms/__manifest__.py | 142 +- pms/data/cron_jobs.xml | 12 +- pms/data/email_template_cancel.xml | 28 +- pms/data/email_template_exit.xml | 12 +- pms/data/email_template_reserv.xml | 28 +- pms/data/menus.xml | 65 +- pms/data/pms_data.xml | 31 +- pms/data/pms_sequence.xml | 4 +- pms/demo/pms_demo.xml | 306 ++- pms/models/inherited_account_move.py | 147 +- pms/models/inherited_account_move_line.py | 38 +- pms/models/inherited_account_payment.py | 147 +- pms/models/inherited_ir_http.py | 42 +- pms/models/inherited_mail_compose_message.py | 23 +- pms/models/inherited_payment_return.py | 33 +- pms/models/inherited_product_pricelist.py | 58 +- pms/models/inherited_product_template.py | 29 +- pms/models/inherited_res_company.py | 6 +- pms/models/inherited_res_partner.py | 53 +- pms/models/inherited_res_users.py | 26 +- pms/models/pms_amenity.py | 22 +- pms/models/pms_amenity_type.py | 19 +- pms/models/pms_board_service.py | 2 +- pms/models/pms_board_service_room_type.py | 158 +- .../pms_board_service_room_type_line.py | 22 +- pms/models/pms_cancelation_rule.py | 51 +- pms/models/pms_checkin_partner.py | 239 +-- pms/models/pms_floor.py | 18 +- pms/models/pms_folio.py | 1164 ++++++----- pms/models/pms_property.py | 74 +- pms/models/pms_reservation.py | 1794 +++++++++-------- pms/models/pms_reservation_line.py | 58 +- pms/models/pms_room.py | 111 +- pms/models/pms_room_closure_reason.py | 12 +- pms/models/pms_room_type.py | 169 +- pms/models/pms_room_type_class.py | 27 +- pms/models/pms_room_type_restriction.py | 34 +- pms/models/pms_room_type_restriction_item.py | 46 +- pms/models/pms_service.py | 570 +++--- pms/models/pms_service_line.py | 86 +- pms/models/pms_shared_room.py | 129 +- pms/readme/CONFIGURE.rst | 2 +- pms/readme/CREDITS.rst | 2 +- pms/readme/DESCRIPTION.rst | 2 +- pms/readme/INSTALL.rst | 2 +- pms/readme/ROADMAP.rst | 2 +- pms/readme/USAGE.rst | 2 +- pms/report/pms_folio.xml | 2 +- pms/report/pms_folio_templates.xml | 514 +++-- pms/security/pms_security.xml | 8 +- .../src/js/views/list/list_controller.js | 52 +- .../src/js/widgets/switch_hotel_menu.js | 128 +- pms/static/src/xml/pms_base_templates.xml | 26 +- pms/templates/pms_email_template.xml | 196 +- pms/tests/__init__.py | 1 - pms/tests/common.py | 90 +- pms/tests/test_folio.py | 19 +- pms/tests/test_hotel_property.py | 3 +- pms/tests/test_hotel_room.py | 32 +- pms/tests/test_hotel_room_type.py | 17 +- pms/tests/test_inherited_ir_http.py | 11 +- pms/tests/test_inherited_product_pricelist.py | 15 +- pms/tests/test_massive_changes.py | 47 +- pms/tests/test_reservation.py | 124 +- pms/views/general.xml | 9 +- pms/views/inherited_account_move_views.xml | 32 +- pms/views/inherited_account_payment_views.xml | 215 +- .../inherited_product_pricelist_views.xml | 24 +- .../inherited_product_template_views.xml | 18 +- pms/views/inherited_res_partner_views.xml | 53 +- pms/views/inherited_res_users_views.xml | 16 +- pms/views/inherited_webclient_templates.xml | 7 +- pms/views/pms_amenity_type_views.xml | 24 +- pms/views/pms_amenity_views.xml | 44 +- .../pms_board_service_room_type_views.xml | 14 +- pms/views/pms_board_service_views.xml | 30 +- pms/views/pms_cancelation_rule_views.xml | 34 +- pms/views/pms_checkin_partner_views.xml | 215 +- pms/views/pms_floor_views.xml | 24 +- pms/views/pms_folio_views.xml | 459 +++-- pms/views/pms_property_views.xml | 41 +- pms/views/pms_reservation_views.xml | 1104 +++++++--- pms/views/pms_room_closure_reason_views.xml | 25 +- pms/views/pms_room_type_class_views.xml | 60 +- .../pms_room_type_restriction_item_views.xml | 81 +- pms/views/pms_room_type_restriction_views.xml | 133 +- pms/views/pms_room_type_views.xml | 101 +- pms/views/pms_room_views.xml | 193 +- pms/views/pms_service_line_views.xml | 89 +- pms/views/pms_service_views.xml | 107 +- pms/views/pms_shared_room_views.xml | 127 +- pms/wizard/__init__.py | 1 - pms/wizard/folio_make_invoice_advance.py | 688 ++++--- .../folio_make_invoice_advance_views.xml | 252 ++- pms/wizard/massive_changes.py | 302 +-- pms/wizard/massive_changes.xml | 192 +- pms/wizard/massive_price_reservation_days.py | 46 +- pms/wizard/massive_price_reservation_days.xml | 72 +- pms/wizard/service_on_day.py | 53 +- pms/wizard/service_on_day.xml | 50 +- pms/wizard/split_reservation.py | 19 +- pms/wizard/split_reservation.xml | 48 +- pms/wizard/wizard_reservation.py | 700 ++++--- pms/wizard/wizard_reservation.xml | 263 ++- 106 files changed, 7510 insertions(+), 5824 deletions(-) diff --git a/.travis.yml b/.travis.yml index f72334ccb..83d22cf69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,28 +10,34 @@ addons: postgresql: "9.2" # minimal postgresql version for the daterange method apt: packages: - - expect-dev # provides unbuffer utility - - python-lxml # because pip installation is slow + - expect-dev # provides unbuffer utility + - python-lxml # because pip installation is slow env: global: - - VERSION="11.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" MAKEPOT="0" -# - TRANSIFEX_USER='transbot@odoo-community.org' -# - secure: "XLhGdCIh86zcqww9qBpnk8Xqsf1Pcgw9SKr7X0KYBHJofHj4Z6Kq/oVFjpZ1LSjadsaABKbwY7h4hvKEpxZwptCv+fNTOKYy7hXFLGYnDeNeWu4zA4LI7TA5uPvyZjZ+g2xc+9dzR/VbfRHNqjvmgiEidxxqLeOnNFZ5CHdOdCw=" + - VERSION="11.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0" MAKEPOT="0" + # - TRANSIFEX_USER='transbot@odoo-community.org' + # - secure: "XLhGdCIh86zcqww9qBpnk8Xqsf1Pcgw9SKr7X0KYBHJofHj4Z6Kq/oVFjpZ1LSjadsaABKbwY7h4hvKEpxZwptCv+fNTOKYy7hXFLGYnDeNeWu4zA4LI7TA5uPvyZjZ+g2xc+9dzR/VbfRHNqjvmgiEidxxqLeOnNFZ5CHdOdCw=" matrix: - # Option temporarily disabled - #- LINT_CHECK="1" - - TESTS="1" ODOO_REPO="odoo/odoo" - - TESTS="1" ODOO_REPO="OCA/OCB" + # Option temporarily disabled + #- LINT_CHECK="1" + - TESTS="1" ODOO_REPO="odoo/odoo" + - TESTS="1" ODOO_REPO="OCA/OCB" install: - - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools --depth=1 + - git clone https://github.com/OCA/maintainer-quality-tools.git + ${HOME}/maintainer-quality-tools --depth=1 - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - - git clone -b ${VERSION} https://github.com/OCA/web.git ${HOME}/dependencies/web --depth=1 - - git clone -b ${VERSION} https://github.com/OCA/partner-contact.git ${HOME}/dependencies/partner-contact --depth=1 - - git clone -b ${VERSION} https://github.com/OCA/account-payment.git ${HOME}/dependencies/account-payment --depth=1 - - git clone -b ${VERSION} https://github.com/OCA/connector.git ${HOME}/dependencies/connector --depth=1 - - git clone -b ${VERSION} https://github.com/OCA/queue.git ${HOME}/dependencies/queue --depth=1 + - git clone -b ${VERSION} https://github.com/OCA/web.git ${HOME}/dependencies/web + --depth=1 + - git clone -b ${VERSION} https://github.com/OCA/partner-contact.git + ${HOME}/dependencies/partner-contact --depth=1 + - git clone -b ${VERSION} https://github.com/OCA/account-payment.git + ${HOME}/dependencies/account-payment --depth=1 + - git clone -b ${VERSION} https://github.com/OCA/connector.git + ${HOME}/dependencies/connector --depth=1 + - git clone -b ${VERSION} https://github.com/OCA/queue.git ${HOME}/dependencies/queue + --depth=1 - pip install odoorpc - pip install cachetools>=2.0.1 - travis_install_nightly diff --git a/pms/__init__.py b/pms/__init__.py index 4d2f414c9..d6c56a95f 100644 --- a/pms/__init__.py +++ b/pms/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models from . import wizard diff --git a/pms/__manifest__.py b/pms/__manifest__.py index d4e3a01c0..59265b75d 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -2,79 +2,75 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'PMS (Property Management System)', - 'summary': "A property management system", - 'version': '13.0.1.0.0', - 'development_status': 'Beta', - 'category': 'Generic Modules/Property Management System', - 'website': 'https://github.com/hootel/hootel', - 'author': 'Darío Lodeiros, ' - 'Alexandre Díaz, ' - 'Jose Luis Algara, ' - 'Pablo Quesada ', - 'license': "AGPL-3", - 'application': True, - 'installable': True, - 'depends': [ - 'base', - 'mail', - 'account_payment_return', - 'partner_firstname', - 'email_template_qweb', - 'sales_team', - 'sale' + "name": "PMS (Property Management System)", + "summary": "A property management system", + "version": "13.0.1.0.0", + "development_status": "Beta", + "category": "Generic Modules/Property Management System", + "website": "https://github.com/hootel/hootel", + "author": "Darío Lodeiros, " + "Alexandre Díaz, " + "Jose Luis Algara, " + "Pablo Quesada ", + "license": "AGPL-3", + "application": True, + "installable": True, + "depends": [ + "base", + "mail", + "account_payment_return", + "partner_firstname", + "email_template_qweb", + "sales_team", + "sale", ], - 'data': [ - 'security/pms_security.xml', - 'security/ir.model.access.csv', - 'data/cron_jobs.xml', - 'data/pms_data.xml', - 'data/pms_sequence.xml', - 'data/email_template_cancel.xml', - 'data/email_template_reserv.xml', - 'data/email_template_exit.xml', - 'report/pms_folio.xml', - 'report/pms_folio_templates.xml', - 'templates/pms_email_template.xml', - 'wizard/massive_changes.xml', - 'wizard/massive_price_reservation_days.xml', - 'wizard/service_on_day.xml', - 'wizard/split_reservation.xml', - 'wizard/wizard_reservation.xml', - 'views/general.xml', - 'data/menus.xml', - 'views/pms_amenity_views.xml', - 'views/pms_amenity_type_views.xml', - 'views/pms_board_service_views.xml', - 'views/pms_board_service_room_type_views.xml', - 'views/pms_cancelation_rule_views.xml', - 'views/pms_checkin_partner_views.xml', - 'views/pms_floor_views.xml', - 'views/pms_folio_views.xml', - 'views/pms_property_views.xml', - 'views/pms_reservation_views.xml', - 'views/pms_room_type_views.xml', - 'views/pms_room_views.xml', - 'views/pms_room_closure_reason_views.xml', - 'views/inherited_account_payment_views.xml', - 'views/inherited_account_move_views.xml', - 'views/inherited_res_users_views.xml', - 'views/pms_room_type_class_views.xml', - 'views/pms_room_type_restriction_views.xml', - 'views/pms_room_type_restriction_item_views.xml', - 'views/pms_service_views.xml', - 'views/pms_service_line_views.xml', - 'views/pms_shared_room_views.xml', - 'views/inherited_res_partner_views.xml', - 'views/inherited_product_pricelist_views.xml', - 'views/inherited_product_template_views.xml', - 'views/inherited_webclient_templates.xml', - 'wizard/folio_make_invoice_advance_views.xml', - ], - 'demo': [ - 'demo/pms_demo.xml' - ], - 'qweb': [ - 'static/src/xml/pms_base_templates.xml', + "data": [ + "security/pms_security.xml", + "security/ir.model.access.csv", + "data/cron_jobs.xml", + "data/pms_data.xml", + "data/pms_sequence.xml", + "data/email_template_cancel.xml", + "data/email_template_reserv.xml", + "data/email_template_exit.xml", + "report/pms_folio.xml", + "report/pms_folio_templates.xml", + "templates/pms_email_template.xml", + "wizard/massive_changes.xml", + "wizard/massive_price_reservation_days.xml", + "wizard/service_on_day.xml", + "wizard/split_reservation.xml", + "wizard/wizard_reservation.xml", + "views/general.xml", + "data/menus.xml", + "views/pms_amenity_views.xml", + "views/pms_amenity_type_views.xml", + "views/pms_board_service_views.xml", + "views/pms_board_service_room_type_views.xml", + "views/pms_cancelation_rule_views.xml", + "views/pms_checkin_partner_views.xml", + "views/pms_floor_views.xml", + "views/pms_folio_views.xml", + "views/pms_property_views.xml", + "views/pms_reservation_views.xml", + "views/pms_room_type_views.xml", + "views/pms_room_views.xml", + "views/pms_room_closure_reason_views.xml", + "views/inherited_account_payment_views.xml", + "views/inherited_account_move_views.xml", + "views/inherited_res_users_views.xml", + "views/pms_room_type_class_views.xml", + "views/pms_room_type_restriction_views.xml", + "views/pms_room_type_restriction_item_views.xml", + "views/pms_service_views.xml", + "views/pms_service_line_views.xml", + "views/pms_shared_room_views.xml", + "views/inherited_res_partner_views.xml", + "views/inherited_product_pricelist_views.xml", + "views/inherited_product_template_views.xml", + "views/inherited_webclient_templates.xml", + "wizard/folio_make_invoice_advance_views.xml", ], + "demo": ["demo/pms_demo.xml"], + "qweb": ["static/src/xml/pms_base_templates.xml",], } diff --git a/pms/data/cron_jobs.xml b/pms/data/cron_jobs.xml index 20d8bf032..7adf1a6a5 100644 --- a/pms/data/cron_jobs.xml +++ b/pms/data/cron_jobs.xml @@ -1,8 +1,6 @@ - + - - Automatic Checkout on past reservations @@ -13,11 +11,11 @@ code - + model.autocheckout() - - - diff --git a/pms/data/email_template_cancel.xml b/pms/data/email_template_cancel.xml index 898addefb..741fbc9b3 100644 --- a/pms/data/email_template_cancel.xml +++ b/pms/data/email_template_cancel.xml @@ -1,15 +1,17 @@ - + - - - - Cancel Reservation-Send by Email - Cancelación de su reserva en ${object.company_id.property_name} - ${(object.partner_id.id or '')} - - - + + + + Cancel Reservation-Send by Email + Cancelación de su reserva en ${object.company_id.property_name} + ${(object.partner_id.id or '')} + + + /*Global Styles*/ @@ -706,6 +708,6 @@ ]]> - - - + + + diff --git a/pms/data/email_template_exit.xml b/pms/data/email_template_exit.xml index 349f259ea..4aeec1d78 100644 --- a/pms/data/email_template_exit.xml +++ b/pms/data/email_template_exit.xml @@ -1,15 +1,19 @@ - + Exit Reservation-Send by Email - Gracias por alojarse con nosotros en ${object.company_id.property_name} + Gracias por alojarse con nosotros en ${object.company_id.property_name} ${(object.partner_id.id or '')} - + - /*Global Styles*/ + /*Global Styles*/ .marco {bgcolor:#f6f6f6; margin: 0; padding: 0; min-width: 100%!important;} a { color: #5e96ea; text-decoration: none; font-weight: bold;} img {height: auto;} diff --git a/pms/data/email_template_reserv.xml b/pms/data/email_template_reserv.xml index 991b46a50..24a9ed47d 100644 --- a/pms/data/email_template_reserv.xml +++ b/pms/data/email_template_reserv.xml @@ -1,15 +1,19 @@ - + - - - - Confirm Reservation-Send by Email - Confirmación de los detalles de su reserva en ${object.company_id.property_name} - ${(object.partner_id.id or '')} - - - + + + Confirm Reservation-Send by Email + Confirmación de los detalles de su reserva en ${object.company_id.property_name} + ${(object.partner_id.id or '')} + + + /*Global Styles*/ .marco { @@ -1037,6 +1041,6 @@ ]]> - - + + diff --git a/pms/data/menus.xml b/pms/data/menus.xml index 25fce53b3..89ce00b3c 100644 --- a/pms/data/menus.xml +++ b/pms/data/menus.xml @@ -1,27 +1,44 @@ - + - - - - - - - - - - - - + + + + + - + sequence="10" + action="action_pms_massive_change" + /> diff --git a/pms/data/pms_data.xml b/pms/data/pms_data.xml index 99e1e9499..ecae390d8 100644 --- a/pms/data/pms_data.xml +++ b/pms/data/pms_data.xml @@ -1,42 +1,35 @@ - + - - - + - - + - - - + Restriction Plan - My Property - - - + + + Rua Street Demo, s/n Commitsun city - + 15703 +34 123 456 879 commitsun@hootel.com https://www.commitsun.com - - - - - + + + + diff --git a/pms/data/pms_sequence.xml b/pms/data/pms_sequence.xml index d0cf5f98c..952b46c00 100644 --- a/pms/data/pms_sequence.xml +++ b/pms/data/pms_sequence.xml @@ -1,7 +1,6 @@ - + - PMS Folio @@ -9,6 +8,5 @@ F/ 5 - diff --git a/pms/demo/pms_demo.xml b/pms/demo/pms_demo.xml index 7c01ff3eb..97d3022a6 100644 --- a/pms/demo/pms_demo.xml +++ b/pms/demo/pms_demo.xml @@ -1,9 +1,7 @@ - + - - - + Ground Floor @@ -13,9 +11,7 @@ Second Floor - - - + Toiletries @@ -25,146 +21,141 @@ Kitchen facilities - - - + Shampoo and Soap - + High-quality Shampoo and Soap Essential Herbs - + Hair Dryer - + High speed Wired Internet access - + Wi-Fi - + Microwave oven - + Half-sized Refrigerator - + - - - + Room Conference - - - + Economic ECO 21.00 - - - + + Single SNG 20.00 - - + + Double DBL 25.00 - - + + Triple TRP 35.00 - - + + - Conference Room CFR 80.00 - - + + - - - + Economic-101 - - + + 2 Single-101 - - + + 1 Single-102 - - + + 1 Single-103 - - + + 1 Double-201 - - + + 2 1 Double-202 - - + + 2 Triple-203 - - + + 3 Open Talk Away Room - - + + 1 - - - + Breakfast Buffet 5.0 @@ -173,7 +164,6 @@ True True - Extra Bed 15.0 @@ -185,7 +175,6 @@ True True - Late Check-out 10.0 @@ -194,7 +183,6 @@ False False - Lunch 15.0 @@ -203,7 +191,6 @@ True True - Dinner 20.0 @@ -212,7 +199,6 @@ True True - Free Bar 40.0 @@ -221,116 +207,94 @@ True True - - - + BreakFast - + 'amount': 3})]" + /> fixed - Half Board - + ]" + /> fixed - FullBoard - + ]" + /> fixed - - - + - - - - + + + fixed - - - - + + + fixed - - - - - + + + + fixed - - - - - - + + + + fixed - - - - + + + fixed - - - + Maintenance - Used for closing of rooms which require a maintenance. You can specify the reason in the own reservation. + Used for closing of rooms which require a maintenance. You can specify the reason in the own reservation. - VIP Privacy - Used for closing of rooms for extra privacy. + Used for closing of rooms for extra privacy. - - - + - - + + })]" + /> - - + + })]" + /> - - + + })]" + /> - - + + })]" + /> - + out - + })]" + /> - Restriction Plan Demo - My pms Demo - - - + + + - - - + Economic ECO 21.00 - - - + + - + Single SNG 20.00 - - + + - diff --git a/pms/models/inherited_account_move.py b/pms/models/inherited_account_move.py index c50f80f61..71040d312 100644 --- a/pms/models/inherited_account_move.py +++ b/pms/models/inherited_account_move.py @@ -1,26 +1,25 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import models, fields, api, _ import json + +from odoo import _, api, fields, models from odoo.tools import float_is_zero class AccountMove(models.Model): - _inherit = 'account.move' + _inherit = "account.move" # Field Declarations folio_ids = fields.Many2many( - comodel_name='pms.folio', - compute='_computed_folio_origin') - pms_property_id = fields.Many2one( - 'pms.property') - from_folio = fields.Boolean( - compute='_computed_folio_origin') + comodel_name="pms.folio", compute="_computed_folio_origin" + ) + pms_property_id = fields.Many2one("pms.property") + from_folio = fields.Boolean(compute="_computed_folio_origin") outstanding_folios_debits_widget = fields.Text( - compute='_get_outstanding_folios_JSON') - has_folios_outstanding = fields.Boolean( - compute='_get_outstanding_folios_JSON') + compute="_get_outstanding_folios_JSON" + ) + has_folios_outstanding = fields.Boolean(compute="_get_outstanding_folios_JSON") # Compute and Search methods @@ -28,8 +27,8 @@ class AccountMove(models.Model): for inv in self: inv.from_folio = False inv.folio_ids = False - folios = inv.mapped('invoice_line_ids.reservation_ids.folio_id') - folios |= inv.mapped('invoice_line_ids.service_ids.folio_id') + folios = inv.mapped("invoice_line_ids.reservation_ids.folio_id") + folios |= inv.mapped("invoice_line_ids.service_ids.folio_id") if folios: inv.from_folio = True inv.folio_ids = [(6, 0, folios.ids)] @@ -38,21 +37,19 @@ class AccountMove(models.Model): def action_folio_payments(self): self.ensure_one() - sales = self.mapped('invoice_line_ids.sale_line_ids.order_id') - folios = self.env['pms.folio'].search([ - ('order_id.id', 'in', sales.ids) - ]) - payments_obj = self.env['account.payment'] - payments = payments_obj.search([('folio_id', 'in', folios.ids)]) - payment_ids = payments.mapped('id') - return{ - 'name': _('Payments'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.payment', - 'target': 'new', - 'type': 'ir.actions.act_window', - 'domain': [('id', 'in', payment_ids)], + sales = self.mapped("invoice_line_ids.sale_line_ids.order_id") + folios = self.env["pms.folio"].search([("order_id.id", "in", sales.ids)]) + payments_obj = self.env["account.payment"] + payments = payments_obj.search([("folio_id", "in", folios.ids)]) + payment_ids = payments.mapped("id") + return { + "name": _("Payments"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.payment", + "target": "new", + "type": "ir.actions.act_window", + "domain": [("id", "in", payment_ids)], } # Business methods @@ -60,62 +57,68 @@ class AccountMove(models.Model): self.ensure_one() self.outstanding_folios_debits_widget = json.dumps(False) if self.from_folio: - payment_ids = self.folio_ids.mapped('payment_ids.id') - if self.state == 'open': - account_partner = self.env['res.partner'].\ - _find_accounting_partner(self.partner_id).id + payment_ids = self.folio_ids.mapped("payment_ids.id") + if self.state == "open": + account_partner = ( + self.env["res.partner"]._find_accounting_partner(self.partner_id).id + ) domain = [ - ('account_id', '=', self.account_id.id), - ('partner_id', '!=', account_partner), - ('reconciled', '=', False), - ('payment_id', 'in', payment_ids), - '|', '&', - ('amount_residual_currency', '!=', 0.0), - ('currency_id', '!=', None), - '&', ('amount_residual_currency', '=', 0.0), - '&', ('currency_id', '=', None), - ('amount_residual', '!=', 0.0)] - if self.type in ('out_invoice', 'in_refund'): - domain.extend([('credit', '>', 0), ('debit', '=', 0)]) - type_payment = _('Outstanding credits in Folio') + ("account_id", "=", self.account_id.id), + ("partner_id", "!=", account_partner), + ("reconciled", "=", False), + ("payment_id", "in", payment_ids), + "|", + "&", + ("amount_residual_currency", "!=", 0.0), + ("currency_id", "!=", None), + "&", + ("amount_residual_currency", "=", 0.0), + "&", + ("currency_id", "=", None), + ("amount_residual", "!=", 0.0), + ] + if self.type in ("out_invoice", "in_refund"): + domain.extend([("credit", ">", 0), ("debit", "=", 0)]) + type_payment = _("Outstanding credits in Folio") else: - domain.extend([('credit', '=', 0), ('debit', '>', 0)]) - type_payment = _('Outstanding debits') - info = {'title': '', - 'outstanding': True, - 'content': [], - 'move_id': self.id} - lines = self.env['account.move.line'].search(domain) + domain.extend([("credit", "=", 0), ("debit", ">", 0)]) + type_payment = _("Outstanding debits") + info = { + "title": "", + "outstanding": True, + "content": [], + "move_id": self.id, + } + lines = self.env["account.move.line"].search(domain) currency_id = self.currency_id if len(lines) != 0: for line in lines: # get the outstanding residual value in inv. currency - if line.currency_id and line.currency_id == \ - self.currency_id: + if line.currency_id and line.currency_id == self.currency_id: amount_to_show = abs(line.amount_residual_currency) else: - amount_to_show = line.company_id.currency_id.\ - with_context(date=line.date).\ - compute(abs(line.amount_residual), - self.currency_id) + amount_to_show = line.company_id.currency_id.with_context( + date=line.date + ).compute(abs(line.amount_residual), self.currency_id) if float_is_zero( - amount_to_show, - precision_rounding=self.currency_id.rounding - ): + amount_to_show, precision_rounding=self.currency_id.rounding + ): continue if line.ref: - title = '%s : %s' % (line.move_id.name, line.ref) + title = "{} : {}".format(line.move_id.name, line.ref) else: title = line.move_id.name - info['content'].append({ - 'journal_name': line.ref or line.move_id.name, - 'title': title, - 'amount': amount_to_show, - 'currency': currency_id.symbol, - 'id': line.id, - 'position': currency_id.position, - 'digits': [69, self.currency_id.decimal_places], - }) - info['title'] = type_payment + info["content"].append( + { + "journal_name": line.ref or line.move_id.name, + "title": title, + "amount": amount_to_show, + "currency": currency_id.symbol, + "id": line.id, + "position": currency_id.position, + "digits": [69, self.currency_id.decimal_places], + } + ) + info["title"] = type_payment self.outstanding_folios_debits_widget = json.dumps(info) self.has_folio_outstanding = True diff --git a/pms/models/inherited_account_move_line.py b/pms/models/inherited_account_move_line.py index 1800b96ac..82f22403a 100644 --- a/pms/models/inherited_account_move_line.py +++ b/pms/models/inherited_account_move_line.py @@ -5,21 +5,33 @@ from odoo import fields, models class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" # Fields declaration reservation_ids = fields.Many2many( - 'pms.reservation', - 'reservation_move_rel', - 'move_line_id', 'reservation_id', - string='Reservations', readonly=True, copy=False) + "pms.reservation", + "reservation_move_rel", + "move_line_id", + "reservation_id", + string="Reservations", + readonly=True, + copy=False, + ) service_ids = fields.Many2many( - 'pms.service', - 'service_line_move_rel', - 'move_line_id', 'service_id', - string='Services', readonly=True, copy=False) + "pms.service", + "service_line_move_rel", + "move_line_id", + "service_id", + string="Services", + readonly=True, + copy=False, + ) reservation_line_ids = fields.Many2many( - 'pms.reservation.line', - 'reservation_line_move_rel', - 'move_line_id', 'reservation_line_id', - string='Reservation Lines', readonly=True, copy=False) + "pms.reservation.line", + "reservation_line_move_rel", + "move_line_id", + "reservation_line_id", + string="Reservation Lines", + readonly=True, + copy=False, + ) diff --git a/pms/models/inherited_account_payment.py b/pms/models/inherited_account_payment.py index d21ad0555..ba0853f81 100644 --- a/pms/models/inherited_account_payment.py +++ b/pms/models/inherited_account_payment.py @@ -1,49 +1,51 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _, api, fields, models from odoo.exceptions import except_orm -from odoo import models, fields, api, _ class AccountPayment(models.Model): - _inherit = 'account.payment' + _inherit = "account.payment" # Fields declaration - folio_id = fields.Many2one( - 'pms.folio', - string='Folio') + folio_id = fields.Many2one("pms.folio", string="Folio") amount_total_folio = fields.Float( - compute="_compute_folio_amount", store=True, - string="Total amount in folio", + compute="_compute_folio_amount", store=True, string="Total amount in folio", ) - save_amount = fields.Monetary(string='onchange_amount') + save_amount = fields.Monetary(string="onchange_amount") save_date = fields.Date() save_journal_id = fields.Integer() # Compute and Search methods - - @api.depends('state') + + @api.depends("state") def _compute_folio_amount(self): # FIXME: Finalize method res = [] fol = () for payment in self: if payment.folio_id: - fol = payment.env['pms.folio'].search([ - ('id', '=', payment.folio_id.id) - ]) + fol = payment.env["pms.folio"].search( + [("id", "=", payment.folio_id.id)] + ) else: return if not any(fol): return if len(fol) > 1: - raise except_orm(_('Warning'), _('This pay is related with \ - more than one Reservation.')) + raise except_orm( + _("Warning"), + _( + "This pay is related with \ + more than one Reservation." + ), + ) else: fol.compute_amount() return res # Constraints and onchanges - @api.onchange('amount', 'payment_date', 'journal_id') + @api.onchange("amount", "payment_date", "journal_id") def onchange_amount(self): if self._origin: self.save_amount = self._origin.amount @@ -52,100 +54,107 @@ class AccountPayment(models.Model): # Action methods """WIP""" - + def return_payment_folio(self): journal = self.journal_id partner = self.partner_id amount = self.amount reference = self.communication - account_move_lines = self.move_line_ids.filtered(lambda x: ( - x.account_id.internal_type == 'receivable')) + account_move_lines = self.move_line_ids.filtered( + lambda x: (x.account_id.internal_type == "receivable") + ) return_line_vals = { - 'move_line_ids': [(6, False, [x.id for x in account_move_lines])], - 'partner_id': partner.id, - 'amount': amount, - 'reference': reference, - } + "move_line_ids": [(6, False, [x.id for x in account_move_lines])], + "partner_id": partner.id, + "amount": amount, + "reference": reference, + } return_vals = { - 'journal_id': journal.id, - 'line_ids': [(0, 0, return_line_vals)], - } - return_pay = self.env['payment.return'].create(return_vals) + "journal_id": journal.id, + "line_ids": [(0, 0, return_line_vals)], + } + return_pay = self.env["payment.return"].create(return_vals) if self.save_amount: self.amount = self.save_amount if self.save_date: self.payment_date = self.save_date if self.save_journal_id: - self.journal_id = self.env['account.journal'].browse( - self.save_journal_id) + self.journal_id = self.env["account.journal"].browse(self.save_journal_id) return_pay.action_confirm() # Business methods - + def modify(self): self.cancel() vals = { - 'journal_id': self.journal_id, - 'partner_id': self.partner_id, - 'amount': self.amount, - 'payment_date': self.payment_date, - 'communication': self.communication, - 'state': 'draft'} + "journal_id": self.journal_id, + "partner_id": self.partner_id, + "amount": self.amount, + "payment_date": self.payment_date, + "communication": self.communication, + "state": "draft", + } self.update(vals) - self.with_context({'ignore_notification_post': True}).post() + self.with_context({"ignore_notification_post": True}).post() self._compute_folio_amount() if self.folio_id: msg = _("Payment %s modified: \n") % (self.communication) if self.save_amount and self.save_amount != self.amount: msg += _("Amount from %s to %s %s \n") % ( - self.save_amount, self.amount, self.currency_id.symbol) + self.save_amount, + self.amount, + self.currency_id.symbol, + ) if self.save_date and self.save_date != self.payment_date: - msg += _("Date from %s to %s \n") % ( - self.save_date, self.payment_date) - if self.save_journal_id and \ - self.save_journal_id != self.journal_id.id: + msg += _("Date from %s to %s \n") % (self.save_date, self.payment_date) + if self.save_journal_id and self.save_journal_id != self.journal_id.id: msg += _("Journal from %s to %s") % ( - self.env['account.journal'].browse( - self.save_journal_id).name, self.journal_id.name) - self.folio_id.message_post(subject=_('Payment'), body=msg) + self.env["account.journal"].browse(self.save_journal_id).name, + self.journal_id.name, + ) + self.folio_id.message_post(subject=_("Payment"), body=msg) - def delete(self): msg = False if self.folio_id: - msg = _("Deleted payment: %s %s ") % ( - self.amount, self.currency_id.symbol) + msg = _("Deleted payment: %s %s ") % (self.amount, self.currency_id.symbol) self.cancel() - self.move_name = '' + self.move_name = "" self.unlink() if msg: - self.folio_id.message_post(subject=_('Payment Deleted'), body=msg) + self.folio_id.message_post(subject=_("Payment Deleted"), body=msg) - def post(self): rec = super(AccountPayment, self).post() if rec and not self._context.get("ignore_notification_post", False): for pay in self: if pay.folio_id: - msg = _("Payment of %s %s registered from %s \ - using %s payment method") % \ - (pay.amount, pay.currency_id.symbol, - pay.communication, pay.journal_id.name) - pay.folio_id.message_post(subject=_('Payment'), body=msg) + msg = ( + _( + "Payment of %s %s registered from %s \ + using %s payment method" + ) + % ( + pay.amount, + pay.currency_id.symbol, + pay.communication, + pay.journal_id.name, + ) + ) + pay.folio_id.message_post(subject=_("Payment"), body=msg) - def modify_payment(self): self.ensure_one() - view_form_id = self.env.ref('pms.account_payment_view_form_folio').id + view_form_id = self.env.ref("pms.account_payment_view_form_folio").id # moves = self.mapped('move_ids.id') - return{ - 'name': _('Payment'), - 'view_type': 'form', - 'views': [(view_form_id, 'form')], - 'view_mode': 'tree,form', - 'res_model': 'account.payment', - 'target': 'new', - 'init_mode': 'edit', - 'type': 'ir.actions.act_window', - 'res_id': self.id, + return { + "name": _("Payment"), + "view_type": "form", + "views": [(view_form_id, "form")], + "view_mode": "tree,form", + "res_model": "account.payment", + "target": "new", + "init_mode": "edit", + "type": "ir.actions.act_window", + "res_id": self.id, } diff --git a/pms/models/inherited_ir_http.py b/pms/models/inherited_ir_http.py index 77caa06aa..5fdf1d9f0 100644 --- a/pms/models/inherited_ir_http.py +++ b/pms/models/inherited_ir_http.py @@ -2,13 +2,13 @@ # Copyright 2019 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, _ -from odoo.http import request +from odoo import _, models from odoo.exceptions import MissingError +from odoo.http import request class IrHttp(models.AbstractModel): - _inherit = 'ir.http' + _inherit = "ir.http" def session_info(self): res = super().session_info() @@ -16,21 +16,33 @@ class IrHttp(models.AbstractModel): display_switch_pms_menu = len(user.pms_property_ids) > 1 # TODO: limit properties to the current company? # or switch company automatically - res['pms_property_id'] = request.env.user.pms_property_id.id if \ - request.session.uid else None - res['user_properties'] = { - 'current_property': (user.pms_property_id.id, user.pms_property_id.name), - 'allowed_properties': [ - (property.id, property.name) for property in user.pms_property_ids - ] - } if display_switch_pms_menu else False + res["pms_property_id"] = ( + request.env.user.pms_property_id.id if request.session.uid else None + ) + res["user_properties"] = ( + { + "current_property": ( + user.pms_property_id.id, + user.pms_property_id.name, + ), + "allowed_properties": [ + (property.id, property.name) for property in user.pms_property_ids + ], + } + if display_switch_pms_menu + else False + ) if user.pms_property_id.company_id in user.company_ids: user.company_id = user.pms_property_id.company_id - res['company_id'] = user.pms_property_id.company_id.id + res["company_id"] = user.pms_property_id.company_id.id else: - return res #TODO Review method + return res # TODO Review method raise MissingError( - _("Wrong property and company access settings for this user. " - "Please review property and company for user %s") % user.name) + _( + "Wrong property and company access settings for this user. " + "Please review property and company for user %s" + ) + % user.name + ) return res diff --git a/pms/models/inherited_mail_compose_message.py b/pms/models/inherited_mail_compose_message.py index 054019c63..a617d61ca 100644 --- a/pms/models/inherited_mail_compose_message.py +++ b/pms/models/inherited_mail_compose_message.py @@ -5,20 +5,19 @@ from odoo import api, models class MailComposeMessage(models.TransientModel): - _inherit = 'mail.compose.message' + _inherit = "mail.compose.message" - def send_mail(self, auto_commit=False): - if self._context.get('default_model') == 'pms.folio' and \ - self._context.get('default_res_id') and \ - self._context.get('mark_so_as_sent'): - folio = self.env['pms.folio'].browse([ - self._context['default_res_id'] - ]) + if ( + self._context.get("default_model") == "pms.folio" + and self._context.get("default_res_id") + and self._context.get("mark_so_as_sent") + ): + folio = self.env["pms.folio"].browse([self._context["default_res_id"]]) if folio: - cmds = [(1, lid, {'to_send': False}) for lid in - folio.reservation_ids.ids] + cmds = [ + (1, lid, {"to_send": False}) for lid in folio.reservation_ids.ids + ] if any(cmds): folio.reservation_ids = cmds - return super(MailComposeMessage, self).send_mail( - auto_commit=auto_commit) + return super(MailComposeMessage, self).send_mail(auto_commit=auto_commit) diff --git a/pms/models/inherited_payment_return.py b/pms/models/inherited_payment_return.py index 0c1210504..3fdc2a2ba 100644 --- a/pms/models/inherited_payment_return.py +++ b/pms/models/inherited_payment_return.py @@ -1,38 +1,35 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, _ +from openerp import _, api, fields, models class PaymentReturn(models.Model): - _inherit = 'payment.return' + _inherit = "payment.return" # Fields declaration - folio_id = fields.Many2one( - 'pms.folio', - string='Folio') + folio_id = fields.Many2one("pms.folio", string="Folio") pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='folio_id.pms_property_id') + "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" + ) # Business methods - + def action_confirm(self): pay = super(PaymentReturn, self).action_confirm() if pay: folio_ids = [] - folios = self.env['pms.folio'].browse(folio_ids) + folios = self.env["pms.folio"].browse(folio_ids) for line in self.line_ids: - payments = self.env['account.payment'].search([ - ('move_line_ids', 'in', line.move_line_ids.ids) - ]) - folios_line = self.env['pms.folio'].browse( - payments.mapped('folio_id.id')) + payments = self.env["account.payment"].search( + [("move_line_ids", "in", line.move_line_ids.ids)] + ) + folios_line = self.env["pms.folio"].browse( + payments.mapped("folio_id.id") + ) for folio in folios_line: if self.id not in folio.return_ids.ids: - folio.update({'return_ids': [(4, self.id)]}) + folio.update({"return_ids": [(4, self.id)]}) msg = _("Return of %s registered") % (line.amount) - folio.message_post(subject=_('Payment Return'), body=msg) + folio.message_post(subject=_("Payment Return"), body=msg) folios += folios_line folios.compute_amount() diff --git a/pms/models/inherited_product_pricelist.py b/pms/models/inherited_product_pricelist.py index b33d66d25..6bbfe054d 100644 --- a/pms/models/inherited_product_pricelist.py +++ b/pms/models/inherited_product_pricelist.py @@ -1,6 +1,6 @@ # Copyright 2017 Alexandre Díaz, Pablo Quesada, Darío Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -9,39 +9,47 @@ class ProductPricelist(models.Model): A pricelist marked as daily is used as a daily rate plan for room types and therefore is related only with one property. """ - _inherit = 'product.pricelist' + + _inherit = "product.pricelist" # Fields declaration pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') + "pms.property", string="Properties", required=False, ondelete="restrict" + ) cancelation_rule_id = fields.Many2one( - 'pms.cancelation.rule', - string="Cancelation Policy") - pricelist_type = fields.Selection([ - ('daily', 'Daily Plan')], - string='Pricelist Type', - default='daily') - is_staff = fields.Boolean('Is Staff') + "pms.cancelation.rule", string="Cancelation Policy" + ) + pricelist_type = fields.Selection( + [("daily", "Daily Plan")], string="Pricelist Type", default="daily" + ) + is_staff = fields.Boolean("Is Staff") # Constraints and onchanges - @api.constrains('pricelist_type', 'pms_property_ids') + @api.constrains("pricelist_type", "pms_property_ids") def _check_pricelist_type_property_ids(self): for record in self: - if record.pricelist_type == 'daily' and len(record.pms_property_ids) != 1: + if record.pricelist_type == "daily" and len(record.pms_property_ids) != 1: raise ValidationError( - _("A daily pricelist is used as a daily Rate Plan " - "for room types and therefore must be related with " - "one and only one property.")) + _( + "A daily pricelist is used as a daily Rate Plan " + "for room types and therefore must be related with " + "one and only one property." + ) + ) - if record.pricelist_type == 'daily' and len(record.pms_property_ids) == 1: - pms_property_id = self.env['pms.property'].search([ - ('default_pricelist_id', '=', record.id) - ]) or None + if record.pricelist_type == "daily" and len(record.pms_property_ids) == 1: + pms_property_id = ( + self.env["pms.property"].search( + [("default_pricelist_id", "=", record.id)] + ) + or None + ) if pms_property_id and pms_property_id != record.pms_property_ids: raise ValidationError( - _("Relationship mismatch.") + " " + - _("This pricelist is used as default in a " - "different property.")) + _("Relationship mismatch.") + + " " + + _( + "This pricelist is used as default in a " + "different property." + ) + ) diff --git a/pms/models/inherited_product_template.py b/pms/models/inherited_product_template.py index 7f47a4798..8f788d13d 100644 --- a/pms/models/inherited_product_template.py +++ b/pms/models/inherited_product_template.py @@ -1,25 +1,26 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class ProductTemplate(models.Model): _inherit = "product.template" pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - per_day = fields.Boolean('Unit increment per day') - per_person = fields.Boolean('Unit increment per person') - consumed_on = fields.Selection([ - ('before', 'Before night'), - ('after', 'After night')], 'Consumed', default='before') - daily_limit = fields.Integer('Daily limit') - is_extra_bed = fields.Boolean('Is extra bed', default=False) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + per_day = fields.Boolean("Unit increment per day") + per_person = fields.Boolean("Unit increment per person") + consumed_on = fields.Selection( + [("before", "Before night"), ("after", "After night")], + "Consumed", + default="before", + ) + daily_limit = fields.Integer("Daily limit") + is_extra_bed = fields.Boolean("Is extra bed", default=False) show_in_calendar = fields.Boolean( - 'Show in Calendar', + "Show in Calendar", default=False, - help='Specifies if the product is shown in the calendar information.') + help="Specifies if the product is shown in the calendar information.", + ) diff --git a/pms/models/inherited_res_company.py b/pms/models/inherited_res_company.py index 5673e78d2..393dc3ede 100644 --- a/pms/models/inherited_res_company.py +++ b/pms/models/inherited_res_company.py @@ -1,14 +1,14 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class ResCompany(models.Model): - _inherit = 'res.company' + _inherit = "res.company" # Fields declaration - pms_property_ids = fields.One2many('pms.property', 'company_id', 'Properties') + pms_property_ids = fields.One2many("pms.property", "company_id", "Properties") # TODO: need extra explanation or remove otherwise # additional_hours = fields.Integer('Additional Hours', # help="Provide the min hours value for \ diff --git a/pms/models/inherited_res_partner.py b/pms/models/inherited_res_partner.py index 2d12144fb..9f12eb3f3 100644 --- a/pms/models/inherited_res_partner.py +++ b/pms/models/inherited_res_partner.py @@ -1,48 +1,54 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models import logging + +from odoo import api, fields, models + _logger = logging.getLogger(__name__) class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" # Fields declaration main_partner_id = fields.Many2one( - 'res.partner', - string='Destination Partner fusion') + "res.partner", string="Destination Partner fusion" + ) reservations_count = fields.Integer( - 'Reservations', compute='_compute_reservations_count') - folios_count = fields.Integer( - 'Folios', compute='_compute_folios_count') - unconfirmed = fields.Boolean('Unconfirmed', default=True) - is_tour_operator = fields.Boolean('Is Tour Operator') + "Reservations", compute="_compute_reservations_count" + ) + folios_count = fields.Integer("Folios", compute="_compute_folios_count") + unconfirmed = fields.Boolean("Unconfirmed", default=True) + is_tour_operator = fields.Boolean("Is Tour Operator") # Compute and Search methods def _compute_reservations_count(self): - pms_reservation_obj = self.env['pms.reservation'] + pms_reservation_obj = self.env["pms.reservation"] for record in self: - record.reservations_count = pms_reservation_obj.search_count([ - ('partner_id.id', '=', record.id) - ]) + record.reservations_count = pms_reservation_obj.search_count( + [("partner_id.id", "=", record.id)] + ) def _compute_folios_count(self): - pms_folio_obj = self.env['pms.folio'] + pms_folio_obj = self.env["pms.folio"] for record in self: - record.folios_count = pms_folio_obj.search_count([ - ('partner_id.id', '=', record.id) - ]) + record.folios_count = pms_folio_obj.search_count( + [("partner_id.id", "=", record.id)] + ) # ORM Overrides @api.model - def name_search(self, name, args=None, operator='ilike', limit=100): + def name_search(self, name, args=None, operator="ilike", limit=100): if not args: args = [] - domain = ['|', '|', ('phone', operator, name), - ('mobile', operator, name), ('email', operator, name), - ] + domain = [ + "|", + "|", + ("phone", operator, name), + ("mobile", operator, name), + ("email", operator, name), + ] partners = self.search(domain + args, limit=limit,) res = partners.name_get() if limit: @@ -50,7 +56,8 @@ class ResPartner(models.Model): else: limit_rest = limit if limit_rest or not limit: - args += [('id', 'not in', partners.ids)] + args += [("id", "not in", partners.ids)] res += super(ResPartner, self).name_search( - name, args=args, operator=operator, limit=limit_rest) + name, args=args, operator=operator, limit=limit_rest + ) return res diff --git a/pms/models/inherited_res_users.py b/pms/models/inherited_res_users.py index e2533ba5f..e43fb3856 100644 --- a/pms/models/inherited_res_users.py +++ b/pms/models/inherited_res_users.py @@ -1,10 +1,10 @@ # Copyright 2019 Pablo Quesada # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, api, fields +from odoo import api, fields, models class ResUsers(models.Model): - _inherit = 'res.users' + _inherit = "res.users" # Default Methods ang Gets @api.model @@ -13,15 +13,17 @@ class ResUsers(models.Model): # Fields declaration pms_property_id = fields.Many2one( - 'pms.property', - string='Property', + "pms.property", + string="Property", default=_get_default_pms_property, - help='The property this user is currently working for.', - context={'user_preference': True}) + help="The property this user is currently working for.", + context={"user_preference": True}, + ) pms_property_ids = fields.Many2many( - 'pms.property', - 'pms_property_users_rel', - 'user_id', - 'pms_property_id', - string='Properties', - default=_get_default_pms_property) + "pms.property", + "pms_property_users_rel", + "user_id", + "pms_property_id", + string="Properties", + default=_get_default_pms_property, + ) diff --git a/pms/models/pms_amenity.py b/pms/models/pms_amenity.py index ba4127903..3546434d8 100644 --- a/pms/models/pms_amenity.py +++ b/pms/models/pms_amenity.py @@ -1,24 +1,20 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsRoomAmenity(models.Model): - _name = 'pms.amenity' - _description = 'Room amenities' + _name = "pms.amenity" + _description = "Room amenities" # Fields declaration - name = fields.Char('Amenity Name', translate=True, required=True) + name = fields.Char("Amenity Name", translate=True, required=True) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - room_amenity_type_id = fields.Many2one( - 'pms.amenity.type', - 'Amenity Category') - default_code = fields.Char('Internal Reference') - active = fields.Boolean('Active', default=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + room_amenity_type_id = fields.Many2one("pms.amenity.type", "Amenity Category") + default_code = fields.Char("Internal Reference") + active = fields.Boolean("Active", default=True) # TODO: Constrain coherence pms_property_ids with amenity types pms_property_ids diff --git a/pms/models/pms_amenity_type.py b/pms/models/pms_amenity_type.py index a94ac3080..0db9e652f 100644 --- a/pms/models/pms_amenity_type.py +++ b/pms/models/pms_amenity_type.py @@ -1,20 +1,21 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsRoomAmenityType(models.Model): - _name = 'pms.amenity.type' - _description = 'Amenities Type' + _name = "pms.amenity.type" + _description = "Amenities Type" # Fields declaration - name = fields.Char('Amenity Type Name', translate=True, required=True) + name = fields.Char("Amenity Type Name", translate=True, required=True) pms_property_ids = fields.Many2many( - 'pms.property', string='Properties', required=False, ondelete='restrict') - room_amenity_ids = fields.One2many('pms.amenity', - 'room_amenity_type_id', - 'Amenities in this category') - active = fields.Boolean('Active', default=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + room_amenity_ids = fields.One2many( + "pms.amenity", "room_amenity_type_id", "Amenities in this category" + ) + active = fields.Boolean("Active", default=True) # TODO: Constrain coherence pms_property_ids with amenities pms_property_ids diff --git a/pms/models/pms_board_service.py b/pms/models/pms_board_service.py index a3174e3a4..ed4e63fae 100644 --- a/pms/models/pms_board_service.py +++ b/pms/models/pms_board_service.py @@ -1,6 +1,6 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models, fields +from odoo import api, fields, models class PmsBoardService(models.Model): diff --git a/pms/models/pms_board_service_room_type.py b/pms/models/pms_board_service_room_type.py index 191cf3de2..24cbeb3c1 100644 --- a/pms/models/pms_board_service_room_type.py +++ b/pms/models/pms_board_service_room_type.py @@ -1,15 +1,15 @@ # Copyright 2017 Dario # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError class PmsBoardServiceRoomType(models.Model): - _name = 'pms.board.service.room.type' - _table = 'pms_board_service_room_type_rel' - _rec_name = 'pms_board_service_id' + _name = "pms.board.service.room.type" + _table = "pms_board_service_room_type_rel" + _rec_name = "pms_board_service_id" _log_access = False - _description = 'Board Service included in Room' + _description = "Board Service included in Room" # Default Methods ang Gets @@ -17,124 +17,128 @@ class PmsBoardServiceRoomType(models.Model): result = [] for res in self: if res.pricelist_id: - name = u'%s (%s)' % ( + name = u"{} ({})".format( res.pms_board_service_id.name, - res.pricelist_id.name) + res.pricelist_id.name, + ) else: - name = u'%s (%s)' % (res.pms_board_service_id.name, - _('Generic')) + name = u"{} ({})".format(res.pms_board_service_id.name, _("Generic")) result.append((res.id, name)) return result # Fields declaration pms_board_service_id = fields.Many2one( - 'pms.board.service', - string='Board Service', + "pms.board.service", + string="Board Service", index=True, - ondelete='cascade', - required=True) + ondelete="cascade", + required=True, + ) pms_room_type_id = fields.Many2one( - 'pms.room.type', - string='Room Type', + "pms.room.type", + string="Room Type", index=True, - ondelete='cascade', - required=True) + ondelete="cascade", + required=True, + ) pricelist_id = fields.Many2one( - 'product.pricelist', - string='Pricelist', - required=False) + "product.pricelist", string="Pricelist", required=False + ) board_service_line_ids = fields.One2many( - 'pms.board.service.room.type.line', - 'pms_board_service_room_type_id') + "pms.board.service.room.type.line", "pms_board_service_room_type_id" + ) pms_property_id = fields.Many2one( - 'pms.property', - related='pms_room_type_id.pms_property_id') - price_type = fields.Selection([ - ('fixed', 'Fixed'), - ('percent', 'Percent')], - string='Type', - default='fixed', - required=True) + "pms.property", related="pms_room_type_id.pms_property_id" + ) + price_type = fields.Selection( + [("fixed", "Fixed"), ("percent", "Percent")], + string="Type", + default="fixed", + required=True, + ) amount = fields.Float( - 'Amount', - digits=('Product Price'), - compute='_compute_board_amount', - store=True) + "Amount", digits=("Product Price"), compute="_compute_board_amount", store=True + ) # Compute and Search methods - @api.depends('board_service_line_ids.amount') + @api.depends("board_service_line_ids.amount") def _compute_board_amount(self): for record in self: total = 0 for service in record.board_service_line_ids: total += service.amount - record.update({'amount': total}) + record.update({"amount": total}) # Constraints and onchanges - @api.constrains('pricelist_id') + @api.constrains("pricelist_id") def constrains_pricelist_id(self): for record in self: if self.pricelist_id: - board_pricelist = self.env['pms.board.service.room.type'].search([ - ('pricelist_id', '=', record.pricelist_id.id), - ('pms_room_type_id', '=', record.pms_room_type_id.id), - ('pms_board_service_id', '=', - record.pms_board_service_id.id), - ('id', '!=', record.id) - ]) + board_pricelist = self.env["pms.board.service.room.type"].search( + [ + ("pricelist_id", "=", record.pricelist_id.id), + ("pms_room_type_id", "=", record.pms_room_type_id.id), + ("pms_board_service_id", "=", record.pms_board_service_id.id), + ("id", "!=", record.id), + ] + ) if board_pricelist: raise UserError( - _("This Board Service in this Room can't repeat pricelist")) + _("This Board Service in this Room can't repeat pricelist") + ) else: - board_pricelist = self.env['pms.board.service.room.type'].search([ - ('pricelist_id', '=', False), - ('pms_room_type_id', '=', record.pms_room_type_id.id), - ('pms_board_service_id', '=', - record.pms_board_service_id.id), - ('id', '!=', record.id) - ]) + board_pricelist = self.env["pms.board.service.room.type"].search( + [ + ("pricelist_id", "=", False), + ("pms_room_type_id", "=", record.pms_room_type_id.id), + ("pms_board_service_id", "=", record.pms_board_service_id.id), + ("id", "!=", record.id), + ] + ) if board_pricelist: raise UserError( - _("This Board Service in this Room \ - can't repeat without pricelist")) + _( + "This Board Service in this Room \ + can't repeat without pricelist" + ) + ) # Action methods def open_board_lines_form(self): - action = self.env.ref( - 'pms.action_pms_board_service_room_type_view').read()[0] - action['views'] = [(self.env.ref( - 'pms.pms_board_service_room_type_form').id, 'form')] - action['res_id'] = self.id - action['target'] = 'new' + action = self.env.ref("pms.action_pms_board_service_room_type_view").read()[0] + action["views"] = [ + (self.env.ref("pms.pms_board_service_room_type_form").id, "form") + ] + action["res_id"] = self.id + action["target"] = "new" return action # ORM Overrides def init(self): self._cr.execute( - 'SELECT indexname FROM pg_indexes WHERE indexname = %s', - ('pms_board_service_id_pms_room_type_id_pricelist_id',)) + "SELECT indexname FROM pg_indexes WHERE indexname = %s", + ("pms_board_service_id_pms_room_type_id_pricelist_id",), + ) if not self._cr.fetchone(): self._cr.execute( - 'CREATE INDEX pms_board_service_id_pms_room_type_id_pricelist_id \ + "CREATE INDEX pms_board_service_id_pms_room_type_id_pricelist_id \ ON pms_board_service_room_type_rel \ - (pms_board_service_id, pms_room_type_id, pricelist_id)') + (pms_board_service_id, pms_room_type_id, pricelist_id)" + ) @api.model def create(self, vals): - if 'pms_board_service_id' in vals: + if "pms_board_service_id" in vals: vals.update( - self.prepare_board_service_reservation_ids( - vals['pms_board_service_id']) + self.prepare_board_service_reservation_ids(vals["pms_board_service_id"]) ) return super(PmsBoardServiceRoomType, self).create(vals) - def write(self, vals): - if 'pms_board_service_id' in vals: + if "pms_board_service_id" in vals: vals.update( - self.prepare_board_service_reservation_ids( - vals['pms_board_service_id']) + self.prepare_board_service_reservation_ids(vals["pms_board_service_id"]) ) return super(PmsBoardServiceRoomType, self).write(vals) @@ -145,11 +149,9 @@ class PmsBoardServiceRoomType(models.Model): Prepare line to price products config """ cmds = [(5, 0, 0)] - board_service = self.env['pms.board.service'].browse( - board_service_id) + board_service = self.env["pms.board.service"].browse(board_service_id) for line in board_service.board_service_line_ids: - cmds.append((0, False, { - 'product_id': line.product_id.id, - 'amount': line.amount - })) - return {'board_service_line_ids': cmds} + cmds.append( + (0, False, {"product_id": line.product_id.id, "amount": line.amount}) + ) + return {"board_service_line_ids": cmds} diff --git a/pms/models/pms_board_service_room_type_line.py b/pms/models/pms_board_service_room_type_line.py index ec6df2781..8e89e1551 100644 --- a/pms/models/pms_board_service_room_type_line.py +++ b/pms/models/pms_board_service_room_type_line.py @@ -2,20 +2,20 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models + class PmsBoardServiceRoomTypeLine(models.Model): - _name = 'pms.board.service.room.type.line' - _description = 'Services on Board Service included in Room' + _name = "pms.board.service.room.type.line" + _description = "Services on Board Service included in Room" # Fields declaration pms_board_service_room_type_id = fields.Many2one( - 'pms.board.service.room.type', - 'Board Service Room', - ondelete='cascade', - required=True) - product_id = fields.Many2one( - 'product.product', - 'Product', + "pms.board.service.room.type", + "Board Service Room", + ondelete="cascade", required=True, - readonly=True) + ) + product_id = fields.Many2one( + "product.product", "Product", required=True, readonly=True + ) # TODO def default_amount "amount of service" - amount = fields.Float('Amount', digits=('Product Price'), default=0.0) + amount = fields.Float("Amount", digits=("Product Price"), default=0.0) diff --git a/pms/models/pms_cancelation_rule.py b/pms/models/pms_cancelation_rule.py index ae98f98de..22d7dfe17 100644 --- a/pms/models/pms_cancelation_rule.py +++ b/pms/models/pms_cancelation_rule.py @@ -1,40 +1,39 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models # TODO: refactoring to cancellation.rule class PmsCancelationRule(models.Model): - _name = 'pms.cancelation.rule' - _description = 'Cancelation Rules' + _name = "pms.cancelation.rule" + _description = "Cancelation Rules" # Fields declaration - name = fields.Char('Amenity Name', translate=True, required=True) + name = fields.Char("Amenity Name", translate=True, required=True) pricelist_ids = fields.One2many( - 'product.pricelist', - 'cancelation_rule_id', - 'Pricelist that use this rule') + "product.pricelist", "cancelation_rule_id", "Pricelist that use this rule" + ) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - active = fields.Boolean('Active', default=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + active = fields.Boolean("Active", default=True) days_intime = fields.Integer( - 'Days Late', - help='Maximum number of days for free cancellation before Checkin') - penalty_late = fields.Integer('% Penalty Late', defaul="100") - apply_on_late = fields.Selection([ - ('first', 'First Day'), - ('all', 'All Days'), - ('days', 'Specify days')], 'Late apply on', default='first') - days_late = fields.Integer('Late first days', default="2") - penalty_noshow = fields.Integer('% Penalty No Show', default="100") - apply_on_noshow = fields.Selection([ - ('first', 'First Day'), - ('all', 'All Days'), - ('days', 'Specify days')], 'No Show apply on', default='all') - days_noshow = fields.Integer('NoShow first days', default="2") + "Days Late", help="Maximum number of days for free cancellation before Checkin" + ) + penalty_late = fields.Integer("% Penalty Late", defaul="100") + apply_on_late = fields.Selection( + [("first", "First Day"), ("all", "All Days"), ("days", "Specify days")], + "Late apply on", + default="first", + ) + days_late = fields.Integer("Late first days", default="2") + penalty_noshow = fields.Integer("% Penalty No Show", default="100") + apply_on_noshow = fields.Selection( + [("first", "First Day"), ("all", "All Days"), ("days", "Specify days")], + "No Show apply on", + default="all", + ) + days_noshow = fields.Integer("NoShow first days", default="2") # TODO: Constrain coherence pms_property_ids pricelist and cancelation_rules diff --git a/pms/models/pms_checkin_partner.py b/pms/models/pms_checkin_partner.py index 4f2ed3670..354bc47e1 100644 --- a/pms/models/pms_checkin_partner.py +++ b/pms/models/pms_checkin_partner.py @@ -2,72 +2,72 @@ # Copyright 2018 Alexandre Diaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import datetime -from odoo import models, fields, api, _ + +from odoo import _, api, fields, models from odoo.exceptions import ValidationError -from odoo.tools import ( - DEFAULT_SERVER_DATETIME_FORMAT, - DEFAULT_SERVER_DATE_FORMAT) +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT class PmsCheckinPartner(models.Model): - _name = 'pms.checkin.partner' - _description = 'Partner Checkins' + _name = "pms.checkin.partner" + _description = "Partner Checkins" # Default Methods ang Gets def _default_reservation_id(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) return reservation return False def _default_partner_id(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) partner_ids = [] if reservation.folio_id: for room in reservation.folio_id.reservation_ids: - partner_ids.append(room.mapped( - 'checkin_partner_ids.partner_id.id')) - if 'checkin_partner_ids' in self.env.context: - for checkin in self.env.context['checkin_partner_ids']: + partner_ids.append(room.mapped("checkin_partner_ids.partner_id.id")) + if "checkin_partner_ids" in self.env.context: + for checkin in self.env.context["checkin_partner_ids"]: if checkin[0] == 0: - partner_ids.append(checkin[2].get('partner_id')) - if self._context.get('include_customer') and \ - reservation.partner_id.id not in partner_ids and \ - not reservation.partner_id.is_company: + partner_ids.append(checkin[2].get("partner_id")) + if ( + self._context.get("include_customer") + and reservation.partner_id.id not in partner_ids + and not reservation.partner_id.is_company + ): return reservation.partner_id return False def _default_folio_id(self): - if 'folio_id' in self.env.context: - folio = self.env['pms.folio'].browse([ - self.env.context['folio_id'] - ]) + if "folio_id" in self.env.context: + folio = self.env["pms.folio"].browse([self.env.context["folio_id"]]) return folio - if 'reservation_id' in self.env.context: - folio = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]).folio_id + if "reservation_id" in self.env.context: + folio = ( + self.env["pms.reservation"] + .browse([self.env.context["reservation_id"]]) + .folio_id + ) return folio return False def _default_enter_date(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) return reservation.checkin return False def _default_exit_date(self): - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) return reservation.checkout return False @@ -77,123 +77,124 @@ class PmsCheckinPartner(models.Model): # Fields declaration partner_id = fields.Many2one( - 'res.partner', - default=_default_partner_id, - required=True) - reservation_id = fields.Many2one( - 'pms.reservation', - default=_default_reservation_id) + "res.partner", default=_default_partner_id, required=True + ) + reservation_id = fields.Many2one("pms.reservation", default=_default_reservation_id) folio_id = fields.Many2one( - 'pms.folio', - default=_default_folio_id, - readonly=True, - required=True) + "pms.folio", default=_default_folio_id, readonly=True, required=True + ) pms_property_id = fields.Many2one( - 'pms.property', - default=_get_default_pms_property, - required=True) - email = fields.Char('E-mail', related='partner_id.email') - mobile = fields.Char('Mobile', related='partner_id.mobile') + "pms.property", default=_get_default_pms_property, required=True + ) + email = fields.Char("E-mail", related="partner_id.email") + mobile = fields.Char("Mobile", related="partner_id.mobile") enter_date = fields.Date(default=_default_enter_date, required=True) exit_date = fields.Date(default=_default_exit_date, required=True) - arrival_hour = fields.Char('Arrival Hour', - help="Default Arrival Hour (HH:MM)") - departure_hour = fields.Char('Departure Hour', - help="Default Departure Hour (HH:MM)") - auto_booking = fields.Boolean('Get in Now', default=False) - state = fields.Selection([ - ('draft', 'Pending Entry'), - ('booking', 'On Board'), - ('done', 'Out'), - ('cancelled', 'Cancelled')], - string='State', + arrival_hour = fields.Char("Arrival Hour", help="Default Arrival Hour (HH:MM)") + departure_hour = fields.Char( + "Departure Hour", help="Default Departure Hour (HH:MM)" + ) + auto_booking = fields.Boolean("Get in Now", default=False) + state = fields.Selection( + [ + ("draft", "Pending Entry"), + ("booking", "On Board"), + ("done", "Out"), + ("cancelled", "Cancelled"), + ], + string="State", readonly=True, - default=lambda *a: 'draft', - track_visibility='onchange') + default=lambda *a: "draft", + track_visibility="onchange", + ) # Constraints and onchanges - @api.constrains('exit_date', 'enter_date') + @api.constrains("exit_date", "enter_date") def _check_exit_date(self): for record in self: date_in = fields.Date.from_string(record.enter_date) date_out = fields.Date.from_string(record.exit_date) if date_out < date_in: raise models.ValidationError( - _('Departure date (%s) is prior to arrival on %s') % - (date_out, date_in)) + _("Departure date (%s) is prior to arrival on %s") + % (date_out, date_in) + ) - @api.onchange('enter_date', 'exit_date') + @api.onchange("enter_date", "exit_date") def _onchange_enter_date(self): date_in = fields.Date.from_string(self.enter_date) date_out = fields.Date.from_string(self.exit_date) if date_out <= date_in: date_out = date_in + datetime.timedelta(days=1) - self.update({'exit_date': date_out}) + self.update({"exit_date": date_out}) raise ValidationError( - _('Departure date, is prior to arrival. Check it now. %s') % - date_out) + _("Departure date, is prior to arrival. Check it now. %s") % date_out + ) - - @api.onchange('partner_id') + @api.onchange("partner_id") def _check_partner_id(self): for record in self: if record.partner_id: if record.partner_id.is_company: raise models.ValidationError( - _('A Checkin Guest is configured like a company, \ - modify it in contact form if its a mistake')) - indoor_partner_ids = record.reservation_id.\ - checkin_partner_ids.filtered( - lambda r: r.id != record.id - ).mapped('partner_id.id') + _( + "A Checkin Guest is configured like a company, \ + modify it in contact form if its a mistake" + ) + ) + indoor_partner_ids = record.reservation_id.checkin_partner_ids.filtered( + lambda r: r.id != record.id + ).mapped("partner_id.id") if indoor_partner_ids.count(record.partner_id.id) > 1: record.partner_id = None raise models.ValidationError( - _('This guest is already registered in the room')) + _("This guest is already registered in the room") + ) # Action methods def action_on_board(self): for record in self: if record.reservation_id.checkin > fields.Date.today(): - raise models.ValidationError( - _('It is not yet checkin day!')) + raise models.ValidationError(_("It is not yet checkin day!")) hour = record._get_arrival_hour() vals = { - 'state': 'booking', - 'arrival_hour': hour, + "state": "booking", + "arrival_hour": hour, } record.update(vals) - if record.reservation_id.state == 'confirm': - record.reservation_id.state = 'booking' + if record.reservation_id.state == "confirm": + record.reservation_id.state = "booking" if record.reservation_id.splitted: - master_reservation = \ - record.reservation_id.parent_reservation or \ - record.reservation_id - splitted_reservs = self.env['pms.reservation'].search([ - ('splitted', '=', True), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ('folio_id', '=', record.folio_id.id), - ('id', '!=', record.id), - ('state', '=', 'confirm') - ]) + master_reservation = ( + record.reservation_id.parent_reservation + or record.reservation_id + ) + splitted_reservs = self.env["pms.reservation"].search( + [ + ("splitted", "=", True), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ("folio_id", "=", record.folio_id.id), + ("id", "!=", record.id), + ("state", "=", "confirm"), + ] + ) if splitted_reservs: - splitted_reservs.update({'state': 'booking'}) + splitted_reservs.update({"state": "booking"}) return { "type": "ir.actions.do_nothing", } - def action_done(self): for record in self: - if record.state == 'booking': + if record.state == "booking": hour = record._get_departure_hour() vals = { - 'state': 'done', - 'departure_hour': hour, + "state": "done", + "departure_hour": hour, } record.update(vals) return True @@ -202,7 +203,7 @@ class PmsCheckinPartner(models.Model): @api.model def create(self, vals): record = super(PmsCheckinPartner, self).create(vals) - if vals.get('auto_booking', False): + if vals.get("auto_booking", False): record.action_on_board() return record @@ -212,16 +213,17 @@ class PmsCheckinPartner(models.Model): tz_property = self.env.user.pms_property_id.tz today = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Date.today(), - DEFAULT_SERVER_DATE_FORMAT)) + datetime.datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT), + ) default_arrival_hour = self.env.user.pms_property_id.default_arrival_hour - if self.reservation_id.checkin < today.strftime( - DEFAULT_SERVER_DATE_FORMAT): + if self.reservation_id.checkin < today.strftime(DEFAULT_SERVER_DATE_FORMAT): return default_arrival_hour now = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Datetime.now(), - DEFAULT_SERVER_DATETIME_FORMAT)) + datetime.datetime.strptime( + fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT + ), + ) arrival_hour = now.strftime("%H:%M") return arrival_hour @@ -230,15 +232,16 @@ class PmsCheckinPartner(models.Model): tz_property = self.env.user.pms_property_id.tz today = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Date.today(), - DEFAULT_SERVER_DATE_FORMAT)) + datetime.datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT), + ) default_departure_hour = self.env.user.pms_property_id.default_departure_hour - if self.reservation_id.checkout < today.strftime( - DEFAULT_SERVER_DATE_FORMAT): + if self.reservation_id.checkout < today.strftime(DEFAULT_SERVER_DATE_FORMAT): return default_departure_hour now = fields.Datetime.context_timestamp( self.with_context(tz=tz_property), - datetime.datetime.strptime(fields.Datetime.now(), - DEFAULT_SERVER_DATETIME_FORMAT)) + datetime.datetime.strptime( + fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT + ), + ) departure_hour = now.strftime("%H:%M") return departure_hour diff --git a/pms/models/pms_floor.py b/pms/models/pms_floor.py index 3322aaf56..b59ead9f8 100644 --- a/pms/models/pms_floor.py +++ b/pms/models/pms_floor.py @@ -1,6 +1,6 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsFloor(models.Model): @@ -8,14 +8,10 @@ class PmsFloor(models.Model): _description = "Ubication" # Fields declaration - name = fields.Char('Ubication Name', - translate=True, - size=64, - required=True, - index=True) + name = fields.Char( + "Ubication Name", translate=True, size=64, required=True, index=True + ) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - sequence = fields.Integer('Sequence') + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + sequence = fields.Integer("Sequence") diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 64bf67c4f..a5a080a29 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -2,14 +2,14 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models class PmsFolio(models.Model): - _name = 'pms.folio' - _description = 'PMS Folio' - _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin'] - _order = 'id' + _name = "pms.folio" + _description = "PMS Folio" + _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] + _order = "id" # Default Methods ang Gets @api.model @@ -18,17 +18,15 @@ class PmsFolio(models.Model): If the guest has an invoicing address set, this method return diff_invoicing = True, else, return False """ - if 'folio_id' in self.env.context: - folio = self.env['pms.folio'].browse([ - self.env.context['folio_id'] - ]) + if "folio_id" in self.env.context: + folio = self.env["pms.folio"].browse([self.env.context["folio_id"]]) if folio.partner_id.id == folio.partner_invoice_id.id: return False return True @api.model def _get_default_team(self): - return self.env['crm.team']._get_default_team_id() + return self.env["crm.team"]._get_default_team_id() @api.model def _get_default_pms_property(self): @@ -36,230 +34,259 @@ class PmsFolio(models.Model): # Fields declaration name = fields.Char( - String='Folio Number', - readonly=True, - index=True, - default=lambda self: _('New')) + String="Folio Number", readonly=True, index=True, default=lambda self: _("New") + ) pms_property_id = fields.Many2one( - 'pms.property', - default=_get_default_pms_property, - required=True) + "pms.property", default=_get_default_pms_property, required=True + ) partner_id = fields.Many2one( - 'res.partner', - track_visibility='onchange', - ondelete='restrict') + "res.partner", track_visibility="onchange", ondelete="restrict" + ) reservation_ids = fields.One2many( - 'pms.reservation', - 'folio_id', + "pms.reservation", + "folio_id", readonly=False, - states={'done': [('readonly', True)]}, - help="Room reservation detail.",) + states={"done": [("readonly", True)]}, + help="Room reservation detail.", + ) service_ids = fields.One2many( - 'pms.service', - 'folio_id', + "pms.service", + "folio_id", readonly=False, - states={'done': [('readonly', True)]}, + states={"done": [("readonly", True)]}, help="Services detail provide to customer and it will " - "include in main Invoice.") + "include in main Invoice.", + ) company_id = fields.Many2one( - 'res.company', - 'Company', - default=lambda self: self.env.company) + "res.company", "Company", default=lambda self: self.env.company + ) analytic_account_id = fields.Many2one( - 'account.analytic.account', - 'Analytic Account', + "account.analytic.account", + "Analytic Account", readonly=True, - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, + states={"draft": [("readonly", False)], "sent": [("readonly", False)]}, help="The analytic account related to a folio.", - copy=False) - currency_id = fields.Many2one( - 'res.currency', - related='pricelist_id.currency_id', - string='Currency', - readonly=True, - required=True, - ondelete='restrict',) - pricelist_id = fields.Many2one( - 'product.pricelist', - string='Pricelist', - required=True, - ondelete='restrict', - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, - help="Pricelist for current folio.") - user_id = fields.Many2one( - 'res.users', - string='Salesperson', - index=True, - ondelete='restrict', - track_visibility='onchange', - default=lambda self: self.env.user) - tour_operator_id = fields.Many2one( - 'res.partner', - 'Tour Operator', - ondelete='restrict', - domain=[('is_tour_operator', '=', True)]) - payment_ids = fields.One2many( - 'account.payment', - 'folio_id', - readonly=True) - return_ids = fields.One2many( - 'payment.return', - 'folio_id', - readonly=True) - payment_term_id = fields.Many2one( - 'account.payment.term', - string='Payment Terms') - checkin_partner_ids = fields.One2many( - 'pms.checkin.partner', - 'folio_id') - move_ids = fields.Many2many( - 'account.move', - string='Invoices', - compute='_get_invoiced', - readonly=True, - copy=False) - partner_invoice_id = fields.Many2one( - 'res.partner', - string='Invoice Address', - required=True, - states={'done': [('readonly', True)]}, - help="Invoice address for current sales order.") - partner_parent_id = fields.Many2one( - related="partner_id.parent_id") - partner_invoice_state_id = fields.Many2one( - related="partner_invoice_id.state_id") - partner_invoice_country_id = fields.Many2one( - related="partner_invoice_id.country_id") - fiscal_position_id = fields.Many2one( - 'account.fiscal.position', - string='Fiscal Position') - closure_reason_id = fields.Many2one( - 'room.closure.reason') - segmentation_ids = fields.Many2many( - 'res.partner.category', - string='Segmentation', - ondelete='restrict') - team_id = fields.Many2one( - 'crm.team', - string='Sales Team', - ondelete='restrict', - change_default=True, - default=_get_default_team) - client_order_ref = fields.Char(string='Customer Reference', copy=False) - reservation_type = fields.Selection([ - ('normal', 'Normal'), - ('staff', 'Staff'), - ('out', 'Out of Service')], - string='Type', - default=lambda *a: 'normal') - channel_type = fields.Selection([ - ('door', 'Door'), - ('mail', 'Mail'), - ('phone', 'Phone'), - ('call', 'Call Center'), - ('web', 'Web'), - ('agency', 'Agencia'), - ('operator', 'Tour operador'), - ('virtualdoor', 'Virtual Door'), ], 'Sales Channel', default='door') - date_order = fields.Datetime( - string='Order Date', - required=True, - readonly=True, - index=True, - states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=False, - default=fields.Datetime.now) + ) + currency_id = fields.Many2one( + "res.currency", + related="pricelist_id.currency_id", + string="Currency", + readonly=True, + required=True, + ondelete="restrict", + ) + pricelist_id = fields.Many2one( + "product.pricelist", + string="Pricelist", + required=True, + ondelete="restrict", + states={"draft": [("readonly", False)], "sent": [("readonly", False)]}, + help="Pricelist for current folio.", + ) + user_id = fields.Many2one( + "res.users", + string="Salesperson", + index=True, + ondelete="restrict", + track_visibility="onchange", + default=lambda self: self.env.user, + ) + tour_operator_id = fields.Many2one( + "res.partner", + "Tour Operator", + ondelete="restrict", + domain=[("is_tour_operator", "=", True)], + ) + payment_ids = fields.One2many("account.payment", "folio_id", readonly=True) + return_ids = fields.One2many("payment.return", "folio_id", readonly=True) + payment_term_id = fields.Many2one("account.payment.term", string="Payment Terms") + checkin_partner_ids = fields.One2many("pms.checkin.partner", "folio_id") + move_ids = fields.Many2many( + "account.move", + string="Invoices", + compute="_get_invoiced", + readonly=True, + copy=False, + ) + partner_invoice_id = fields.Many2one( + "res.partner", + string="Invoice Address", + required=True, + states={"done": [("readonly", True)]}, + help="Invoice address for current sales order.", + ) + partner_parent_id = fields.Many2one(related="partner_id.parent_id") + partner_invoice_state_id = fields.Many2one(related="partner_invoice_id.state_id") + partner_invoice_country_id = fields.Many2one( + related="partner_invoice_id.country_id" + ) + fiscal_position_id = fields.Many2one( + "account.fiscal.position", string="Fiscal Position" + ) + closure_reason_id = fields.Many2one("room.closure.reason") + segmentation_ids = fields.Many2many( + "res.partner.category", string="Segmentation", ondelete="restrict" + ) + team_id = fields.Many2one( + "crm.team", + string="Sales Team", + ondelete="restrict", + change_default=True, + default=_get_default_team, + ) + client_order_ref = fields.Char(string="Customer Reference", copy=False) + reservation_type = fields.Selection( + [("normal", "Normal"), ("staff", "Staff"), ("out", "Out of Service")], + string="Type", + default=lambda *a: "normal", + ) + channel_type = fields.Selection( + [ + ("door", "Door"), + ("mail", "Mail"), + ("phone", "Phone"), + ("call", "Call Center"), + ("web", "Web"), + ("agency", "Agencia"), + ("operator", "Tour operador"), + ("virtualdoor", "Virtual Door"), + ], + "Sales Channel", + default="door", + ) + date_order = fields.Datetime( + string="Order Date", + required=True, + readonly=True, + index=True, + states={"draft": [("readonly", False)], "sent": [("readonly", False)]}, + copy=False, + default=fields.Datetime.now, + ) confirmation_date = fields.Datetime( - string='Confirmation Date', + string="Confirmation Date", readonly=True, index=True, help="Date on which the folio is confirmed.", - copy=False) - state = fields.Selection([ - ('draft', 'Quotation'), - ('sent', 'Quotation Sent'), - ('confirm', 'Confirmed'), - ('done', 'Locked'), - ('cancel', 'Cancelled'), ], - string='Status', + copy=False, + ) + state = fields.Selection( + [ + ("draft", "Quotation"), + ("sent", "Quotation Sent"), + ("confirm", "Confirmed"), + ("done", "Locked"), + ("cancel", "Cancelled"), + ], + string="Status", readonly=True, copy=False, index=True, - track_visibility='onchange', - default='draft') + track_visibility="onchange", + default="draft", + ) # Partner fields for being used directly in the Folio views--------- - email = fields.Char('E-mail', related='partner_id.email') - mobile = fields.Char('Mobile', related='partner_id.mobile') - phone = fields.Char('Phone', related='partner_id.phone') - partner_internal_comment = fields.Text(string='Internal Partner Notes', - related='partner_id.comment') + email = fields.Char("E-mail", related="partner_id.email") + mobile = fields.Char("Mobile", related="partner_id.mobile") + phone = fields.Char("Phone", related="partner_id.phone") + partner_internal_comment = fields.Text( + string="Internal Partner Notes", related="partner_id.comment" + ) # Payment Fields----------------------------------------------------- - credit_card_details = fields.Text('Credit Card Details') + credit_card_details = fields.Text("Credit Card Details") # Amount Fields------------------------------------------------------ - pending_amount = fields.Monetary(compute='compute_amount', - store=True, - string="Pending in Folio") - refund_amount = fields.Monetary(compute='compute_amount', - store=True, - string="Payment Returns") - invoices_paid = fields.Monetary(compute='compute_amount', - store=True, track_visibility='onchange', - string="Payments") - amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, - readonly=True, compute='_amount_all', - track_visibility='onchange') - amount_tax = fields.Monetary(string='Taxes', store=True, - readonly=True, compute='_amount_all') - amount_total = fields.Monetary(string='Total', store=True, - readonly=True, compute='_amount_all', - track_visibility='always') - # Checkin Fields----------------------------------------------------- - booking_pending = fields.Integer( - 'Booking pending', compute='_compute_checkin_partner_count') - checkin_partner_count = fields.Integer( - 'Checkin counter', compute='_compute_checkin_partner_count') - checkin_partner_pending_count = fields.Integer( - 'Checkin Pending', compute='_compute_checkin_partner_count') - # Invoice Fields----------------------------------------------------- - invoice_count = fields.Integer(compute='_get_invoiced') - invoice_status = fields.Selection([ - ('invoiced', 'Fully Invoiced'), - ('to invoice', 'To Invoice'), - ('no', 'Nothing to Invoice')], - string='Invoice Status', - compute='_get_invoiced', + pending_amount = fields.Monetary( + compute="compute_amount", store=True, string="Pending in Folio" + ) + refund_amount = fields.Monetary( + compute="compute_amount", store=True, string="Payment Returns" + ) + invoices_paid = fields.Monetary( + compute="compute_amount", + store=True, + track_visibility="onchange", + string="Payments", + ) + amount_untaxed = fields.Monetary( + string="Untaxed Amount", store=True, readonly=True, - default='no') + compute="_amount_all", + track_visibility="onchange", + ) + amount_tax = fields.Monetary( + string="Taxes", store=True, readonly=True, compute="_amount_all" + ) + amount_total = fields.Monetary( + string="Total", + store=True, + readonly=True, + compute="_amount_all", + track_visibility="always", + ) + # Checkin Fields----------------------------------------------------- + booking_pending = fields.Integer( + "Booking pending", compute="_compute_checkin_partner_count" + ) + checkin_partner_count = fields.Integer( + "Checkin counter", compute="_compute_checkin_partner_count" + ) + checkin_partner_pending_count = fields.Integer( + "Checkin Pending", compute="_compute_checkin_partner_count" + ) + # Invoice Fields----------------------------------------------------- + invoice_count = fields.Integer(compute="_get_invoiced") + invoice_status = fields.Selection( + [ + ("invoiced", "Fully Invoiced"), + ("to invoice", "To Invoice"), + ("no", "Nothing to Invoice"), + ], + string="Invoice Status", + compute="_get_invoiced", + store=True, + readonly=True, + default="no", + ) partner_invoice_vat = fields.Char(related="partner_invoice_id.vat") - partner_invoice_name = fields.Char(related="partner_invoice_id.name", string="Partner Name") - partner_invoice_street = fields.Char(related="partner_invoice_id.street", string="Street") - partner_invoice_street2 = fields.Char(related="partner_invoice_id.street", string="Street2") + partner_invoice_name = fields.Char( + related="partner_invoice_id.name", string="Partner Name" + ) + partner_invoice_street = fields.Char( + related="partner_invoice_id.street", string="Street" + ) + partner_invoice_street2 = fields.Char( + related="partner_invoice_id.street", string="Street2" + ) partner_invoice_zip = fields.Char(related="partner_invoice_id.zip") partner_invoice_city = fields.Char(related="partner_invoice_id.city") partner_invoice_email = fields.Char(related="partner_invoice_id.email") partner_invoice_lang = fields.Selection(related="partner_invoice_id.lang") # WorkFlow Mail Fields----------------------------------------------- has_confirmed_reservations_to_send = fields.Boolean( - compute='_compute_has_confirmed_reservations_to_send') + compute="_compute_has_confirmed_reservations_to_send" + ) has_cancelled_reservations_to_send = fields.Boolean( - compute='_compute_has_cancelled_reservations_to_send') - has_checkout_to_send = fields.Boolean( - compute='_compute_has_checkout_to_send') + compute="_compute_has_cancelled_reservations_to_send" + ) + has_checkout_to_send = fields.Boolean(compute="_compute_has_checkout_to_send") # Generic Fields----------------------------------------------------- - internal_comment = fields.Text(string='Internal Folio Notes') - cancelled_reason = fields.Text('Cause of cancelled') + internal_comment = fields.Text(string="Internal Folio Notes") + cancelled_reason = fields.Text("Cause of cancelled") prepaid_warning_days = fields.Integer( - 'Prepaid Warning Days', - help='Margin in days to create a notice if a payment \ - advance has not been recorded') - note = fields.Text('Terms and conditions') - sequence = fields.Integer(string='Sequence', default=10) + "Prepaid Warning Days", + help="Margin in days to create a notice if a payment \ + advance has not been recorded", + ) + note = fields.Text("Terms and conditions") + sequence = fields.Integer(string="Sequence", default=10) # Compute and Search methods - @api.depends('state', 'reservation_ids.invoice_status', - 'service_ids.invoice_status') + @api.depends( + "state", "reservation_ids.invoice_status", "service_ids.invoice_status" + ) def _get_invoiced(self): """ Compute the invoice status of a Folio. Possible statuses: @@ -277,186 +304,217 @@ class PmsFolio(models.Model): refund is not directly linked to the Folio. """ for folio in self: - move_ids = folio.reservation_ids.mapped('move_line_ids').\ - mapped('move_id').filtered(lambda r: r.type in [ - 'out_invoice', 'out_refund']) - invoice_ids = folio.service_ids.mapped('move_line_ids').mapped( - 'move_id').filtered(lambda r: r.type in [ - 'out_invoice', 'out_refund']) + move_ids = ( + folio.reservation_ids.mapped("move_line_ids") + .mapped("move_id") + .filtered(lambda r: r.type in ["out_invoice", "out_refund"]) + ) + invoice_ids = ( + folio.service_ids.mapped("move_line_ids") + .mapped("move_id") + .filtered(lambda r: r.type in ["out_invoice", "out_refund"]) + ) # TODO: Search for invoices which have been 'cancelled' # (filter_refund = 'modify' in 'account.move.refund') # use like as origin may contains multiple references # (e.g. 'SO01, SO02') - refunds = invoice_ids.search([ - ('invoice_origin', 'like', folio.name), - ('company_id', '=', folio.company_id.id)]).filtered( - lambda r: r.type in ['out_invoice', 'out_refund']) - invoice_ids |= refunds.filtered( - lambda r: folio.id in r.folio_ids.ids) + refunds = invoice_ids.search( + [ + ("invoice_origin", "like", folio.name), + ("company_id", "=", folio.company_id.id), + ] + ).filtered(lambda r: r.type in ["out_invoice", "out_refund"]) + invoice_ids |= refunds.filtered(lambda r: folio.id in r.folio_ids.ids) # Search for refunds as well - refund_ids = self.env['account.move'].browse() + refund_ids = self.env["account.move"].browse() if invoice_ids: for inv in invoice_ids: - refund_ids += refund_ids.search([ - ('type', '=', 'out_refund'), - ('invoice_origin', '=', inv.number), - ('invoice_origin', '!=', False), - ('journal_id', '=', inv.journal_id.id)]) + refund_ids += refund_ids.search( + [ + ("type", "=", "out_refund"), + ("invoice_origin", "=", inv.number), + ("invoice_origin", "!=", False), + ("journal_id", "=", inv.journal_id.id), + ] + ) # Ignore the status of the deposit product - deposit_product_id = self.env['sale.advance.payment.inv'].\ - _default_product_id() + deposit_product_id = self.env[ + "sale.advance.payment.inv" + ]._default_product_id() service_invoice_status = [ - service.invoice_status for service in folio.service_ids - if service.product_id != deposit_product_id] + service.invoice_status + for service in folio.service_ids + if service.product_id != deposit_product_id + ] reservation_invoice_status = [ - reservation.invoice_status for reservation in - folio.reservation_ids] + reservation.invoice_status for reservation in folio.reservation_ids + ] - if folio.state not in ('confirm', 'done'): - invoice_status = 'no' - elif any(invoice_status == 'to invoice' for - invoice_status in service_invoice_status) or \ - any(invoice_status == 'to invoice' for invoice_status - in reservation_invoice_status): - invoice_status = 'to invoice' - elif all(invoice_status == 'invoiced' for invoice_status in - service_invoice_status) or \ - any(invoice_status == 'invoiced' for invoice_status in - reservation_invoice_status): - invoice_status = 'invoiced' + if folio.state not in ("confirm", "done"): + invoice_status = "no" + elif any( + invoice_status == "to invoice" + for invoice_status in service_invoice_status + ) or any( + invoice_status == "to invoice" + for invoice_status in reservation_invoice_status + ): + invoice_status = "to invoice" + elif all( + invoice_status == "invoiced" + for invoice_status in service_invoice_status + ) or any( + invoice_status == "invoiced" + for invoice_status in reservation_invoice_status + ): + invoice_status = "invoiced" else: - invoice_status = 'no' + invoice_status = "no" - folio.update({ - 'invoice_count': len(set(move_ids.ids + refund_ids.ids)), - 'move_ids': move_ids.ids + refund_ids.ids, - 'invoice_status': invoice_status - }) + folio.update( + { + "invoice_count": len(set(move_ids.ids + refund_ids.ids)), + "move_ids": move_ids.ids + refund_ids.ids, + "invoice_status": invoice_status, + } + ) - @api.depends('reservation_ids.price_total', 'service_ids.price_total') + @api.depends("reservation_ids.price_total", "service_ids.price_total") def _amount_all(self): """ Compute the total amounts of the SO. """ for record in self: amount_untaxed = amount_tax = 0.0 - amount_untaxed = \ - sum(record.reservation_ids.mapped('price_subtotal')) + \ - sum(record.service_ids.mapped('price_subtotal')) - amount_tax = sum(record.reservation_ids.mapped('price_tax')) + \ - sum(record.service_ids.mapped('price_tax')) - record.update({ - 'amount_untaxed': record.pricelist_id.currency_id.round( - amount_untaxed), - 'amount_tax': record.pricelist_id.currency_id.round( - amount_tax), - 'amount_total': amount_untaxed + amount_tax, - }) - - @api.depends('amount_total', 'payment_ids', 'return_ids', - 'reservation_type', 'state') + amount_untaxed = sum(record.reservation_ids.mapped("price_subtotal")) + sum( + record.service_ids.mapped("price_subtotal") + ) + amount_tax = sum(record.reservation_ids.mapped("price_tax")) + sum( + record.service_ids.mapped("price_tax") + ) + record.update( + { + "amount_untaxed": record.pricelist_id.currency_id.round( + amount_untaxed + ), + "amount_tax": record.pricelist_id.currency_id.round(amount_tax), + "amount_total": amount_untaxed + amount_tax, + } + ) + @api.depends( + "amount_total", "payment_ids", "return_ids", "reservation_type", "state" + ) def compute_amount(self): - acc_pay_obj = self.env['account.payment'] + acc_pay_obj = self.env["account.payment"] for record in self: - if record.reservation_type in ('staff', 'out'): + if record.reservation_type in ("staff", "out"): vals = { - 'pending_amount': 0, - 'invoices_paid': 0, - 'refund_amount': 0, + "pending_amount": 0, + "invoices_paid": 0, + "refund_amount": 0, } record.update(vals) else: total_inv_refund = 0 - payments = acc_pay_obj.search([ - ('folio_id', '=', record.id) - ]) + payments = acc_pay_obj.search([("folio_id", "=", record.id)]) total_paid = sum(pay.amount for pay in payments) - return_lines = self.env['payment.return.line'].search([ - ('move_line_ids', 'in', payments.mapped( - 'move_line_ids.id')), - ('return_id.state', '=', 'done') - ]) - total_inv_refund = sum( - pay_return.amount for pay_return in return_lines) + return_lines = self.env["payment.return.line"].search( + [ + ("move_line_ids", "in", payments.mapped("move_line_ids.id")), + ("return_id.state", "=", "done"), + ] + ) + total_inv_refund = sum(pay_return.amount for pay_return in return_lines) total = record.amount_total # REVIEW: Must We ignored services in cancelled folios # pending amount? - if record.state == 'cancelled': - total = total - \ - sum(record.service_ids.mapped('price_total')) + if record.state == "cancelled": + total = total - sum(record.service_ids.mapped("price_total")) vals = { - 'pending_amount': total - total_paid + total_inv_refund, - 'invoices_paid': total_paid, - 'refund_amount': total_inv_refund, + "pending_amount": total - total_paid + total_inv_refund, + "invoices_paid": total_paid, + "refund_amount": total_inv_refund, } record.update(vals) - @api.depends('reservation_ids') + @api.depends("reservation_ids") def _compute_has_confirmed_reservations_to_send(self): has_to_send = False - if self.reservation_type != 'out': + if self.reservation_type != "out": for rline in self.reservation_ids: if rline.splitted: master_reservation = rline.parent_reservation or rline - has_to_send = self.env['pms.reservation'].search_count([ - ('splitted', '=', True), - ('folio_id', '=', self.id), - ('to_send', '=', True), - ('state', 'in', ('confirm', 'booking')), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ]) > 0 - elif rline.to_send and rline.state in ('confirm', 'booking'): + has_to_send = ( + self.env["pms.reservation"].search_count( + [ + ("splitted", "=", True), + ("folio_id", "=", self.id), + ("to_send", "=", True), + ("state", "in", ("confirm", "booking")), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ] + ) + > 0 + ) + elif rline.to_send and rline.state in ("confirm", "booking"): has_to_send = True break self.has_confirmed_reservations_to_send = has_to_send else: self.has_confirmed_reservations_to_send = False - @api.depends('reservation_ids') + @api.depends("reservation_ids") def _compute_has_cancelled_reservations_to_send(self): has_to_send = False - if self.reservation_type != 'out': + if self.reservation_type != "out": for rline in self.reservation_ids: if rline.splitted: master_reservation = rline.parent_reservation or rline - has_to_send = self.env['pms.reservation'].search_count([ - ('splitted', '=', True), - ('folio_id', '=', self.id), - ('to_send', '=', True), - ('state', '=', 'cancelled'), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ]) > 0 - elif rline.to_send and rline.state == 'cancelled': + has_to_send = ( + self.env["pms.reservation"].search_count( + [ + ("splitted", "=", True), + ("folio_id", "=", self.id), + ("to_send", "=", True), + ("state", "=", "cancelled"), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ] + ) + > 0 + ) + elif rline.to_send and rline.state == "cancelled": has_to_send = True break self.has_cancelled_reservations_to_send = has_to_send else: self.has_cancelled_reservations_to_send = False - @api.depends('reservation_ids') + @api.depends("reservation_ids") def _compute_has_checkout_to_send(self): has_to_send = True - if self.reservation_type != 'out': + if self.reservation_type != "out": for rline in self.reservation_ids: if rline.splitted: master_reservation = rline.parent_reservation or rline - nreservs = self.env['pms.reservation'].search_count([ - ('splitted', '=', True), - ('folio_id', '=', self.id), - ('to_send', '=', True), - ('state', '=', 'done'), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ]) + nreservs = self.env["pms.reservation"].search_count( + [ + ("splitted", "=", True), + ("folio_id", "=", self.id), + ("to_send", "=", True), + ("state", "=", "done"), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ] + ) if nreservs != len(self.reservation_ids): has_to_send = False - elif not rline.to_send or rline.state != 'done': + elif not rline.to_send or rline.state != "done": has_to_send = False break self.has_checkout_to_send = has_to_send @@ -465,7 +523,7 @@ class PmsFolio(models.Model): # Constraints and onchanges - @api.onchange('partner_id') + @api.onchange("partner_id") def onchange_partner_id(self): """ Update the following fields when the partner is changed: @@ -475,43 +533,49 @@ class PmsFolio(models.Model): - Delivery address """ if not self.partner_id: - self.update({ - 'partner_invoice_id': False, - 'payment_term_id': False, - 'fiscal_position_id': False, - }) + self.update( + { + "partner_invoice_id": False, + "payment_term_id": False, + "fiscal_position_id": False, + } + ) return - addr = self.partner_id.address_get(['invoice']) - pricelist = self.partner_id.property_product_pricelist and \ - self.partner_id.property_product_pricelist.id or \ - self.env.user.pms_property_id.default_pricelist_id.id + addr = self.partner_id.address_get(["invoice"]) + pricelist = ( + self.partner_id.property_product_pricelist + and self.partner_id.property_product_pricelist.id + or self.env.user.pms_property_id.default_pricelist_id.id + ) values = { - 'pricelist_id': pricelist, - 'payment_term_id': self.partner_id.property_payment_term_id and - self.partner_id.property_payment_term_id.id or False, - 'partner_invoice_id': addr['invoice'], - 'user_id': self.partner_id.user_id.id or self.env.uid, + "pricelist_id": pricelist, + "payment_term_id": self.partner_id.property_payment_term_id + and self.partner_id.property_payment_term_id.id + or False, + "partner_invoice_id": addr["invoice"], + "user_id": self.partner_id.user_id.id or self.env.uid, } - if self.env['ir.config_parameter'].sudo().\ - get_param('sale.use_sale_note') and \ - self.env.user.company_id.sale_note: - values['note'] = self.with_context( - lang=self.partner_id.lang).env.user.company_id.sale_note + if ( + self.env["ir.config_parameter"].sudo().get_param("sale.use_sale_note") + and self.env.user.company_id.sale_note + ): + values["note"] = self.with_context( + lang=self.partner_id.lang + ).env.user.company_id.sale_note if self.partner_id.team_id: - values['team_id'] = self.partner_id.team_id.id + values["team_id"] = self.partner_id.team_id.id self.update(values) - - @api.onchange('pricelist_id') + @api.onchange("pricelist_id") def onchange_pricelist_id(self): - values = {'reservation_type': self.env['pms.folio']. - calcule_reservation_type( - self.pricelist_id.is_staff, - self.reservation_type - )} + values = { + "reservation_type": self.env["pms.folio"].calcule_reservation_type( + self.pricelist_id.is_staff, self.reservation_type + ) + } self.update(values) # Action methods @@ -520,226 +584,224 @@ class PmsFolio(models.Model): self.ensure_one() partner = self.partner_id.id amount = self.pending_amount - view_id = self.env.ref('pms.account_payment_view_form_folio').id - return{ - 'name': _('Register Payment'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'account.payment', - 'type': 'ir.actions.act_window', - 'view_id': view_id, - 'context': { - 'default_folio_id': self.id, - 'default_amount': amount, - 'default_payment_type': 'inbound', - 'default_partner_type': 'customer', - 'default_partner_id': partner, - 'default_communication': self.name, + view_id = self.env.ref("pms.account_payment_view_form_folio").id + return { + "name": _("Register Payment"), + "view_type": "form", + "view_mode": "form", + "res_model": "account.payment", + "type": "ir.actions.act_window", + "view_id": view_id, + "context": { + "default_folio_id": self.id, + "default_amount": amount, + "default_payment_type": "inbound", + "default_partner_type": "customer", + "default_partner_id": partner, + "default_communication": self.name, }, - 'target': 'new', + "target": "new", } - def open_moves_folio(self): - invoices = self.mapped('move_ids') - action = self.env.ref('account.action_move_out_invoice_type').read()[0] + invoices = self.mapped("move_ids") + action = self.env.ref("account.action_move_out_invoice_type").read()[0] if len(invoices) > 1: - action['domain'] = [('id', 'in', invoices.ids)] + action["domain"] = [("id", "in", invoices.ids)] elif len(invoices) == 1: - action['views'] = [ - (self.env.ref('account.view_move_form').id, 'form')] - action['res_id'] = invoices.ids[0] + action["views"] = [(self.env.ref("account.view_move_form").id, "form")] + action["res_id"] = invoices.ids[0] else: - action = {'type': 'ir.actions.act_window_close'} + action = {"type": "ir.actions.act_window_close"} return action - def action_return_payments(self): self.ensure_one() return_move_ids = [] - acc_pay_obj = self.env['account.payment'] - payments = acc_pay_obj.search([ - '|', - ('move_ids', 'in', self.move_ids.ids), - ('folio_id', '=', self.id) - ]) + acc_pay_obj = self.env["account.payment"] + payments = acc_pay_obj.search( + ["|", ("move_ids", "in", self.move_ids.ids), ("folio_id", "=", self.id)] + ) return_move_ids += self.move_ids.filtered( - lambda invoice: invoice.type == 'out_refund').mapped( - 'payment_move_line_ids.move_id.id') - return_lines = self.env['payment.return.line'].search([ - ('move_line_ids', 'in', payments.mapped('move_line_ids.id')), - ]) - return_move_ids += return_lines.mapped('return_id.move_id.id') + lambda invoice: invoice.type == "out_refund" + ).mapped("payment_move_line_ids.move_id.id") + return_lines = self.env["payment.return.line"].search( + [("move_line_ids", "in", payments.mapped("move_line_ids.id")),] + ) + return_move_ids += return_lines.mapped("return_id.move_id.id") - return{ - 'name': _('Returns'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'account.move', - 'type': 'ir.actions.act_window', - 'domain': [('id', 'in', return_move_ids)], + return { + "name": _("Returns"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "account.move", + "type": "ir.actions.act_window", + "domain": [("id", "in", return_move_ids)], } - def action_checks(self): self.ensure_one() - rooms = self.mapped('reservation_ids.id') + rooms = self.mapped("reservation_ids.id") return { - 'name': _('Checkins'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'pms.checkin.partner', - 'type': 'ir.actions.act_window', - 'domain': [('reservation_id', 'in', rooms)], - 'target': 'new', + "name": _("Checkins"), + "view_type": "form", + "view_mode": "tree,form", + "res_model": "pms.checkin.partner", + "type": "ir.actions.act_window", + "domain": [("reservation_id", "in", rooms)], + "target": "new", } - def send_reservation_mail(self): - ''' + """ This function opens a window to compose an email, template message loaded by default. @param self: object pointer - ''' + """ # Debug Stop ------------------- # import wdb; wdb.set_trace() # Debug Stop ------------------- self.ensure_one() - ir_model_data = self.env['ir.model.data'] + ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( - 'pms', - 'email_template_reservation')[1] + "pms", "email_template_reservation" + )[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference( - 'mail', - 'email_compose_message_wizard_form')[1] + "mail", "email_compose_message_wizard_form" + )[1] except ValueError: compose_form_id = False ctx = dict() - ctx.update({ - 'default_model': 'pms.folio', - 'default_res_id': self._ids[0], - 'default_use_template': bool(template_id), - 'default_template_id': template_id, - 'default_composition_mode': 'comment', - 'force_send': True, - 'mark_so_as_sent': True - }) + ctx.update( + { + "default_model": "pms.folio", + "default_res_id": self._ids[0], + "default_use_template": bool(template_id), + "default_template_id": template_id, + "default_composition_mode": "comment", + "force_send": True, + "mark_so_as_sent": True, + } + ) return { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, - 'target': 'new', - 'context': ctx, - 'force_send': True + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_model": "mail.compose.message", + "views": [(compose_form_id, "form")], + "view_id": compose_form_id, + "target": "new", + "context": ctx, + "force_send": True, } - def send_exit_mail(self): - ''' + """ This function opens a window to compose an email, template message loaded by default. @param self: object pointer - ''' + """ # Debug Stop ------------------- # import wdb; wdb.set_trace() # Debug Stop ------------------- self.ensure_one() - ir_model_data = self.env['ir.model.data'] + ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( - 'pms', - 'mail_template_pms_exit')[1] + "pms", "mail_template_pms_exit" + )[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference( - 'mail', - 'email_compose_message_wizard_form')[1] + "mail", "email_compose_message_wizard_form" + )[1] except ValueError: compose_form_id = False ctx = dict() - ctx.update({ - 'default_model': 'pms.reservation', - 'default_res_id': self._ids[0], - 'default_use_template': bool(template_id), - 'default_template_id': template_id, - 'default_composition_mode': 'comment', - 'force_send': True, - 'mark_so_as_sent': True - }) + ctx.update( + { + "default_model": "pms.reservation", + "default_res_id": self._ids[0], + "default_use_template": bool(template_id), + "default_template_id": template_id, + "default_composition_mode": "comment", + "force_send": True, + "mark_so_as_sent": True, + } + ) return { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, - 'target': 'new', - 'context': ctx, - 'force_send': True + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_model": "mail.compose.message", + "views": [(compose_form_id, "form")], + "view_id": compose_form_id, + "target": "new", + "context": ctx, + "force_send": True, } - def send_cancel_mail(self): - ''' + """ This function opens a window to compose an email, template message loaded by default. @param self: object pointer - ''' + """ self.ensure_one() - ir_model_data = self.env['ir.model.data'] + ir_model_data = self.env["ir.model.data"] try: template_id = ir_model_data.get_object_reference( - 'pms', - 'mail_template_pms_cancel')[1] + "pms", "mail_template_pms_cancel" + )[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference( - 'mail', - 'email_compose_message_wizard_form')[1] + "mail", "email_compose_message_wizard_form" + )[1] except ValueError: compose_form_id = False ctx = dict() - ctx.update({ - 'default_model': 'pms.reservation', - 'default_res_id': self._ids[0], - 'default_use_template': bool(template_id), - 'default_template_id': template_id, - 'default_composition_mode': 'comment', - 'force_send': True, - 'mark_so_as_sent': True - }) + ctx.update( + { + "default_model": "pms.reservation", + "default_res_id": self._ids[0], + "default_use_template": bool(template_id), + "default_template_id": template_id, + "default_composition_mode": "comment", + "force_send": True, + "mark_so_as_sent": True, + } + ) return { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, - 'target': 'new', - 'context': ctx, - 'force_send': True + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_model": "mail.compose.message", + "views": [(compose_form_id, "form")], + "view_id": compose_form_id, + "target": "new", + "context": ctx, + "force_send": True, } # ORM Overrides @api.model def create(self, vals): - if vals.get('name', _('New')) == _('New') or 'name' not in vals: - if 'company_id' in vals: - vals['name'] = self.env['ir.sequence'].with_context( - force_company=vals['company_id'] - ).next_by_code('pms.folio') or _('New') + if vals.get("name", _("New")) == _("New") or "name" not in vals: + if "company_id" in vals: + vals["name"] = self.env["ir.sequence"].with_context( + force_company=vals["company_id"] + ).next_by_code("pms.folio") or _("New") else: - vals['name'] = self.env['ir.sequence'].next_by_code( - 'pms.folio') or _('New') + vals["name"] = self.env["ir.sequence"].next_by_code("pms.folio") or _( + "New" + ) vals.update(self._prepare_add_missing_fields(vals)) result = super(PmsFolio, self).create(vals) return result @@ -749,55 +811,48 @@ class PmsFolio(models.Model): def _prepare_add_missing_fields(self, values): """ Deduce missing required fields from the onchange """ res = {} - onchange_fields = ['partner_invoice_id', - 'pricelist_id', - 'payment_term_id'] - if values.get('partner_id'): + onchange_fields = ["partner_invoice_id", "pricelist_id", "payment_term_id"] + if values.get("partner_id"): line = self.new(values) if any(f not in values for f in onchange_fields): line.onchange_partner_id() for field in onchange_fields: if field not in values: - res[field] = line._fields[field].convert_to_write( - line[field], line) + res[field] = line._fields[field].convert_to_write(line[field], line) return res @api.model def calcule_reservation_type(self, is_staff, current_type): - if current_type == 'out': - return 'out' + if current_type == "out": + return "out" elif is_staff: - return 'staff' + return "staff" else: - return 'normal' - + return "normal" def action_done(self): - reservation_ids = self.mapped('reservation_ids') + reservation_ids = self.mapped("reservation_ids") for line in reservation_ids: if line.state == "booking": line.action_reservation_checkout() - def action_cancel(self): for folio in self: for reservation in folio.reservation_ids.filtered( - lambda res: res.state != 'cancelled'): + lambda res: res.state != "cancelled" + ): reservation.action_cancel() - self.write({ - 'state': 'cancel', - }) + self.write( + {"state": "cancel",} + ) return True - def action_confirm(self): - for folio in self.filtered(lambda folio: folio.partner_id not in - folio.message_partner_ids): + for folio in self.filtered( + lambda folio: folio.partner_id not in folio.message_partner_ids + ): folio.message_subscribe([folio.partner_id.id]) - self.write({ - 'state': 'confirm', - 'confirmation_date': fields.Datetime.now() - }) + self.write({"state": "confirm", "confirmation_date": fields.Datetime.now()}) # if self.env.context.get('send_email'): # self.force_quotation_send() @@ -812,65 +867,68 @@ class PmsFolio(models.Model): CHECKIN/OUT PROCESS """ - def _compute_checkin_partner_count(self): for record in self: - if record.reservation_type == 'normal' and record.reservation_ids: + if record.reservation_type == "normal" and record.reservation_ids: filtered_reservs = record.reservation_ids.filtered( - lambda x: x.state != 'cancelled' and - not x.parent_reservation) + lambda x: x.state != "cancelled" and not x.parent_reservation + ) mapped_checkin_partner = filtered_reservs.mapped( - 'checkin_partner_ids.id') + "checkin_partner_ids.id" + ) record.checkin_partner_count = len(mapped_checkin_partner) mapped_checkin_partner_count = filtered_reservs.mapped( - lambda x: (x.adults + x.children) - - len(x.checkin_partner_ids)) - record.checkin_partner_pending_count = sum( - mapped_checkin_partner_count) - + lambda x: (x.adults + x.children) - len(x.checkin_partner_ids) + ) + record.checkin_partner_pending_count = sum(mapped_checkin_partner_count) def get_grouped_reservations_json(self, state, import_all=False): self.ensure_one() info_grouped = [] for rline in self.reservation_ids: - if (import_all or rline.to_send) and \ - not rline.parent_reservation and rline.state == state: + if ( + (import_all or rline.to_send) + and not rline.parent_reservation + and rline.state == state + ): dates = (rline.real_checkin, rline.real_checkout) vals = { - 'num': len( + "num": len( self.reservation_ids.filtered( - lambda r: r.real_checkin == dates[0] and - r.real_checkout == dates[1] and - r.room_type_id.id == rline.room_type_id.id and - (r.to_send or import_all) and - not r.parent_reservation and - r.state == rline.state) + lambda r: r.real_checkin == dates[0] + and r.real_checkout == dates[1] + and r.room_type_id.id == rline.room_type_id.id + and (r.to_send or import_all) + and not r.parent_reservation + and r.state == rline.state + ) ), - 'room_type': { - 'id': rline.room_type_id.id, - 'name': rline.room_type_id.name, + "room_type": { + "id": rline.room_type_id.id, + "name": rline.room_type_id.name, }, - 'checkin': dates[0], - 'checkout': dates[1], - 'nights': len(rline.reservation_line_ids), - 'adults': rline.adults, - 'childrens': rline.children, + "checkin": dates[0], + "checkout": dates[1], + "nights": len(rline.reservation_line_ids), + "adults": rline.adults, + "childrens": rline.children, } founded = False for srline in info_grouped: - if srline['num'] == vals['num'] and \ - srline['room_type']['id'] == \ - vals['room_type']['id'] and \ - srline['checkin'] == vals['checkin'] and \ - srline['checkout'] == vals['checkout']: + if ( + srline["num"] == vals["num"] + and srline["room_type"]["id"] == vals["room_type"]["id"] + and srline["checkin"] == vals["checkin"] + and srline["checkout"] == vals["checkout"] + ): founded = True break if not founded: info_grouped.append(vals) - return sorted(sorted(info_grouped, key=lambda k: k['num'], - reverse=True), - key=lambda k: k['room_type']['id']) - + return sorted( + sorted(info_grouped, key=lambda k: k["num"], reverse=True), + key=lambda k: k["room_type"]["id"], + ) def _get_tax_amount_by_group(self): self.ensure_one() @@ -878,30 +936,28 @@ class PmsFolio(models.Model): for line in self.reservation_ids: price_reduce = line.price_total product = line.room_type_id.product_id - taxes = line.tax_ids.compute_all( - price_reduce, quantity=1, product=product)['taxes'] + taxes = line.tax_ids.compute_all(price_reduce, quantity=1, product=product)[ + "taxes" + ] for tax in line.tax_ids: group = tax.tax_group_id - res.setdefault(group, {'amount': 0.0, 'base': 0.0}) + res.setdefault(group, {"amount": 0.0, "base": 0.0}) for t in taxes: - if t['id'] == tax.id or t['id'] in \ - tax.children_tax_ids.ids: - res[group]['amount'] += t['amount'] - res[group]['base'] += t['base'] + if t["id"] == tax.id or t["id"] in tax.children_tax_ids.ids: + res[group]["amount"] += t["amount"] + res[group]["base"] += t["base"] for line in self.service_ids: price_reduce = line.price_unit * (1.0 - line.discount / 100.0) taxes = line.tax_ids.compute_all( - price_reduce, quantity=line.product_qty, - product=line.product_id)['taxes'] + price_reduce, quantity=line.product_qty, product=line.product_id + )["taxes"] for tax in line.tax_ids: group = tax.tax_group_id - res.setdefault(group, {'amount': 0.0, 'base': 0.0}) + res.setdefault(group, {"amount": 0.0, "base": 0.0}) for t in taxes: - if t['id'] == tax.id or t['id'] in \ - tax.children_tax_ids.ids: - res[group]['amount'] += t['amount'] - res[group]['base'] += t['base'] + if t["id"] == tax.id or t["id"] in tax.children_tax_ids.ids: + res[group]["amount"] += t["amount"] + res[group]["base"] += t["base"] res = sorted(res.items(), key=lambda l: l[0].sequence) - res = [(l[0].name, l[1]['amount'], l[1]['base'], len(res)) - for l in res] + res = [(l[0].name, l[1]["amount"], l[1]["base"], len(res)) for l in res] return res diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index cdcf49fa4..5a1994aea 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -3,61 +3,59 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import re -from odoo import models, fields, api, _ + +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class PmsProperty(models.Model): - _name = 'pms.property' - _description = 'Property' - _inherits = {'res.partner': 'partner_id'} + _name = "pms.property" + _description = "Property" + _inherits = {"res.partner": "partner_id"} # Fields declaration partner_id = fields.Many2one( - 'res.partner', - 'Property', - required=True, - delegate=True, - ondelete='cascade') + "res.partner", "Property", required=True, delegate=True, ondelete="cascade" + ) company_id = fields.Many2one( - 'res.company', + "res.company", required=True, - help='The company that owns or operates this property.') + help="The company that owns or operates this property.", + ) user_ids = fields.Many2many( - 'res.users', - 'pms_property_users_rel', - 'pms_property_id', - 'user_id', - string='Accepted Users') - room_type_ids = fields.One2many( - 'pms.room.type', - 'pms_property_id', - 'Room Types') - room_ids = fields.One2many( - 'pms.room', - 'pms_property_id', - 'Rooms') + "res.users", + "pms_property_users_rel", + "pms_property_id", + "user_id", + string="Accepted Users", + ) + room_type_ids = fields.One2many("pms.room.type", "pms_property_id", "Room Types") + room_ids = fields.One2many("pms.room", "pms_property_id", "Rooms") default_pricelist_id = fields.Many2one( - 'product.pricelist', - string='Product Pricelist', + "product.pricelist", + string="Product Pricelist", required=True, - help='The default pricelist used in this property.') + help="The default pricelist used in this property.", + ) default_restriction_id = fields.Many2one( - 'pms.room.type.restriction', - 'Restriction Plan', + "pms.room.type.restriction", + "Restriction Plan", required=True, - help='The default restriction plan used in this property.') - default_arrival_hour = fields.Char('Arrival Hour (GMT)', - help="HH:mm Format", default="14:00") - default_departure_hour = fields.Char('Departure Hour (GMT)', - help="HH:mm Format", default="12:00") - default_cancel_policy_days = fields.Integer('Cancellation Days') - default_cancel_policy_percent = fields.Float('Percent to pay') + help="The default restriction plan used in this property.", + ) + default_arrival_hour = fields.Char( + "Arrival Hour (GMT)", help="HH:mm Format", default="14:00" + ) + default_departure_hour = fields.Char( + "Departure Hour (GMT)", help="HH:mm Format", default="12:00" + ) + default_cancel_policy_days = fields.Integer("Cancellation Days") + default_cancel_policy_percent = fields.Float("Percent to pay") # Constraints and onchanges - @api.constrains('default_arrival_hour', 'default_departure_hour') + @api.constrains("default_arrival_hour", "default_departure_hour") def _check_hours(self): - r = re.compile('[0-2][0-9]:[0-5][0-9]') + r = re.compile("[0-2][0-9]:[0-5][0-9]") if not r.match(self.default_arrival_hour): raise ValidationError(_("Invalid arrival hour (Format: HH:mm)")) if not r.match(self.default_departure_hour): diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 14a8c5f3f..5de7e7f91 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -1,62 +1,65 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging +import re import time from datetime import timedelta -import re + +from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError from odoo.tools import ( - float_is_zero, - float_compare, DEFAULT_SERVER_DATE_FORMAT, - DEFAULT_SERVER_DATETIME_FORMAT) -from odoo import models, fields, api, _ -import logging + DEFAULT_SERVER_DATETIME_FORMAT, + float_compare, + float_is_zero, +) + _logger = logging.getLogger(__name__) class PmsReservation(models.Model): - _name = 'pms.reservation' - _description = 'Reservation' - _inherit = ['mail.thread', 'mail.activity.mixin', 'portal.mixin'] + _name = "pms.reservation" + _description = "Reservation" + _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] _order = "last_updated_res desc, name" # Default Methods ang Gets def _get_default_checkin(self): folio = False - if 'folio_id' in self._context: - folio = self.env['pms.folio'].search([ - ('id', '=', self._context['folio_id']) - ]) + if "folio_id" in self._context: + folio = self.env["pms.folio"].search( + [("id", "=", self._context["folio_id"])] + ) if folio and folio.reservation_ids: return folio.reservation_ids[0].checkin else: tz_property = self.env.user.pms_property_id.tz today = fields.Date.context_today(self.with_context(tz=tz_property)) - return fields.Date.from_string(today).strftime( - DEFAULT_SERVER_DATE_FORMAT) + return fields.Date.from_string(today).strftime(DEFAULT_SERVER_DATE_FORMAT) def _get_default_checkout(self): folio = False - if 'folio_id' in self._context: - folio = self.env['pms.folio'].search([ - ('id', '=', self._context['folio_id']) - ]) + if "folio_id" in self._context: + folio = self.env["pms.folio"].search( + [("id", "=", self._context["folio_id"])] + ) if folio and folio.reservation_ids: return folio.reservation_ids[0].checkout else: tz_property = self.env.user.pms_property_id.tz today = fields.Date.context_today(self.with_context(tz=tz_property)) - return (fields.Date.from_string(today) + timedelta(days=1)).\ - strftime(DEFAULT_SERVER_DATE_FORMAT) + return (fields.Date.from_string(today) + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ) def _get_default_arrival_hour(self): folio = False default_arrival_hour = self.env.user.pms_property_id.default_arrival_hour - if 'folio_id' in self._context: - folio = self.env['pms.folio'].search([ - ('id', '=', self._context['folio_id']) - ]) + if "folio_id" in self._context: + folio = self.env["pms.folio"].search( + [("id", "=", self._context["folio_id"])] + ) if folio and folio.reservation_ids: return folio.reservation_ids[0].arrival_hour else: @@ -65,10 +68,10 @@ class PmsReservation(models.Model): def _get_default_departure_hour(self): folio = False default_departure_hour = self.env.user.pms_property_id.default_departure_hour - if 'folio_id' in self._context: - folio = self.env['pms.folio'].search([ - ('id', '=', self._context['folio_id']) - ]) + if "folio_id" in self._context: + folio = self.env["pms.folio"].search( + [("id", "=", self._context["folio_id"])] + ) if folio and folio.reservation_ids: return folio.reservation_ids[0].departure_hour else: @@ -80,151 +83,141 @@ class PmsReservation(models.Model): If the guest has an invoicing address set, this method return diff_invoicing = True, else, return False """ - if 'reservation_id' in self.env.context: - reservation = self.env['pms.reservation'].browse([ - self.env.context['reservation_id'] - ]) + if "reservation_id" in self.env.context: + reservation = self.env["pms.reservation"].browse( + [self.env.context["reservation_id"]] + ) if reservation.partner_id.id == reservation.partner_invoice_id.id: return False return True # Fields declaration - name = fields.Text('Reservation Description', required=True) + name = fields.Text("Reservation Description", required=True) room_id = fields.Many2one( - 'pms.room', - string='Room', - track_visibility='onchange', - ondelete='restrict') + "pms.room", string="Room", track_visibility="onchange", ondelete="restrict" + ) folio_id = fields.Many2one( - 'pms.folio', - string='Folio', - track_visibility='onchange', - ondelete='cascade') + "pms.folio", string="Folio", track_visibility="onchange", ondelete="cascade" + ) board_service_room_id = fields.Many2one( - 'pms.board.service.room.type', - string='Board Service') + "pms.board.service.room.type", string="Board Service" + ) room_type_id = fields.Many2one( - 'pms.room.type', - string='Room Type', - required=True, - track_visibility='onchange') - partner_id = fields.Many2one( - related='folio_id.partner_id') - tour_operator_id = fields.Many2one( - related='folio_id.tour_operator_id') - partner_invoice_id = fields.Many2one( - related='folio_id.partner_invoice_id') - partner_invoice_state_id = fields.Many2one( - related="partner_invoice_id.state_id") + "pms.room.type", string="Room Type", required=True, track_visibility="onchange" + ) + partner_id = fields.Many2one(related="folio_id.partner_id") + tour_operator_id = fields.Many2one(related="folio_id.tour_operator_id") + partner_invoice_id = fields.Many2one(related="folio_id.partner_invoice_id") + partner_invoice_state_id = fields.Many2one(related="partner_invoice_id.state_id") partner_invoice_country_id = fields.Many2one( - related="partner_invoice_id.country_id") - partner_parent_id = fields.Many2one( - related="partner_id.parent_id") - closure_reason_id = fields.Many2one( - related='folio_id.closure_reason_id') + related="partner_invoice_id.country_id" + ) + partner_parent_id = fields.Many2one(related="partner_id.parent_id") + closure_reason_id = fields.Many2one(related="folio_id.closure_reason_id") company_id = fields.Many2one( - related='folio_id.company_id', - string='Company', - store=True, - readonly=True) + related="folio_id.company_id", string="Company", store=True, readonly=True + ) pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='folio_id.pms_property_id') + "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" + ) reservation_line_ids = fields.One2many( - 'pms.reservation.line', - 'reservation_id', - required=True) - service_ids = fields.One2many( - 'pms.service', - 'reservation_id') - pricelist_id = fields.Many2one( - 'product.pricelist', - related='folio_id.pricelist_id') + "pms.reservation.line", "reservation_id", required=True + ) + service_ids = fields.One2many("pms.service", "reservation_id") + pricelist_id = fields.Many2one("product.pricelist", related="folio_id.pricelist_id") # TODO: Warning Mens to update pricelist - checkin_partner_ids = fields.One2many( - 'pms.checkin.partner', - 'reservation_id') - parent_reservation = fields.Many2one( - 'pms.reservation', - string='Parent Reservation') - segmentation_ids = fields.Many2many( - related='folio_id.segmentation_ids') + checkin_partner_ids = fields.One2many("pms.checkin.partner", "reservation_id") + parent_reservation = fields.Many2one("pms.reservation", string="Parent Reservation") + segmentation_ids = fields.Many2many(related="folio_id.segmentation_ids") currency_id = fields.Many2one( - 'res.currency', - related='pricelist_id.currency_id', - string='Currency', + "res.currency", + related="pricelist_id.currency_id", + string="Currency", readonly=True, - required=True) + required=True, + ) tax_ids = fields.Many2many( - 'account.tax', - string='Taxes', - ondelete='restrict', - domain=['|', ('active', '=', False), ('active', '=', True)]) + "account.tax", + string="Taxes", + ondelete="restrict", + domain=["|", ("active", "=", False), ("active", "=", True)], + ) move_line_ids = fields.Many2many( - 'account.move.line', - 'reservation_move_rel', - 'reservation_id', - 'move_line_id', - string='Invoice Lines', - copy=False) - analytic_tag_ids = fields.Many2many( - 'account.analytic.tag', - string='Analytic Tags') + "account.move.line", + "reservation_move_rel", + "reservation_id", + "move_line_id", + string="Invoice Lines", + copy=False, + ) + analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags") localizator = fields.Char( - string='Localizator', - compute='_compute_localizator', - store=True) - sequence = fields.Integer(string='Sequence', default=10) - reservation_no = fields.Char('Reservation No', size=64, readonly=True) - adults = fields.Integer('Adults', size=64, readonly=False, - track_visibility='onchange', - help='List of adults there in guest list. ') - children = fields.Integer('Children', size=64, readonly=False, - track_visibility='onchange', - help='Number of children there in guest list.') - to_assign = fields.Boolean('To Assign', track_visibility='onchange') - state = fields.Selection([ - ('draft', 'Pre-reservation'), - ('confirm', 'Pending Entry'), - ('booking', 'On Board'), - ('done', 'Out'), - ('cancelled', 'Cancelled')], - string='Status', - readonly=True, - default=lambda *a: 'draft', - copy=False, - track_visibility='onchange') - reservation_type = fields.Selection(related='folio_id.reservation_type', - default=lambda *a: 'normal') - invoice_count = fields.Integer(related='folio_id.invoice_count') - credit_card_details = fields.Text(related='folio_id.credit_card_details') - cancelled_reason = fields.Selection([ - ('late', 'Late'), - ('intime', 'In time'), - ('noshow', 'No Show')], - string='Cause of cancelled', - track_visibility='onchange') - out_service_description = fields.Text('Cause of out of service') - checkin = fields.Date('Check In', required=True, - default=_get_default_checkin) - checkout = fields.Date('Check Out', required=True, - default=_get_default_checkout) - real_checkin = fields.Date('Arrival', required=True, - track_visibility='onchange') - real_checkout = fields.Date('Departure', required=True, - track_visibility='onchange') - arrival_hour = fields.Char('Arrival Hour', - default=_get_default_arrival_hour, - help="Default Arrival Hour (HH:MM)") - departure_hour = fields.Char('Departure Hour', - default=_get_default_departure_hour, - help="Default Departure Hour (HH:MM)") + string="Localizator", compute="_compute_localizator", store=True + ) + sequence = fields.Integer(string="Sequence", default=10) + reservation_no = fields.Char("Reservation No", size=64, readonly=True) + adults = fields.Integer( + "Adults", + size=64, + readonly=False, + track_visibility="onchange", + help="List of adults there in guest list. ", + ) + children = fields.Integer( + "Children", + size=64, + readonly=False, + track_visibility="onchange", + help="Number of children there in guest list.", + ) + to_assign = fields.Boolean("To Assign", track_visibility="onchange") + state = fields.Selection( + [ + ("draft", "Pre-reservation"), + ("confirm", "Pending Entry"), + ("booking", "On Board"), + ("done", "Out"), + ("cancelled", "Cancelled"), + ], + string="Status", + readonly=True, + default=lambda *a: "draft", + copy=False, + track_visibility="onchange", + ) + reservation_type = fields.Selection( + related="folio_id.reservation_type", default=lambda *a: "normal" + ) + invoice_count = fields.Integer(related="folio_id.invoice_count") + credit_card_details = fields.Text(related="folio_id.credit_card_details") + cancelled_reason = fields.Selection( + [("late", "Late"), ("intime", "In time"), ("noshow", "No Show")], + string="Cause of cancelled", + track_visibility="onchange", + ) + out_service_description = fields.Text("Cause of out of service") + checkin = fields.Date("Check In", required=True, default=_get_default_checkin) + checkout = fields.Date("Check Out", required=True, default=_get_default_checkout) + real_checkin = fields.Date("Arrival", required=True, track_visibility="onchange") + real_checkout = fields.Date("Departure", required=True, track_visibility="onchange") + arrival_hour = fields.Char( + "Arrival Hour", + default=_get_default_arrival_hour, + help="Default Arrival Hour (HH:MM)", + ) + departure_hour = fields.Char( + "Departure Hour", + default=_get_default_departure_hour, + help="Default Departure Hour (HH:MM)", + ) partner_invoice_vat = fields.Char(related="partner_invoice_id.vat") partner_invoice_name = fields.Char(related="partner_invoice_id.name") - partner_invoice_street = fields.Char(related="partner_invoice_id.street", string="Street") - partner_invoice_street2 = fields.Char(related="partner_invoice_id.street", string="Street2") + partner_invoice_street = fields.Char( + related="partner_invoice_id.street", string="Street" + ) + partner_invoice_street2 = fields.Char( + related="partner_invoice_id.street", string="Street2" + ) partner_invoice_zip = fields.Char(related="partner_invoice_id.zip") partner_invoice_city = fields.Char(related="partner_invoice_id.city") partner_invoice_email = fields.Char(related="partner_invoice_id.email") @@ -235,115 +228,132 @@ class PmsReservation(models.Model): # searching on a computed field can also be enabled by setting the # search parameter. The value is a method name returning a Domains checkin_partner_count = fields.Integer( - 'Checkin counter', - compute='_compute_checkin_partner_count') + "Checkin counter", compute="_compute_checkin_partner_count" + ) checkin_partner_pending_count = fields.Integer( - 'Checkin Pending Num', - compute='_compute_checkin_partner_count', - search='_search_checkin_partner_pending') + "Checkin Pending Num", + compute="_compute_checkin_partner_count", + search="_search_checkin_partner_pending", + ) customer_sleep_here = fields.Boolean( default=True, string="Include customer", - help="Indicates if the customer sleeps in this room") + help="Indicates if the customer sleeps in this room", + ) # check_rooms = fields.Boolean('Check Rooms') - splitted = fields.Boolean('Splitted', default=False) - overbooking = fields.Boolean('Is Overbooking', default=False) - reselling = fields.Boolean('Is Reselling', default=False) - nights = fields.Integer('Nights', compute='_computed_nights', store=True) - channel_type = fields.Selection([ - ('door', 'Door'), - ('mail', 'Mail'), - ('phone', 'Phone'), - ('call', 'Call Center'), - ('web', 'Web'), - ('agency', 'Agencia'), - ('operator', 'Tour operador'), - ('virtualdoor', 'Virtual Door'), ], - string='Sales Channel', - default='door') - last_updated_res = fields.Datetime('Last Updated') - folio_pending_amount = fields.Monetary(related='folio_id.pending_amount') - shared_folio = fields.Boolean(compute='_computed_shared') + splitted = fields.Boolean("Splitted", default=False) + overbooking = fields.Boolean("Is Overbooking", default=False) + reselling = fields.Boolean("Is Reselling", default=False) + nights = fields.Integer("Nights", compute="_computed_nights", store=True) + channel_type = fields.Selection( + [ + ("door", "Door"), + ("mail", "Mail"), + ("phone", "Phone"), + ("call", "Call Center"), + ("web", "Web"), + ("agency", "Agencia"), + ("operator", "Tour operador"), + ("virtualdoor", "Virtual Door"), + ], + string="Sales Channel", + default="door", + ) + last_updated_res = fields.Datetime("Last Updated") + folio_pending_amount = fields.Monetary(related="folio_id.pending_amount") + shared_folio = fields.Boolean(compute="_computed_shared") # Used to notify is the reservation folio has other reservations/services - email = fields.Char('E-mail', related='partner_id.email') - mobile = fields.Char('Mobile', related='partner_id.mobile') - phone = fields.Char('Phone', related='partner_id.phone') - partner_internal_comment = fields.Text(string='Internal Partner Notes', - related='partner_id.comment') - folio_internal_comment = fields.Text(string='Internal Folio Notes', - related='folio_id.internal_comment') - preconfirm = fields.Boolean('Auto confirm to Save', default=True) - to_send = fields.Boolean('To Send', default=True) - call_center = fields.Boolean(default='set_call_center_user') + email = fields.Char("E-mail", related="partner_id.email") + mobile = fields.Char("Mobile", related="partner_id.mobile") + phone = fields.Char("Phone", related="partner_id.phone") + partner_internal_comment = fields.Text( + string="Internal Partner Notes", related="partner_id.comment" + ) + folio_internal_comment = fields.Text( + string="Internal Folio Notes", related="folio_id.internal_comment" + ) + preconfirm = fields.Boolean("Auto confirm to Save", default=True) + to_send = fields.Boolean("To Send", default=True) + call_center = fields.Boolean(default="set_call_center_user") has_confirmed_reservations_to_send = fields.Boolean( - related='folio_id.has_confirmed_reservations_to_send', - readonly=True) + related="folio_id.has_confirmed_reservations_to_send", readonly=True + ) has_cancelled_reservations_to_send = fields.Boolean( - related='folio_id.has_cancelled_reservations_to_send', - readonly=True) + related="folio_id.has_cancelled_reservations_to_send", readonly=True + ) has_checkout_to_send = fields.Boolean( - related='folio_id.has_checkout_to_send', - readonly=True) - to_print = fields.Boolean( - 'Print', help='Print in Folio Report', default=True) - invoice_status = fields.Selection([ - ('invoiced', 'Fully Invoiced'), - ('to invoice', 'To Invoice'), - ('no', 'Nothing to Invoice')], - string='Invoice Status', - compute='_compute_invoice_status', - store=True, - readonly=True, - default='no') + related="folio_id.has_checkout_to_send", readonly=True + ) + to_print = fields.Boolean("Print", help="Print in Folio Report", default=True) + invoice_status = fields.Selection( + [ + ("invoiced", "Fully Invoiced"), + ("to invoice", "To Invoice"), + ("no", "Nothing to Invoice"), + ], + string="Invoice Status", + compute="_compute_invoice_status", + store=True, + readonly=True, + default="no", + ) qty_to_invoice = fields.Float( - compute='_get_to_invoice_qty', - string='To Invoice', + compute="_get_to_invoice_qty", + string="To Invoice", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) qty_invoiced = fields.Float( - compute='_get_invoice_qty', - string='Invoiced', + compute="_get_invoice_qty", + string="Invoiced", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) price_subtotal = fields.Monetary( - string='Subtotal', + string="Subtotal", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_reservation') + digits=("Product Price"), + compute="_compute_amount_reservation", + ) price_total = fields.Monetary( - string='Total', + string="Total", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_reservation') + digits=("Product Price"), + compute="_compute_amount_reservation", + ) price_tax = fields.Float( - string='Taxes Amount', + string="Taxes Amount", readonly=True, store=True, - compute='_compute_amount_reservation') + compute="_compute_amount_reservation", + ) price_services = fields.Monetary( - string='Services Total', + string="Services Total", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_room_services') + digits=("Product Price"), + compute="_compute_amount_room_services", + ) price_room_services_set = fields.Monetary( - string='Room Services Total', + string="Room Services Total", readonly=True, store=True, - digits=('Product Price'), - compute='_compute_amount_set') + digits=("Product Price"), + compute="_compute_amount_set", + ) discount = fields.Float( - string='Discount (€)', - digits=('Discount'), - compute='_compute_discount', - store=True) + string="Discount (€)", + digits=("Discount"), + compute="_compute_discount", + store=True, + ) # Compute and Search methods - @api.depends('state', 'qty_to_invoice', 'qty_invoiced') + @api.depends("state", "qty_to_invoice", "qty_invoiced") def _compute_invoice_status(self): """ Compute the invoice status of a Reservation. Possible statuses: @@ -356,22 +366,27 @@ class PmsReservation(models.Model): - invoiced: the quantity invoiced is larger or equal to the quantity ordered. """ - precision = self.env['decimal.precision'].precision_get( - 'Product Unit of Measure') + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) for line in self: - if line.state in ('draft'): - line.invoice_status = 'no' - elif not float_is_zero(line.qty_to_invoice, - precision_digits=precision): - line.invoice_status = 'to invoice' - elif float_compare(line.qty_invoiced, - len(line.reservation_line_ids), - precision_digits=precision) >= 0: - line.invoice_status = 'invoiced' + if line.state in ("draft"): + line.invoice_status = "no" + elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): + line.invoice_status = "to invoice" + elif ( + float_compare( + line.qty_invoiced, + len(line.reservation_line_ids), + precision_digits=precision, + ) + >= 0 + ): + line.invoice_status = "invoiced" else: - line.invoice_status = 'no' + line.invoice_status = "no" - @api.depends('qty_invoiced', 'nights', 'folio_id.state') + @api.depends("qty_invoiced", "nights", "folio_id.state") def _get_to_invoice_qty(self): """ Compute the quantity to invoice. If the invoice policy is order, @@ -379,14 +394,12 @@ class PmsReservation(models.Model): Otherwise, the quantity delivered is used. """ for line in self: - if line.folio_id.state not in ['draft']: - line.qty_to_invoice = len( - line.reservation_line_ids) - line.qty_invoiced + if line.folio_id.state not in ["draft"]: + line.qty_to_invoice = len(line.reservation_line_ids) - line.qty_invoiced else: line.qty_to_invoice = 0 - @api.depends('move_line_ids.move_id.state', - 'move_line_ids.quantity') + @api.depends("move_line_ids.move_id.state", "move_line_ids.quantity") def _get_invoice_qty(self): """ Compute the quantity invoiced. If case of a refund, the quantity @@ -397,204 +410,221 @@ class PmsReservation(models.Model): qty_invoiced = 0.0 for day in line.reservation_line_ids: invoice_lines = day.move_line_ids.filtered( - lambda r: r.move_id.state != 'cancel') - qty_invoiced += len(invoice_lines.filtered( - lambda r: r.move_id.type == 'out_invoice') - ) - len(invoice_lines.filtered( - lambda r: r.move_id.type == - 'out_refund')) + lambda r: r.move_id.state != "cancel" + ) + qty_invoiced += len( + invoice_lines.filtered(lambda r: r.move_id.type == "out_invoice") + ) - len( + invoice_lines.filtered(lambda r: r.move_id.type == "out_refund") + ) line.qty_invoiced = qty_invoiced - @api.depends('checkin', 'checkout') + @api.depends("checkin", "checkout") def _computed_nights(self): for res in self: if res.checkin and res.checkout: res.nights = ( - fields.Date.from_string( - res.checkout) - fields.Date.from_string(res.checkin) + fields.Date.from_string(res.checkout) + - fields.Date.from_string(res.checkin) ).days - @api.depends('folio_id', 'checkin', 'checkout') + @api.depends("folio_id", "checkin", "checkout") def _compute_localizator(self): for record in self: if record.folio_id: - #TODO: Review Datetime type no char v13 + # TODO: Review Datetime type no char v13 localizator = re.sub("[^0-9]", "", record.folio_id.name) # checkout = int(re.sub("[^0-9]", "", record.checkout)) # checkin = int(re.sub("[^0-9]", "", record.checkin)) # localizator += str((checkin + checkout) % 99) record.localizator = localizator - @api.depends('service_ids.price_total') + @api.depends("service_ids.price_total") def _compute_amount_room_services(self): for record in self: - record.price_services = sum( - record.mapped('service_ids.price_total')) + record.price_services = sum(record.mapped("service_ids.price_total")) - @api.depends('price_services', 'price_total') + @api.depends("price_services", "price_total") def _compute_amount_set(self): for record in self: - record.price_room_services_set = record.price_services + \ - record.price_total + record.price_room_services_set = record.price_services + record.price_total - @api.depends('reservation_line_ids.discount', - 'reservation_line_ids.cancel_discount') + @api.depends( + "reservation_line_ids.discount", "reservation_line_ids.cancel_discount" + ) def _compute_discount(self): for record in self: discount = 0 for line in record.reservation_line_ids: first_discount = line.price * ((line.discount or 0.0) * 0.01) price = line.price - first_discount - cancel_discount = price * \ - ((line.cancel_discount or 0.0) * 0.01) + cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01) discount += first_discount + cancel_discount record.discount = discount - @api.depends('reservation_line_ids.price', 'discount', 'tax_ids') + @api.depends("reservation_line_ids.price", "discount", "tax_ids") def _compute_amount_reservation(self): """ Compute the amounts of the reservation. """ for record in self: - amount_room = sum(record.reservation_line_ids.mapped('price')) + amount_room = sum(record.reservation_line_ids.mapped("price")) if amount_room > 0: product = record.room_type_id.product_id price = amount_room - record.discount taxes = record.tax_ids.compute_all( - price, record.currency_id, 1, product=product) - record.update({ - 'price_tax': sum(t.get('amount', 0.0) for t in taxes.get( - 'taxes', [])), - 'price_total': taxes['total_included'], - 'price_subtotal': taxes['total_excluded'], - }) + price, record.currency_id, 1, product=product + ) + record.update( + { + "price_tax": sum( + t.get("amount", 0.0) for t in taxes.get("taxes", []) + ), + "price_total": taxes["total_included"], + "price_subtotal": taxes["total_excluded"], + } + ) # Constraints and onchanges - @api.constrains('adults') + @api.constrains("adults") def _check_adults(self): for record in self: extra_bed = record.service_ids.filtered( - lambda r: r.product_id.is_extra_bed is True) + lambda r: r.product_id.is_extra_bed is True + ) if record.adults > record.room_id.get_capacity(len(extra_bed)): - raise ValidationError( - _("Persons can't be higher than room capacity")) + raise ValidationError(_("Persons can't be higher than room capacity")) if record.adults == 0: raise ValidationError(_("Reservation has no adults")) # TODO: Use default values on checkin /checkout is empty - @api.constrains('checkin', 'checkout', 'state', - 'room_id', 'overbooking', 'reselling') + @api.constrains( + "checkin", "checkout", "state", "room_id", "overbooking", "reselling" + ) def check_dates(self): """ 1.-When date_order is less then checkin date or Checkout date should be greater than the checkin date. 3.-Check the reservation dates are not occuped """ - _logger.info('check_dates') + _logger.info("check_dates") if fields.Date.from_string(self.checkin) >= fields.Date.from_string( - self.checkout): - raise ValidationError(_('Room line Check In Date Should be \ - less than the Check Out Date!')) - if not self.overbooking \ - and self.state not in ('cancelled') \ - and not self._context.get("ignore_avail_restrictions", False): - occupied = self.env['pms.reservation'].get_reservations( + self.checkout + ): + raise ValidationError( + _( + "Room line Check In Date Should be \ + less than the Check Out Date!" + ) + ) + if ( + not self.overbooking + and self.state not in ("cancelled") + and not self._context.get("ignore_avail_restrictions", False) + ): + occupied = self.env["pms.reservation"].get_reservations( self.checkin, - (fields.Date.from_string(self.checkout) - timedelta(days=1)). - strftime(DEFAULT_SERVER_DATE_FORMAT)) + (fields.Date.from_string(self.checkout) - timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + ) occupied = occupied.filtered( - lambda r: r.room_id.id == self.room_id.id and - r.id != self.id) - occupied_name = ', '.join(str(x.folio_id.name) for x in occupied) + lambda r: r.room_id.id == self.room_id.id and r.id != self.id + ) + occupied_name = ", ".join(str(x.folio_id.name) for x in occupied) if occupied: - warning_msg = _('You tried to change/confirm \ + warning_msg = ( + _( + "You tried to change/confirm \ reservation with room those already reserved in this \ - reservation period: %s ') % occupied_name + reservation period: %s " + ) + % occupied_name + ) raise ValidationError(warning_msg) - - @api.constrains('checkin_partner_ids') + @api.constrains("checkin_partner_ids") def _max_checkin_partner_ids(self): for record in self: - if len(record.checkin_partner_ids) > record.adults + \ - record.children: - raise models.ValidationError( - _('The room already is completed')) + if len(record.checkin_partner_ids) > record.adults + record.children: + raise models.ValidationError(_("The room already is completed")) - @api.onchange('adults', 'room_id') + @api.onchange("adults", "room_id") def onchange_room_id(self): if self.room_id: write_vals = {} extra_bed = self.service_ids.filtered( - lambda r: r.product_id.is_extra_bed is True) + lambda r: r.product_id.is_extra_bed is True + ) if self.room_id.get_capacity(len(extra_bed)) < self.adults: raise UserError( - _('%s people do not fit in this room! ;)') % (self.adults)) + _("%s people do not fit in this room! ;)") % (self.adults) + ) if self.adults == 0: - write_vals.update({'adults': self.room_id.capacity}) + write_vals.update({"adults": self.room_id.capacity}) if not self.room_type_id: - write_vals.update( - {'room_type_id': self.room_id.room_type_id.id}) + write_vals.update({"room_type_id": self.room_id.room_type_id.id}) self.update(write_vals) - @api.onchange('cancelled_reason') + @api.onchange("cancelled_reason") def onchange_cancelled_reason(self): for record in self: record._compute_cancelled_discount() - @api.onchange('partner_id') + @api.onchange("partner_id") def onchange_partner_id(self): - addr = self.partner_id.address_get(['invoice']) - pricelist = self.partner_id.property_product_pricelist and \ - self.partner_id.property_product_pricelist.id or \ - self.env.user.pms_property_id.default_pricelist_id.id + addr = self.partner_id.address_get(["invoice"]) + pricelist = ( + self.partner_id.property_product_pricelist + and self.partner_id.property_product_pricelist.id + or self.env.user.pms_property_id.default_pricelist_id.id + ) values = { - 'pricelist_id': pricelist, - 'partner_invoice_id': addr['invoice'], + "pricelist_id": pricelist, + "partner_invoice_id": addr["invoice"], } self.update(values) - - @api.onchange('pricelist_id') + @api.onchange("pricelist_id") def onchange_pricelist_id(self): - values = {'reservation_type': self.env['pms.folio']. - calcule_reservation_type( - self.pricelist_id.is_staff, - self.reservation_type)} + values = { + "reservation_type": self.env["pms.folio"].calcule_reservation_type( + self.pricelist_id.is_staff, self.reservation_type + ) + } self.update(values) - @api.onchange('reservation_type') + @api.onchange("reservation_type") def assign_partner_company_on_out_service(self): - if self.reservation_type == 'out': - self.update({'partner_id': - self.env.user.company_id.partner_id.id}) + if self.reservation_type == "out": + self.update({"partner_id": self.env.user.company_id.partner_id.id}) - - @api.onchange('checkin_partner_ids') + @api.onchange("checkin_partner_ids") def onchange_checkin_partner_ids(self): for record in self: - if len(record.checkin_partner_ids) > \ - record.adults + record.children: - raise models.ValidationError( - _('The room already is completed')) + if len(record.checkin_partner_ids) > record.adults + record.children: + raise models.ValidationError(_("The room already is completed")) - @api.onchange('room_type_id', 'pricelist_id', 'reservation_type') + @api.onchange("room_type_id", "pricelist_id", "reservation_type") def onchange_overwrite_price_by_day(self): """ We need to overwrite the prices even if they were already established """ if self.room_type_id and self.checkin and self.checkout: days_diff = ( - fields.Date.from_string(self.checkout) - - fields.Date.from_string(self.checkin) + fields.Date.from_string(self.checkout) + - fields.Date.from_string(self.checkin) ).days - self.update(self.prepare_reservation_lines( - self.checkin, - days_diff, - self.pricelist_id.id, - update_old_prices=True)) + self.update( + self.prepare_reservation_lines( + self.checkin, + days_diff, + self.pricelist_id.id, + update_old_prices=True, + ) + ) - @api.onchange('checkin', 'checkout') + @api.onchange("checkin", "checkout") def onchange_dates(self): """ We need to update prices respecting those that were already established @@ -606,21 +636,24 @@ class PmsReservation(models.Model): checkin_dt = fields.Date.from_string(self.checkin) checkout_dt = fields.Date.from_string(self.checkout) if checkin_dt >= checkout_dt: - self.checkout = (fields.Date.from_string(self.checkin) + - timedelta(days=1)).strftime( - DEFAULT_SERVER_DATE_FORMAT) + self.checkout = ( + fields.Date.from_string(self.checkin) + timedelta(days=1) + ).strftime(DEFAULT_SERVER_DATE_FORMAT) if self.room_type_id: days_diff = ( - fields.Date.from_string(self.checkout) - - fields.Date.from_string(self.checkin) + fields.Date.from_string(self.checkout) + - fields.Date.from_string(self.checkin) ).days - self.update(self.prepare_reservation_lines( - self.checkin, - days_diff, - self.pricelist_id.id, - update_old_prices=False)) + self.update( + self.prepare_reservation_lines( + self.checkin, + days_diff, + self.pricelist_id.id, + update_old_prices=False, + ) + ) - @api.onchange('checkin', 'checkout', 'room_type_id') + @api.onchange("checkin", "checkout", "room_type_id") def onchange_room_type_id(self): """ When change de room_type_id, we calc the line description and tax_ids @@ -628,48 +661,52 @@ class PmsReservation(models.Model): if self.room_type_id and self.checkin and self.checkout: checkin_dt = fields.Date.from_string(self.checkin) checkout_dt = fields.Date.from_string(self.checkout) - checkin_str = checkin_dt.strftime('%d/%m/%Y') - checkout_str = checkout_dt.strftime('%d/%m/%Y') - self.name = self.room_type_id.name + ': ' + checkin_str + ' - '\ - + checkout_str + checkin_str = checkin_dt.strftime("%d/%m/%Y") + checkout_str = checkout_dt.strftime("%d/%m/%Y") + self.name = ( + self.room_type_id.name + ": " + checkin_str + " - " + checkout_str + ) self._compute_tax_ids() - @api.onchange('checkin', 'checkout') + @api.onchange("checkin", "checkout") def onchange_update_service_per_day(self): services = self.service_ids.filtered(lambda r: r.per_day is True) for service in services: service.onchange_product_id() - - @api.onchange('checkin', 'checkout', 'room_id') + @api.onchange("checkin", "checkout", "room_id") def onchange_room_availabiltiy_domain(self): self.ensure_one() if self.checkin and self.checkout: - if self.overbooking or self.reselling or \ - self.state in ('cancelled'): + if self.overbooking or self.reselling or self.state in ("cancelled"): return - occupied = self.env['pms.reservation'].get_reservations( + occupied = self.env["pms.reservation"].get_reservations( self.checkin, - (fields.Date.from_string(self.checkout) - timedelta(days=1)). - strftime(DEFAULT_SERVER_DATE_FORMAT)) - rooms_occupied = occupied.mapped('room_id.id') + (fields.Date.from_string(self.checkout) - timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + ) + rooms_occupied = occupied.mapped("room_id.id") if self.room_id: occupied = occupied.filtered( - lambda r: r.room_id.id == self.room_id.id and - r.id != self._origin.id) + lambda r: r.room_id.id == self.room_id.id + and r.id != self._origin.id + ) if occupied: - occupied_name = ', '.join( - str(x.folio_id.name) for x in occupied) - warning_msg = _('You tried to change/confirm \ + occupied_name = ", ".join(str(x.folio_id.name) for x in occupied) + warning_msg = ( + _( + "You tried to change/confirm \ reservation with room those already reserved in this \ - reservation period: %s ') % occupied_name + reservation period: %s " + ) + % occupied_name + ) raise ValidationError(warning_msg) - domain_rooms = [ - ('id', 'not in', rooms_occupied) - ] - return {'domain': {'room_id': domain_rooms}} + domain_rooms = [("id", "not in", rooms_occupied)] + return {"domain": {"room_id": domain_rooms}} - @api.onchange('board_service_room_id') + @api.onchange("board_service_room_id") def onchange_board_service(self): if self.board_service_room_id: board_services = [(5, 0, 0)] @@ -677,262 +714,264 @@ class PmsReservation(models.Model): product = line.product_id if product.per_day: res = { - 'product_id': product.id, - 'is_board_service': True, - 'folio_id': self.folio_id.id, - 'reservation_id': self.id, + "product_id": product.id, + "is_board_service": True, + "folio_id": self.folio_id.id, + "reservation_id": self.id, } - line = self.env['pms.service'].new(res) + line = self.env["pms.service"].new(res) + res.update(self.env["pms.service"]._prepare_add_missing_fields(res)) res.update( - self.env['pms.service']._prepare_add_missing_fields( - res)) - res.update(self.env['pms.service'].prepare_service_ids( - dfrom=self.checkin, - days=self.nights, - per_person=product.per_person, - persons=self.adults, - old_line_days=False, - consumed_on=product.consumed_on,)) + self.env["pms.service"].prepare_service_ids( + dfrom=self.checkin, + days=self.nights, + per_person=product.per_person, + persons=self.adults, + old_line_days=False, + consumed_on=product.consumed_on, + ) + ) board_services.append((0, False, res)) - other_services = self.service_ids.filtered( - lambda r: not r.is_board_service) - self.update({'service_ids': board_services}) + other_services = self.service_ids.filtered(lambda r: not r.is_board_service) + self.update({"service_ids": board_services}) self.service_ids |= other_services - for service in self.service_ids.filtered( - lambda r: r.is_board_service): + for service in self.service_ids.filtered(lambda r: r.is_board_service): service._compute_tax_ids() service.price_unit = service._compute_price_unit() # Action methods def open_invoices_reservation(self): - invoices = self.folio_id.mapped('move_ids') - action = self.env.ref('account.action_move_out_invoice_type').read()[0] + invoices = self.folio_id.mapped("move_ids") + action = self.env.ref("account.action_move_out_invoice_type").read()[0] if len(invoices) > 1: - action['domain'] = [('id', 'in', invoices.ids)] + action["domain"] = [("id", "in", invoices.ids)] elif len(invoices) == 1: - action['views'] = [ - (self.env.ref('account.view_move_form').id, 'form')] - action['res_id'] = invoices.ids[0] + action["views"] = [(self.env.ref("account.view_move_form").id, "form")] + action["res_id"] = invoices.ids[0] else: - action = self.env.ref( - 'pms.action_view_folio_advance_payment_inv').read()[0] - action['context'] = {'default_reservation_id': self.id, - 'default_folio_id': self.folio_id.id} + action = self.env.ref("pms.action_view_folio_advance_payment_inv").read()[0] + action["context"] = { + "default_reservation_id": self.id, + "default_folio_id": self.folio_id.id, + } return action - def create_invoice(self): - action = self.env.ref( - 'pms.action_view_folio_advance_payment_inv').read()[0] - action['context'] = {'default_reservation_id': self.id, - 'default_folio_id': self.folio_id.id} + action = self.env.ref("pms.action_view_folio_advance_payment_inv").read()[0] + action["context"] = { + "default_reservation_id": self.id, + "default_folio_id": self.folio_id.id, + } return action - def open_folio(self): - action = self.env.ref( - 'pms.open_pms_folio1_form_tree_all').read()[0] + action = self.env.ref("pms.open_pms_folio1_form_tree_all").read()[0] if self.folio_id: - action['views'] = [ - (self.env.ref('pms.pms_folio_view_form').id, 'form')] - action['res_id'] = self.folio_id.id + action["views"] = [(self.env.ref("pms.pms_folio_view_form").id, "form")] + action["res_id"] = self.folio_id.id else: - action = {'type': 'ir.actions.act_window_close'} + action = {"type": "ir.actions.act_window_close"} return action - def open_reservation_form(self): - action = self.env.ref( - 'pms.open_pms_reservation_form_tree_all').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_reservation_view_form').id, 'form')] - action['res_id'] = self.id + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["views"] = [(self.env.ref("pms.pms_reservation_view_form").id, "form")] + action["res_id"] = self.id return action - def action_pay_folio(self): self.ensure_one() return self.folio_id.action_pay() - def action_pay_reservation(self): self.ensure_one() partner = self.partner_id.id amount = min(self.price_room_services_set, self.folio_pending_amount) - note = self.folio_id.name + ' (' + self.name + ')' - view_id = self.env.ref('pms.account_payment_view_form_folio').id - return{ - 'name': _('Register Payment'), - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'account.payment', - 'type': 'ir.actions.act_window', - 'view_id': view_id, - 'context': { - 'default_folio_id': self.folio_id.id, - 'default_room_id': self.id, - 'default_amount': amount, - 'default_payment_type': 'inbound', - 'default_partner_type': 'customer', - 'default_partner_id': partner, - 'default_communication': note, + note = self.folio_id.name + " (" + self.name + ")" + view_id = self.env.ref("pms.account_payment_view_form_folio").id + return { + "name": _("Register Payment"), + "view_type": "form", + "view_mode": "form", + "res_model": "account.payment", + "type": "ir.actions.act_window", + "view_id": view_id, + "context": { + "default_folio_id": self.folio_id.id, + "default_room_id": self.id, + "default_amount": amount, + "default_payment_type": "inbound", + "default_partner_type": "customer", + "default_partner_id": partner, + "default_communication": note, }, - 'target': 'new', + "target": "new", } # ORM Overrides @api.model - def name_search(self, name='', args=None, operator='ilike', limit=100): + def name_search(self, name="", args=None, operator="ilike", limit=100): if args is None: args = [] - if not(name == '' and operator == 'ilike'): + if not (name == "" and operator == "ilike"): args += [ - '|', - ('folio_id.name', operator, name), - ('room_id.name', operator, name) + "|", + ("folio_id.name", operator, name), + ("room_id.name", operator, name), ] return super(PmsReservation, self).name_search( - name='', args=args, operator='ilike', limit=limit) - + name="", args=args, operator="ilike", limit=limit + ) def name_get(self): result = [] for res in self: - name = u'%s (%s)' % (res.folio_id.name, res.room_id.name) + name = u"{} ({})".format(res.folio_id.name, res.room_id.name) result.append((res.id, name)) return result @api.model def create(self, vals): - if 'room_id' not in vals: + if "room_id" not in vals: vals.update(self._autoassign(vals)) vals.update(self._prepare_add_missing_fields(vals)) - if 'folio_id' in vals and 'channel_type' not in vals: - folio = self.env["pms.folio"].browse(vals['folio_id']) - vals.update({'channel_type': folio.channel_type}) - elif 'partner_id' in vals: - folio_vals = {'partner_id': int(vals.get('partner_id')), - 'channel_type': vals.get('channel_type')} + if "folio_id" in vals and "channel_type" not in vals: + folio = self.env["pms.folio"].browse(vals["folio_id"]) + vals.update({"channel_type": folio.channel_type}) + elif "partner_id" in vals: + folio_vals = { + "partner_id": int(vals.get("partner_id")), + "channel_type": vals.get("channel_type"), + } # Create the folio in case of need # (To allow to create reservations direct) folio = self.env["pms.folio"].create(folio_vals) - vals.update({'folio_id': folio.id, - 'reservation_type': vals.get('reservation_type'), - 'channel_type': vals.get('channel_type')}) - if vals.get('service_ids'): - for service in vals['service_ids']: + vals.update( + { + "folio_id": folio.id, + "reservation_type": vals.get("reservation_type"), + "channel_type": vals.get("channel_type"), + } + ) + if vals.get("service_ids"): + for service in vals["service_ids"]: if service[2]: - service[2]['folio_id'] = folio.id - user = self.env['res.users'].browse(self.env.uid) - if user.has_group('pms.group_pms_call'): - vals.update({ - 'to_assign': True, - 'channel_type': 'call' - }) - vals.update({ - 'last_updated_res': fields.Datetime.now(), - }) + service[2]["folio_id"] = folio.id + user = self.env["res.users"].browse(self.env.uid) + if user.has_group("pms.group_pms_call"): + vals.update({"to_assign": True, "channel_type": "call"}) + vals.update( + {"last_updated_res": fields.Datetime.now(),} + ) if self.compute_price_out_vals(vals): days_diff = ( - fields.Date.from_string( - vals['checkout']) - fields.Date.from_string( - vals['checkin']) + fields.Date.from_string(vals["checkout"]) + - fields.Date.from_string(vals["checkin"]) ).days - vals.update(self.prepare_reservation_lines( - vals['checkin'], - days_diff, - vals['pricelist_id'], - vals=vals)) # REVISAR el unlink - if 'checkin' in vals and 'checkout' in vals \ - and 'real_checkin' not in vals and 'real_checkout' not in vals: - vals['real_checkin'] = vals['checkin'] - vals['real_checkout'] = vals['checkout'] + vals.update( + self.prepare_reservation_lines( + vals["checkin"], days_diff, vals["pricelist_id"], vals=vals + ) + ) # REVISAR el unlink + if ( + "checkin" in vals + and "checkout" in vals + and "real_checkin" not in vals + and "real_checkout" not in vals + ): + vals["real_checkin"] = vals["checkin"] + vals["real_checkout"] = vals["checkout"] record = super(PmsReservation, self).create(vals) if record.preconfirm: record.confirm() return record - def write(self, vals): if self.notify_update(vals): - vals.update({ - 'last_updated_res': fields.Datetime.now() - }) + vals.update({"last_updated_res": fields.Datetime.now()}) for record in self: - checkin = vals['checkin'] if 'checkin' in vals \ - else record.checkin - checkout = vals['checkout'] if 'checkout' in vals \ - else record.checkout - if not record.splitted and not vals.get('splitted', False): - if 'checkin' in vals: - vals['real_checkin'] = vals['checkin'] - if 'checkout' in vals: - vals['real_checkout'] = vals['checkout'] + checkin = vals["checkin"] if "checkin" in vals else record.checkin + checkout = vals["checkout"] if "checkout" in vals else record.checkout + if not record.splitted and not vals.get("splitted", False): + if "checkin" in vals: + vals["real_checkin"] = vals["checkin"] + if "checkout" in vals: + vals["real_checkout"] = vals["checkout"] - real_checkin = vals['real_checkin'] if 'real_checkin' in vals \ - else record.real_checkin - real_checkout = vals['real_checkout'] if 'real_checkout' in vals \ + real_checkin = ( + vals["real_checkin"] if "real_checkin" in vals else record.real_checkin + ) + real_checkout = ( + vals["real_checkout"] + if "real_checkout" in vals else record.real_checkout + ) days_diff = ( - fields.Date.from_string(checkout) - - fields.Date.from_string(checkin) + fields.Date.from_string(checkout) - fields.Date.from_string(checkin) ).days if self.compute_board_services(vals): record.service_ids.filtered( - lambda r: r.is_board_service is True).unlink() + lambda r: r.is_board_service is True + ).unlink() board_services = [] - board = self.env['pms.board.service.room.type'].browse( - vals['board_service_room_id']) + board = self.env["pms.board.service.room.type"].browse( + vals["board_service_room_id"] + ) for line in board.board_service_line_ids: res = { - 'product_id': line.product_id.id, - 'is_board_service': True, - 'folio_id': vals.get('folio_id'), - 'reservation_id': self.id, + "product_id": line.product_id.id, + "is_board_service": True, + "folio_id": vals.get("folio_id"), + "reservation_id": self.id, } - res.update( - self.env['pms.service']._prepare_add_missing_fields( - res)) + res.update(self.env["pms.service"]._prepare_add_missing_fields(res)) board_services.append((0, False, res)) # REVIEW: Why I need add manually the old IDs if # board service is (0,0,(-)) ¿?¿?¿ record.update( - {'service_ids': [(6, 0, record.service_ids.ids)] + - board_services}) + {"service_ids": [(6, 0, record.service_ids.ids)] + board_services} + ) if record.compute_price_out_vals(vals): - pricelist_id = vals['pricelist_id'] if 'pricelist_id' in \ - vals else record.pricelist_id.id - record.update(record.prepare_reservation_lines( - checkin, - days_diff, - pricelist_id, - vals=vals)) # REVIEW unlink + pricelist_id = ( + vals["pricelist_id"] + if "pricelist_id" in vals + else record.pricelist_id.id + ) + record.update( + record.prepare_reservation_lines( + checkin, days_diff, pricelist_id, vals=vals + ) + ) # REVIEW unlink if record.compute_qty_service_day(vals): service_days_diff = ( - fields.Date.from_string(real_checkout) - - fields.Date.from_string(real_checkin) + fields.Date.from_string(real_checkout) + - fields.Date.from_string(real_checkin) ).days for service in record.service_ids: if service.product_id.per_day: - service.update(service.prepare_service_ids( - dfrom=real_checkin, - days=service_days_diff, - per_person=service.product_id.per_person, - persons=service.reservation_id.adults, - old_line_days=service.service_line_ids, - consumed_on=service.product_id.consumed_on, - )) - if ('checkin' in vals and record.checkin != vals['checkin']) or\ - ('checkout' in vals and record.checkout != vals['checkout']) or\ - ('state' in vals and record.state != vals['state']): - record.update({'to_send': True}) - user = self.env['res.users'].browse(self.env.uid) - if user.has_group('pms.group_pms_call'): - vals.update({ - 'to_assign': True, - }) + service.update( + service.prepare_service_ids( + dfrom=real_checkin, + days=service_days_diff, + per_person=service.product_id.per_person, + persons=service.reservation_id.adults, + old_line_days=service.service_line_ids, + consumed_on=service.product_id.consumed_on, + ) + ) + if ( + ("checkin" in vals and record.checkin != vals["checkin"]) + or ("checkout" in vals and record.checkout != vals["checkout"]) + or ("state" in vals and record.state != vals["state"]) + ): + record.update({"to_send": True}) + user = self.env["res.users"].browse(self.env.uid) + if user.has_group("pms.group_pms_call"): + vals.update( + {"to_assign": True,} + ) record = super(PmsReservation, self).write(vals) return record @@ -943,27 +982,28 @@ class PmsReservation(models.Model): # Yes?, then, this is share folio ;) for record in self: if record.folio_id: - record.shared_folio = \ - len(record.folio_id.reservation_ids) > 1 \ - or any(record.folio_id.service_ids.filtered( - lambda x: x.reservation_id.id != record.id)) - + record.shared_folio = len(record.folio_id.reservation_ids) > 1 or any( + record.folio_id.service_ids.filtered( + lambda x: x.reservation_id.id != record.id + ) + ) def compute_board_services(self, vals): """ We must compute service_ids when we have a board_service_id without service_ids associated to reservation """ - if 'board_service_room_id' in vals: - if 'service_ids' in vals: - for service in vals['service_ids']: - if 'is_board_service' in service[2] and \ - service[2]['is_board_service'] is True: + if "board_service_room_id" in vals: + if "service_ids" in vals: + for service in vals["service_ids"]: + if ( + "is_board_service" in service[2] + and service[2]["is_board_service"] is True + ): return False return True return False - def compute_qty_service_day(self, vals): """ Compute if it is necesary calc price in write/create @@ -971,11 +1011,13 @@ class PmsReservation(models.Model): self.ensure_one() if not vals: vals = {} - if 'service_ids' in vals: + if "service_ids" in vals: return False - if ('checkin' in vals and self.checkin != vals['checkin']) or \ - ('checkout' in vals and self.checkout != vals['checkout']) or \ - ('adults' in vals and self.checkout != vals['adults']): + if ( + ("checkin" in vals and self.checkin != vals["checkin"]) + or ("checkout" in vals and self.checkout != vals["checkout"]) + or ("adults" in vals and self.checkout != vals["adults"]) + ): return True return False @@ -983,225 +1025,216 @@ class PmsReservation(models.Model): def _prepare_add_missing_fields(self, values): """ Deduce missing required fields from the onchange """ res = {} - onchange_fields = ['room_id', 'tax_ids', - 'currency_id', 'name', 'service_ids'] - if values.get('room_type_id'): - if not values.get('reservation_type'): - values['reservation_type'] = 'normal' + onchange_fields = ["room_id", "tax_ids", "currency_id", "name", "service_ids"] + if values.get("room_type_id"): + if not values.get("reservation_type"): + values["reservation_type"] = "normal" line = self.new(values) if any(f not in values for f in onchange_fields): line.onchange_room_id() line.onchange_room_type_id() - if 'pricelist_id' not in values: + if "pricelist_id" not in values: line.onchange_partner_id() - onchange_fields.append('pricelist_id') + onchange_fields.append("pricelist_id") for field in onchange_fields: - if field == 'service_ids': + if field == "service_ids": if self.compute_board_services(values): line.onchange_board_service() res[field] = line._fields[field].convert_to_write( - line[field], line) + line[field], line + ) if field not in values: - res[field] = line._fields[field].convert_to_write( - line[field], line) + res[field] = line._fields[field].convert_to_write(line[field], line) return res @api.model def _autoassign(self, values): res = {} - checkin = values.get('checkin') - checkout = values.get('checkout') - room_type_id = values.get('room_type_id') + checkin = values.get("checkin") + checkout = values.get("checkout") + room_type_id = values.get("room_type_id") if checkin and checkout and room_type_id: - if 'overbooking' not in values: - room_chosen = self.env['pms.room.type'].\ - check_availability_room_type( - checkin, - (fields.Date.from_string(checkout) - - timedelta(days=1)).strftime( - DEFAULT_SERVER_DATE_FORMAT - ), - room_type_id)[0] + if "overbooking" not in values: + room_chosen = self.env["pms.room.type"].check_availability_room_type( + checkin, + (fields.Date.from_string(checkout) - timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + room_type_id, + )[0] # Check room_chosen exist else: - room_chosen = self.env['pms.room.type'].browse( - room_type_id).room_ids[0] - res.update({ - 'room_id': room_chosen.id - }) + room_chosen = self.env["pms.room.type"].browse(room_type_id).room_ids[0] + res.update({"room_id": room_chosen.id}) return res @api.model def autocheckout(self): - reservations = self.env['pms.reservation'].search([ - ('state', 'not in', ('done', 'cancelled')), - ('checkout', '<', fields.Date.today()) - ]) + reservations = self.env["pms.reservation"].search( + [ + ("state", "not in", ("done", "cancelled")), + ("checkout", "<", fields.Date.today()), + ] + ) for res in reservations: res.action_reservation_checkout() - res_without_checkin = reservations.filtered( - lambda r: r.state != 'booking') + res_without_checkin = reservations.filtered(lambda r: r.state != "booking") for res in res_without_checkin: msg = _("No checkin was made for this reservation") - res.message_post(subject=_('No Checkins!'), - subtype='mt_comment', body=msg) + res.message_post(subject=_("No Checkins!"), subtype="mt_comment", body=msg) return True - def notify_update(self, vals): - if 'checkin' in vals or \ - 'checkout' in vals or \ - 'discount' in vals or \ - 'state' in vals or \ - 'room_type_id' in vals or \ - 'to_assign' in vals: + if ( + "checkin" in vals + or "checkout" in vals + or "discount" in vals + or "state" in vals + or "room_type_id" in vals + or "to_assign" in vals + ): return True return False - def overbooking_button(self): self.ensure_one() self.overbooking = not self.overbooking - def generate_copy_values(self, checkin=False, checkout=False): self.ensure_one() return { - 'name': self.name, - 'adults': self.adults, - 'children': self.children, - 'checkin': checkin or self.checkin, - 'checkout': checkout or self.checkout, - 'folio_id': self.folio_id.id, - 'parent_reservation': self.parent_reservation.id, - 'state': self.state, - 'overbooking': self.overbooking, - 'reselling': self.reselling, - 'price_total': self.price_total, - 'price_tax': self.price_tax, - 'price_subtotal': self.price_subtotal, - 'splitted': self.splitted, - 'room_type_id': self.room_type_id.id, - 'room_id': self.room_id.id, - 'real_checkin': self.real_checkin, - 'real_checkout': self.real_checkout, + "name": self.name, + "adults": self.adults, + "children": self.children, + "checkin": checkin or self.checkin, + "checkout": checkout or self.checkout, + "folio_id": self.folio_id.id, + "parent_reservation": self.parent_reservation.id, + "state": self.state, + "overbooking": self.overbooking, + "reselling": self.reselling, + "price_total": self.price_total, + "price_tax": self.price_tax, + "price_subtotal": self.price_subtotal, + "splitted": self.splitted, + "room_type_id": self.room_type_id.id, + "room_id": self.room_id.id, + "real_checkin": self.real_checkin, + "real_checkout": self.real_checkout, } """ STATE WORKFLOW ----------------------------------------------------- """ - def confirm(self): - ''' + """ @param self: object pointer - ''' - _logger.info('confirm') - pms_reserv_obj = self.env['pms.reservation'] - user = self.env['res.users'].browse(self.env.uid) + """ + _logger.info("confirm") + pms_reserv_obj = self.env["pms.reservation"] + user = self.env["res.users"].browse(self.env.uid) for record in self: vals = {} - if user.has_group('pms.group_pms_call'): - vals.update({'channel_type': 'call'}) + if user.has_group("pms.group_pms_call"): + vals.update({"channel_type": "call"}) if record.checkin_partner_ids: - vals.update({'state': 'booking'}) + vals.update({"state": "booking"}) else: - vals.update({'state': 'confirm'}) + vals.update({"state": "confirm"}) record.write(vals) - record.reservation_line_ids.update({ - 'cancel_discount': 0 - }) - if record.folio_id.state != 'confirm': + record.reservation_line_ids.update({"cancel_discount": 0}) + if record.folio_id.state != "confirm": record.folio_id.action_confirm() if record.splitted: master_reservation = record.parent_reservation or record - splitted_reservs = pms_reserv_obj.search([ - ('splitted', '=', True), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ('folio_id', '=', record.folio_id.id), - ('id', '!=', record.id), - ('state', 'not in', ('confirm', 'booking')) - ]) + splitted_reservs = pms_reserv_obj.search( + [ + ("splitted", "=", True), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ("folio_id", "=", record.folio_id.id), + ("id", "!=", record.id), + ("state", "not in", ("confirm", "booking")), + ] + ) if master_reservation.checkin_partner_ids: - record.update({'state': 'booking'}) + record.update({"state": "booking"}) splitted_reservs.confirm() return True - def button_done(self): - ''' + """ @param self: object pointer - ''' + """ for record in self: record.action_reservation_checkout() return True - def action_cancel(self): for record in self: - cancel_reason = 'intime' if self._context.get( - "no_penalty", False) \ + cancel_reason = ( + "intime" + if self._context.get("no_penalty", False) else record.compute_cancelation_reason() + ) if self._context.get("no_penalty", False): _logger.info("Modified Reservation - No Penalty") - record.write({ - 'state': 'cancelled', - 'cancelled_reason': cancel_reason - }) + record.write({"state": "cancelled", "cancelled_reason": cancel_reason}) record._compute_cancelled_discount() if record.splitted: master_reservation = record.parent_reservation or record - splitted_reservs = self.env['pms.reservation'].search([ - ('splitted', '=', True), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ('folio_id', '=', record.folio_id.id), - ('id', '!=', record.id), - ('state', '!=', 'cancelled') - ]) + splitted_reservs = self.env["pms.reservation"].search( + [ + ("splitted", "=", True), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ("folio_id", "=", record.folio_id.id), + ("id", "!=", record.id), + ("state", "!=", "cancelled"), + ] + ) splitted_reservs.action_cancel() record.folio_id.compute_amount() - def compute_cancelation_reason(self): self.ensure_one() pricelist = self.pricelist_id if pricelist and pricelist.cancelation_rule_id: tz_property = self.env.user.pms_property_id.tz - today = fields.Date.context_today(self.with_context( - tz=tz_property)) - days_diff = (fields.Date.from_string(self.real_checkin) - - fields.Date.from_string(today)).days + today = fields.Date.context_today(self.with_context(tz=tz_property)) + days_diff = ( + fields.Date.from_string(self.real_checkin) + - fields.Date.from_string(today) + ).days if days_diff < 0: - return 'noshow' + return "noshow" elif days_diff < pricelist.cancelation_rule_id.days_intime: - return 'late' + return "late" else: - return 'intime' + return "intime" return False - def draft(self): for record in self: - record.state = 'draft' - record.reservation_line_ids.update({ - 'cancel_discount': 0 - }) + record.state = "draft" + record.reservation_line_ids.update({"cancel_discount": 0}) if record.splitted: master_reservation = record.parent_reservation or record - splitted_reservs = self.env['pms.reservation'].search([ - ('splitted', '=', True), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ('folio_id', '=', record.folio_id.id), - ('id', '!=', record.id), - ('state', '!=', 'draft') - ]) + splitted_reservs = self.env["pms.reservation"].search( + [ + ("splitted", "=", True), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ("folio_id", "=", record.folio_id.id), + ("id", "!=", record.id), + ("state", "!=", "draft"), + ] + ) splitted_reservs.draft() """ @@ -1214,87 +1247,87 @@ class PmsReservation(models.Model): """ if not vals: vals = {} - if ('reservation_line_ids' not in vals and - ('checkout' in vals or 'checkin' in vals or - 'room_type_id' in vals or 'pricelist_id' in vals)): + if "reservation_line_ids" not in vals and ( + "checkout" in vals + or "checkin" in vals + or "room_type_id" in vals + or "pricelist_id" in vals + ): return True return False - def _compute_cancelled_discount(self): self.ensure_one() pricelist = self.pricelist_id - if self.state == 'cancelled': + if self.state == "cancelled": # REVIEW: Set 0 qty on cancel room services # (view constrain service_line_days) for service in self.service_ids: - service.service_line_ids.write({'day_qty': 0}) + service.service_line_ids.write({"day_qty": 0}) service._compute_days_qty() - if self.cancelled_reason and pricelist and \ - pricelist.cancelation_rule_id: + if self.cancelled_reason and pricelist and pricelist.cancelation_rule_id: date_start_dt = fields.Date.from_string( - self.real_checkin or self.checkin) + self.real_checkin or self.checkin + ) date_end_dt = fields.Date.from_string( - self.real_checkout or self.checkout) + self.real_checkout or self.checkout + ) days = abs((date_end_dt - date_start_dt).days) rule = pricelist.cancelation_rule_id - if self.cancelled_reason == 'late': + if self.cancelled_reason == "late": discount = 100 - rule.penalty_late - if rule.apply_on_late == 'first': + if rule.apply_on_late == "first": days = 1 - elif rule.apply_on_late == 'days': + elif rule.apply_on_late == "days": days = rule.days_late - elif self.cancelled_reason == 'noshow': + elif self.cancelled_reason == "noshow": discount = 100 - rule.penalty_noshow - if rule.apply_on_noshow == 'first': + if rule.apply_on_noshow == "first": days = 1 - elif rule.apply_on_noshow == 'days': + elif rule.apply_on_noshow == "days": days = rule.days_late - 1 - elif self.cancelled_reason == 'intime': + elif self.cancelled_reason == "intime": discount = 100 checkin = self.real_checkin or self.checkin dates = [] for i in range(0, days): - dates.append((fields.Date.from_string(checkin) + - timedelta(days=i)).strftime( - DEFAULT_SERVER_DATE_FORMAT)) + dates.append( + (fields.Date.from_string(checkin) + timedelta(days=i)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ) + ) + self.reservation_line_ids.filtered(lambda r: r.date in dates).update( + {"cancel_discount": discount} + ) self.reservation_line_ids.filtered( - lambda r: r.date in dates).update({ - 'cancel_discount': discount - }) - self.reservation_line_ids.filtered( - lambda r: r.date not in dates).update({ - 'cancel_discount': 100 - }) + lambda r: r.date not in dates + ).update({"cancel_discount": 100}) else: - self.reservation_line_ids.update({ - 'cancel_discount': 0 - }) + self.reservation_line_ids.update({"cancel_discount": 0}) else: - self.reservation_line_ids.update({ - 'cancel_discount': 0 - }) + self.reservation_line_ids.update({"cancel_discount": 0}) @api.model - def prepare_reservation_lines(self, dfrom, days, - pricelist_id, vals=False, - update_old_prices=False): + def prepare_reservation_lines( + self, dfrom, days, pricelist_id, vals=False, update_old_prices=False + ): discount = 0 cmds = [(5, 0, 0)] if not vals: vals = {} - room_type_id = vals.get('room_type_id') or self.room_type_id.id - product = self.env['pms.room.type'].browse(room_type_id).product_id - partner = self.env['res.partner'].browse( - vals.get('partner_id') or self.partner_id.id) - if 'discount' in vals and vals.get('discount') > 0: - discount = vals.get('discount') + room_type_id = vals.get("room_type_id") or self.room_type_id.id + product = self.env["pms.room.type"].browse(room_type_id).product_id + partner = self.env["res.partner"].browse( + vals.get("partner_id") or self.partner_id.id + ) + if "discount" in vals and vals.get("discount") > 0: + discount = vals.get("discount") for i in range(0, days): - idate = (fields.Date.from_string(dfrom) + - timedelta(days=i)).strftime(DEFAULT_SERVER_DATE_FORMAT) - old_line = self.reservation_line_ids.filtered( - lambda r: r.date == idate) + idate = (fields.Date.from_string(dfrom) + timedelta(days=i)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ) + old_line = self.reservation_line_ids.filtered(lambda r: r.date == idate) if update_old_prices or not old_line: product = product.with_context( lang=partner.lang, @@ -1302,25 +1335,27 @@ class PmsReservation(models.Model): quantity=1, date=idate, pricelist=pricelist_id, - uom=product.uom_id.id) + uom=product.uom_id.id, + ) # REVIEW this forces to have configured the taxes # included in the price line_price = product.price if old_line and old_line.id: - cmds.append((1, old_line.id, { - 'price': line_price, - 'discount': discount - })) + cmds.append( + (1, old_line.id, {"price": line_price, "discount": discount}) + ) else: - cmds.append((0, False, { - 'date': idate, - 'price': line_price, - 'discount': discount - })) + cmds.append( + ( + 0, + False, + {"date": idate, "price": line_price, "discount": discount}, + ) + ) else: line_price = old_line.price cmds.append((4, old_line.id)) - return {'reservation_line_ids': cmds} + return {"reservation_line_ids": cmds} """ AVAILABILTY PROCESS ------------------------------------------------ @@ -1336,15 +1371,17 @@ class PmsReservation(models.Model): """ domain = self._get_domain_reservations_occupation(dfrom, dto) # _logger.info(domain) - return self.env['pms.reservation'].search(domain) + return self.env["pms.reservation"].search(domain) @api.model def _get_domain_reservations_occupation(self, dfrom, dto): - domain = [('reservation_line_ids.date', '>=', dfrom), - ('reservation_line_ids.date', '<=', dto), - ('state', '!=', 'cancelled'), - ('overbooking', '=', False), - ('reselling', '=', False)] + domain = [ + ("reservation_line_ids.date", ">=", dfrom), + ("reservation_line_ids.date", "<=", dto), + ("state", "!=", "cancelled"), + ("overbooking", "=", False), + ("reselling", "=", False), + ] return domain # INFO: This function is not in use and should include `dto` in the search @@ -1363,30 +1400,29 @@ class PmsReservation(models.Model): pms.reservation(36,)], } """ - domain = [('date', '>=', dfrom), - ('date', '<', dto)] - lines = self.env['pms.reservation.line'].search(domain) + domain = [("date", ">=", dfrom), ("date", "<", dto)] + lines = self.env["pms.reservation.line"].search(domain) reservations_dates = {} for record in lines: # kumari.net/index.php/programming/programmingcat/22-python-making-a-dictionary-of-lists-a-hash-of-arrays # reservations_dates.setdefault(record.date,[]).append(record.reservation_id.room_type_id) reservations_dates.setdefault(record.date, []).append( - [record.reservation_id, record.reservation_id.room_type_id]) + [record.reservation_id, record.reservation_id.room_type_id] + ) return reservations_dates """ CHECKIN/OUT PROCESS ------------------------------------------------ """ - def _compute_checkin_partner_count(self): - _logger.info('_compute_checkin_partner_count') + _logger.info("_compute_checkin_partner_count") for record in self: - if record.reservation_type != 'out': + if record.reservation_type != "out": record.checkin_partner_count = len(record.checkin_partner_ids) - record.checkin_partner_pending_count = \ - (record.adults + record.children) - \ - len(record.checkin_partner_ids) + record.checkin_partner_pending_count = ( + record.adults + record.children + ) - len(record.checkin_partner_ids) else: record.checkin_partner_count = 0 record.checkin_partner_pending_count = 0 @@ -1395,59 +1431,61 @@ class PmsReservation(models.Model): def _search_checkin_partner_pending(self, operator, value): self.ensure_one() - recs = self.search([]).filtered( - lambda x: x.checkin_partner_pending_count > 0) - return [('id', 'in', [x.id for x in recs])] if recs else [] - + recs = self.search([]).filtered(lambda x: x.checkin_partner_pending_count > 0) + return [("id", "in", [x.id for x in recs])] if recs else [] def action_reservation_checkout(self): for record in self: - record.state = 'done' + record.state = "done" if record.checkin_partner_ids: record.checkin_partner_ids.filtered( - lambda check: check.state == 'booking').action_done() + lambda check: check.state == "booking" + ).action_done() if record.splitted: master_reservation = record.parent_reservation or record - splitted_reservs = self.env['pms.reservation'].search([ - ('splitted', '=', True), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id), - ('folio_id', '=', record.folio_id.id), - ('id', '!=', record.id), - ('state', 'not in', ('cancelled', 'done')) - ]) + splitted_reservs = self.env["pms.reservation"].search( + [ + ("splitted", "=", True), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ("folio_id", "=", record.folio_id.id), + ("id", "!=", record.id), + ("state", "not in", ("cancelled", "done")), + ] + ) if splitted_reservs: - splitted_reservs.update({'state': 'done'}) + splitted_reservs.update({"state": "done"}) return True - def action_checks(self): self.ensure_one() - action = self.env.ref( - 'pms.open_pms_reservation_form_tree_all').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_reservation_checkin_view_form').id, - 'form')] - action['res_id'] = self.id - action['target'] = 'new' + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["views"] = [ + (self.env.ref("pms.pms_reservation_checkin_view_form").id, "form") + ] + action["res_id"] = self.id + action["target"] = "new" return action """ RESERVATION SPLITTED ----------------------------------------------- """ - def split(self, nights): for record in self: date_start_dt = fields.Date.from_string(record.checkin) date_end_dt = fields.Date.from_string(record.checkout) date_diff = abs((date_end_dt - date_start_dt).days) - new_start_date_dt = date_start_dt + \ - timedelta(days=date_diff - nights) + new_start_date_dt = date_start_dt + timedelta(days=date_diff - nights) if nights >= date_diff or nights < 1: - raise ValidationError(_("Invalid Nights! Max is \ - '%d'") % (date_diff - 1)) + raise ValidationError( + _( + "Invalid Nights! Max is \ + '%d'" + ) + % (date_diff - 1) + ) vals = record.generate_copy_values( new_start_date_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT), @@ -1458,39 +1496,56 @@ class PmsReservation(models.Model): for rline in record.reservation_line_ids: rline_dt = fields.Date.from_string(rline.date) if rline_dt >= new_start_date_dt: - reservation_lines[1].append((0, False, { - 'date': rline.date, - 'price': rline.price, - 'cancel_discount': rline.cancel_discount, - 'discount': rline.discount, - 'move_line_ids': rline.move_line_ids, - 'state': rline.state, - })) + reservation_lines[1].append( + ( + 0, + False, + { + "date": rline.date, + "price": rline.price, + "cancel_discount": rline.cancel_discount, + "discount": rline.discount, + "move_line_ids": rline.move_line_ids, + "state": rline.state, + }, + ) + ) reservation_lines[0].append((2, rline.id, False)) parent_res = record.parent_reservation or record - vals.update({ - 'splitted': True, - 'parent_reservation': parent_res.id, - 'room_type_id': parent_res.room_type_id.id, - 'state': parent_res.state, - 'reservation_line_ids': reservation_lines[1], - 'preconfirm': False, - }) - reservation_copy = self.env['pms.reservation'].with_context({ - 'ignore_avail_restrictions': True}).create(vals) + vals.update( + { + "splitted": True, + "parent_reservation": parent_res.id, + "room_type_id": parent_res.room_type_id.id, + "state": parent_res.state, + "reservation_line_ids": reservation_lines[1], + "preconfirm": False, + } + ) + reservation_copy = ( + self.env["pms.reservation"] + .with_context({"ignore_avail_restrictions": True}) + .create(vals) + ) if not reservation_copy: - raise ValidationError(_("Unexpected error copying record. \ - Can't split reservation!")) - record.write({ - 'checkout': new_start_date_dt.strftime( - DEFAULT_SERVER_DATETIME_FORMAT), - 'splitted': True, - 'reservation_line_ids': reservation_lines[0], - }) + raise ValidationError( + _( + "Unexpected error copying record. \ + Can't split reservation!" + ) + ) + record.write( + { + "checkout": new_start_date_dt.strftime( + DEFAULT_SERVER_DATETIME_FORMAT + ), + "splitted": True, + "reservation_line_ids": reservation_lines[0], + } + ) return True - def unify(self): self.ensure_one() if not self.splitted: @@ -1498,18 +1553,20 @@ class PmsReservation(models.Model): master_reservation = self.parent_reservation or self - splitted_reservs = self.env['pms.reservation'].search([ - ('splitted', '=', True), - ('folio_id', '=', self.folio_id.id), - '|', - ('parent_reservation', '=', master_reservation.id), - ('id', '=', master_reservation.id) - ]) + splitted_reservs = self.env["pms.reservation"].search( + [ + ("splitted", "=", True), + ("folio_id", "=", self.folio_id.id), + "|", + ("parent_reservation", "=", master_reservation.id), + ("id", "=", master_reservation.id), + ] + ) self.unify_books(splitted_reservs) - self_is_master = (master_reservation == self) + self_is_master = master_reservation == self if not self_is_master: - return {'type': 'ir.actions.act_window_close'} + return {"type": "ir.actions.act_window_close"} @api.model def unify_ids(self, reserv_ids): @@ -1518,14 +1575,20 @@ class PmsReservation(models.Model): @api.model def unify_books(self, splitted_reservs): - parent_reservation = splitted_reservs[0].parent_reservation or \ - splitted_reservs[0] - room_type_ids = splitted_reservs.mapped('room_type_id.id') - if len(room_type_ids) > 1 or \ - (len(room_type_ids) == 1 and - parent_reservation.room_type_id.id != room_type_ids[0]): - raise ValidationError(_("This reservation can't be unified: They \ - all need to be in the same room")) + parent_reservation = ( + splitted_reservs[0].parent_reservation or splitted_reservs[0] + ) + room_type_ids = splitted_reservs.mapped("room_type_id.id") + if len(room_type_ids) > 1 or ( + len(room_type_ids) == 1 + and parent_reservation.room_type_id.id != room_type_ids[0] + ): + raise ValidationError( + _( + "This reservation can't be unified: They \ + all need to be in the same room" + ) + ) # Search checkout last_checkout = splitted_reservs[0].checkout @@ -1539,18 +1602,24 @@ class PmsReservation(models.Model): master_reservation = reserv # Agrupate reservation lines - reservation_line_ids = splitted_reservs.mapped('reservation_line_ids') + reservation_line_ids = splitted_reservs.mapped("reservation_line_ids") reservation_line_ids.sorted(key=lambda r: r.date) rlines = [(5, False, False)] for rline in reservation_line_ids: - rlines.append((0, False, { - 'date': rline.date, - 'price': rline.price, - 'cancel_discount': rline.cancel_discount, - 'discount': rline.discount, - 'move_line_ids': rline.move_line_ids, - 'state': rline.state, - })) + rlines.append( + ( + 0, + False, + { + "date": rline.date, + "price": rline.price, + "cancel_discount": rline.cancel_discount, + "discount": rline.discount, + "move_line_ids": rline.move_line_ids, + "state": rline.state, + }, + ) + ) # Unify osplitted_reservs = splitted_reservs - master_reservation @@ -1562,39 +1631,35 @@ class PmsReservation(models.Model): _logger.info(master_reservation.real_checkout) _logger.info(last_checkout) - master_reservation.write({ - 'checkout': last_checkout, - 'splitted': master_reservation.real_checkin != first_checkin or - master_reservation.real_checkout != last_checkout, - 'reservation_line_ids': rlines, - }) + master_reservation.write( + { + "checkout": last_checkout, + "splitted": master_reservation.real_checkin != first_checkin + or master_reservation.real_checkout != last_checkout, + "reservation_line_ids": rlines, + } + ) return True - def open_master(self): self.ensure_one() if not self.parent_reservation: raise ValidationError(_("This is the parent reservation")) - action = self.env.ref( - 'pms.open_pms_reservation_form_tree_all').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_reservation_view_form').id, 'form')] - action['res_id'] = self.parent_reservation.id + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["views"] = [(self.env.ref("pms.pms_reservation_view_form").id, "form")] + action["res_id"] = self.parent_reservation.id return action """ MAILING PROCESS """ - def send_reservation_mail(self): return self.folio_id.send_reservation_mail() - def send_exit_mail(self): return self.folio_id.send_exit_mail() - def send_cancel_mail(self): return self.folio_id.send_cancel_mail() @@ -1602,18 +1667,17 @@ class PmsReservation(models.Model): INVOICING PROCESS """ - def _compute_tax_ids(self): for record in self: # If company_id is set, always filter taxes by the company - folio = record.folio_id or self.env.context.get('default_folio_id') - product = self.env['product.product'].browse( - record.room_type_id.product_id.id) + folio = record.folio_id or self.env.context.get("default_folio_id") + product = self.env["product.product"].browse( + record.room_type_id.product_id.id + ) record.tax_ids = product.taxes_id.filtered( - lambda r: not record.company_id or - r.company_id == folio.company_id) - + lambda r: not record.company_id or r.company_id == folio.company_id + ) def set_call_center_user(self): - user = self.env['res.users'].browse(self.env.uid) - return user.has_group('pms.group_pms_call') + user = self.env["res.users"].browse(self.env.uid) + return user.has_group("pms.group_pms_call") diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index 6b0e1f0d5..75a8e7152 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -1,7 +1,7 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -16,57 +16,55 @@ class PmsReservationLine(models.Model): result = [] for res in self: date = fields.Date.from_string(res.date) - name = u'%s/%s' % (date.day, date.month) + name = u"{}/{}".format(date.day, date.month) result.append((res.id, name)) return result # Fields declaration reservation_id = fields.Many2one( - 'pms.reservation', - string='Reservation', - ondelete='cascade', + "pms.reservation", + string="Reservation", + ondelete="cascade", required=True, - copy=False) + copy=False, + ) move_line_ids = fields.Many2many( - 'account.move.line', - 'reservation_line_move_rel', - 'reservation_line_id', - 'move_line_id', - string='Invoice Lines', + "account.move.line", + "reservation_line_move_rel", + "reservation_line_id", + "move_line_id", + string="Invoice Lines", readonly=True, - copy=False) + copy=False, + ) pms_property_id = fields.Many2one( - 'pms.property', + "pms.property", store=True, readonly=True, - related='reservation_id.pms_property_id') - date = fields.Date('Date') - state = fields.Selection(related='reservation_id.state') - price = fields.Float( - string='Price', - digits=('Product Price')) + related="reservation_id.pms_property_id", + ) + date = fields.Date("Date") + state = fields.Selection(related="reservation_id.state") + price = fields.Float(string="Price", digits=("Product Price")) cancel_discount = fields.Float( - string='Cancel Discount (%)', - digits=('Discount'), default=0.0) - discount = fields.Float( - string='Discount (%)', - digits=('Discount'), default=0.0) + string="Cancel Discount (%)", digits=("Discount"), default=0.0 + ) + discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0) # Constraints and onchanges - @api.constrains('date') + @api.constrains("date") def constrains_duplicated_date(self): for record in self: duplicated = record.reservation_id.reservation_line_ids.filtered( - lambda r: r.date == record.date and - r.id != record.id + lambda r: r.date == record.date and r.id != record.id ) if duplicated: - raise ValidationError(_('Duplicated reservation line date')) + raise ValidationError(_("Duplicated reservation line date")) - @api.constrains('state') + @api.constrains("state") def constrains_service_cancel(self): for record in self: - if record.state == 'cancelled': + if record.state == "cancelled": room_services = record.reservation_id.service_ids for service in room_services: cancel_lines = service.service_line_ids.filtered( diff --git a/pms/models/pms_room.py b/pms/models/pms_room.py index 603274a31..b908e97c3 100644 --- a/pms/models/pms_room.py +++ b/pms/models/pms_room.py @@ -2,7 +2,7 @@ # Copyright 2017 Dario Lodeiros # Copyright 2018 Pablo Quesada # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -11,82 +11,95 @@ class PmsRoom(models.Model): and also for speeches (conference rooms), parking, relax with cafe con leche, spa... """ - _name = 'pms.room' - _description = 'Property Room' + + _name = "pms.room" + _description = "Property Room" _order = "sequence, room_type_id, name" # Fields declaration - name = fields.Char('Room Name', required=True) + name = fields.Char("Room Name", required=True) pms_property_id = fields.Many2one( - 'pms.property', + "pms.property", store=True, readonly=True, - related='room_type_id.pms_property_id') + related="room_type_id.pms_property_id", + ) room_type_id = fields.Many2one( - 'pms.room.type', - 'Property Room Type', - required=True, - ondelete='restrict') - shared_room_id = fields.Many2one( - 'pms.shared.room', - 'Shared Room', - default=False) + "pms.room.type", "Property Room Type", required=True, ondelete="restrict" + ) + shared_room_id = fields.Many2one("pms.shared.room", "Shared Room", default=False) floor_id = fields.Many2one( - 'pms.floor', - 'Ubication', - help='At which floor the room is located.') - capacity = fields.Integer('Capacity') - to_be_cleaned = fields.Boolean('To be Cleaned', default=False) - extra_beds_allowed = fields.Integer('Extra beds allowed', - default='0', - required=True) + "pms.floor", "Ubication", help="At which floor the room is located." + ) + capacity = fields.Integer("Capacity") + to_be_cleaned = fields.Boolean("To be Cleaned", default=False) + extra_beds_allowed = fields.Integer( + "Extra beds allowed", default="0", required=True + ) description_sale = fields.Text( - 'Sale Description', translate=True, + "Sale Description", + translate=True, help="A description of the Product that you want to communicate to " - " your customers. This description will be copied to every Sales " - " Order, Delivery Order and Customer Invoice/Credit Note") - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', default=0) + " your customers. This description will be copied to every Sales " + " Order, Delivery Order and Customer Invoice/Credit Note", + ) + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", default=0) # Constraints and onchanges - @api.constrains('capacity') + @api.constrains("capacity") def _check_capacity(self): for record in self: if record.capacity < 1: - raise ValidationError(_("The capacity of the \ - room must be greater than 0.")) + raise ValidationError( + _( + "The capacity of the \ + room must be greater than 0." + ) + ) # CRUD methods @api.model def create(self, vals): - if vals.get('pms_property_id', self.env.user.pms_property_id.id) != \ - self.env['pms.room.type'].browse( - vals['room_type_id']).pms_property_id.id: - raise ValidationError( - _("A room cannot be created in a room type \ - of another property.")) + if ( + vals.get("pms_property_id", self.env.user.pms_property_id.id) + != self.env["pms.room.type"].browse(vals["room_type_id"]).pms_property_id.id + ): + raise ValidationError( + _( + "A room cannot be created in a room type \ + of another property." + ) + ) return super().create(vals) - def write(self, vals): for record in self: - if vals.get('pms_property_id', record.pms_property_id.id) != record.pms_property_id.id: + if ( + vals.get("pms_property_id", record.pms_property_id.id) + != record.pms_property_id.id + ): raise ValidationError( - _("A room cannot be changed to another property.") + " " + - _("%s does not belong to %s.") - % (record, record.pms_property_id)) - room_type_ids = self.env['pms.room.type'].search([ - ('pms_property_id', '=', record.pms_property_id.id) - ]).ids - if vals.get('room_type_id', record.room_type_id.id) \ - not in room_type_ids: + _("A room cannot be changed to another property.") + + " " + + _("%s does not belong to %s.") % (record, record.pms_property_id) + ) + room_type_ids = ( + self.env["pms.room.type"] + .search([("pms_property_id", "=", record.pms_property_id.id)]) + .ids + ) + if vals.get("room_type_id", record.room_type_id.id) not in room_type_ids: raise ValidationError( - _("A room cannot be changed to a room type of \ - another property or unlinked from a room type.")) + _( + "A room cannot be changed to a room type of \ + another property or unlinked from a room type." + ) + ) return super().write(vals) # Business methods - + def get_capacity(self, extra_bed=0): if not self.shared_room_id: return self.capacity + extra_bed diff --git a/pms/models/pms_room_closure_reason.py b/pms/models/pms_room_closure_reason.py index d579b551e..48ad08e4a 100644 --- a/pms/models/pms_room_closure_reason.py +++ b/pms/models/pms_room_closure_reason.py @@ -1,6 +1,6 @@ # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class RoomClosureReason(models.Model): @@ -8,10 +8,8 @@ class RoomClosureReason(models.Model): _description = "Cause of out of service" # Fields declaration - name = fields.Char('Name', translate=True, required=True) + name = fields.Char("Name", translate=True, required=True) pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - description = fields.Text('Description', translate=True) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + description = fields.Text("Description", translate=True) diff --git a/pms/models/pms_room_type.py b/pms/models/pms_room_type.py index 4ca354ab7..de5d263f8 100644 --- a/pms/models/pms_room_type.py +++ b/pms/models/pms_room_type.py @@ -1,7 +1,7 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -10,9 +10,10 @@ class PmsRoomType(models.Model): With the term 'room type' is meant a sales type of residential accommodation: for example, a Double Room, a Economic Room, an Apartment, a Tent, a Caravan... """ + _name = "pms.room.type" _description = "Room Type" - _inherits = {'product.product': 'product_id'} + _inherits = {"product.product": "product_id"} _order = "sequence, code_type, name" # Default methods @@ -22,50 +23,50 @@ class PmsRoomType(models.Model): # Fields declaration product_id = fields.Many2one( - 'product.product', - 'Product Room Type', + "product.product", + "Product Room Type", required=True, delegate=True, - ondelete='cascade') + ondelete="cascade", + ) pms_property_id = fields.Many2one( - 'pms.property', - 'Property', + "pms.property", + "Property", required=True, - ondelete='restrict', - default=_get_default_pms_property,) - room_ids = fields.One2many( - 'pms.room', - 'room_type_id', - 'Rooms') - class_id = fields.Many2one( - 'pms.room.type.class', - 'Property Type Class') + ondelete="restrict", + default=_get_default_pms_property, + ) + room_ids = fields.One2many("pms.room", "room_type_id", "Rooms") + class_id = fields.Many2one("pms.room.type.class", "Property Type Class") board_service_room_type_ids = fields.One2many( - 'pms.board.service.room.type', - 'pms_room_type_id', - string='Board Services') + "pms.board.service.room.type", "pms_room_type_id", string="Board Services" + ) room_amenity_ids = fields.Many2many( - 'pms.amenity', - 'pms_room_type_aminity_rel', - 'room_type_ids', - 'amenity_ids', - string='Room Type Amenities', - help='List of Amenities.') - code_type = fields.Char('Code', required=True, ) - shared_room = fields.Boolean('Shared Room', default=False, - help="This room type is reservation by beds") - total_rooms_count = fields.Integer( - compute='_compute_total_rooms', store=True) - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', default=0) + "pms.amenity", + "pms_room_type_aminity_rel", + "room_type_ids", + "amenity_ids", + string="Room Type Amenities", + help="List of Amenities.", + ) + code_type = fields.Char("Code", required=True,) + shared_room = fields.Boolean( + "Shared Room", default=False, help="This room type is reservation by beds" + ) + total_rooms_count = fields.Integer(compute="_compute_total_rooms", store=True) + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", default=0) _sql_constraints = [ - ('code_type_pms_unique', 'unique(code_type, pms_property_id)', - 'Room Type Code must be unique by Property!'), + ( + "code_type_pms_unique", + "unique(code_type, pms_property_id)", + "Room Type Code must be unique by Property!", + ), ] # Constraints and onchanges - @api.depends('room_ids', 'room_ids.active') + @api.depends("room_ids", "room_ids.active") def _compute_total_rooms(self): for record in self: record.total_rooms_count = len(record.room_ids) @@ -74,43 +75,38 @@ class PmsRoomType(models.Model): @api.model def create(self, vals): """ Add room types as not purchase services. """ - vals.update({ - 'purchase_ok': False, - 'type': 'service', - }) + vals.update( + {"purchase_ok": False, "type": "service",} + ) return super().create(vals) - def unlink(self): for record in self: record.product_id.unlink() return super().unlink() # Business methods - + def get_capacity(self): self.ensure_one() - capacities = self.room_ids.mapped('capacity') + capacities = self.room_ids.mapped("capacity") return min(capacities) if any(capacities) else 0 @api.model - def check_availability_room_type(self, dfrom, dto, - room_type_id=False, notthis=[]): + def check_availability_room_type(self, dfrom, dto, room_type_id=False, notthis=[]): """ Check the max availability for an specific type of room in a range of dates """ - reservations = self.env['pms.reservation'].get_reservations(dfrom, - dto) - reservations_rooms = reservations.mapped('room_id.id') - free_rooms = self.env['pms.room'].search([ - ('id', 'not in', reservations_rooms), - ('id', 'not in', notthis) - ]) + reservations = self.env["pms.reservation"].get_reservations(dfrom, dto) + reservations_rooms = reservations.mapped("room_id.id") + free_rooms = self.env["pms.room"].search( + [("id", "not in", reservations_rooms), ("id", "not in", notthis)] + ) if room_type_id: - rooms_linked = self.env['pms.room.type'].search([ - ('id', '=', room_type_id) - ]).room_ids + rooms_linked = ( + self.env["pms.room.type"].search([("id", "=", room_type_id)]).room_ids + ) free_rooms = free_rooms & rooms_linked return free_rooms.sorted(key=lambda r: r.sequence) @@ -126,38 +122,43 @@ class PmsRoomType(models.Model): Return Dict Code Room Types: subdict with day, discount, price """ vals = {} - room_type_ids = kwargs.get('room_type_ids', False) - room_types = self.env['pms.room.type'].browse(room_type_ids) if \ - room_type_ids else self.env['pms.room.type'].search([]) - date_from = kwargs.get('date_from', False) - days = kwargs.get('days', False) - discount = kwargs.get('discount', False) + room_type_ids = kwargs.get("room_type_ids", False) + room_types = ( + self.env["pms.room.type"].browse(room_type_ids) + if room_type_ids + else self.env["pms.room.type"].search([]) + ) + date_from = kwargs.get("date_from", False) + days = kwargs.get("days", False) + discount = kwargs.get("discount", False) if not date_from or not days: - raise ValidationError(_('Date From and days are mandatory')) - partner_id = kwargs.get('partner_id', False) - partner = self.env['res.partner'].browse(partner_id) + raise ValidationError(_("Date From and days are mandatory")) + partner_id = kwargs.get("partner_id", False) + partner = self.env["res.partner"].browse(partner_id) pricelist_id = kwargs.get( - 'pricelist_id', - partner.property_product_pricelist.id and - partner.property_product_pricelist.id or - self.env.user.pms_property_id.default_pricelist_id.id) - vals.update({ - 'partner_id': partner_id if partner_id else False, - 'discount': discount, - }) + "pricelist_id", + partner.property_product_pricelist.id + and partner.property_product_pricelist.id + or self.env.user.pms_property_id.default_pricelist_id.id, + ) + vals.update( + {"partner_id": partner_id if partner_id else False, "discount": discount,} + ) rate_vals = {} for room_type in room_types: - vals.update({'room_type_id': room_type.id}) - room_vals = self.env['pms.reservation'].\ - prepare_reservation_lines( - date_from, - days, - pricelist_id=pricelist_id, - vals=vals, - update_old_prices=False) - rate_vals.update({ - room_type.id: [item[2] for item in - room_vals['reservation_line_ids'] if - item[2]] - }) + vals.update({"room_type_id": room_type.id}) + room_vals = self.env["pms.reservation"].prepare_reservation_lines( + date_from, + days, + pricelist_id=pricelist_id, + vals=vals, + update_old_prices=False, + ) + rate_vals.update( + { + room_type.id: [ + item[2] for item in room_vals["reservation_line_ids"] if item[2] + ] + } + ) return rate_vals diff --git a/pms/models/pms_room_type_class.py b/pms/models/pms_room_type_class.py index f2769b744..3535b8a70 100644 --- a/pms/models/pms_room_type_class.py +++ b/pms/models/pms_room_type_class.py @@ -1,7 +1,7 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields +from odoo import fields, models class PmsRoomTypeClass(models.Model): @@ -10,25 +10,22 @@ class PmsRoomTypeClass(models.Model): residential accommodation: for example, a Room, a Bed, an Apartment, a Tent, a Caravan... """ + _name = "pms.room.type.class" _description = "Room Type Class" _order = "sequence, name, code_class" # Fields declaration - name = fields.Char('Class Name', required=True, translate=True) + name = fields.Char("Class Name", required=True, translate=True) # Relationship between models pms_property_ids = fields.Many2many( - 'pms.property', - string='Properties', - required=False, - ondelete='restrict') - room_type_ids = fields.One2many( - 'pms.room.type', - 'class_id', - 'Types') - code_class = fields.Char('Code') - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', default=0) + "pms.property", string="Properties", required=False, ondelete="restrict" + ) + room_type_ids = fields.One2many("pms.room.type", "class_id", "Types") + code_class = fields.Char("Code") + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", default=0) - _sql_constraints = [('code_class_unique', 'unique(code_class)', - 'Room Class Code must be unique!')] + _sql_constraints = [ + ("code_class_unique", "unique(code_class)", "Room Class Code must be unique!") + ] diff --git a/pms/models/pms_room_type_restriction.py b/pms/models/pms_room_type_restriction.py index 7591be257..42ba993b5 100644 --- a/pms/models/pms_room_type_restriction.py +++ b/pms/models/pms_room_type_restriction.py @@ -1,13 +1,14 @@ # Copyright 2017 Alexandre Díaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api +from odoo import api, fields, models class PmsRoomTypeRestriction(models.Model): """ The room type restriction is used as a daily restriction plan for room types and therefore is related only with one property. """ - _name = 'pms.room.type.restriction' - _description = 'Reservation restriction plan' + + _name = "pms.room.type.restriction" + _description = "Reservation restriction plan" # Default methods @api.model @@ -15,19 +16,22 @@ class PmsRoomTypeRestriction(models.Model): return self.env.user.pms_property_id or None # Fields declaration - name = fields.Char('Restriction Plan Name', required=True) + name = fields.Char("Restriction Plan Name", required=True) pms_property_id = fields.Many2one( - 'pms.property', - 'Property', - ondelete='restrict', - default=_get_default_pms_property) + "pms.property", + "Property", + ondelete="restrict", + default=_get_default_pms_property, + ) item_ids = fields.One2many( - 'pms.room.type.restriction.item', - 'restriction_id', - string='Restriction Items', - copy=True) + "pms.room.type.restriction.item", + "restriction_id", + string="Restriction Items", + copy=True, + ) active = fields.Boolean( - 'Active', + "Active", default=True, - help='If unchecked, it will allow you to hide the ' - 'restriction plan without removing it.') + help="If unchecked, it will allow you to hide the " + "restriction plan without removing it.", + ) diff --git a/pms/models/pms_room_type_restriction_item.py b/pms/models/pms_room_type_restriction_item.py index b3029d19b..e1cacc06a 100644 --- a/pms/models/pms_room_type_restriction_item.py +++ b/pms/models/pms_room_type_restriction_item.py @@ -1,47 +1,49 @@ # Copyright 2017 Alexandre Díaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class PmsRoomTypeRestrictionItem(models.Model): - _name = 'pms.room.type.restriction.item' - _description = 'Reservation restriction by day' + _name = "pms.room.type.restriction.item" + _description = "Reservation restriction by day" # Field Declarations - restriction_id = fields.Many2one('pms.room.type.restriction', - 'Restriction Plan', ondelete='cascade', - index=True) - room_type_id = fields.Many2one('pms.room.type', 'Room Type', - required=True, ondelete='cascade') - date = fields.Date('Date') + restriction_id = fields.Many2one( + "pms.room.type.restriction", "Restriction Plan", ondelete="cascade", index=True + ) + room_type_id = fields.Many2one( + "pms.room.type", "Room Type", required=True, ondelete="cascade" + ) + date = fields.Date("Date") min_stay = fields.Integer("Min. Stay") min_stay_arrival = fields.Integer("Min. Stay Arrival") max_stay = fields.Integer("Max. Stay") max_stay_arrival = fields.Integer("Max. Stay Arrival") - closed = fields.Boolean('Closed') - closed_departure = fields.Boolean('Closed Departure') - closed_arrival = fields.Boolean('Closed Arrival') + closed = fields.Boolean("Closed") + closed_departure = fields.Boolean("Closed Departure") + closed_arrival = fields.Boolean("Closed Arrival") - _sql_constraints = [('room_type_registry_unique', - 'unique(restriction_id, room_type_id, date)', - 'Only can exists one restriction in the same \ - day for the same room type!')] + _sql_constraints = [ + ( + "room_type_registry_unique", + "unique(restriction_id, room_type_id, date)", + "Only can exists one restriction in the same \ + day for the same room type!", + ) + ] # Constraints and onchanges - @api.constrains('min_stay', 'min_stay_arrival', 'max_stay', - 'max_stay_arrival') + @api.constrains("min_stay", "min_stay_arrival", "max_stay", "max_stay_arrival") def _check_min_stay(self): for record in self: if record.min_stay < 0: raise ValidationError(_("Min. Stay can't be less than zero")) elif record.min_stay_arrival < 0: - raise ValidationError( - _("Min. Stay Arrival can't be less than zero")) + raise ValidationError(_("Min. Stay Arrival can't be less than zero")) elif record.max_stay < 0: raise ValidationError(_("Max. Stay can't be less than zero")) elif record.max_stay_arrival < 0: - raise ValidationError( - _("Max. Stay Arrival can't be less than zero")) + raise ValidationError(_("Max. Stay Arrival can't be less than zero")) diff --git a/pms/models/pms_service.py b/pms/models/pms_service.py index 50b3a3499..55553db8b 100644 --- a/pms/models/pms_service.py +++ b/pms/models/pms_service.py @@ -1,19 +1,18 @@ # Copyright 2017 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ -from odoo.tools import ( - float_is_zero, - float_compare, - DEFAULT_SERVER_DATE_FORMAT) -from datetime import timedelta import logging +from datetime import timedelta + +from odoo import _, api, fields, models +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, float_compare, float_is_zero + _logger = logging.getLogger(__name__) class PmsService(models.Model): - _name = 'pms.service' - _description = 'Services and its charges' + _name = "pms.service" + _description = "Services and its charges" # Default methods @@ -21,141 +20,129 @@ class PmsService(models.Model): result = [] for rec in self: name = [] - name.append('%(name)s' % {'name': rec.name}) + name.append("{name}".format(name=rec.name)) if rec.reservation_id.name: - name.append('%(name)s' % {'name': rec.reservation_id.name}) + name.append("{name}".format(name=rec.reservation_id.name)) result.append((rec.id, ", ".join(name))) return result @api.model def _default_reservation_id(self): - if self.env.context.get('reservation_ids'): - ids = [item[1] for item in self.env.context['reservation_ids']] - return self.env['pms.reservation'].browse([ - (ids)], limit=1) - elif self.env.context.get('default_reservation_id'): - return self.env.context.get('default_reservation_id') + if self.env.context.get("reservation_ids"): + ids = [item[1] for item in self.env.context["reservation_ids"]] + return self.env["pms.reservation"].browse([(ids)], limit=1) + elif self.env.context.get("default_reservation_id"): + return self.env.context.get("default_reservation_id") return False @api.model def _default_folio_id(self): - if 'folio_id' in self._context: - return self._context['folio_id'] + if "folio_id" in self._context: + return self._context["folio_id"] return False # Fields declaration - name = fields.Char('Service description', required=True) + name = fields.Char("Service description", required=True) product_id = fields.Many2one( - 'product.product', - 'Service', - ondelete='restrict', - required=True) + "product.product", "Service", ondelete="restrict", required=True + ) folio_id = fields.Many2one( - 'pms.folio', - 'Folio', - ondelete='cascade', - default=_default_folio_id) + "pms.folio", "Folio", ondelete="cascade", default=_default_folio_id + ) reservation_id = fields.Many2one( - 'pms.reservation', - 'Room', - default=_default_reservation_id) - service_line_ids = fields.One2many( - 'pms.service.line', - 'service_id') + "pms.reservation", "Room", default=_default_reservation_id + ) + service_line_ids = fields.One2many("pms.service.line", "service_id") company_id = fields.Many2one( - related='folio_id.company_id', - string='Company', - store=True, - readonly=True) + related="folio_id.company_id", string="Company", store=True, readonly=True + ) pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='folio_id.pms_property_id') + "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" + ) tax_ids = fields.Many2many( - 'account.tax', - string='Taxes', - domain=['|', ('active', '=', False), ('active', '=', True)]) + "account.tax", + string="Taxes", + domain=["|", ("active", "=", False), ("active", "=", True)], + ) move_line_ids = fields.Many2many( - 'account.move.line', - 'service_line_move_rel', - 'service_id', - 'move_line_id', - string='move Lines', - copy=False) - analytic_tag_ids = fields.Many2many( - 'account.analytic.tag', - string='Analytic Tags') + "account.move.line", + "service_line_move_rel", + "service_id", + "move_line_id", + string="move Lines", + copy=False, + ) + analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags") currency_id = fields.Many2one( - related='folio_id.currency_id', - store=True, - string='Currency', - readonly=True) - sequence = fields.Integer(string='Sequence', default=10) - state = fields.Selection(related='folio_id.state') - per_day = fields.Boolean(related='product_id.per_day', related_sudo=True) - product_qty = fields.Integer('Quantity', default=1) + related="folio_id.currency_id", store=True, string="Currency", readonly=True + ) + sequence = fields.Integer(string="Sequence", default=10) + state = fields.Selection(related="folio_id.state") + per_day = fields.Boolean(related="product_id.per_day", related_sudo=True) + product_qty = fields.Integer("Quantity", default=1) days_qty = fields.Integer(compute="_compute_days_qty", store=True) is_board_service = fields.Boolean() - to_print = fields.Boolean('Print', help='Print in Folio Report') + to_print = fields.Boolean("Print", help="Print in Folio Report") # Non-stored related field to allow portal user to # see the image of the product he has ordered product_image = fields.Binary( - 'Product Image', related="product_id.image_1024", - store=False, related_sudo=True) - invoice_status = fields.Selection([ - ('invoiced', 'Fully Invoiced'), - ('to invoice', 'To Invoice'), - ('no', 'Nothing to Invoice')], - string='Invoice Status', - compute='_compute_invoice_status', - store=True, - readonly=True, - default='no') - channel_type = fields.Selection([ - ('door', 'Door'), - ('mail', 'Mail'), - ('phone', 'Phone'), - ('call', 'Call Center'), - ('web', 'Web')], - string='Sales Channel') + "Product Image", related="product_id.image_1024", store=False, related_sudo=True + ) + invoice_status = fields.Selection( + [ + ("invoiced", "Fully Invoiced"), + ("to invoice", "To Invoice"), + ("no", "Nothing to Invoice"), + ], + string="Invoice Status", + compute="_compute_invoice_status", + store=True, + readonly=True, + default="no", + ) + channel_type = fields.Selection( + [ + ("door", "Door"), + ("mail", "Mail"), + ("phone", "Phone"), + ("call", "Call Center"), + ("web", "Web"), + ], + string="Sales Channel", + ) price_unit = fields.Float( - 'Unit Price', - required=True, - digits=('Product Price'), default=0.0) - discount = fields.Float( - string='Discount (%)', - digits=('Discount'), default=0.0) + "Unit Price", required=True, digits=("Product Price"), default=0.0 + ) + discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0) qty_to_invoice = fields.Float( - compute='_get_to_invoice_qty', - string='To Invoice', + compute="_get_to_invoice_qty", + string="To Invoice", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) qty_invoiced = fields.Float( - compute='_get_invoice_qty', - string='Invoiced', + compute="_get_invoice_qty", + string="Invoiced", store=True, readonly=True, - digits=('Product Unit of Measure')) + digits=("Product Unit of Measure"), + ) price_subtotal = fields.Monetary( - string='Subtotal', - readonly=True, - store=True, - compute='_compute_amount_service') + string="Subtotal", readonly=True, store=True, compute="_compute_amount_service" + ) price_total = fields.Monetary( - string='Total', - readonly=True, - store=True, - compute='_compute_amount_service') + string="Total", readonly=True, store=True, compute="_compute_amount_service" + ) price_tax = fields.Float( - string='Taxes Amount', + string="Taxes Amount", readonly=True, store=True, - compute='_compute_amount_service') + compute="_compute_amount_service", + ) # Compute and Search methods - @api.depends('qty_invoiced', 'product_qty', 'folio_id.state') + @api.depends("qty_invoiced", "product_qty", "folio_id.state") def _get_to_invoice_qty(self): """ Compute the quantity to invoice. If the invoice policy is order, @@ -163,13 +150,12 @@ class PmsService(models.Model): Otherwise, the quantity delivered is used. """ for line in self: - if line.folio_id.state not in ['draft']: + if line.folio_id.state not in ["draft"]: line.qty_to_invoice = line.product_qty - line.qty_invoiced else: line.qty_to_invoice = 0 - @api.depends('move_line_ids.move_id.state', - 'move_line_ids.quantity') + @api.depends("move_line_ids.move_id.state", "move_line_ids.quantity") def _get_invoice_qty(self): """ Compute the quantity invoiced. If case of a refund, @@ -183,16 +169,18 @@ class PmsService(models.Model): for line in self: qty_invoiced = 0.0 for invoice_line in line.move_line_ids: - if invoice_line.move_id.state != 'cancel': - if invoice_line.move_id.type == 'out_invoice': + if invoice_line.move_id.state != "cancel": + if invoice_line.move_id.type == "out_invoice": qty_invoiced += invoice_line.uom_id._compute_quantity( - invoice_line.quantity, line.product_id.uom_id) - elif invoice_line.move_id.type == 'out_refund': + invoice_line.quantity, line.product_id.uom_id + ) + elif invoice_line.move_id.type == "out_refund": qty_invoiced -= move_line.uom_id._compute_quantity( - invoice_line.quantity, line.product_id.uom_id) + invoice_line.quantity, line.product_id.uom_id + ) line.qty_invoiced = qty_invoiced - @api.depends('product_qty', 'qty_to_invoice', 'qty_invoiced') + @api.depends("product_qty", "qty_to_invoice", "qty_invoiced") def _compute_invoice_status(self): """ Compute the invoice status of a SO line. Possible statuses: @@ -212,58 +200,65 @@ class PmsService(models.Model): - invoiced: the quantity invoiced is larger or equal to the quantity ordered. """ - precision = self.env['decimal.precision'].precision_get( - 'Product Unit of Measure') + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) for line in self: - if line.folio_id.state in ('draft'): - line.invoice_status = 'no' - elif not float_is_zero(line.qty_to_invoice, - precision_digits=precision): - line.invoice_status = 'to invoice' - elif float_compare(line.qty_invoiced, line.product_qty, - precision_digits=precision) >= 0: - line.invoice_status = 'invoiced' + if line.folio_id.state in ("draft"): + line.invoice_status = "no" + elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): + line.invoice_status = "to invoice" + elif ( + float_compare( + line.qty_invoiced, line.product_qty, precision_digits=precision + ) + >= 0 + ): + line.invoice_status = "invoiced" else: - line.invoice_status = 'no' + line.invoice_status = "no" - @api.depends('product_qty', 'discount', 'price_unit', 'tax_ids') + @api.depends("product_qty", "discount", "price_unit", "tax_ids") def _compute_amount_service(self): """ Compute the amounts of the service line. """ for record in self: - folio = record.folio_id or self.env['pms.folio'].browse( - self.env.context.get('default_folio_id')) + folio = record.folio_id or self.env["pms.folio"].browse( + self.env.context.get("default_folio_id") + ) reservation = record.reservation_id or self.env.context.get( - 'reservation_id') + "reservation_id" + ) currency = folio.currency_id if folio else reservation.currency_id product = record.product_id price = record.price_unit * (1 - (record.discount or 0.0) * 0.01) taxes = record.tax_ids.compute_all( - price, currency, record.product_qty, product=product) + price, currency, record.product_qty, product=product + ) - record.update({ - 'price_tax': sum(t.get('amount', 0.0) for t in - taxes.get('taxes', [])), - 'price_total': taxes['total_included'], - 'price_subtotal': taxes['total_excluded'], - }) + record.update( + { + "price_tax": sum( + t.get("amount", 0.0) for t in taxes.get("taxes", []) + ), + "price_total": taxes["total_included"], + "price_subtotal": taxes["total_excluded"], + } + ) - @api.depends('service_line_ids.day_qty') + @api.depends("service_line_ids.day_qty") def _compute_days_qty(self): for record in self: if record.per_day: - qty = sum(record.service_line_ids.mapped('day_qty')) - vals = { - 'days_qty': qty, - 'product_qty': qty - } + qty = sum(record.service_line_ids.mapped("day_qty")) + vals = {"days_qty": qty, "product_qty": qty} else: - vals = {'days_qty': 0} + vals = {"days_qty": 0} record.update(vals) # Constraints and onchanges - @api.onchange('product_id') + @api.onchange("product_id") def onchange_product_id(self): """ Compute the default quantity according to the @@ -274,13 +269,13 @@ class PmsService(models.Model): if not self.product_id: return vals = {} - vals['product_qty'] = 1.0 + vals["product_qty"] = 1.0 for record in self: if record.per_day and record.reservation_id: product = record.product_id - if self.env.context.get('default_reservation_id'): - reservation = self.env['pms.reservation'].browse( - self.env.context.get('default_reservation_id') + if self.env.context.get("default_reservation_id"): + reservation = self.env["pms.reservation"].browse( + self.env.context.get("default_reservation_id") ) else: reservation = record.reservation_id @@ -293,14 +288,16 @@ class PmsService(models.Model): checkin_dt = fields.Date.from_string(checkin) checkout_dt = fields.Date.from_string(checkout) nights = abs((checkout_dt - checkin_dt).days) - vals.update(record.prepare_service_ids( - dfrom=checkin, - days=nights, - per_person=product.per_person, - persons=reservation.adults, - old_line_days=record.service_line_ids, - consumed_on=product.consumed_on, - )) + vals.update( + record.prepare_service_ids( + dfrom=checkin, + days=nights, + per_person=product.per_person, + persons=reservation.adults, + old_line_days=record.service_line_ids, + consumed_on=product.consumed_on, + ) + ) if record.product_id.daily_limit > 0: for day in record.service_line_ids: day.no_free_resources() @@ -308,64 +305,62 @@ class PmsService(models.Model): Description and warnings """ product = self.product_id.with_context( - lang=self.folio_id.partner_id.lang, - partner=self.folio_id.partner_id.id + lang=self.folio_id.partner_id.lang, partner=self.folio_id.partner_id.id ) title = False message = False warning = {} - if product.sale_line_warn != 'no-message': + if product.sale_line_warn != "no-message": title = _("Warning for %s") % product.name message = product.sale_line_warn_msg - warning['title'] = title - warning['message'] = message - result = {'warning': warning} - if product.sale_line_warn == 'block': + warning["title"] = title + warning["message"] = message + result = {"warning": warning} + if product.sale_line_warn == "block": self.product_id = False return result name = product.name_get()[0][1] if product.description_sale: - name += '\n' + product.description_sale - vals['name'] = name + name += "\n" + product.description_sale + vals["name"] = name """ Compute tax and price unit """ self._compute_tax_ids() - vals['price_unit'] = self._compute_price_unit() + vals["price_unit"] = self._compute_price_unit() record.update(vals) # Action methods def open_service_ids(self): - action = self.env.ref('pms.action_pms_services_form').read()[0] - action['views'] = [ - (self.env.ref('pms.pms_service_view_form').id, 'form')] - action['res_id'] = self.id - action['target'] = 'new' + action = self.env.ref("pms.action_pms_services_form").read()[0] + action["views"] = [(self.env.ref("pms.pms_service_view_form").id, "form")] + action["res_id"] = self.id + action["target"] = "new" return action # ORM Overrides @api.model - def name_search(self, name='', args=None, operator='ilike', limit=100): + def name_search(self, name="", args=None, operator="ilike", limit=100): if args is None: args = [] - if not(name == '' and operator == 'ilike'): + if not (name == "" and operator == "ilike"): args += [ - '|', - ('reservation_id.name', operator, name), - ('name', operator, name) + "|", + ("reservation_id.name", operator, name), + ("name", operator, name), ] return super(PmsService, self).name_search( - name='', args=args, operator='ilike', limit=limit) + name="", args=args, operator="ilike", limit=limit + ) @api.model def create(self, vals): vals.update(self._prepare_add_missing_fields(vals)) if self.compute_lines_out_vals(vals): - reservation = self.env['pms.reservation'].browse( - vals['reservation_id']) - product = self.env['product.product'].browse(vals['product_id']) + reservation = self.env["pms.reservation"].browse(vals["reservation_id"]) + product = self.env["product.product"].browse(vals["product_id"]) if reservation.splitted: checkin = reservation.real_checkin checkout = reservation.real_checkout @@ -375,33 +370,34 @@ class PmsService(models.Model): checkin_dt = fields.Date.from_string(checkin) checkout_dt = fields.Date.from_string(checkout) nights = abs((checkout_dt - checkin_dt).days) - vals.update(self.prepare_service_ids( - dfrom=checkin, - days=nights, - per_person=product.per_person, - persons=reservation.adults, - old_day_lines=False, - consumed_on=product.consumed_on, - )) + vals.update( + self.prepare_service_ids( + dfrom=checkin, + days=nights, + per_person=product.per_person, + persons=reservation.adults, + old_day_lines=False, + consumed_on=product.consumed_on, + ) + ) record = super(PmsService, self).create(vals) return record - def write(self, vals): # If you write product, We must check if its necesary create or delete # service lines - if vals.get('product_id'): - product = self.env['product.product'].browse( - vals.get('product_id')) + if vals.get("product_id"): + product = self.env["product.product"].browse(vals.get("product_id")) if not product.per_day: - vals.update({ - 'service_line_ids': [(5, 0, 0)] - }) + vals.update({"service_line_ids": [(5, 0, 0)]}) else: for record in self: - reservations = self.env['pms.reservation'] - reservation = reservations.browse(vals['reservation_id']) \ - if 'reservation_id' in vals else record.reservation_id + reservations = self.env["pms.reservation"] + reservation = ( + reservations.browse(vals["reservation_id"]) + if "reservation_id" in vals + else record.reservation_id + ) if reservation.splitted: checkin = reservation.real_checkin checkout = reservation.real_checkout @@ -411,14 +407,16 @@ class PmsService(models.Model): checkin_dt = fields.Date.from_string(checkin) checkout_dt = fields.Date.from_string(checkout) nights = abs((checkout_dt - checkin_dt).days) - record.update(record.prepare_service_ids( - dfrom=checkin, - days=nights, - per_person=product.per_person, - persons=reservation.adults, - old_line_days=self.service_line_ids, - consumed_on=product.consumed_on, - )) + record.update( + record.prepare_service_ids( + dfrom=checkin, + days=nights, + per_person=product.per_person, + persons=reservation.adults, + old_line_days=self.service_line_ids, + consumed_on=product.consumed_on, + ) + ) res = super(PmsService, self).write(vals) return res @@ -427,103 +425,123 @@ class PmsService(models.Model): def _prepare_add_missing_fields(self, values): """ Deduce missing required fields from the onchange """ res = {} - onchange_fields = ['price_unit', 'tax_ids', 'name'] - if values.get('product_id'): + onchange_fields = ["price_unit", "tax_ids", "name"] + if values.get("product_id"): line = self.new(values) if any(f not in values for f in onchange_fields): line.onchange_product_id() for field in onchange_fields: if field not in values: - res[field] = line._fields[field].convert_to_write( - line[field], line) + res[field] = line._fields[field].convert_to_write(line[field], line) return res - def compute_lines_out_vals(self, vals): """ Compute if It is necesary service days in write/create """ if not vals: vals = {} - if 'product_id' in vals: - product = self.env['product.product'].browse(vals['product_id']) \ - if 'product_id' in vals else self.product_id - if (product.per_day and 'service_line_ids' not in vals): + if "product_id" in vals: + product = ( + self.env["product.product"].browse(vals["product_id"]) + if "product_id" in vals + else self.product_id + ) + if product.per_day and "service_line_ids" not in vals: return True return False - def _compute_tax_ids(self): for record in self: # If company_id is set, always filter taxes by the company - folio = record.folio_id or self.env['pms.folio'].browse( - self.env.context.get('default_folio_id')) + folio = record.folio_id or self.env["pms.folio"].browse( + self.env.context.get("default_folio_id") + ) reservation = record.reservation_id or self.env.context.get( - 'reservation_id') + "reservation_id" + ) origin = folio if folio else reservation record.tax_ids = record.product_id.taxes_id.filtered( - lambda r: not record.company_id or - r.company_id == origin.company_id) - + lambda r: not record.company_id or r.company_id == origin.company_id + ) def _get_display_price(self, product): - folio = self.folio_id or self.env.context.get('default_folio_id') - reservation = self.reservation_id or self.env.context.get( - 'reservation_id') + folio = self.folio_id or self.env.context.get("default_folio_id") + reservation = self.reservation_id or self.env.context.get("reservation_id") origin = folio if folio else reservation - if origin.pricelist_id.discount_policy == 'with_discount': + if origin.pricelist_id.discount_policy == "with_discount": return product.with_context(pricelist=origin.pricelist_id.id).price product_context = dict( self.env.context, partner_id=origin.partner_id.id, date=folio.date_order if folio else fields.Date.today(), - uom=self.product_id.uom_id.id) + uom=self.product_id.uom_id.id, + ) final_price, rule_id = origin.pricelist_id.with_context( - product_context).get_product_price_rule( - self.product_id, - self.product_qty or 1.0, - origin.partner_id) + product_context + ).get_product_price_rule( + self.product_id, self.product_qty or 1.0, origin.partner_id + ) base_price, currency_id = self.with_context( - product_context)._get_real_price_currency( - product, - rule_id, - self.product_qty, - self.product_id.uom_id, - origin.pricelist_id.id) + product_context + )._get_real_price_currency( + product, + rule_id, + self.product_qty, + self.product_id.uom_id, + origin.pricelist_id.id, + ) if currency_id != origin.pricelist_id.currency_id.id: - base_price = self.env['res.currency'].browse( - currency_id).with_context(product_context).compute( - base_price, - origin.pricelist_id.currency_id) + base_price = ( + self.env["res.currency"] + .browse(currency_id) + .with_context(product_context) + .compute(base_price, origin.pricelist_id.currency_id) + ) # negative discounts (= surcharge) are included in the display price return max(base_price, final_price) - def _compute_price_unit(self): self.ensure_one() - folio = self.folio_id or self.env.context.get('default_folio_id') - reservation = self.reservation_id or self.env.context.get( - 'reservation_id') + folio = self.folio_id or self.env.context.get("default_folio_id") + reservation = self.reservation_id or self.env.context.get("reservation_id") origin = reservation if reservation else folio if origin: partner = origin.partner_id pricelist = origin.pricelist_id if reservation and self.is_board_service: board_room_type = reservation.board_service_room_id - if board_room_type.price_type == 'fixed': - return self.env['pms.board.service.room.type.line'].\ - search([ - ('pms_board_service_room_type_id', - '=', board_room_type.id), - ('product_id', '=', self.product_id.id)]).amount + if board_room_type.price_type == "fixed": + return ( + self.env["pms.board.service.room.type.line"] + .search( + [ + ( + "pms_board_service_room_type_id", + "=", + board_room_type.id, + ), + ("product_id", "=", self.product_id.id), + ] + ) + .amount + ) else: - return (reservation.price_total * - self.env['pms.board.service.room.type.line']. - search([ - ('pms_board_service_room_type_id', - '=', board_room_type.id), - ('product_id', '=', self.product_id.id)]) - .amount) / 100 + return ( + reservation.price_total + * self.env["pms.board.service.room.type.line"] + .search( + [ + ( + "pms_board_service_room_type_id", + "=", + board_room_type.id, + ), + ("product_id", "=", self.product_id.id), + ] + ) + .amount + ) / 100 else: product = self.product_id.with_context( lang=partner.lang, @@ -532,12 +550,14 @@ class PmsService(models.Model): date=folio.date_order if folio else fields.Date.today(), pricelist=pricelist.id, uom=self.product_id.uom_id.id, - fiscal_position=False + fiscal_position=False, ) - return self.env['account.tax']._fix_tax_included_price_company( + return self.env["account.tax"]._fix_tax_included_price_company( self._get_display_price(product), - product.taxes_id, self.tax_ids, - origin.company_id) + product.taxes_id, + self.tax_ids, + origin.company_id, + ) @api.model def prepare_service_ids(self, **kwargs): @@ -545,28 +565,26 @@ class PmsService(models.Model): Prepare line and respect the old manual changes on lines """ cmds = [(5, 0, 0)] - old_line_days = kwargs.get('old_line_days') - consumed_on = kwargs.get('consumed_on') if kwargs.get( - 'consumed_on') else 'before' + old_line_days = kwargs.get("old_line_days") + consumed_on = ( + kwargs.get("consumed_on") if kwargs.get("consumed_on") else "before" + ) total_qty = 0 day_qty = 1 # WARNING: Change adults in reservation NOT update qty service!! - if kwargs.get('per_person'): - day_qty = kwargs.get('persons') - for i in range(0, kwargs.get('days')): - if consumed_on == 'after': + if kwargs.get("per_person"): + day_qty = kwargs.get("persons") + for i in range(0, kwargs.get("days")): + if consumed_on == "after": i += 1 - idate = (fields.Date.from_string(kwargs.get('dfrom')) + - timedelta(days=i)).strftime( - DEFAULT_SERVER_DATE_FORMAT) - if not old_line_days or idate not in old_line_days.mapped('date'): - cmds.append((0, False, { - 'date': idate, - 'day_qty': day_qty - })) + idate = ( + fields.Date.from_string(kwargs.get("dfrom")) + timedelta(days=i) + ).strftime(DEFAULT_SERVER_DATE_FORMAT) + if not old_line_days or idate not in old_line_days.mapped("date"): + cmds.append((0, False, {"date": idate, "day_qty": day_qty})) total_qty = total_qty + day_qty else: old_line = old_line_days.filtered(lambda r: r.date == idate) cmds.append((4, old_line.id)) total_qty = total_qty + old_line.day_qty - return {'service_line_ids': cmds, 'product_qty': total_qty} + return {"service_line_ids": cmds, "product_qty": total_qty} diff --git a/pms/models/pms_service_line.py b/pms/models/pms_service_line.py index 06fa2d394..18247e536 100644 --- a/pms/models/pms_service_line.py +++ b/pms/models/pms_service_line.py @@ -1,7 +1,7 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -12,50 +12,39 @@ class PmsServiceLine(models.Model): # Fields declaration service_id = fields.Many2one( - 'pms.service', - string='Service Room', - ondelete='cascade', + "pms.service", + string="Service Room", + ondelete="cascade", required=True, - copy=False) - product_id = fields.Many2one( - related='service_id.product_id', - store=True) + copy=False, + ) + product_id = fields.Many2one(related="service_id.product_id", store=True) tax_ids = fields.Many2many( - 'account.tax', - string='Taxes', - related="service_id.tax_ids", - readonly="True") + "account.tax", string="Taxes", related="service_id.tax_ids", readonly="True" + ) pms_property_id = fields.Many2one( - 'pms.property', - store=True, - readonly=True, - related='service_id.pms_property_id') - date = fields.Date('Date') - day_qty = fields.Integer('Units') + "pms.property", store=True, readonly=True, related="service_id.pms_property_id" + ) + date = fields.Date("Date") + day_qty = fields.Integer("Units") price_total = fields.Float( - 'Price Total', - compute='_compute_price_total', - store=True) + "Price Total", compute="_compute_price_total", store=True + ) price_unit = fields.Float( - 'Unit Price', - related="service_id.price_unit", - readonly=True, - store=True) + "Unit Price", related="service_id.price_unit", readonly=True, store=True + ) room_id = fields.Many2one( - string='Room', - related="service_id.reservation_id", - readonly=True, - store=True) + string="Room", related="service_id.reservation_id", readonly=True, store=True + ) discount = fields.Float( - 'Discount', - related="service_id.discount", - readonly=True, - store=True) + "Discount", related="service_id.discount", readonly=True, store=True + ) cancel_discount = fields.Float( - 'Discount cancel', compute='_compute_cancel_discount') + "Discount cancel", compute="_compute_cancel_discount" + ) # Compute and Search methods - @api.depends('day_qty', 'service_id.price_total') + @api.depends("day_qty", "service_id.price_total") def _compute_price_total(self): """ Used to reports @@ -63,26 +52,33 @@ class PmsServiceLine(models.Model): for record in self: if record.service_id.product_qty != 0: record.price_total = ( - record.service_id.price_total * record.day_qty) \ - / record.service_id.product_qty + record.service_id.price_total * record.day_qty + ) / record.service_id.product_qty else: record.price_total = 0 # Constraints and onchanges - @api.constrains('day_qty') + @api.constrains("day_qty") def no_free_resources(self): for record in self: limit = record.product_id.daily_limit if limit > 0: - out_qty = sum(self.env['pms.service.line'].search([ - ('product_id', '=', record.product_id.id), - ('date', '=', record.date), - ('service_id', '!=', record.service_id.id) - ]).mapped('day_qty')) + out_qty = sum( + self.env["pms.service.line"] + .search( + [ + ("product_id", "=", record.product_id.id), + ("date", "=", record.date), + ("service_id", "!=", record.service_id.id), + ] + ) + .mapped("day_qty") + ) if limit < out_qty + record.day_qty: raise ValidationError( - _("%s limit exceeded for %s") % - (record.service_id.product_id.name, record.date)) + _("%s limit exceeded for %s") + % (record.service_id.product_id.name, record.date) + ) # Business methods def _cancel_discount(self): diff --git a/pms/models/pms_shared_room.py b/pms/models/pms_shared_room.py index 24010fa0b..f5f6854f5 100644 --- a/pms/models/pms_shared_room.py +++ b/pms/models/pms_shared_room.py @@ -2,111 +2,112 @@ # Copyright 2017 Dario Lodeiros # Copyright 2018 Pablo Quesada # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models, fields, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError class PmsSharedRoom(models.Model): - _name = 'pms.shared.room' - _description = 'Shared Room' + _name = "pms.shared.room" + _description = "Shared Room" _order = "room_type_id, name" # Fields declaration - name = fields.Char('Room Name', required=True) + name = fields.Char("Room Name", required=True) room_type_id = fields.Many2one( - 'pms.room.type', - 'Room Type', + "pms.room.type", + "Room Type", required=True, - ondelete='restrict', - domain=[('shared_room', '=', True)] - ) + ondelete="restrict", + domain=[("shared_room", "=", True)], + ) pms_property_id = fields.Many2one( - 'pms.property', + "pms.property", store=True, readonly=True, - related='room_type_id.pms_property_id') + related="room_type_id.pms_property_id", + ) floor_id = fields.Many2one( - 'pms.floor', - 'Ubication', - ondelete='restrict', - help='At which floor the room is located.') + "pms.floor", + "Ubication", + ondelete="restrict", + help="At which floor the room is located.", + ) bed_ids = fields.One2many( - 'pms.room', - 'shared_room_id', - readonly=True, - ondelete='restrict',) - active = fields.Boolean('Active', default=True) - sequence = fields.Integer('Sequence', required=True) - beds = fields.Integer('Beds') + "pms.room", "shared_room_id", readonly=True, ondelete="restrict", + ) + active = fields.Boolean("Active", default=True) + sequence = fields.Integer("Sequence", required=True) + beds = fields.Integer("Beds") description_sale = fields.Text( - 'Sale Description', + "Sale Description", translate=True, help="A description of the Product that you want to communicate to " - " your customers. This description will be copied to every Sales " - " Order, Delivery Order and Customer Invoice/Credit Note") + " your customers. This description will be copied to every Sales " + " Order, Delivery Order and Customer Invoice/Credit Note", + ) # Constraints and onchanges - @api.constrains('beds') + @api.constrains("beds") def _constrain_beds(self): self.ensure_one() if self.beds < 1: raise ValidationError(_("Room beds can't be less than one")) if len(self.bed_ids) > self.beds: - raise ValidationError(_( - "If you want to eliminate beds in the \ - room you must deactivate the beds from your form")) + raise ValidationError( + _( + "If you want to eliminate beds in the \ + room you must deactivate the beds from your form" + ) + ) beds = [] - inactive_beds = self.env['pms.room'].search([ - ('active', '=', False), - ('shared_room_id', '=', self.id) - ]) + inactive_beds = self.env["pms.room"].search( + [("active", "=", False), ("shared_room_id", "=", self.id)] + ) for i in range(len(self.bed_ids), self.beds): if inactive_beds: bed = inactive_beds[0] - bed.update({'active': True}) + bed.update({"active": True}) inactive_beds -= bed continue - name = u'%s (%s)' % (self.name, i + 1) + name = u"{} ({})".format(self.name, i + 1) bed_vals = { - 'name': name, - 'capacity': 1, - 'room_type_id': self.room_type_id.id, - 'sequence': self.sequence, - 'floor_id': self.floor_id.id if self.floor_id else False, - 'shared_room_id': self.id, + "name": name, + "capacity": 1, + "room_type_id": self.room_type_id.id, + "sequence": self.sequence, + "floor_id": self.floor_id.id if self.floor_id else False, + "shared_room_id": self.id, } beds.append((0, False, bed_vals)) if beds: - self.update({ - 'bed_ids': beds - }) + self.update({"bed_ids": beds}) - @api.constrains('active') + @api.constrains("active") def _constrain_active(self): - self.bed_ids.write({ - 'active': self.active, - }) + self.bed_ids.write( + {"active": self.active,} + ) - @api.constrains('room_type_id') + @api.constrains("room_type_id") def _constrain_room_type_id(self): - self.bed_ids.write({ - 'room_type_id': self.room_type_id.id, - }) + self.bed_ids.write( + {"room_type_id": self.room_type_id.id,} + ) - @api.constrains('floor_id') + @api.constrains("floor_id") def _constrain_floor_id(self): - self.bed_ids.write({ - 'floor_id': self.floor_id.id, - }) + self.bed_ids.write( + {"floor_id": self.floor_id.id,} + ) - @api.constrains('sequence') + @api.constrains("sequence") def _constrain_sequence(self): - self.bed_ids.write({ - 'sequence': self.sequence, - }) + self.bed_ids.write( + {"sequence": self.sequence,} + ) - @api.constrains('descrition_sale') + @api.constrains("descrition_sale") def _constrain_descrition_sale(self): - self.bed_ids.write({ - 'description_sale': self.descrition_sale, - }) + self.bed_ids.write( + {"description_sale": self.descrition_sale,} + ) diff --git a/pms/readme/CONFIGURE.rst b/pms/readme/CONFIGURE.rst index be4b60011..c26b4de76 100644 --- a/pms/readme/CONFIGURE.rst +++ b/pms/readme/CONFIGURE.rst @@ -3,4 +3,4 @@ the module before using it; it is aimed at advanced users. ] You will find the hotel settings in `Settings > Users & Companies > Hotels > Your Hotel. -This module required additional configuration for company, accounting, invoicing and user privileges. \ No newline at end of file +This module required additional configuration for company, accounting, invoicing and user privileges. diff --git a/pms/readme/CREDITS.rst b/pms/readme/CREDITS.rst index 61d2f78cc..cbd69e8ad 100644 --- a/pms/readme/CREDITS.rst +++ b/pms/readme/CREDITS.rst @@ -1,2 +1,2 @@ .. [ This file is optional and contains additional credits, other than -authors, contributors, and maintainers. ] \ No newline at end of file +authors, contributors, and maintainers. ] diff --git a/pms/readme/DESCRIPTION.rst b/pms/readme/DESCRIPTION.rst index 2e7db9fc7..d2b3d8f57 100644 --- a/pms/readme/DESCRIPTION.rst +++ b/pms/readme/DESCRIPTION.rst @@ -4,4 +4,4 @@ This module is an all-in-one property management system (PMS) focused on medium- for managing every aspect of your property's daily operations. You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory, -reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities. \ No newline at end of file +reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities. diff --git a/pms/readme/INSTALL.rst b/pms/readme/INSTALL.rst index f6504d125..2e88cc45a 100644 --- a/pms/readme/INSTALL.rst +++ b/pms/readme/INSTALL.rst @@ -2,4 +2,4 @@ installation instructions, such as installing non-python dependencies. The audience is systems administrators. ] This module depends on modules ``base``, ``sale_stock``, ``account_payment_return``, ``partner_firstname``, -and ``account_cancel``. Ensure yourself to have all them in your addons list. \ No newline at end of file +and ``account_cancel``. Ensure yourself to have all them in your addons list. diff --git a/pms/readme/ROADMAP.rst b/pms/readme/ROADMAP.rst index d49912e0a..e39c36eaa 100644 --- a/pms/readme/ROADMAP.rst +++ b/pms/readme/ROADMAP.rst @@ -1,4 +1,4 @@ .. [ Enumerate known caveats and future potential improvements. It is mostly intended for end-users, and can also help potential new contributors discovering new features to implement. ] -- [ ] \ No newline at end of file +- [ ] diff --git a/pms/readme/USAGE.rst b/pms/readme/USAGE.rst index 7fac9ecd5..e59fca743 100644 --- a/pms/readme/USAGE.rst +++ b/pms/readme/USAGE.rst @@ -3,4 +3,4 @@ for end-users. As all other rst files included in the README, it MUST NOT contai only body text (paragraphs, lists, tables, etc). Should you need a more elaborate structure to explain the addon, please create a Sphinx documentation (which may include this file as a "quick start" section). ] -To use this module, please, read the complete user guide at https://roomdoo.com. \ No newline at end of file +To use this module, please, read the complete user guide at https://roomdoo.com. diff --git a/pms/report/pms_folio.xml b/pms/report/pms_folio.xml index b9bc673d0..42ac8b644 100644 --- a/pms/report/pms_folio.xml +++ b/pms/report/pms_folio.xml @@ -1,4 +1,4 @@ - + + - + diff --git a/pms/security/pms_security.xml b/pms/security/pms_security.xml index ce3f78ca8..93734e609 100644 --- a/pms/security/pms_security.xml +++ b/pms/security/pms_security.xml @@ -1,22 +1,18 @@ - + - Property Management / User - Property Management/ Manager - + - Property Management / CallCenter - diff --git a/pms/static/src/js/views/list/list_controller.js b/pms/static/src/js/views/list/list_controller.js index c783b9984..7c75d376e 100644 --- a/pms/static/src/js/views/list/list_controller.js +++ b/pms/static/src/js/views/list/list_controller.js @@ -1,28 +1,32 @@ -odoo.define('pms.ListController', function(require) { -'use strict'; -/* - * Pms - * GNU Public License - * Alexandre Díaz - */ +odoo.define("pms.ListController", function(require) { + "use strict"; + /* + * Pms + * GNU Public License + * Alexandre Díaz + */ -var ListController = require('web.ListController'); -var Core = require('web.core'); + var ListController = require("web.ListController"); + var Core = require("web.core"); -var _t = Core._t; - -ListController.include({ - - renderButtons: function () { - this._super.apply(this, arguments); // Sets this.$buttons - var self = this; - if (this.modelName === 'pms.reservation') { - this.$buttons.append(""); - this.$buttons.find('.oe_open_reservation_wizard').on('click', function(){ - self.do_action('pms.open_wizard_reservations'); - }); - } - } -}); + var _t = Core._t; + ListController.include({ + renderButtons: function() { + this._super.apply(this, arguments); // Sets this.$buttons + var self = this; + if (this.modelName === "pms.reservation") { + this.$buttons.append( + "" + ); + this.$buttons + .find(".oe_open_reservation_wizard") + .on("click", function() { + self.do_action("pms.open_wizard_reservations"); + }); + } + }, + }); }); diff --git a/pms/static/src/js/widgets/switch_hotel_menu.js b/pms/static/src/js/widgets/switch_hotel_menu.js index bb985f3c7..515440f2e 100644 --- a/pms/static/src/js/widgets/switch_hotel_menu.js +++ b/pms/static/src/js/widgets/switch_hotel_menu.js @@ -1,61 +1,77 @@ -odoo.define('pms.SwitchPmsMenu', function(require) { -"use strict"; +odoo.define("pms.SwitchPmsMenu", function(require) { + "use strict"; -var config = require('web.config'); -var core = require('web.core'); -var session = require('web.session'); -var SystrayMenu = require('web.SystrayMenu'); -var Widget = require('web.Widget'); + var config = require("web.config"); + var core = require("web.core"); + var session = require("web.session"); + var SystrayMenu = require("web.SystrayMenu"); + var Widget = require("web.Widget"); -var _t = core._t; + var _t = core._t; -var SwitchPmsMenu = Widget.extend({ - template: 'pms.SwitchPmsMenu', - willStart: function() { - this.isMobile = config.device.isMobile; - if (!session.user_pms) { - return $.Deferred().reject(); - } - return this._super(); - }, - start: function() { - var self = this; - this.$el.on('click', '.dropdown-menu li a[data-menu]', _.debounce(function(ev) { - ev.preventDefault(); - var pms_property_id = $(ev.currentTarget).data('property-id'); - self._rpc({ - model: 'res.users', - method: 'write', - args: [[session.uid], {'pms_property_id': pms_property_id}], - }) - .then(function() { - location.reload(); - }); - }, 1500, true)); - - var properties_list = ''; - if (this.isMobile) { - propertiess_list = '
  • ' + _t('Tap on the list to change property') + '
  • '; - } - else { - self.$('.oe_topbar_name').text(session.user_properties.current_property[1]); - } - _.each(session.user_properties.allowed_propierties, function(property) { - var a = ''; - if (property[0] === session.user_properties.current_property[0]) { - a = ''; - } else { - a = ''; + var SwitchPmsMenu = Widget.extend({ + template: "pms.SwitchPmsMenu", + willStart: function() { + this.isMobile = config.device.isMobile; + if (!session.user_pms) { + return $.Deferred().reject(); } - properties_list += '
  • ' + a + property[1] + '
  • '; - }); - self.$('.dropdown-menu').html(properties_list); - return this._super(); - }, -}); - -SystrayMenu.Items.push(SwitchPmsMenu); - -return SwitchPmsMenu; - + return this._super(); + }, + start: function() { + var self = this; + this.$el.on( + "click", + ".dropdown-menu li a[data-menu]", + _.debounce( + function(ev) { + ev.preventDefault(); + var pms_property_id = $(ev.currentTarget).data("property-id"); + self._rpc({ + model: "res.users", + method: "write", + args: [[session.uid], {pms_property_id: pms_property_id}], + }).then(function() { + location.reload(); + }); + }, + 1500, + true + ) + ); + + var properties_list = ""; + if (this.isMobile) { + propertiess_list = + '
  • ' + + _t("Tap on the list to change property") + + "
  • "; + } else { + self.$(".oe_topbar_name").text( + session.user_properties.current_property[1] + ); + } + _.each(session.user_properties.allowed_propierties, function(property) { + var a = ""; + if (property[0] === session.user_properties.current_property[0]) { + a = ''; + } else { + a = ''; + } + properties_list += + '
  • ' + + a + + property[1] + + "
  • "; + }); + self.$(".dropdown-menu").html(properties_list); + return this._super(); + }, + }); + + SystrayMenu.Items.push(SwitchPmsMenu); + + return SwitchPmsMenu; }); diff --git a/pms/static/src/xml/pms_base_templates.xml b/pms/static/src/xml/pms_base_templates.xml index 60a185ef3..7c3dee2ae 100644 --- a/pms/static/src/xml/pms_base_templates.xml +++ b/pms/static/src/xml/pms_base_templates.xml @@ -1,12 +1,18 @@ diff --git a/pms/templates/pms_email_template.xml b/pms/templates/pms_email_template.xml index 47efc3ed1..d94e4050e 100644 --- a/pms/templates/pms_email_template.xml +++ b/pms/templates/pms_email_template.xml @@ -1,117 +1,139 @@ - + - - + + + Property: Reservation Confirmed - - ${('%s <%s>' % (object.pms_property_id.partner_id.name, object.pms_property_id.partner_id.email) or '')|safe} + + ${('%s <%s>' % (object.pms_property_id.partner_id.name, object.pms_property_id.partner_id.email) or '')|safe} ${(object.email or '')|safe} ${(object.partner_id.id or '')} ${object.partner_id.lang} - Your reservation ${object.name} has been confirmed by the property staff - + Your reservation ${object.name} has been confirmed by the property staff + qweb - - - + + diff --git a/pms/tests/__init__.py b/pms/tests/__init__.py index 2f32bd6cf..dc537dc29 100644 --- a/pms/tests/__init__.py +++ b/pms/tests/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution diff --git a/pms/tests/common.py b/pms/tests/common.py index be7844f90..dc9aa610d 100644 --- a/pms/tests/common.py +++ b/pms/tests/common.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -20,46 +19,50 @@ # along with this program. If not, see . # ############################################################################## +import logging from datetime import timedelta + from odoo import api, fields from odoo.tests import common -from odoo.tools import ( - DEFAULT_SERVER_DATE_FORMAT, - DEFAULT_SERVER_DATETIME_FORMAT) -import logging +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT + _logger = logging.getLogger(__name__) class TestHotel(common.SavepointCase): - @classmethod def _init_mock_hotel(cls): return True def create_folio(self, creator, partner): # Create Folio - folio = self.env['hotel.folio'].sudo(creator).create({ - 'partner_id': partner.id, - }) + folio = ( + self.env["hotel.folio"].sudo(creator).create({"partner_id": partner.id,}) + ) self.assertTrue(folio, "Can't create folio") return folio - def create_reservation(self, creator, folio, checkin, checkout, room, - resname, adults=1, children=0): + def create_reservation( + self, creator, folio, checkin, checkout, room, resname, adults=1, children=0 + ): # Create Reservation (Special Room) - reservation = self.env['hotel.reservation'].sudo(creator).create({ - 'name': resname, - 'adults': adults, - 'children': children, - 'checkin': checkin.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'checkout': checkout.strftime(DEFAULT_SERVER_DATETIME_FORMAT), - 'folio_id': folio.id, - 'room_type_id': room.price_room_type.id, - 'product_id': room.product_id.id, - }) - self.assertTrue( - reservation, - "Hotel Calendar can't create a new reservation!") + reservation = ( + self.env["hotel.reservation"] + .sudo(creator) + .create( + { + "name": resname, + "adults": adults, + "children": children, + "checkin": checkin.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + "checkout": checkout.strftime(DEFAULT_SERVER_DATETIME_FORMAT), + "folio_id": folio.id, + "room_type_id": room.price_room_type.id, + "product_id": room.product_id.id, + } + ) + ) + self.assertTrue(reservation, "Hotel Calendar can't create a new reservation!") # Create Reservation Lines + Update Reservation Price # days_diff = date_utils.date_diff(checkin, checkout, hours=False) @@ -79,27 +82,26 @@ class TestHotel(common.SavepointCase): cls._init_mock_hotel() # Create Tests Records - cls.main_hotel_property = cls.env.ref('hotel.main_hotel_property') - cls.demo_hotel_property = cls.env.ref('hotel.demo_hotel_property') + cls.main_hotel_property = cls.env.ref("hotel.main_hotel_property") + cls.demo_hotel_property = cls.env.ref("hotel.demo_hotel_property") - cls.room_type_0 = cls.env.ref('hotel.hotel_room_type_0') - cls.room_type_1 = cls.env.ref('hotel.hotel_room_type_1') - cls.room_type_2 = cls.env.ref('hotel.hotel_room_type_2') - cls.room_type_3 = cls.env.ref('hotel.hotel_room_type_3') + cls.room_type_0 = cls.env.ref("hotel.hotel_room_type_0") + cls.room_type_1 = cls.env.ref("hotel.hotel_room_type_1") + cls.room_type_2 = cls.env.ref("hotel.hotel_room_type_2") + cls.room_type_3 = cls.env.ref("hotel.hotel_room_type_3") - cls.demo_room_type_0 = cls.env.ref('hotel.demo_hotel_room_type_0') - cls.demo_room_type_1 = cls.env.ref('hotel.demo_hotel_room_type_1') + cls.demo_room_type_0 = cls.env.ref("hotel.demo_hotel_room_type_0") + cls.demo_room_type_1 = cls.env.ref("hotel.demo_hotel_room_type_1") - cls.room_0 = cls.env.ref('hotel.hotel_room_0') - cls.room_1 = cls.env.ref('hotel.hotel_room_1') - cls.room_2 = cls.env.ref('hotel.hotel_room_2') - cls.room_3 = cls.env.ref('hotel.hotel_room_3') - cls.room_4 = cls.env.ref('hotel.hotel_room_4') - cls.room_5 = cls.env.ref('hotel.hotel_room_5') - cls.room_6 = cls.env.ref('hotel.hotel_room_6') + cls.room_0 = cls.env.ref("hotel.hotel_room_0") + cls.room_1 = cls.env.ref("hotel.hotel_room_1") + cls.room_2 = cls.env.ref("hotel.hotel_room_2") + cls.room_3 = cls.env.ref("hotel.hotel_room_3") + cls.room_4 = cls.env.ref("hotel.hotel_room_4") + cls.room_5 = cls.env.ref("hotel.hotel_room_5") + cls.room_6 = cls.env.ref("hotel.hotel_room_6") - cls.list0 = cls.env.ref('product.list0') - cls.list1 = cls.env['product.pricelist'].create({ - 'name': 'Test Pricelist', - 'pricelist_type': '' - }) + cls.list0 = cls.env.ref("product.list0") + cls.list1 = cls.env["product.pricelist"].create( + {"name": "Test Pricelist", "pricelist_type": ""} + ) diff --git a/pms/tests/test_folio.py b/pms/tests/test_folio.py index 4c53e7912..6f5e26f5b 100644 --- a/pms/tests/test_folio.py +++ b/pms/tests/test_folio.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -21,12 +20,13 @@ # ############################################################################## from datetime import timedelta -from .common import TestHotel + from odoo.addons.hotel import date_utils +from .common import TestHotel + class TestHotelReservations(TestHotel): - def test_cancel_folio(self): now_utc_dt = date_utils.now() @@ -39,17 +39,18 @@ class TestHotelReservations(TestHotel): org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #1") + "Reservation Test #1", + ) reservation_b = self.create_reservation( self.user_hotel_manager, folio, org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_simple_100, - "Reservation Test #2") - self.assertEqual(len(folio.reservation_ids), 2, 'Invalid room lines count') + "Reservation Test #2", + ) + self.assertEqual(len(folio.reservation_ids), 2, "Invalid room lines count") folio.action_cancel() - self.assertEqual(folio.state, 'cancel', 'Invalid folio state') + self.assertEqual(folio.state, "cancel", "Invalid folio state") for rline in folio.reservation_ids: - self.assertEqual(rline.state, 'cancelled', - 'Invalid reservation state') + self.assertEqual(rline.state, "cancelled", "Invalid reservation state") diff --git a/pms/tests/test_hotel_property.py b/pms/tests/test_hotel_property.py index 2e4efbca4..5f01cb038 100644 --- a/pms/tests/test_hotel_property.py +++ b/pms/tests/test_hotel_property.py @@ -1,7 +1,8 @@ -from .common import TestHotel from odoo import fields from odoo.exceptions import ValidationError +from .common import TestHotel + class TestHotelProperty(TestHotel): diff --git a/pms/tests/test_hotel_room.py b/pms/tests/test_hotel_room.py index d047b00e0..c4aa1e140 100644 --- a/pms/tests/test_hotel_room.py +++ b/pms/tests/test_hotel_room.py @@ -19,36 +19,36 @@ # along with this program. If not, see . # ############################################################################## -from .common import TestHotel from odoo.exceptions import ValidationError +from .common import TestHotel + class TestHotelRoom(TestHotel): - def test_rooms_by_hotel(self): # A room cannot be created in a room type of another hotel with self.assertRaises(ValidationError): - record = self.env['hotel.room'].sudo().create({ - 'name': 'Test Room', - 'hotel_id': self.demo_hotel_property.id, - 'room_type_id': self.room_type_0.id, - }) + record = ( + self.env["hotel.room"] + .sudo() + .create( + { + "name": "Test Room", + "hotel_id": self.demo_hotel_property.id, + "room_type_id": self.room_type_0.id, + } + ) + ) # A room cannot be changed to another hotel with self.assertRaises(ValidationError): - self.room_0.sudo().write({ - 'hotel_id': self.demo_room_type_0.hotel_id.id - }) + self.room_0.sudo().write({"hotel_id": self.demo_room_type_0.hotel_id.id}) def test_rooms_by_room_type(self): # A room cannot be changed to a room type of another hotel with self.assertRaises(ValidationError): - self.room_0.sudo().write({ - 'room_type_id': self.demo_room_type_1.id - }) + self.room_0.sudo().write({"room_type_id": self.demo_room_type_1.id}) def test_check_capacity(self): # The capacity of the room must be greater than 0 with self.assertRaises(ValidationError): - self.room_0.sudo().write({ - 'capacity': 0 - }) + self.room_0.sudo().write({"capacity": 0}) diff --git a/pms/tests/test_hotel_room_type.py b/pms/tests/test_hotel_room_type.py index a1bf6ec82..bff93c081 100644 --- a/pms/tests/test_hotel_room_type.py +++ b/pms/tests/test_hotel_room_type.py @@ -19,10 +19,12 @@ # along with this program. If not, see . # ############################################################################## -from .common import TestHotel from psycopg2 import IntegrityError + from odoo.tools import mute_logger +from .common import TestHotel + class TestHotelRoomType(TestHotel): @@ -30,15 +32,12 @@ class TestHotelRoomType(TestHotel): # code type must be unique by hotel def test_code_type_unique_by_hotel(self): - with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'): - self.room_type_0.sudo().write({ - 'code_type': self.room_type_1.code_type - }) + with self.assertRaises(IntegrityError), mute_logger("odoo.sql_db"): + self.room_type_0.sudo().write({"code_type": self.room_type_1.code_type}) # code type can be used in other hotel def test_code_type_shared_by_hotel(self): - test_result = self.demo_room_type_0.sudo().write({ - 'code_type': self.room_type_0.code_type - }) + test_result = self.demo_room_type_0.sudo().write( + {"code_type": self.room_type_0.code_type} + ) self.assertEqual(test_result, True) - diff --git a/pms/tests/test_inherited_ir_http.py b/pms/tests/test_inherited_ir_http.py index 63518fcf1..c3c92f698 100644 --- a/pms/tests/test_inherited_ir_http.py +++ b/pms/tests/test_inherited_ir_http.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- from .common import TestHotel class TestInheritedIrHttp(TestHotel): - def test_user_hotel_company(self): - admin_user = self.env.ref('base.user_root') - self.assertTrue(admin_user.hotel_id.company_id in admin_user.company_ids, - "Wrong hotel and company access settings for %s" % admin_user.name) - + admin_user = self.env.ref("base.user_root") + self.assertTrue( + admin_user.hotel_id.company_id in admin_user.company_ids, + "Wrong hotel and company access settings for %s" % admin_user.name, + ) diff --git a/pms/tests/test_inherited_product_pricelist.py b/pms/tests/test_inherited_product_pricelist.py index be7f56507..abfe2e408 100644 --- a/pms/tests/test_inherited_product_pricelist.py +++ b/pms/tests/test_inherited_product_pricelist.py @@ -1,7 +1,8 @@ -from .common import TestHotel from odoo import fields from odoo.exceptions import ValidationError +from .common import TestHotel + class TestInheritedProductPricelist(TestHotel): @@ -17,11 +18,13 @@ class TestInheritedProductPricelist(TestHotel): self.list0.hotel_ids = False # create a valid record using a daily pricelist - test_result = self.env['product.pricelist'].create({ - 'name': 'Test Daily Pricelist', - 'hotel_ids': [(4, self.demo_hotel_property.id)] - }) - self.assertEqual(test_result.pricelist_type, 'daily') + test_result = self.env["product.pricelist"].create( + { + "name": "Test Daily Pricelist", + "hotel_ids": [(4, self.demo_hotel_property.id)], + } + ) + self.assertEqual(test_result.pricelist_type, "daily") self.assertEqual(test_result.hotel_ids, self.demo_hotel_property) def test_pricelist_by_hotel(self): diff --git a/pms/tests/test_massive_changes.py b/pms/tests/test_massive_changes.py index 83b0a3ee9..1da2d6b8f 100644 --- a/pms/tests/test_massive_changes.py +++ b/pms/tests/test_massive_changes.py @@ -1,7 +1,8 @@ -from .common import TestHotel from odoo import fields from odoo.exceptions import ValidationError +from .common import TestHotel + class TestMassiveChanges(TestHotel): @@ -10,59 +11,51 @@ class TestMassiveChanges(TestHotel): # base massive change record def base_massive_change_vals(self, hotel_id=None): return { - 'hotel_id': hotel_id and hotel_id.id or self.main_hotel_property.id, - 'date_start': fields.Date.today(), - 'date_end': fields.Date.today(), + "hotel_id": hotel_id and hotel_id.id or self.main_hotel_property.id, + "date_start": fields.Date.today(), + "date_end": fields.Date.today(), } def pricelist_massive_change_vals(self, pricelist_id=None): return { - 'pricelist_id': pricelist_id and pricelist_id.id or self.list0.id, - 'price': 50.0, + "pricelist_id": pricelist_id and pricelist_id.id or self.list0.id, + "price": 50.0, } def test_daily_pricelist(self): # Only daily pricelist can be manage by a massive change - self.list0.pricelist_type = '' + self.list0.pricelist_type = "" with self.assertRaises(ValidationError): vals = self.base_massive_change_vals() - vals.update( - self.pricelist_massive_change_vals() - ) - self.env['hotel.wizard.massive.changes'].create(vals) + vals.update(self.pricelist_massive_change_vals()) + self.env["hotel.wizard.massive.changes"].create(vals) # create a valid record using a daily pricelist - self.list0.pricelist_type = 'daily' - test_result = self.env['hotel.wizard.massive.changes'].create(vals) + self.list0.pricelist_type = "daily" + test_result = self.env["hotel.wizard.massive.changes"].create(vals) self.assertEqual(test_result.pricelist_id, self.list0) def test_pricelist_by_hotel(self): # Ensure the pricelist plan belongs to the current hotel #1 with self.assertRaises(ValidationError): vals = self.base_massive_change_vals(self.demo_hotel_property) - vals.update( - self.pricelist_massive_change_vals() - ) - self.env['hotel.wizard.massive.changes'].create(vals) + vals.update(self.pricelist_massive_change_vals()) + self.env["hotel.wizard.massive.changes"].create(vals) # Ensure the pricelist plan belongs to the current hotel #2 with self.assertRaises(ValidationError): vals = self.base_massive_change_vals() - vals.update( - self.pricelist_massive_change_vals(self.list1) - ) + vals.update(self.pricelist_massive_change_vals(self.list1)) self.list1.hotel_ids = self.demo_hotel_property - self.list1.pricelist_type = 'daily' - self.env['hotel.wizard.massive.changes'].create(vals) + self.list1.pricelist_type = "daily" + self.env["hotel.wizard.massive.changes"].create(vals) # create a valid record using the current hotel vals = self.base_massive_change_vals() - vals.update( - self.pricelist_massive_change_vals(self.list1) - ) + vals.update(self.pricelist_massive_change_vals(self.list1)) self.list1.hotel_ids = self.main_hotel_property - self.list1.pricelist_type = 'daily' - test_result = self.env['hotel.wizard.massive.changes'].create(vals) + self.list1.pricelist_type = "daily" + test_result = self.env["hotel.wizard.massive.changes"].create(vals) self.assertEqual(test_result.pricelist_id.hotel_ids, self.main_hotel_property) def test_do_massive_change(self): diff --git a/pms/tests/test_reservation.py b/pms/tests/test_reservation.py index dc02750e0..acb7b7af9 100644 --- a/pms/tests/test_reservation.py +++ b/pms/tests/test_reservation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -21,19 +20,23 @@ # ############################################################################## import datetime -from datetime import timedelta -from odoo import fields -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT -from openerp.exceptions import ValidationError -from .common import TestHotel -from odoo.addons.hotel import date_utils -import pytz import logging +from datetime import timedelta + +import pytz +from openerp.exceptions import ValidationError +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT + +from odoo import fields + +from odoo.addons.hotel import date_utils + +from .common import TestHotel + _logger = logging.getLogger(__name__) class TestHotelReservations(TestHotel): - def test_create_reservation(self): now_utc_dt = date_utils.now() reserv_start_utc_dt = now_utc_dt + timedelta(days=3) @@ -45,25 +48,32 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #1") + "Reservation Test #1", + ) - reserv_start_dt = date_utils.dt_as_timezone(reserv_start_utc_dt, - self.tz_hotel) - reserv_end_dt = date_utils.dt_as_timezone(reserv_end_utc_dt - - timedelta(days=1), - self.tz_hotel) - self.assertEqual(reservation.reservation_lines[0].date, - reserv_start_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - "Reservation lines don't start in the correct date") - self.assertEqual(reservation.reservation_lines[-1].date, - reserv_end_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), - "Reservation lines don't end in the correct date") + reserv_start_dt = date_utils.dt_as_timezone(reserv_start_utc_dt, self.tz_hotel) + reserv_end_dt = date_utils.dt_as_timezone( + reserv_end_utc_dt - timedelta(days=1), self.tz_hotel + ) + self.assertEqual( + reservation.reservation_lines[0].date, + reserv_start_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), + "Reservation lines don't start in the correct date", + ) + self.assertEqual( + reservation.reservation_lines[-1].date, + reserv_end_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), + "Reservation lines don't end in the correct date", + ) total_price = 0.0 for rline in reservation.reservation_lines: total_price += rline.price - self.assertEqual(folio.amount_untaxed, total_price, - "Folio amount doesn't match with reservation lines") + self.assertEqual( + folio.amount_untaxed, + total_price, + "Folio amount doesn't match with reservation lines", + ) def test_create_reservations(self): now_utc_dt = date_utils.now() @@ -76,7 +86,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #1") + "Reservation Test #1", + ) reserv_start_utc_dt = reserv_end_utc_dt reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) @@ -87,7 +98,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #2") + "Reservation Test #2", + ) reserv_end_utc_dt = now_utc_dt + timedelta(days=3) reserv_start_utc_dt = reserv_end_utc_dt - timedelta(days=1) @@ -98,7 +110,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Reservation Test #3") + "Reservation Test #3", + ) reserv_start_utc_dt = now_utc_dt + timedelta(days=3) reserv_end_utc_dt = reserv_start_utc_dt + timedelta(days=3) @@ -109,7 +122,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_simple_100, - "Reservation Test #4") + "Reservation Test #4", + ) def test_create_invalid_reservations(self): now_utc_dt = date_utils.now() @@ -123,7 +137,8 @@ class TestHotelReservations(TestHotel): org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_double_200, - "Original Reservation Test #1") + "Original Reservation Test #1", + ) # Same Dates reserv_start_utc_dt = now_utc_dt + timedelta(days=3) @@ -136,7 +151,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #1") + "Invalid Reservation Test #1", + ) # Inside Org Reservation (Start Same Date) reserv_start_utc_dt = now_utc_dt + timedelta(days=3) @@ -149,7 +165,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #2") + "Invalid Reservation Test #2", + ) # Inside Org Reservation (Start after) reserv_start_utc_dt = now_utc_dt + timedelta(days=4) @@ -162,7 +179,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #3") + "Invalid Reservation Test #3", + ) # Intersect Org Reservation (Start before) reserv_start_utc_dt = now_utc_dt + timedelta(days=2) @@ -175,7 +193,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #4") + "Invalid Reservation Test #4", + ) # Intersect Org Reservation (End Same) reserv_start_utc_dt = org_reserv_end_utc_dt - timedelta(days=2) @@ -188,7 +207,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #5") + "Invalid Reservation Test #5", + ) # Intersect Org Reservation (End after) reserv_start_utc_dt = org_reserv_end_utc_dt - timedelta(days=2) @@ -201,7 +221,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #6") + "Invalid Reservation Test #6", + ) # Overlays Org Reservation reserv_start_utc_dt = org_reserv_start_utc_dt - timedelta(days=2) @@ -214,7 +235,8 @@ class TestHotelReservations(TestHotel): reserv_start_utc_dt, reserv_end_utc_dt, self.hotel_room_double_200, - "Invalid Reservation Test #7") + "Invalid Reservation Test #7", + ) # Checkin > Checkout with self.assertRaises(ValidationError): @@ -225,14 +247,17 @@ class TestHotelReservations(TestHotel): org_reserv_end_utc_dt, org_reserv_start_utc_dt, self.hotel_room_simple_100, - "Invalid Reservation Test #8") + "Invalid Reservation Test #8", + ) def test_modify_reservation(self): now_utc_dt = date_utils.now() # 5.0, 15.0, 15.0, 35.0, 35.0, 10.0, 10.0 - room_type_prices = self.prices_tmp[self.hotel_room_double_200.price_room_type.id] + room_type_prices = self.prices_tmp[ + self.hotel_room_double_200.price_room_type.id + ] org_reserv_start_utc_dt = now_utc_dt + timedelta(days=1) org_reserv_end_utc_dt = org_reserv_start_utc_dt + timedelta(days=2) folio = self.create_folio(self.user_hotel_manager, self.partner_2) @@ -242,19 +267,28 @@ class TestHotelReservations(TestHotel): org_reserv_start_utc_dt, org_reserv_end_utc_dt, self.hotel_room_double_200, - "Original Reservation Test #1") + "Original Reservation Test #1", + ) ndate = org_reserv_start_utc_dt for r_k, r_v in enumerate(reservation.reservation_lines): self.assertEqual(r_v.date, ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)) - self.assertEqual(r_v.price, room_type_prices[r_k+1]) + self.assertEqual(r_v.price, room_type_prices[r_k + 1]) ndate = ndate + timedelta(days=1) self.assertEqual(reservation.amount_room, 30.0) ndate = org_reserv_start_utc_dt + timedelta(days=1) - line = reservation.reservation_lines.filtered(lambda r: r.date == ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)) - reservation.reservation_lines = [(1, line.id, {'price': 100.0})] + line = reservation.reservation_lines.filtered( + lambda r: r.date == ndate.strftime(DEFAULT_SERVER_DATE_FORMAT) + ) + reservation.reservation_lines = [(1, line.id, {"price": 100.0})] self.assertEqual(reservation.amount_room, 115.0) - reservation.sudo(self.user_hotel_manager).write({ - 'checkin': (org_reserv_start_utc_dt + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT), - 'checkout': (org_reserv_end_utc_dt + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT), - }) + reservation.sudo(self.user_hotel_manager).write( + { + "checkin": (org_reserv_start_utc_dt + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + "checkout": (org_reserv_end_utc_dt + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATE_FORMAT + ), + } + ) self.assertEqual(reservation.amount_room, 135.0) diff --git a/pms/views/general.xml b/pms/views/general.xml index ac7898093..7d5672a99 100644 --- a/pms/views/general.xml +++ b/pms/views/general.xml @@ -1,11 +1,12 @@ - + -