From e7c0c3e5bdb4e641306158c3934ebf813c220f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Lodeiros?= Date: Tue, 27 Apr 2021 20:01:42 +0200 Subject: [PATCH] [ADD]multi_pms_properties (#66) * [WIP]pms: models check_pms_property * [WIP][IMP+REF] multi_pms_properties: refactor and added test skeleton * [FIX] inherit create models * [ADD] room multiproperty check * [ADD] room multiproperty check * [IMP] Multiproperty checks in pms models * [IMP] Fix Multiproperty checks in pms models * [IMP] Add multiproperty domain in multi_pms_properties module * [IMP] Fix multiproperty checks in pms tests * [IMP] Fix multiproperty checks logic * [IMP] Auto Domains * [IMP] availability property results, domain preferred_room_id * [IMP] model domain properties * [ADD] pms multiproperty depends * [IMP] models and views multiproperty checks * [FIX] Multiple rebase multiproperty fixes * [ADD] Readme * [ADD] Company - multiproperty checks * [ADD] travis server wide modules multiproperty * [FIX] travis conf load * [FIX] travis conf load2 * [FIX] travis conf load2 Co-authored-by: Eric Antones Co-authored-by: Sara Lago --- .travis.yml | 5 +- multi_pms_properties/README.rst | 101 ++++ multi_pms_properties/__init__.py | 64 +++ multi_pms_properties/__manifest__.py | 17 + multi_pms_properties/models.py | 186 ++++++++ multi_pms_properties/readme/CONFIGURE.rst | 7 + multi_pms_properties/readme/CONTRIBUTORS.rst | 2 + multi_pms_properties/readme/DESCRIPTION.rst | 1 + multi_pms_properties/readme/INSTALL.rst | 7 + multi_pms_properties/readme/USAGE.rst | 1 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 446 ++++++++++++++++++ multi_pms_properties/tests/__init__.py | 4 + multi_pms_properties/tests/common.py | 20 + .../tests/multi_pms_properties_tester.py | 17 + .../tests/test_multi_pms_properties.py | 41 ++ pms/__manifest__.py | 1 + pms/models/account_bank_statement.py | 3 + pms/models/account_journal.py | 31 +- pms/models/folio_sale_line.py | 1 + pms/models/payment_return.py | 3 + pms/models/pms_amenity.py | 20 +- pms/models/pms_amenity_type.py | 18 +- pms/models/pms_availability.py | 3 + pms/models/pms_availability_plan.py | 149 +++--- pms/models/pms_availability_plan_rule.py | 68 +-- pms/models/pms_board_service.py | 3 + pms/models/pms_board_service_line.py | 49 +- pms/models/pms_board_service_room_type.py | 37 +- .../pms_board_service_room_type_line.py | 60 ++- pms/models/pms_cancelation_rule.py | 19 +- pms/models/pms_checkin_partner.py | 7 +- pms/models/pms_folio.py | 41 +- pms/models/pms_property.py | 2 +- pms/models/pms_reservation.py | 75 +-- pms/models/pms_reservation_line.py | 5 + pms/models/pms_room.py | 64 +-- pms/models/pms_room_closure_reason.py | 2 + pms/models/pms_room_type.py | 66 +-- pms/models/pms_room_type_class.py | 6 +- pms/models/pms_sale_channel.py | 14 + pms/models/pms_service.py | 6 + pms/models/pms_service_line.py | 3 + pms/models/pms_shared_room.py | 3 + pms/models/pms_ubication.py | 4 + pms/models/product_pricelist.py | 66 +-- pms/models/product_pricelist_item.py | 70 +-- pms/models/product_template.py | 21 +- pms/models/res_partner.py | 15 + pms/tests/test_pms_amenity.py | 116 +---- pms/tests/test_pms_availability_plan_rules.py | 110 ++--- pms/tests/test_pms_board_service_line.py | 4 +- .../test_pms_board_service_room_type_line.py | 9 +- pms/tests/test_pms_folio.py | 4 +- pms/tests/test_pms_pricelist.py | 10 +- pms/tests/test_pms_pricelist_priority.py | 26 +- pms/tests/test_pms_reservation.py | 2 +- pms/tests/test_pms_room.py | 10 +- pms/tests/test_pms_room_type.py | 10 +- pms/tests/test_pms_wizard_folio.py | 5 +- .../pms_board_service_room_type_views.xml | 2 + pms/views/pms_board_service_views.xml | 2 + pms/views/pms_checkin_partner_views.xml | 9 + pms/views/pms_reservation_views.xml | 1 + pms/views/pms_room_type_views.xml | 1 + pms/views/pms_sale_channel.xml | 2 + pms/views/product_pricelist_item_views.xml | 1 - pms/views/res_partner_views.xml | 5 + pms/wizards/wizard_folio.py | 6 + pms/wizards/wizard_folio.xml | 1 - pms/wizards/wizard_folio_availability.py | 6 +- pms/wizards/wizard_massive_changes.py | 9 +- pms/wizards/wizard_massive_changes.xml | 6 +- .../models/pms_housekeeping_task.py | 12 +- .../odoo/addons/multi_pms_properties | 1 + setup/multi_pms_properties/setup.py | 6 + 76 files changed, 1471 insertions(+), 759 deletions(-) create mode 100644 multi_pms_properties/README.rst create mode 100644 multi_pms_properties/__init__.py create mode 100644 multi_pms_properties/__manifest__.py create mode 100644 multi_pms_properties/models.py create mode 100644 multi_pms_properties/readme/CONFIGURE.rst create mode 100644 multi_pms_properties/readme/CONTRIBUTORS.rst create mode 100644 multi_pms_properties/readme/DESCRIPTION.rst create mode 100644 multi_pms_properties/readme/INSTALL.rst create mode 100644 multi_pms_properties/readme/USAGE.rst create mode 100644 multi_pms_properties/static/description/icon.png create mode 100644 multi_pms_properties/static/description/index.html create mode 100644 multi_pms_properties/tests/__init__.py create mode 100644 multi_pms_properties/tests/common.py create mode 100644 multi_pms_properties/tests/multi_pms_properties_tester.py create mode 100644 multi_pms_properties/tests/test_multi_pms_properties.py create mode 120000 setup/multi_pms_properties/odoo/addons/multi_pms_properties create mode 100644 setup/multi_pms_properties/setup.py diff --git a/.travis.yml b/.travis.yml index ca9b93390..2ca711435 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,10 +27,11 @@ jobs: before_install: - stage: test env: - - TESTS=1 ODOO_REPO="odoo/odoo" MAKEPOT="1" + - TESTS=1 ODOO_REPO="odoo/odoo" MAKEPOT="1" OPTIONS="--load + web,multi_pms_properties" - stage: test env: - - TESTS=1 ODOO_REPO="OCA/OCB" + - TESTS=1 ODOO_REPO="OCA/OCB" OPTIONS="--load web,multi_pms_properties" env: global: - VERSION="14.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0" diff --git a/multi_pms_properties/README.rst b/multi_pms_properties/README.rst new file mode 100644 index 000000000..34d6f2f09 --- /dev/null +++ b/multi_pms_properties/README.rst @@ -0,0 +1,101 @@ +==================== +multi_pms_properties +==================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpms-lightgray.png?logo=github + :target: https://github.com/OCA/pms/tree/14.0/multi_pms_properties + :alt: OCA/pms +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pms-14-0/pms-14-0-multi_pms_properties + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/293/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Technical addon to support multipropety in pms + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +To install this module, you only need to add it to your addons, and load it as +a server-wide module. + +This can be done with the ``server_wide_modules`` parameter in ``/etc/odoo.conf`` +or with the ``--load`` command-line parameter + +``server_wide_modules = "multi_pms_properties"`` + +Configuration +============= + +Technical Module to Multi Property Manage System proposit. + +* Use the standar multicompany guidelines applied to pms.property: + + ``_check_pms_properties_auto like model attribute to autocheck on create/write`` + ``check_pms_properties like field attribute to check relational record properties consistence`` + ``This module not implement propety dependent fields`` + +Usage +===== + +To use this module, you need implement pms.property functionality + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* Dario Lodeiros +* Eric Antones + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/pms `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/multi_pms_properties/__init__.py b/multi_pms_properties/__init__.py new file mode 100644 index 000000000..16fb7d2d4 --- /dev/null +++ b/multi_pms_properties/__init__.py @@ -0,0 +1,64 @@ +# Copyright 2021 Dario Lodeiros +# Copyright 2021 Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo import fields +from odoo.tools import config + +from . import models + + +def _description_domain(self, env): + if self.check_company and not self.domain: + if self.company_dependent: + if self.comodel_name == "res.users": + # user needs access to current company (self.env.company) + return "[('company_ids', 'in', allowed_company_ids[0])]" + else: + return "[('company_id', 'in', [allowed_company_ids[0], False])]" + else: + # when using check_company=True on a field on 'res.company', the + # company_id comes from the id of the current record + cid = "id" if self.model_name == "res.company" else "company_id" + if self.comodel_name == "res.users": + # User allowed company ids = user.company_ids + return f"['|', (not {cid}, '=', True), ('company_ids', 'in', [{cid}])]" + else: + return f"[('company_id', 'in', [{cid}, False])]" + + if self.check_pms_properties and not self.domain: + record = env[self.model_name] + # Skip company_id domain to avoid domain multiproperty error in inherited views + if ( + self.check_pms_properties + and not self.domain + and self.name not in ["company_id"] + ): + if self.model_name == "pms.property": + prop1 = "id" + prop2 = f"[{prop1}]" + elif "pms_property_id" in record._fields: + prop1 = "pms_property_id" + prop2 = f"[{prop1}]" + else: + prop1 = prop2 = "pms_property_ids" + coprop = ( + "pms_property_id" + if "pms_property_id" in env[self.comodel_name]._fields + else "pms_property_ids" + ) + return f"['|', '|', \ + (not {prop1}, '=', True), \ + ('{coprop}', 'in', {prop2}), \ + ('{coprop}', '=', False)]" + + return self.domain(env[self.model_name]) if callable(self.domain) else self.domain + + +if "multi_pms_properties" in config.get("server_wide_modules"): + _logger = logging.getLogger(__name__) + _logger.info("monkey patching fields._Relational") + + fields._Relational.check_pms_properties = False + fields._Relational._description_domain = _description_domain diff --git a/multi_pms_properties/__manifest__.py b/multi_pms_properties/__manifest__.py new file mode 100644 index 000000000..c7a0213d1 --- /dev/null +++ b/multi_pms_properties/__manifest__.py @@ -0,0 +1,17 @@ +# © 2013 Therp BV +# © 2014 ACSONE SA/NV +# Copyright 2018 Quartile Limited +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "multi_pms_properties", + "summary": "Multi Properties Manager", + "version": "14.0.1.0.0", + "website": "https://github.com/OCA/pms", + "author": "Therp BV, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Pms", + "depends": ["base"], + "auto_install": False, + "installable": True, +} diff --git a/multi_pms_properties/models.py b/multi_pms_properties/models.py new file mode 100644 index 000000000..a2c0e7050 --- /dev/null +++ b/multi_pms_properties/models.py @@ -0,0 +1,186 @@ +# Copyright 2021 Dario Lodeiros +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, models +from odoo.exceptions import UserError + + +class BaseModel(models.AbstractModel): + _inherit = "base" + _check_pms_properties_auto = False + """On write and create, call ``_check_pms_properties_auto`` to ensure properties + consistency on the relational fields having ``check_pms_properties=True`` + as attribute. + """ + + @api.model_create_multi + def create(self, vals_list): + records = super(BaseModel, self).create(vals_list) + if self._check_pms_properties_auto: + records._check_pms_properties() + return records + + def write(self, vals): + res = super(BaseModel, self).write(vals) + check_pms_properties = False + for fname in vals: + field = self._fields.get(fname) + if ( + fname == "pms_property_id" + or fname == "pms_property_ids" + or fname == "company_id" + or (field.relational and field.check_pms_properties) + ): + check_pms_properties = True + if res and check_pms_properties and self._check_pms_properties_auto: + self._check_pms_properties() + return res + + def _check_pms_properties(self, fnames=None): + """Check the properties of the values of the given field names. + + :param list fnames: names of relational fields to check + :raises UserError: if the `pms_properties` of the value of any field is not + in `[False, self.pms_property_id]` (or `self` if + :class:`~odoo.addons.base.models.pms_property`). + + For :class:`~odoo.addons.base.models.res_users` relational fields, + verifies record company is in `company_ids` fields. + + User with main pms property A, having access to pms property A and B, could be + assigned or linked to records in property B. + """ + if fnames is None: + fnames = self._fields + + regular_fields = self._get_regular_fields(fnames) + + if not regular_fields: + return + + inconsistencies = self._check_inconsistencies(regular_fields) + + if inconsistencies: + lines = [_("Incompatible properties on records:")] + property_msg = _( + """- Record is properties %(pms_properties)r and %(field)r + (%(fname)s: %(values)s) belongs to another properties.""" + ) + record_msg = _( + """- %(record)r belongs to properties %(pms_properties)r and + %(field)r (%(fname)s: %(values)s) belongs to another properties.""" + ) + for record, name, corecords in inconsistencies[:5]: + if record._name == "pms.property": + msg, pms_properties = property_msg, record + else: + msg, pms_properties = ( + record_msg, + record.pms_property_id.name + if "pms_property_id" in record + else ", ".join(repr(p.name) for p in record.pms_property_ids), + ) + field = self.env["ir.model.fields"]._get(self._name, name) + lines.append( + msg + % { + "record": record.display_name, + "pms_properties": pms_properties, + "field": field.field_description, + "fname": field.name, + "values": ", ".join( + repr(rec.display_name) for rec in corecords + ), + } + ) + raise UserError("\n".join(lines)) + + def _get_regular_fields(self, fnames): + regular_fields = [] + for name in fnames: + field = self._fields[name] + if ( + field.relational + and field.check_pms_properties + and ( + "pms_property_id" in self.env[field.comodel_name] + or "pms_property_ids" in self.env[field.comodel_name] + ) + ): + regular_fields.append(name) + return regular_fields + + def _check_inconsistencies(self, regular_fields): + inconsistencies = [] + for record in self: + pms_properties = False + if record._name == "pms.property": + pms_properties = record + if "pms_property_id" in record: + pms_properties = record.pms_property_id + if "pms_property_ids" in record: + pms_properties = record.pms_property_ids + # Check the property & company consistence + if "company_id" in self._fields: + if record.company_id and pms_properties: + property_companies = pms_properties.mapped("company_id.id") + if ( + len(property_companies) > 1 + or record.company_id.id != property_companies[0] + ): + raise UserError( + _( + "You cannot establish a company other than " + "the one with the established properties" + ) + ) + # Check verifies that all + # records linked via relation fields are compatible + # with the properties of the origin document, + for name in regular_fields: + field = self._fields[name] + co_pms_properties = False + + corecord = record.sudo()[name] + # TODO:res.users management properties + if "pms_property_id" in corecord: + co_pms_properties = corecord.pms_property_id + if "pms_property_ids" in corecord: + co_pms_properties = corecord.pms_property_ids + if ( + # There is an inconsistency if: + # + # - Record has properties and corecord too and + # there's no match between them: + # X Pms_room_class with Property1 cannot contain + # Pms_room with property2 X + # + # - Record has a relation one2many with corecord and + # corecord properties aren't included in record properties + # or what is the same, subtraction between corecord properties + # and record properties must be False: + # X Pricelist with Prop1 and Prop2 cannot contain + # Pricelist_item with Prop1 and Prop3 X + # X Pricelist with Prop1 and Prop2 cannot contain + # Pricelist_item with Prop1, Prop2 and Prop3 X + # -In case that record has a relation many2one + # with corecord the condition is the same as avobe + ( + pms_properties + and co_pms_properties + and (not pms_properties & co_pms_properties) + ) + or ( + corecord + and field.type == "one2many" + and pms_properties + and (co_pms_properties - pms_properties) + ) + or ( + field.type == "many2one" + and co_pms_properties + and ((pms_properties - co_pms_properties) or not pms_properties) + ) + ): + inconsistencies.append((record, name, corecord)) + return inconsistencies diff --git a/multi_pms_properties/readme/CONFIGURE.rst b/multi_pms_properties/readme/CONFIGURE.rst new file mode 100644 index 000000000..f03561983 --- /dev/null +++ b/multi_pms_properties/readme/CONFIGURE.rst @@ -0,0 +1,7 @@ +Technical Module to Multi Property Manage System proposit. + +* Use the standar multicompany guidelines applied to pms.property: + + ``_check_pms_properties_auto like model attribute to autocheck on create/write`` + ``check_pms_properties like field attribute to check relational record properties consistence`` + ``This module not implement propety dependent fields`` diff --git a/multi_pms_properties/readme/CONTRIBUTORS.rst b/multi_pms_properties/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..07e8d7663 --- /dev/null +++ b/multi_pms_properties/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Dario Lodeiros +* Eric Antones diff --git a/multi_pms_properties/readme/DESCRIPTION.rst b/multi_pms_properties/readme/DESCRIPTION.rst new file mode 100644 index 000000000..010a01e13 --- /dev/null +++ b/multi_pms_properties/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Technical addon to support multipropety in pms diff --git a/multi_pms_properties/readme/INSTALL.rst b/multi_pms_properties/readme/INSTALL.rst new file mode 100644 index 000000000..b6b2f665f --- /dev/null +++ b/multi_pms_properties/readme/INSTALL.rst @@ -0,0 +1,7 @@ +To install this module, you only need to add it to your addons, and load it as +a server-wide module. + +This can be done with the ``server_wide_modules`` parameter in ``/etc/odoo.conf`` +or with the ``--load`` command-line parameter + +``server_wide_modules = "multi_pms_properties"`` diff --git a/multi_pms_properties/readme/USAGE.rst b/multi_pms_properties/readme/USAGE.rst new file mode 100644 index 000000000..7a9c21a38 --- /dev/null +++ b/multi_pms_properties/readme/USAGE.rst @@ -0,0 +1 @@ +To use this module, you need implement pms.property functionality diff --git a/multi_pms_properties/static/description/icon.png b/multi_pms_properties/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/multi_pms_properties/static/description/index.html b/multi_pms_properties/static/description/index.html new file mode 100644 index 000000000..ec3df3328 --- /dev/null +++ b/multi_pms_properties/static/description/index.html @@ -0,0 +1,446 @@ + + + + + + +multi_pms_properties + + + +
+

multi_pms_properties

+ + +

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

+

Technical addon to support multipropety in pms

+

Table of contents

+ +
+

Installation

+

To install this module, you only need to add it to your addons, and load it as +a server-wide module.

+

This can be done with the server_wide_modules parameter in /etc/odoo.conf +or with the --load command-line parameter

+

server_wide_modules = "multi_pms_properties"

+
+
+

Configuration

+

Technical Module to Multi Property Manage System proposit.

+
    +
  • Use the standar multicompany guidelines applied to pms.property:

    +

    _check_pms_properties_auto like model attribute to autocheck  on create/write +check_pms_properties like field attribute to check relational record properties consistence +This module not implement propety dependent fields

    +
  • +
+
+
+

Usage

+

To use this module, you need implement pms.property functionality

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

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

+

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

+
+
+
+ + diff --git a/multi_pms_properties/tests/__init__.py b/multi_pms_properties/tests/__init__.py new file mode 100644 index 000000000..c80a139bc --- /dev/null +++ b/multi_pms_properties/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021 Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import common +from . import test_multi_pms_properties diff --git a/multi_pms_properties/tests/common.py b/multi_pms_properties/tests/common.py new file mode 100644 index 000000000..d4e9e2183 --- /dev/null +++ b/multi_pms_properties/tests/common.py @@ -0,0 +1,20 @@ +# Copyright 2021 Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +def setup_test_model(env, model_clses): + for model_cls in model_clses: + model_cls._build_model(env.registry, env.cr) + + env.registry.setup_models(env.cr) + env.registry.init_models( + env.cr, + [model_cls._name for model_cls in model_clses], + dict(env.context, update_custom_fields=True), + ) + + +def teardown_test_model(env, model_clses): + for model_cls in model_clses: + del env.registry.models[model_cls._name] + env.registry.setup_models(env.cr) diff --git a/multi_pms_properties/tests/multi_pms_properties_tester.py b/multi_pms_properties/tests/multi_pms_properties_tester.py new file mode 100644 index 000000000..eabf61aa1 --- /dev/null +++ b/multi_pms_properties/tests/multi_pms_properties_tester.py @@ -0,0 +1,17 @@ +# Copyright 2021 Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ParentTester(models.Model): + _name = "pms.parent.tester" + + name = fields.Char(required=True) + + +class ChildTester(models.Model): + _name = "pms.child.tester" + + name = fields.Char(required=True) + parent_id = fields.Many2one("pms.parent.tester", check_pms_properties=True) diff --git a/multi_pms_properties/tests/test_multi_pms_properties.py b/multi_pms_properties/tests/test_multi_pms_properties.py new file mode 100644 index 000000000..10f81e022 --- /dev/null +++ b/multi_pms_properties/tests/test_multi_pms_properties.py @@ -0,0 +1,41 @@ +# Copyright 2021 Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo.tests import common + +from .common import setup_test_model # , teardown_test_model +from .multi_pms_properties_tester import ChildTester, ParentTester + +_logger = logging.getLogger(__name__) + + +@common.tagged("-at_install", "post_install") +class TestMultiPMSProperties(common.SavepointCase): + @classmethod + def setUpClass(cls): + super(TestMultiPMSProperties, cls).setUpClass() + model_classes = [ParentTester, ChildTester] + setup_test_model(cls.env, model_classes) + for mdl_cls in model_classes: + tester_model = cls.env["ir.model"].search([("model", "=", mdl_cls._name)]) + # Access record + cls.env["ir.model.access"].create( + { + "name": "access.%s" % mdl_cls._name, + "model_id": tester_model.id, + "perm_read": 1, + "perm_write": 1, + "perm_create": 1, + "perm_unlink": 1, + } + ) + + # @classmethod + # def tearDownClass(cls): + # teardown_test_model(cls.env, [ParentTester]) + # super(TestMultiPMSProperties, cls).tearDownClass() + + # def test_exist_attribute(self): + # parent = self.env["pms.parent.tester"].create({"name": "parent test"}) diff --git a/pms/__manifest__.py b/pms/__manifest__.py index 9a0036b5b..1268e03ba 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -23,6 +23,7 @@ # "partner_firstname", # "email_template_qweb", "sale", + "multi_pms_properties", ], "data": [ "security/pms_security.xml", diff --git a/pms/models/account_bank_statement.py b/pms/models/account_bank_statement.py index 5e14cfdfb..3eb3d9b35 100644 --- a/pms/models/account_bank_statement.py +++ b/pms/models/account_bank_statement.py @@ -5,3 +5,6 @@ class AccountBankStatement(models.Model): _inherit = "account.bank.statement" property_id = fields.Many2one("pms.property", string="Property", copy=False) + company_id = fields.Many2one( + check_pms_properties=True, + ) diff --git a/pms/models/account_journal.py b/pms/models/account_journal.py index d13ab724d..07121008f 100644 --- a/pms/models/account_journal.py +++ b/pms/models/account_journal.py @@ -1,21 +1,20 @@ -from odoo import _, api, fields, models -from odoo.exceptions import UserError +from odoo import fields, models class AccountJournal(models.Model): _inherit = "account.journal" - pms_property_ids = fields.Many2many("pms.property", string="Property", copy=False) - - @api.constrains("pms_property_ids", "company_id") - def _check_property_company_integrity(self): - for rec in self: - if rec.company_id and rec.pms_property_ids: - property_companies = rec.pms_property_ids.mapped("company_id") - if len(property_companies) > 1 or rec.company_id != property_companies: - raise UserError( - _( - "The company of the properties must match " - "the company on account journal" - ) - ) + pms_property_ids = fields.Many2many( + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + comodel_name="pms.property", + ondelete="restrict", + relation="account_journal_pms_property_rel", + column1="account_journal_id", + column2="pms_property_id", + check_pms_properties=True, + ) + company_id = fields.Many2one( + check_pms_properties=True, + ) diff --git a/pms/models/folio_sale_line.py b/pms/models/folio_sale_line.py index d21141587..ba2a78c61 100644 --- a/pms/models/folio_sale_line.py +++ b/pms/models/folio_sale_line.py @@ -557,6 +557,7 @@ class FolioSaleLine(models.Model): store=True, readonly=True, index=True, + check_pms_properties=True, ) folio_partner_id = fields.Many2one( related="folio_id.partner_id", store=True, string="Customer", readonly=False diff --git a/pms/models/payment_return.py b/pms/models/payment_return.py index c26d878d7..17f7d4d45 100644 --- a/pms/models/payment_return.py +++ b/pms/models/payment_return.py @@ -11,6 +11,9 @@ class PaymentReturn(models.Model): pms_property_id = fields.Many2one( "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" ) + company_id = fields.Many2one( + check_pms_properties=True, + ) # Business methods diff --git a/pms/models/pms_amenity.py b/pms/models/pms_amenity.py index e1d71cfec..1577b4738 100644 --- a/pms/models/pms_amenity.py +++ b/pms/models/pms_amenity.py @@ -1,13 +1,13 @@ # 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 -from odoo.exceptions import ValidationError +from odoo import fields, models class PmsRoomAmenity(models.Model): _name = "pms.amenity" _description = "Room amenity" + _check_pms_properties_auto = True active = fields.Boolean( string="Active", @@ -25,28 +25,18 @@ class PmsRoomAmenity(models.Model): help="Properties with access to the element;" " if not set, all properties can access", comodel_name="pms.property", + ondelete="restrict", relation="pms_amenity_pms_property_rel", column1="amenity_type_id", column2="pms_property_id", + check_pms_properties=True, ) pms_amenity_type_id = fields.Many2one( string="Amenity Category", help="Segment the amenities by categories (multimedia, comfort, etc ...)", comodel_name="pms.amenity.type", - domain="['|', ('pms_property_ids', '=', False),('pms_property_ids', 'in', " - "pms_property_ids)]", + check_pms_properties=True, ) default_code = fields.Char( string="Internal Reference", help="Internal unique identifier of the amenity" ) - - @api.constrains( - "pms_amenity_type_id", - "pms_property_ids", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_amenity_type_id and rec.pms_amenity_type_id.pms_property_ids: - res = rec.pms_property_ids - rec.pms_amenity_type_id.pms_property_ids - if res: - raise ValidationError(_("Property not allowed")) diff --git a/pms/models/pms_amenity_type.py b/pms/models/pms_amenity_type.py index 7af7d1bae..ec982d55d 100644 --- a/pms/models/pms_amenity_type.py +++ b/pms/models/pms_amenity_type.py @@ -1,13 +1,13 @@ # 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 -from odoo.exceptions import ValidationError +from odoo import fields, models class PmsRoomAmenityType(models.Model): _name = "pms.amenity.type" _description = "Amenity Type" + _check_pms_properties_auto = True active = fields.Boolean( string="Active", @@ -25,24 +25,16 @@ class PmsRoomAmenityType(models.Model): help="Properties with access to the element;" " if not set, all properties can access", comodel_name="pms.property", + ondelete="restrict", relation="pms_amenity_type_pms_property_rel", column1="amenity_type_id", column2="pms_property_id", + check_pms_properties=True, ) pms_amenity_ids = fields.One2many( string="Amenities In This Category", help="Amenities included in this type", comodel_name="pms.amenity", inverse_name="pms_amenity_type_id", + check_pms_properties=True, ) - - @api.constrains( - "pms_property_ids", - "pms_amenity_ids", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_property_ids: - res = rec.pms_amenity_ids.pms_property_ids - rec.pms_property_ids - if res: - raise ValidationError(_("Property not allowed")) diff --git a/pms/models/pms_availability.py b/pms/models/pms_availability.py index 4095e6da7..31450f48d 100644 --- a/pms/models/pms_availability.py +++ b/pms/models/pms_availability.py @@ -7,6 +7,7 @@ from odoo.exceptions import ValidationError class PmsAvailability(models.Model): _name = "pms.availability" _description = "Room type availability per day" + _check_pms_properties_auto = True room_type_id = fields.Many2one( string="Room Type", @@ -15,6 +16,7 @@ class PmsAvailability(models.Model): required=True, comodel_name="pms.room.type", ondelete="cascade", + check_pms_properties=True, ) date = fields.Date( string="Date", @@ -37,6 +39,7 @@ class PmsAvailability(models.Model): readonly=True, comodel_name="pms.reservation.line", inverse_name="avail_id", + check_pms_properties=True, ) real_avail = fields.Integer( string="Real Avail", diff --git a/pms/models/pms_availability_plan.py b/pms/models/pms_availability_plan.py index dd218309e..21c5e677d 100644 --- a/pms/models/pms_availability_plan.py +++ b/pms/models/pms_availability_plan.py @@ -13,6 +13,7 @@ class PmsAvailabilityPlan(models.Model): _name = "pms.availability.plan" _description = "Reservation availability plan" + _check_pms_properties_auto = True @api.model def _get_default_pms_property(self): @@ -26,15 +27,18 @@ class PmsAvailabilityPlan(models.Model): help="Properties with access to the element;" " if not set, all properties can access", comodel_name="pms.property", + ondelete="restrict", relation="pms_availability_plan_pms_property_rel", column1="availability_plan_id", column2="pms_property_id", + check_pms_properties=True, ) pms_pricelist_ids = fields.One2many( string="Pricelists", help="Pricelists of the availability plan ", comodel_name="product.pricelist", inverse_name="availability_plan_id", + check_pms_properties=True, ) rule_ids = fields.One2many( @@ -42,6 +46,7 @@ class PmsAvailabilityPlan(models.Model): help="Rules in a availability plan", comodel_name="pms.availability.plan.rule", inverse_name="availability_plan_id", + check_pms_properties=True, ) active = fields.Boolean( @@ -51,76 +56,6 @@ class PmsAvailabilityPlan(models.Model): default=True, ) - @api.model - def get_count_rooms_available( - self, - checkin, - checkout, - room_type_id, - pms_property_id, - current_lines=False, - pricelist_id=False, - ): - if current_lines and not isinstance(current_lines, list): - current_lines = [current_lines] - - avail = self.get_count_real_free_rooms( - checkin, checkout, room_type_id, pms_property_id, current_lines - ) - domain_rules = [ - ("date", ">=", checkin), - ( - "date", - "<=", - checkout, - ), # TODO: only closed_departure take account checkout date! - ("room_type_id", "=", room_type_id), - ("pms_property_id", "=", pms_property_id), - ] - if pricelist_id: - pricelist = self.env["product.pricelist"].browse(pricelist_id) - if pricelist and pricelist.availability_plan_id: - domain_rules.append( - ("availability_plan_id", "=", pricelist.availability_plan_id.id) - ) - rule_items = self.env["pms.availability.plan.rule"].search(domain_rules) - if len(rule_items) > 0: - for item in rule_items: - if self.any_rule_applies(checkin, checkout, item): - return 0 - avail = min(rule_items.mapped("plan_avail")) - return avail - - def get_count_real_free_rooms( - self, - checkin, - checkout, - room_type_id, - pms_property_id, - current_lines=False, - ): - Avail = self.env["pms.availability"] - count_free_rooms = len(self.env["pms.room.type"].browse(room_type_id).room_ids) - if isinstance(checkin, str): - checkin = datetime.datetime.strptime( - checkin, DEFAULT_SERVER_DATE_FORMAT - ).date() - if isinstance(checkout, str): - checkout = datetime.datetime.strptime( - checkout, DEFAULT_SERVER_DATE_FORMAT - ).date() - for avail in Avail.search( - [ - ("date", ">=", checkin), - ("date", "<=", checkout - datetime.timedelta(1)), - ("room_type_id", "=", room_type_id), - ("pms_property_id", "=", pms_property_id), - ] - ): - if avail.real_avail < count_free_rooms: - count_free_rooms = avail.real_avail - return count_free_rooms - @classmethod def any_rule_applies(cls, checkin, checkout, item): reservation_len = (checkout - checkin).days @@ -224,6 +159,80 @@ class PmsAvailabilityPlan(models.Model): domain_rooms.append(("room_type_id", "=", room_type_id)) return self.env["pms.room"].search(domain_rooms) + @api.model + def get_count_rooms_available( + self, + checkin, + checkout, + room_type_id, + pms_property_id, + current_lines=False, + pricelist_id=False, + ): + if current_lines and not isinstance(current_lines, list): + current_lines = [current_lines] + + avail = self.get_count_real_free_rooms( + checkin, checkout, room_type_id, pms_property_id, current_lines + ) + domain_rules = [ + ("date", ">=", checkin), + ( + "date", + "<=", + checkout, + ), # TODO: only closed_departure take account checkout date! + ("room_type_id", "=", room_type_id), + ("pms_property_id", "=", pms_property_id), + ] + if pricelist_id: + pricelist = self.env["product.pricelist"].browse(pricelist_id) + if pricelist and pricelist.availability_plan_id: + domain_rules.append( + ("availability_plan_id", "=", pricelist.availability_plan_id.id) + ) + rule_items = self.env["pms.availability.plan.rule"].search(domain_rules) + if len(rule_items) > 0: + for item in rule_items: + if self.any_rule_applies(checkin, checkout, item): + return 0 + avail = min(rule_items.mapped("plan_avail")) + return avail + + def get_count_real_free_rooms( + self, + checkin, + checkout, + room_type_id, + pms_property_id, + current_lines=False, + ): + Avail = self.env["pms.availability"] + count_free_rooms = len( + self.env["pms.room.type"] + .browse(room_type_id) + .room_ids.filtered(lambda r: r.pms_property_id.id == pms_property_id) + ) + if isinstance(checkin, str): + checkin = datetime.datetime.strptime( + checkin, DEFAULT_SERVER_DATE_FORMAT + ).date() + if isinstance(checkout, str): + checkout = datetime.datetime.strptime( + checkout, DEFAULT_SERVER_DATE_FORMAT + ).date() + for avail in Avail.search( + [ + ("date", ">=", checkin), + ("date", "<=", checkout - datetime.timedelta(1)), + ("room_type_id", "=", room_type_id), + ("pms_property_id", "=", pms_property_id), + ] + ): + if avail.real_avail < count_free_rooms: + count_free_rooms = avail.real_avail + return count_free_rooms + @api.model def splitted_availability( self, diff --git a/pms/models/pms_availability_plan_rule.py b/pms/models/pms_availability_plan_rule.py index 90c2b75f2..e2408f533 100644 --- a/pms/models/pms_availability_plan_rule.py +++ b/pms/models/pms_availability_plan_rule.py @@ -7,6 +7,7 @@ from odoo.exceptions import ValidationError class PmsAvailabilityPlanRule(models.Model): _name = "pms.availability.plan.rule" _description = "Reservation rule by day" + _check_pms_properties_auto = True availability_plan_id = fields.Many2one( string="Availability Plan", @@ -14,6 +15,7 @@ class PmsAvailabilityPlanRule(models.Model): index=True, comodel_name="pms.availability.plan", ondelete="cascade", + check_pms_properties=True, ) room_type_id = fields.Many2one( string="Room Type", @@ -21,6 +23,7 @@ class PmsAvailabilityPlanRule(models.Model): required=True, comodel_name="pms.room.type", ondelete="cascade", + check_pms_properties=True, ) date = fields.Date( string="Date", @@ -83,17 +86,6 @@ class PmsAvailabilityPlanRule(models.Model): required=True, comodel_name="pms.property", ) - allowed_property_ids = fields.Many2many( - string="Allowed Properties", - help="Allowed properties for user", - store=True, - readonly=True, - compute="_compute_allowed_property_ids", - comodel_name="pms.property", - relation="allowed_availability_move_rel", - column1="availability_plan_rule_id", - column2="property_id", - ) avail_id = fields.Many2one( string="Avail record", comodel_name="pms.availability", @@ -101,6 +93,7 @@ class PmsAvailabilityPlanRule(models.Model): store=True, readonly=False, ondelete="restrict", + check_pms_properties=True, ) real_avail = fields.Integer( string="Real availability", @@ -171,59 +164,6 @@ class PmsAvailabilityPlanRule(models.Model): if not record.max_avail: record.max_avail = record.room_type_id.default_max_avail - @api.depends( - "availability_plan_id.pms_property_ids", "room_type_id.pms_property_ids" - ) - def _compute_allowed_property_ids(self): - - for rule in self: - properties = [] - - if not ( - rule.availability_plan_id.pms_property_ids - or rule.room_type_id.pms_property_ids - ): - rule.allowed_property_ids = False - else: - if rule.availability_plan_id.pms_property_ids: - if rule.room_type_id.pms_property_ids: - for prp in rule.availability_plan_id.pms_property_ids: - if prp in rule.room_type_id.pms_property_ids: - properties.append(prp) - rule.allowed_property_ids = [ - (4, prop.id) for prop in properties - ] - else: - rule.allowed_property_ids = ( - rule.availability_plan_id.pms_property_ids - ) - else: - rule.allowed_property_ids = rule.room_type_id.pms_property_ids - - @api.constrains( - "allowed_property_ids", - "pms_property_id", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_property_id and rec.allowed_property_ids: - if rec.pms_property_id.id not in rec.allowed_property_ids.ids: - raise ValidationError(_("Property not allowed")) - - # @api.constrains( - # "allowed_property_ids", - # "pms_property_ids", - # ) - # def _check_property_integrity(self): - # for rule in self: - # for p in rule.pms_property_ids: - # allowed = list( - # set(rule.room_type_id.pms_property_ids.ids) - # & - # set(rule.availability_plan_id.pms_property_ids.ids)) - # if p.id not in allowed: - # raise ValidationError(_("Property not allowed")) - @api.constrains("min_stay", "min_stay_arrival", "max_stay", "max_stay_arrival") def _check_min_max_stay(self): for record in self: diff --git a/pms/models/pms_board_service.py b/pms/models/pms_board_service.py index c05b1ee23..2287dc0ca 100644 --- a/pms/models/pms_board_service.py +++ b/pms/models/pms_board_service.py @@ -7,6 +7,7 @@ from odoo.exceptions import ValidationError class PmsBoardService(models.Model): _name = "pms.board.service" _description = "Board Services" + _check_pms_properties_auto = True name = fields.Char( string="Board Service Name", @@ -32,10 +33,12 @@ class PmsBoardService(models.Model): help="Properties with access to the element;" " if not set, all properties can access", required=False, + ondelete="restrict", comodel_name="pms.property", relation="pms_board_service_pms_property_rel", column1="board_service_id", column2="pms_property_id", + check_pms_properties=True, ) pms_board_service_room_type_ids = fields.One2many( string="Board Services Room Type", diff --git a/pms/models/pms_board_service_line.py b/pms/models/pms_board_service_line.py index 93f515fc5..9be0d7d3c 100644 --- a/pms/models/pms_board_service_line.py +++ b/pms/models/pms_board_service_line.py @@ -1,12 +1,12 @@ # 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 ValidationError +from odoo import api, fields, models class PmsBoardServiceLine(models.Model): _name = "pms.board.service.line" _description = "Services on Board Service included" + _check_pms_properties_auto = True pms_board_service_id = fields.Many2one( string="Board Service", @@ -14,19 +14,25 @@ class PmsBoardServiceLine(models.Model): required=True, comodel_name="pms.board.service", ondelete="cascade", + check_pms_properties=True, ) product_id = fields.Many2one( string="Product", help="Product associated with this board service line", required=True, comodel_name="product.product", + check_pms_properties=True, ) pms_property_ids = fields.Many2many( string="Properties", help="Properties with access to the element;" " if not set, all properties can access", comodel_name="pms.property", - related="pms_board_service_id.pms_property_ids", + relation="pms_board_service_line_pms_property_rel", + column1="pms_board_service_line_id", + column2="pms_property_id", + store=True, + check_pms_properties=True, ) amount = fields.Float( string="Amount", @@ -44,10 +50,33 @@ class PmsBoardServiceLine(models.Model): if self.product_id: self.update({"amount": self.product_id.list_price}) - @api.constrains("pms_property_ids", "product_id") - def _check_property_integrity(self): - for record in self: - if record.pms_property_ids and record.product_id.pms_property_ids: - for pms_property in record.pms_property_ids: - if pms_property not in record.product_id.pms_property_ids: - raise ValidationError(_("Property not allowed in product")) + @api.model + def create(self, vals): + properties = False + if "pms_board_service_id" in vals: + board_service = self.env["pms.board.service"].browse( + vals["pms_board_service_id"] + ) + properties = board_service.pms_property_ids + if properties: + vals.update( + { + "pms_property_ids": properties, + } + ) + return super(PmsBoardServiceLine, self).create(vals) + + def write(self, vals): + properties = False + if "pms_board_service_id" in vals: + board_service = self.env["pms.board.service"].browse( + vals["pms_board_service_id"] + ) + properties = board_service.pms_property_ids + if properties: + vals.update( + { + "pms_property_ids": properties, + } + ) + return super(PmsBoardServiceLine, self).write(vals) diff --git a/pms/models/pms_board_service_room_type.py b/pms/models/pms_board_service_room_type.py index 6323297da..33aeb7263 100644 --- a/pms/models/pms_board_service_room_type.py +++ b/pms/models/pms_board_service_room_type.py @@ -10,6 +10,7 @@ class PmsBoardServiceRoomType(models.Model): _rec_name = "pms_board_service_id" _log_access = False _description = "Board Service included in Room" + _check_pms_properties_auto = True pms_board_service_id = fields.Many2one( string="Board Service", @@ -18,14 +19,19 @@ class PmsBoardServiceRoomType(models.Model): index=True, comodel_name="pms.board.service", ondelete="cascade", + check_pms_properties=True, ) pms_property_ids = fields.Many2many( string="Properties", - help="Properties with access to the element; " - "if not set, all properties can access", + help="Properties with access to the element;" + " if not set, all properties can access", required=False, - comodel_name="pms.property", ondelete="restrict", + comodel_name="pms.property", + relation="pms_board_service_room_type_pms_property_rel", + column1="pms_board_service_room_type_id", + column2="pms_property_id", + check_pms_properties=True, ) pms_room_type_id = fields.Many2one( string="Room Type", @@ -33,18 +39,15 @@ class PmsBoardServiceRoomType(models.Model): required=True, index=True, comodel_name="pms.room.type", - domain=[ - "|", - ("pms_property_ids", "=", False), - ("pms_property_ids", "in", pms_property_ids), - ], ondelete="cascade", + check_pms_properties=True, ) board_service_line_ids = fields.One2many( string="Board Service Lines", help="Services included in this Board Service", comodel_name="pms.board.service.room.type.line", inverse_name="pms_board_service_room_type_id", + required=True, ) amount = fields.Float( string="Amount", @@ -104,10 +107,28 @@ class PmsBoardServiceRoomType(models.Model): @api.model def create(self, vals): + properties = False if "pms_board_service_id" in vals: vals.update( self.prepare_board_service_reservation_ids(vals["pms_board_service_id"]) ) + board_service = self.env["pms.board.service"].browse( + vals["pms_board_service_id"] + ) + properties = board_service.pms_property_ids + if "pms_room_type_id" in vals: + room_type = self.env["pms.room.type"].browse(vals["pms_room_type_id"]) + properties = ( + properties + room_type.pms_property_ids + if properties + else room_type.pms_property_ids + ) + if properties: + vals.update( + { + "pms_property_ids": properties, + } + ) return super(PmsBoardServiceRoomType, self).create(vals) def write(self, vals): diff --git a/pms/models/pms_board_service_room_type_line.py b/pms/models/pms_board_service_room_type_line.py index c59bf9851..fb5038190 100644 --- a/pms/models/pms_board_service_room_type_line.py +++ b/pms/models/pms_board_service_room_type_line.py @@ -1,12 +1,12 @@ # 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 ValidationError +from odoo import api, fields, models class PmsBoardServiceRoomTypeLine(models.Model): _name = "pms.board.service.room.type.line" _description = "Services on Board Service included in Room" + _check_pms_properties_auto = True # Fields declaration pms_board_service_room_type_id = fields.Many2one( @@ -16,12 +16,22 @@ class PmsBoardServiceRoomTypeLine(models.Model): comodel_name="pms.board.service.room.type", ondelete="cascade", ) + pms_property_ids = fields.Many2many( + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + comodel_name="pms.property", + relation="pms_board_service_room_type_line_pms_property_rel", + column1="pms_board_service_room_type_id", + column2="pms_property_id", + check_pms_properties=True, + ) product_id = fields.Many2one( string="Product", help="Product associated with this board service room type line", - readonly=True, - required=True, comodel_name="product.product", + readonly=True, + check_pms_properties=True, ) # TODO def default_amount "amount of service" amount = fields.Float( @@ -31,15 +41,33 @@ class PmsBoardServiceRoomTypeLine(models.Model): digits=("Product Price"), ) - @api.constrains("pms_board_service_room_type_id", "product_id") - def _check_property_integrity(self): - for record in self: - if ( - record.pms_board_service_room_type_id.pms_property_ids - and record.product_id.pms_property_ids - ): - for ( - pms_property - ) in record.pms_board_service_room_type_id.pms_property_ids: - if pms_property not in record.product_id.pms_property_ids: - raise ValidationError(_("Property not allowed")) + @api.model + def create(self, vals): + properties = False + if "pms_board_service_room_type_id" in vals: + board_service = self.env["pms.board.service.room.type"].browse( + vals["pms_board_service_room_type_id"] + ) + properties = board_service.pms_property_ids + if properties: + vals.update( + { + "pms_property_ids": properties, + } + ) + return super(PmsBoardServiceRoomTypeLine, self).create(vals) + + def write(self, vals): + properties = False + if "pms_board_service_room_type_id" in vals: + board_service = self.env["pms.board.service.room.type"].browse( + vals["pms_board_service_room_type_id"] + ) + properties = board_service.pms_property_ids + if properties: + vals.update( + { + "pms_property_ids": properties, + } + ) + return super(PmsBoardServiceRoomTypeLine, self).write(vals) diff --git a/pms/models/pms_cancelation_rule.py b/pms/models/pms_cancelation_rule.py index ccfae8a6b..fc1c559e8 100644 --- a/pms/models/pms_cancelation_rule.py +++ b/pms/models/pms_cancelation_rule.py @@ -8,14 +8,27 @@ from odoo import fields, models class PmsCancelationRule(models.Model): _name = "pms.cancelation.rule" _description = "Cancelation Rules" + _check_pms_properties_auto = True # Fields declaration name = fields.Char(string="Cancelation Rule", 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", + check_pms_properties=True, ) pms_property_ids = fields.Many2many( - "pms.property", string="Properties", required=False, ondelete="restrict" + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + required=False, + ondelete="restrict", + comodel_name="pms.property", + relation="pms_cancelation_rule_pms_property_rel", + column1="pms_cancelation_rule_id", + column2="pms_property_id", + check_pms_properties=True, ) active = fields.Boolean("Active", default=True) days_intime = fields.Integer( @@ -35,5 +48,3 @@ class PmsCancelationRule(models.Model): 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 285c9099b..09baf4009 100644 --- a/pms/models/pms_checkin_partner.py +++ b/pms/models/pms_checkin_partner.py @@ -13,6 +13,7 @@ class PmsCheckinPartner(models.Model): _name = "pms.checkin.partner" _description = "Partner Checkins" _rec_name = "identifier" + _check_pms_properties_auto = True # Fields declaration identifier = fields.Char( @@ -22,11 +23,15 @@ class PmsCheckinPartner(models.Model): "res.partner", domain="[('is_company', '=', False)]", ) - reservation_id = fields.Many2one("pms.reservation") + reservation_id = fields.Many2one( + "pms.reservation", + check_pms_properties=True, + ) folio_id = fields.Many2one( "pms.folio", compute="_compute_folio_id", store=True, + check_pms_properties=True, ) pms_property_id = fields.Many2one( "pms.property", store=True, readonly=True, related="folio_id.pms_property_id" diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 405750e7a..d2696f839 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -18,6 +18,7 @@ class PmsFolio(models.Model): _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] _order = "date_order" _check_company_auto = True + _check_pms_properties_auto = True # Default Methods ang Gets def name_get(self): @@ -54,6 +55,7 @@ class PmsFolio(models.Model): ondelete="restrict", store=True, readonly=False, + check_pms_properties=True, ) reservation_ids = fields.One2many( "pms.reservation", @@ -62,6 +64,7 @@ class PmsFolio(models.Model): states={"done": [("readonly", True)]}, help="Room reservation detail.", check_company=True, + check_pms_properties=True, ) number_of_rooms = fields.Integer( "Number of Rooms", @@ -69,7 +72,7 @@ class PmsFolio(models.Model): store="True", ) number_of_services = fields.Integer( - "Number of Rooms", + "Number of Services", compute="_compute_number_of_services", store="True", ) @@ -81,9 +84,7 @@ class PmsFolio(models.Model): help="Services detail provide to customer and it will " "include in main Invoice.", check_company=True, - domain="['|'," - "('pms_property_id','=',pms_property_id)," - "('pms_property_id','=',False)]", + check_pms_properties=True, ) sale_line_ids = fields.One2many( "folio.sale.line", @@ -98,8 +99,8 @@ class PmsFolio(models.Model): company_id = fields.Many2one( "res.company", "Company", - required=True, - default=lambda self: self.env.company, + compute="_compute_company_id", + store=True, ) move_line_ids = fields.Many2many( "account.move.line", @@ -132,9 +133,7 @@ class PmsFolio(models.Model): store=True, readonly=False, help="Pricelist for current folio.", - domain="['|'," - "(pms_property_id, 'in', 'pms_property_ids')," - "('pms_property_ids','=',False)]", + check_pms_properties=True, ) commission = fields.Float( string="Commission", @@ -158,6 +157,7 @@ class PmsFolio(models.Model): string="Agency", ondelete="restrict", domain=[("is_agency", "=", True)], + check_pms_properties=True, ) channel_type_id = fields.Many2one( "pms.sale.channel", @@ -167,6 +167,7 @@ class PmsFolio(models.Model): string="Direct Sale Channel", ondelete="restrict", domain=[("channel_type", "=", "direct")], + check_pms_properties=True, ) transaction_ids = fields.Many2many( "payment.transaction", @@ -236,6 +237,7 @@ class PmsFolio(models.Model): store=True, readonly=False, help="Invoice address for current group.", + check_pms_properties=True, ) # REVIEW THIS # partner_invoice_state_id = fields.Many2one(related="partner_invoice_id.state_id") @@ -247,9 +249,7 @@ class PmsFolio(models.Model): ) closure_reason_id = fields.Many2one( "room.closure.reason", - domain="['|'," - "(pms_property_id, 'in', 'pms_property_ids')," - "('pms_property_ids', '=', False)]", + check_pms_properties=True, ) segmentation_ids = fields.Many2many( "res.partner.category", string="Segmentation", ondelete="restrict" @@ -472,6 +472,11 @@ class PmsFolio(models.Model): for fsl in folio_sale_lines_to_remove: self.env["folio.sale.line"].browse(fsl).unlink() + @api.depends("pms_property_id") + def _compute_company_id(self): + for record in self: + record.company_id = record.pms_property_id.company_id + @api.model def generate_reservation_services_sale_lines(self, folio, reservation): for service in reservation.service_ids: @@ -1396,18 +1401,6 @@ class PmsFolio(models.Model): _("The Sale Channel does not correspond to the agency's") ) - @api.constrains( - "closure_reason_id", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_property_id: - if ( - rec.pms_property_id.id - not in rec.closure_reason_id.pms_property_ids.ids - ): - raise ValidationError(_("Property not allowed")) - @api.model def _prepare_down_payment_section_line(self, **optional_values): """ diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index 1bfd6f306..36f7c4257 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -30,6 +30,7 @@ class PmsProperty(models.Model): help="The company that owns or operates this property.", comodel_name="res.company", required=True, + check_pms_properties=True, ) user_ids = fields.Many2many( string="Accepted Users", @@ -45,7 +46,6 @@ class PmsProperty(models.Model): comodel_name="pms.room", inverse_name="pms_property_id", ) - # TODO: establecer tarifa publica por defecto default_pricelist_id = fields.Many2one( string="Product Pricelist", help="The default pricelist used in this property.", diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 8109593ae..bb5c900ce 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -16,7 +16,9 @@ class PmsReservation(models.Model): _description = "Reservation" _inherit = ["mail.thread", "mail.activity.mixin", "portal.mixin"] _order = "priority desc, create_date desc, write_date desc" - + # TODO: + # consider near_to_checkin & pending_notifications to order + _check_pms_properties_auto = True _check_company_auto = True name = fields.Text( @@ -37,9 +39,13 @@ class PmsReservation(models.Model): "empty if reservation is splitted", copy=False, comodel_name="pms.room", - domain="[('id', 'in', allowed_room_ids)]", ondelete="restrict", + domain="[" + "('id', 'in', allowed_room_ids)," + "('pms_property_id', '=', pms_property_id)," + "]", tracking=True, + check_pms_properties=True, ) allowed_room_ids = fields.Many2many( string="Allowed Rooms", @@ -68,12 +74,9 @@ class PmsReservation(models.Model): readonly=False, store=True, comodel_name="pms.board.service.room.type", - domain="[" - "'|'," - "('pms_property_ids', 'in', pms_property_id)," - "('pms_property_ids', '=', False)]", compute="_compute_board_service_room_id", tracking=True, + check_pms_properties=True, ) room_type_id = fields.Many2one( string="Room Type", @@ -84,11 +87,9 @@ class PmsReservation(models.Model): copy=False, store=True, comodel_name="pms.room.type", - domain="['|'," - "('pms_property_ids', 'in', pms_property_id)," - "('pms_property_ids', '=', False)]", compute="_compute_room_type_id", tracking=True, + check_pms_properties=True, ) partner_id = fields.Many2one( string="Customer", @@ -99,6 +100,7 @@ class PmsReservation(models.Model): ondelete="restrict", compute="_compute_partner_id", tracking=True, + check_pms_properties=True, ) agency_id = fields.Many2one( string="Agency", @@ -120,9 +122,7 @@ class PmsReservation(models.Model): string="Closure Reason", help="Reason why the reservation cannot be made", related="folio_id.closure_reason_id", - domain="['|'," - "(pms_property_id, 'in', 'pms_property_ids')," - "('pms_property_ids', '=', False)]", + check_pms_properties=True, ) company_id = fields.Many2one( string="Company", @@ -150,6 +150,7 @@ class PmsReservation(models.Model): compute="_compute_reservation_line_ids", comodel_name="pms.reservation.line", inverse_name="reservation_id", + check_pms_properties=True, ) service_ids = fields.One2many( string="Services", @@ -158,11 +159,9 @@ class PmsReservation(models.Model): store=True, comodel_name="pms.service", inverse_name="reservation_id", - domain="['|'," - "('pms_property_id', '=', pms_property_id)," - "('pms_property_id', '=', False)]", compute="_compute_service_ids", check_company=True, + check_pms_properties=True, ) pricelist_id = fields.Many2one( string="Pricelist", @@ -170,12 +169,10 @@ class PmsReservation(models.Model): readonly=False, store=True, comodel_name="product.pricelist", - domain="['|'," - "('pms_property_ids', 'in', pms_property_id)," - "('pms_property_ids', '=', False)]", ondelete="restrict", compute="_compute_pricelist_id", tracking=True, + check_pms_properties=True, ) user_id = fields.Many2one( string="Salesperson", @@ -216,6 +213,7 @@ class PmsReservation(models.Model): compute="_compute_checkin_partner_ids", comodel_name="pms.checkin.partner", inverse_name="reservation_id", + check_pms_properties=True, ) count_pending_arrival = fields.Integer( string="Pending Arrival", @@ -278,7 +276,7 @@ class PmsReservation(models.Model): ) currency_id = fields.Many2one( string="Currency", - hepl="The currency used in relation to the pricelist", + help="The currency used in relation to the pricelist", readonly=True, store=True, related="pricelist_id.currency_id", @@ -1292,44 +1290,7 @@ class PmsReservation(models.Model): if record.agency_id and not record.agency_id.is_agency: raise ValidationError(_("booking agency with wrong configuration: ")) - @api.constrains("pms_property_id", "preferred_room_id") - def _check_room_property_integrity(self): - for record in self: - if record.pms_property_id and record.preferred_room_id.pms_property_id: - if record.pms_property_id != record.preferred_room_id.pms_property_id: - raise ValidationError( - _("Property doesn't match with room property") - ) - - @api.constrains("pms_property_id", "room_type_id") - def _check_room_type_property_integrity(self): - for record in self: - if record.pms_property_id and record.room_type_id.pms_property_ids: - if ( - record.pms_property_id.id - not in record.room_type_id.pms_property_ids.ids - ): - raise ValidationError(_("Property isn't allowed in Room Type")) - - @api.constrains("pms_property_id", "pricelist_id") - def _check_pricelist_property_integrity(self): - for record in self: - if record.pms_property_id and record.pricelist_id.pms_property_ids: - if ( - record.pms_property_id.id - not in record.pricelist_id.pms_property_ids.ids - ): - raise ValidationError(_("Property isn't allowed in Pricelist")) - - @api.constrains("pms_property_id", "board_service_room_id") - def _check_board_service_property_integrity(self): - for record in self: - if record.pms_property_id and record.board_service_room_id.pms_property_ids: - if ( - record.pms_property_id.id - not in record.board_service_room_id.pms_property_ids.ids - ): - raise ValidationError(_("Property isn't allowed in Board Service")) + # Action methods def open_folio(self): action = self.env.ref("pms.open_pms_folio1_form_tree_all").sudo().read()[0] diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index 1ee2b874d..7ac6cda6e 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -14,6 +14,7 @@ class PmsReservationLine(models.Model): _name = "pms.reservation.line" _description = "Reservations by day" _order = "date" + _check_company_auto = True reservation_id = fields.Many2one( string="Reservation", @@ -22,6 +23,7 @@ class PmsReservationLine(models.Model): copy=False, comodel_name="pms.reservation", ondelete="cascade", + check_pms_properties=True, ) room_id = fields.Many2one( string="Room", @@ -31,6 +33,7 @@ class PmsReservationLine(models.Model): compute="_compute_room_id", comodel_name="pms.room", ondelete="restrict", + check_pms_properties=True, ) sale_line_ids = fields.Many2many( @@ -41,6 +44,7 @@ class PmsReservationLine(models.Model): relation="reservation_line_sale_line_rel", column1="reservation_line_id", column2="sale_line_id", + check_pms_properties=True, ) pms_property_id = fields.Many2one( string="Property", @@ -84,6 +88,7 @@ class PmsReservationLine(models.Model): comodel_name="pms.availability", ondelete="restrict", compute="_compute_avail_id", + check_pms_properties=True, ) discount = fields.Float( diff --git a/pms/models/pms_room.py b/pms/models/pms_room.py index a4d6c5938..5511930cc 100644 --- a/pms/models/pms_room.py +++ b/pms/models/pms_room.py @@ -15,6 +15,7 @@ class PmsRoom(models.Model): _name = "pms.room" _description = "Property Room" _order = "sequence, room_type_id, name" + _check_pms_properties_auto = True name = fields.Char( string="Room Name", @@ -44,14 +45,10 @@ class PmsRoom(models.Model): help="Unique room type for the rooms", required=True, comodel_name="pms.room.type", - domain=[ - "|", - ("pms_property_ids", "=", False), - (pms_property_id, "in", "pms_property_ids"), - ], ondelete="restrict", + check_pms_properties=True, ) - # TODO: Dario, design shared rooms + # TODO: design shared rooms shared_room_id = fields.Many2one( string="Shared Room", help="The room can be sold by beds", @@ -62,11 +59,7 @@ class PmsRoom(models.Model): string="Ubication", help="At which ubication the room is located.", comodel_name="pms.ubication", - domain=[ - "|", - ("pms_property_ids", "=", False), - (pms_property_id, "in", "pms_property_ids"), - ], + check_pms_properties=True, ) capacity = fields.Integer( string="Capacity", help="The maximum number of people that can occupy a room" @@ -85,18 +78,6 @@ class PmsRoom(models.Model): translate=True, ) - allowed_property_ids = fields.Many2many( - string="Allowed Properties", - help="Allowed properties for rooms", - store=True, - readonly=True, - compute="_compute_allowed_property_ids", - comodel_name="pms.property", - relation="room_property_rel", - column1="room_id", - column2="property_id", - ) - _sql_constraints = [ ( "room_property_unique", @@ -115,30 +96,7 @@ class PmsRoom(models.Model): result.append((room.id, name)) return result - @api.depends( - "room_type_id", - "room_type_id.pms_property_ids", - "ubication_id", - "ubication_id.pms_property_ids", - ) - # TODO: Dario, revisar flujo de allowed properties - def _compute_allowed_property_ids(self): - for record in self: - if not ( - record.room_type_id.pms_property_ids - or record.ubication_id.pms_property_ids - ): - record.allowed_property_ids = self.env["pms.property"].search([]) - elif not record.room_type_id.pms_property_ids: - record.allowed_property_ids = record.ubication_id.pms_property_ids - elif not record.ubication_id.pms_property_ids: - record.allowed_property_ids = record.room_type_id.pms_property_ids - else: - record.allowed_property_ids = ( - record.room_type_id.pms_property_ids - & record.ubication_id.pms_property_ids - ) - + # Constraints and onchanges @api.constrains("capacity") def _check_capacity(self): for record in self: @@ -150,17 +108,7 @@ class PmsRoom(models.Model): ) ) - @api.constrains( - "allowed_property_ids", - "pms_property_id", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_property_id: - if rec.pms_property_id.id not in rec.allowed_property_ids.ids: - raise ValidationError( - _("Property not allowed in room type or in ubication") - ) + # Business methods def get_capacity(self, extra_bed=0): for record in self: diff --git a/pms/models/pms_room_closure_reason.py b/pms/models/pms_room_closure_reason.py index a1135c623..9d96c53b9 100644 --- a/pms/models/pms_room_closure_reason.py +++ b/pms/models/pms_room_closure_reason.py @@ -6,6 +6,7 @@ from odoo import fields, models class RoomClosureReason(models.Model): _name = "room.closure.reason" _description = "Cause of out of service" + _check_pms_properties_auto = True name = fields.Char( string="Name", @@ -22,6 +23,7 @@ class RoomClosureReason(models.Model): column1="room_closure_reason_type_id", column2="pms_property_id", ondelete="restrict", + check_pms_properties=True, ) description = fields.Text( string="Description", diff --git a/pms/models/pms_room_type.py b/pms/models/pms_room_type.py index 6a25c9cd7..ea627f8e9 100644 --- a/pms/models/pms_room_type.py +++ b/pms/models/pms_room_type.py @@ -17,6 +17,7 @@ class PmsRoomType(models.Model): _description = "Room Type" _inherits = {"product.product": "product_id"} _order = "sequence,default_code,name" + _check_pms_properties_auto = True sequence = fields.Integer( string="Sequence", @@ -30,36 +31,28 @@ class PmsRoomType(models.Model): required=True, delegate=True, ondelete="cascade", + # check_pms_properties=True, ) room_ids = fields.One2many( string="Rooms", help="Rooms that belong to room type.", comodel_name="pms.room", inverse_name="room_type_id", - domain="[" - "'|', " - "('pms_property_id', '=', False), " - "('pms_property_id','in', pms_property_ids)" - "]", + check_pms_properties=True, ) class_id = fields.Many2one( string="Property Type Class", help="Class to which the room type belongs", comodel_name="pms.room.type.class", required=True, - domain="[" - "'|', " - "('pms_property_ids', '=', False), " - "('pms_property_ids', 'in', pms_property_ids)" - "]", + check_pms_properties=True, ) board_service_room_type_ids = fields.One2many( string="Board Services", help="Board Service included in room type", comodel_name="pms.board.service.room.type", inverse_name="pms_room_type_id", - domain="['|', ('pms_property_ids', '=', False), ('pms_property_ids', 'in', " - "pms_property_ids)]", + check_pms_properties=True, ) room_amenity_ids = fields.Many2many( string="Room Type Amenities", @@ -68,11 +61,7 @@ class PmsRoomType(models.Model): relation="pms_room_type_amenity_rel", column1="room_type_id", column2="amenity_id", - domain="[" - "'|', " - "('pms_property_ids', '=', False), " - "('pms_property_ids', 'in', pms_property_ids)" - "]", + check_pms_properties=True, ) default_code = fields.Char( string="Code", @@ -171,16 +160,6 @@ class PmsRoomType(models.Model): ) return self.browse(list(res.values())) - @api.constrains("pms_property_ids", "class_id") - def _check_integrity_property_class(self): - for record in self: - if record.pms_property_ids and record.class_id.pms_property_ids: - for pms_property in record.pms_property_ids: - if pms_property.id not in record.class_id.pms_property_ids.ids: - raise ValidationError( - _("Property isn't allowed in Room Type Class") - ) - @api.constrains("default_code", "pms_property_ids", "company_id") def _check_code_property_company_uniqueness(self): msg = _("Already exists another room type with the same code and properties") @@ -203,39 +182,6 @@ class PmsRoomType(models.Model): if other and other != rec: raise ValidationError(msg) - @api.constrains("room_amenity_ids", "pms_property_ids") - def _check_integrity_property_amenity(self): - for record in self: - if record.room_amenity_ids.pms_property_ids and record.pms_property_ids: - for pms_property in record.pms_property_ids: - if pms_property not in record.room_amenity_ids.pms_property_ids: - raise ValidationError(_("Property not allowed in amenity")) - - @api.constrains("room_ids", "pms_property_ids") - def _check_integrity_property_room(self): - for record in self: - if record.room_ids and record.pms_property_ids: - for room in record.room_ids: - if room.pms_property_id not in record.pms_property_ids: - raise ValidationError(_("Property not allowed in room")) - - # TODO: Not allowed repeat boardservice on room_type with - # same properties os without properties - @api.constrains("board_service_room_type_ids", "pms_property_ids") - def _check_integrity_property_board_service_room_type(self): - for record in self: - if record.board_service_room_type_ids and record.pms_property_ids: - for board_service_room_type in record.board_service_room_type_ids: - if board_service_room_type.pms_property_ids: - for pms_property in record.pms_property_ids: - if ( - pms_property - not in board_service_room_type.pms_property_ids - ): - raise ValidationError( - _("Property not allowed in board service room type") - ) - # ORM Overrides # TODO: Review Check product fields default values to room @api.model diff --git a/pms/models/pms_room_type_class.py b/pms/models/pms_room_type_class.py index 22448d4c6..cd4df128e 100644 --- a/pms/models/pms_room_type_class.py +++ b/pms/models/pms_room_type_class.py @@ -42,15 +42,19 @@ class PmsRoomTypeClass(models.Model): column1="room_type_class_id", column2="pms_property_id", ondelete="restrict", + check_pms_properties=True, ) room_type_ids = fields.One2many( string="Types", help="Room Types that belong to this Room Type Class", comodel_name="pms.room.type", inverse_name="class_id", + check_pms_properties=True, ) default_code = fields.Char( - string="Code", help="Room type class identification code", required=True + string="Code", + help="Room type class identification code", + required=True, ) @api.model diff --git a/pms/models/pms_sale_channel.py b/pms/models/pms_sale_channel.py index 7e4228f23..1f60daab1 100644 --- a/pms/models/pms_sale_channel.py +++ b/pms/models/pms_sale_channel.py @@ -4,6 +4,7 @@ from odoo import fields, models class PmsSaleChannel(models.Model): _name = "pms.sale.channel" _description = "Sales Channel" + _check_pms_properties_auto = True name = fields.Text(string="Sale Channel Name", help="The name of the sale channel") channel_type = fields.Selection( @@ -23,4 +24,17 @@ class PmsSaleChannel(models.Model): relation="pms_sale_channel_product_pricelist_rel", column1="pms_sale_channel_id", column2="product_pricelist_id", + check_pms_properties=True, + ) + pms_property_ids = fields.Many2many( + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + required=False, + ondelete="restrict", + comodel_name="pms.property", + relation="pms_sale_channel_pms_property_rel", + column1="pms_sale_channel_id", + column2="pms_property_id", + check_pms_properties=True, ) diff --git a/pms/models/pms_service.py b/pms/models/pms_service.py index 655024fea..4c962ceb0 100644 --- a/pms/models/pms_service.py +++ b/pms/models/pms_service.py @@ -12,6 +12,7 @@ _logger = logging.getLogger(__name__) class PmsService(models.Model): _name = "pms.service" _description = "Services and its charges" + _check_pms_properties_auto = True name = fields.Char( string="Service description", @@ -26,6 +27,7 @@ class PmsService(models.Model): required=True, comodel_name="product.product", ondelete="restrict", + check_pms_properties=True, ) folio_id = fields.Many2one( string="Folio", @@ -34,6 +36,7 @@ class PmsService(models.Model): store=True, comodel_name="pms.folio", compute="_compute_folio_id", + check_pms_properties=True, ) sale_line_ids = fields.One2many( string="Sale Lines", @@ -41,6 +44,7 @@ class PmsService(models.Model): copy=False, comodel_name="folio.sale.line", inverse_name="service_id", + check_pms_properties=True, ) reservation_id = fields.Many2one( string="Room", @@ -48,6 +52,7 @@ class PmsService(models.Model): default=lambda self: self._default_reservation_id(), comodel_name="pms.reservation", ondelete="cascade", + check_pms_properties=True, ) service_line_ids = fields.One2many( string="Service Lines", @@ -57,6 +62,7 @@ class PmsService(models.Model): comodel_name="pms.service.line", inverse_name="service_id", compute="_compute_service_line_ids", + check_pms_properties=True, ) company_id = fields.Many2one( string="Company", diff --git a/pms/models/pms_service_line.py b/pms/models/pms_service_line.py index 078ace4ff..687320751 100644 --- a/pms/models/pms_service_line.py +++ b/pms/models/pms_service_line.py @@ -10,6 +10,7 @@ class PmsServiceLine(models.Model): _description = "Service by day" _order = "date" _rec_name = "service_id" + _check_pms_properties_auto = True service_id = fields.Many2one( string="Service Room", @@ -30,6 +31,7 @@ class PmsServiceLine(models.Model): help="Product associated with this service line", store=True, related="service_id.product_id", + check_pms_properties=True, ) tax_ids = fields.Many2many( string="Taxes", @@ -93,6 +95,7 @@ class PmsServiceLine(models.Model): readonly=True, store=True, related="service_id.reservation_id", + check_pms_properties=True, ) discount = fields.Float( string="Discount (%)", diff --git a/pms/models/pms_shared_room.py b/pms/models/pms_shared_room.py index 1ac0d3fe1..8566db551 100644 --- a/pms/models/pms_shared_room.py +++ b/pms/models/pms_shared_room.py @@ -10,6 +10,8 @@ class PmsSharedRoom(models.Model): _name = "pms.shared.room" _description = "Shared Room" _order = "room_type_id, name" + _check_pms_properties_auto = True + name = fields.Char( string="Room Name", help="Name of the shared room", required=True ) @@ -39,6 +41,7 @@ class PmsSharedRoom(models.Model): relation="pms_shared_room_pms_property_rel", column1="shared_room_id", column2="pms_property_id", + check_pms_properties=True, ) ubication_id = fields.Many2one( string="Ubication", diff --git a/pms/models/pms_ubication.py b/pms/models/pms_ubication.py index d2a1ca575..5169eefcd 100644 --- a/pms/models/pms_ubication.py +++ b/pms/models/pms_ubication.py @@ -7,6 +7,7 @@ from odoo.exceptions import ValidationError class PmsUbication(models.Model): _name = "pms.ubication" _description = "Ubication" + _check_pms_properties_auto = True name = fields.Char( string="Ubication Name", @@ -27,12 +28,15 @@ class PmsUbication(models.Model): relation="pms_ubication_pms_property_rel", column1="ubication_type_id", column2="pms_property_id", + ondelete="restrict", + check_pms_properties=True, ) pms_room_ids = fields.One2many( string="Rooms", help="Rooms found in this location", comodel_name="pms.room", inverse_name="ubication_id", + check_pms_properties=True, ) @api.constrains( diff --git a/pms/models/product_pricelist.py b/pms/models/product_pricelist.py index 6adca4656..d5858efd2 100644 --- a/pms/models/product_pricelist.py +++ b/pms/models/product_pricelist.py @@ -2,8 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -from odoo import _, api, fields, models -from odoo.exceptions import ValidationError +from odoo import fields, models _logger = logging.getLogger(__name__) @@ -15,37 +14,44 @@ class ProductPricelist(models.Model): """ _inherit = "product.pricelist" + _check_pms_properties_auto = True # Fields declaration pms_property_ids = fields.Many2many( - "pms.property", string="Properties", required=False, ondelete="restrict" + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + required=False, + comodel_name="pms.property", + relation="product_pricelist_pms_property_rel", + column1="product_pricelist_id", + column2="pms_property_id", + ondelete="restrict", + check_pms_properties=True, + ) + company_id = fields.Many2one( + check_pms_properties=True, ) cancelation_rule_id = fields.Many2one( "pms.cancelation.rule", string="Cancelation Policy", - domain=[ - "|", - ("pms_property_ids", "=", False), - ("pms_property_ids", "in", pms_property_ids), - ], + check_pms_properties=True, ) pricelist_type = fields.Selection( [("daily", "Daily Plan")], string="Pricelist Type", default="daily" ) pms_sale_channel_ids = fields.Many2many( - "pms.sale.channel", string="Available Channels" + "pms.sale.channel", + string="Available Channels", + check_pms_properties=True, ) - availability_plan_id = fields.Many2one( comodel_name="pms.availability.plan", string="Availability Plan", ondelete="restrict", - domain=[ - "|", - ("pms_property_ids", "=", False), - ("pms_property_ids", "in", pms_property_ids), - ], + check_pms_properties=True, ) + item_ids = fields.One2many(check_pms_properties=True) # Constraints and onchanges # @api.constrains("pricelist_type", "pms_property_ids") @@ -96,9 +102,9 @@ class ProductPricelist(models.Model): FROM product_pricelist_item item LEFT JOIN product_category categ ON item.categ_id = categ.id - LEFT JOIN pms_property_product_pricelist_rel cab + LEFT JOIN product_pricelist_pms_property_rel cab ON item.pricelist_id = cab.product_pricelist_id - LEFT JOIN pms_property_product_pricelist_item_rel lin + LEFT JOIN product_pricelist_item_pms_property_rel lin ON item.id = lin.product_pricelist_item_id LEFT JOIN board_service_pricelist_item_rel board ON item.id = board.pricelist_item_id @@ -121,10 +127,10 @@ class ProductPricelist(models.Model): item.date_end - item.date_start ASC, item.date_end_overnight - item.date_start_overnight ASC, NULLIF((SELECT COUNT(1) - FROM pms_property_product_pricelist_item_rel l + FROM product_pricelist_item_pms_property_rel l WHERE item.id = l.product_pricelist_item_id) + (SELECT COUNT(1) - FROM pms_property_product_pricelist_rel c + FROM product_pricelist_pms_property_rel c WHERE item.pricelist_id = c.product_pricelist_id),0) NULLS LAST, item.id DESC; @@ -168,25 +174,3 @@ class ProductPricelist(models.Model): "pricelist_id": self.id, }, } - - @api.constrains( - "cancelation_rule_id", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_property_ids: - for p in rec.pms_property_ids: - if p.id not in rec.cancelation_rule_id.pms_property_ids.ids: - raise ValidationError( - _("Property not allowed in cancelation rule") - ) - - @api.constrains("pms_property_ids", "availability_plan_id") - def _check_availability_plan_property_integrity(self): - for record in self: - if record.pms_property_ids and record.availability_plan_id.pms_property_ids: - for pms_property in record.pms_property_ids: - if pms_property not in record.availability_plan_id.pms_property_ids: - raise ValidationError( - _("Property not allowed availability plan") - ) diff --git a/pms/models/product_pricelist_item.py b/pms/models/product_pricelist_item.py index 77f5f007d..79756e74c 100644 --- a/pms/models/product_pricelist_item.py +++ b/pms/models/product_pricelist_item.py @@ -1,14 +1,22 @@ # 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 _, api, fields, models -from odoo.exceptions import ValidationError +from odoo import fields, models class ProductPricelistItem(models.Model): _inherit = "product.pricelist.item" + _check_pms_properties_auto = True pms_property_ids = fields.Many2many( - "pms.property", string="Properties", required=False, ondelete="restrict" + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + comodel_name="pms.property", + relation="product_pricelist_item_pms_property_rel", + column1="product_pricelist_item_id", + column2="pms_property_id", + ondelete="restrict", + check_pms_properties=True, ) date_start_overnight = fields.Date( string="Start Date Overnight", @@ -27,56 +35,8 @@ class ProductPricelistItem(models.Model): string="Board Services on Room Types", ondelete="cascade", # check_company=True, help="""Specify a Board services on Room Types.""", - # domain="[('pms_property_ids', 'in', [allowed_property_ids, False])]", + check_pms_properties=True, ) - - allowed_property_ids = fields.Many2many( - "pms.property", - "allowed_pricelist_move_rel", - "pricelist_item_id", - "property_id", - string="Allowed Properties", - store=True, - readonly=True, - compute="_compute_allowed_property_ids", - ) - - @api.depends("product_id.pms_property_ids", "pricelist_id.pms_property_ids") - def _compute_allowed_property_ids(self): - for record in self: - properties = [] - if record.applied_on == "0_product_variant": - product = record.product_id - elif record.applied_on == "1_product": - product = record.product_tmpl_id - else: - product = False - if not record.pricelist_id.pms_property_ids or not product: - record.allowed_property_ids = False - else: - if record.pricelist_id.pms_property_ids: - if product.pms_property_ids: - properties = list( - set(record.pricelist_id.pms_property_ids.ids) - & set(product.pms_property_ids.ids) - ) - record.allowed_property_ids = self.env["pms.property"].search( - [("id", "in", properties)] - ) - else: - record.allowed_property_ids = product.pms_property_ids - else: - record.allowed_property_ids = product.pms_property_ids - # else: - # record.allowed_property_ids = False - - @api.constrains( - "allowed_property_ids", - "pms_property_ids", - ) - def _check_property_integrity(self): - for rec in self: - if rec.pms_property_ids and rec.allowed_property_ids: - for p in rec.pms_property_ids: - if p.id not in rec.allowed_property_ids.ids: - raise ValidationError(_("Property not allowed")) + pricelist_id = fields.Many2one(check_pms_properties=True) + product_id = fields.Many2one(check_pms_properties=True) + product_tmpl_id = fields.Many2one(check_pms_properties=True) diff --git a/pms/models/product_template.py b/pms/models/product_template.py index 489971167..35311f80d 100644 --- a/pms/models/product_template.py +++ b/pms/models/product_template.py @@ -1,8 +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 _, api, fields, models -from odoo.exceptions import ValidationError +from odoo import fields, models class ProductTemplate(models.Model): @@ -17,6 +16,11 @@ class ProductTemplate(models.Model): relation="product_template_pms_property_rel", column1="product_tmpl_id", column2="pms_property_id", + ondelete="restrict", + check_pms_properties=True, + ) + company_id = fields.Many2one( + check_pms_properties=True, ) per_day = fields.Boolean( string="Unit increment per day", @@ -40,16 +44,3 @@ class ProductTemplate(models.Model): help="Indicates if that product is a extra bed, add +1 capacity in the room", default=False, ) - - @api.constrains("pms_property_ids", "company_id") - def _check_property_company_integrity(self): - for rec in self: - if rec.company_id and rec.pms_property_ids: - property_companies = rec.pms_property_ids.mapped("company_id") - if len(property_companies) > 1 or rec.company_id != property_companies: - raise ValidationError( - _( - "The company of the properties must match " - "the company on the room type" - ) - ) diff --git a/pms/models/res_partner.py b/pms/models/res_partner.py index 132d5943a..acaf9e88e 100644 --- a/pms/models/res_partner.py +++ b/pms/models/res_partner.py @@ -40,6 +40,21 @@ class ResPartner(models.Model): string="Invoice Agency", help="Indicates if agency invoices partner", ) + pms_property_ids = fields.Many2many( + string="Properties", + help="Properties with access to the element;" + " if not set, all properties can access", + required=False, + comodel_name="pms.property", + relation="res_partner_pms_property_rel", + column1="res_partner_id", + column2="pms_property_id", + ondelete="restrict", + check_pms_properties=True, + ) + company_id = fields.Many2one( + check_pms_properties=True, + ) def _compute_reservations_count(self): # TODO: recuperar las reservas de los folios del partner diff --git a/pms/tests/test_pms_amenity.py b/pms/tests/test_pms_amenity.py index fe353515a..e1c176cd2 100644 --- a/pms/tests/test_pms_amenity.py +++ b/pms/tests/test_pms_amenity.py @@ -1,4 +1,4 @@ -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError from .common import TestPms @@ -51,7 +51,7 @@ class TestPmsAmenity(TestPms): } ) # ACT & ASSERT - with self.assertRaises(ValidationError), self.cr.savepoint(): + with self.assertRaises(UserError), self.cr.savepoint(): Amenity.create( { "name": "TestAmenity1", @@ -69,115 +69,3 @@ class TestPmsAmenity(TestPms): ], } ) - - def test_property_allowed(self): - # Creation of a Amenity with Properties compatible with it Amenity Type - # Check Properties of Amenity are in Properties of Amenity Type - # +----------------------------------------+-----------------------------------+ - # | Amenity Type (TestAmenityType1) | Amenity (TestAmenity1) | - # +----------------------------------------+-----------------------------------+ - # | Property1 - Property2 - Property3 | Property1 - Property2 - Property3 | - # +----------------------------------------+-----------------------------------+ - - # ARRANGE - AmenityType = self.env["pms.amenity.type"] - Amenity = self.env["pms.amenity"] - amenity_type1 = AmenityType.create( - { - "name": "TestAmenityType1", - "pms_property_ids": [ - ( - 6, - 0, - [ - self.pms_property1.id, - self.pms_property2.id, - self.pms_property3.id, - ], - ) - ], - } - ) - # ACT - amenity1 = Amenity.create( - { - "name": "TestAmenity1", - "pms_amenity_type_id": amenity_type1.id, - "pms_property_ids": [ - ( - 6, - 0, - [ - self.pms_property1.id, - self.pms_property2.id, - self.pms_property3.id, - ], - ) - ], - } - ) - - # ASSERT - self.assertEqual( - amenity1.pms_property_ids.ids, - amenity_type1.pms_property_ids.ids, - "Properties not allowed in amenity type", - ) - - def test_change_amenity_property(self): - # Creation of a Amenity with Properties compatible with it Amenity Type - # Delete a Property in Amenity Type, check Validation Error when do that - # 1st scenario: - # +----------------------------------------+-----------------------------------+ - # | Amenity Type (TestAmenityType1) | Amenity (TestAmenity1) | - # +----------------------------------------+-----------------------------------+ - # | Property1 - Property2 - Property3 | Property1 - Property2 - Property3 | - # +----------------------------------------+-----------------------------------+ - # 2nd scenario(Error): - # +----------------------------------------+-----------------------------------+ - # | Amenity Type (TestAmenityType1) | Amenity (TestAmenity1) | - # +----------------------------------------+-----------------------------------+ - # | Property1 - Property2 | Property1 - Property2 - Property3 | - # +----------------------------------------+-----------------------------------+ - - # ARRANGE - AmenityType = self.env["pms.amenity.type"] - Amenity = self.env["pms.amenity"] - amenity_type1 = AmenityType.create( - { - "name": "TestAmenityType1", - "pms_property_ids": [ - (4, self.pms_property1.id), - (4, self.pms_property2.id), - (4, self.pms_property3.id), - ], - } - ) - # ACT - Amenity.create( - { - "name": "TestAmenity1", - "pms_amenity_type_id": amenity_type1.id, - "pms_property_ids": [ - ( - 6, - 0, - [ - self.pms_property1.id, - self.pms_property2.id, - self.pms_property3.id, - ], - ) - ], - } - ) - # ASSERT - with self.assertRaises(ValidationError): - amenity_type1.pms_property_ids = [ - ( - 6, - 0, - [self.pms_property1.id, self.pms_property2.id], - ) - ] - amenity_type1.flush() diff --git a/pms/tests/test_pms_availability_plan_rules.py b/pms/tests/test_pms_availability_plan_rules.py index bf3c65d45..e09fc952a 100644 --- a/pms/tests/test_pms_availability_plan_rules.py +++ b/pms/tests/test_pms_availability_plan_rules.py @@ -3,24 +3,42 @@ import datetime from freezegun import freeze_time from odoo import fields -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError, ValidationError from odoo.tests import common @freeze_time("1980-01-01") class TestPmsRoomTypeAvailabilityRules(common.SavepointCase): def create_common_scenario(self): - # product.pricelist - self.test_pricelist1 = self.env["product.pricelist"].create( - { - "name": "test pricelist 1", - } - ) self.test_pricelist2 = self.env["product.pricelist"].create( { "name": "test pricelist 2", } ) + self.test_property1 = self.env["pms.property"].create( + { + "name": "Property 1", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.test_pricelist2.id, + } + ) + self.test_property2 = self.env["pms.property"].create( + { + "name": "Property 2", + "company_id": self.env.ref("base.main_company").id, + "default_pricelist_id": self.test_pricelist2.id, + } + ) + self.test_pricelist1 = self.env["product.pricelist"].create( + { + "name": "test pricelist 1", + "pms_property_ids": [ + (4, self.test_property1.id), + (4, self.test_property2.id), + ], + } + ) + # pms.availability.plan self.test_room_type_availability1 = self.env["pms.availability.plan"].create( { @@ -148,27 +166,6 @@ class TestPmsRoomTypeAvailabilityRules(common.SavepointCase): def create_scenario_multiproperty(self): self.create_common_scenario() - - self.test_property1 = self.env["pms.property"].create( - { - "name": "Property 1", - "company_id": self.env.ref("base.main_company").id, - "default_pricelist_id": self.test_pricelist2.id, - "folio_sequence_id": self.folio_sequence.id, - "reservation_sequence_id": self.reservation_sequence.id, - "checkin_sequence_id": self.checkin_sequence.id, - } - ) - self.test_property2 = self.env["pms.property"].create( - { - "name": "Property 2", - "company_id": self.env.ref("base.main_company").id, - "default_pricelist_id": self.test_pricelist2.id, - "folio_sequence_id": self.folio_sequence.id, - "reservation_sequence_id": self.reservation_sequence.id, - "checkin_sequence_id": self.checkin_sequence.id, - } - ) self.test_property3 = self.env["pms.property"].create( { "name": "Property 3", @@ -583,6 +580,11 @@ class TestPmsRoomTypeAvailabilityRules(common.SavepointCase): "pms_property_id": self.test_property.id, } ) + self.test_pricelist1.pms_property_ids = [ + (4, self.test_property1.id), + (4, self.test_property2.id), + (4, self.test_property.id), + ] r1 = self.env["pms.reservation"].create( { "pms_property_id": self.test_property.id, @@ -627,6 +629,11 @@ class TestPmsRoomTypeAvailabilityRules(common.SavepointCase): "name": "test pricelist 2", } ) + self.test_pricelist1.pms_property_ids = [ + (4, self.test_property1.id), + (4, self.test_property2.id), + (4, self.test_property.id), + ] rule = self.env["pms.availability.plan.rule"].create( { "availability_plan_id": self.test_room_type_availability1.id, @@ -801,52 +808,7 @@ class TestPmsRoomTypeAvailabilityRules(common.SavepointCase): # ASSERT for test_case in test_cases: with self.subTest(k=test_case): - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.availability_rule1.pms_property_id = test_case[ "pms_property_id" ] - - def test_compute_allowed_property_ids(self): - # TEST CASE: - # - - # ARRANGE - self.create_scenario_multiproperty() - # create new room_type - self.test_room_type_special = self.env["pms.room.type"].create( - { - "pms_property_ids": [ - (4, self.test_property1.id), - (4, self.test_property3.id), - ], - "name": "Special Room Test", - "default_code": "SP_Test", - "class_id": self.test_room_type_class.id, - } - ) - # ACT - self.availability_example = self.env["pms.availability.plan"].create( - { - "name": "Availability plan for TEST", - "pms_pricelist_ids": [(6, 0, [self.test_pricelist1.id])], - "pms_property_ids": [ - (4, self.test_property1.id), - (4, self.test_property2.id), - ], - } - ) - self.availability_rule1 = self.env["pms.availability.plan.rule"].create( - { - "availability_plan_id": self.availability_example.id, - "room_type_id": self.test_room_type_special.id, - "date": (fields.datetime.today() + datetime.timedelta(days=2)).date(), - "closed": True, - "pms_property_id": self.test_property1.id, - } - ) - - self.assertIn( - self.test_property1.id, - self.availability_rule1.allowed_property_ids.mapped("id"), - "error", - ) diff --git a/pms/tests/test_pms_board_service_line.py b/pms/tests/test_pms_board_service_line.py index d341228fb..d6a465349 100644 --- a/pms/tests/test_pms_board_service_line.py +++ b/pms/tests/test_pms_board_service_line.py @@ -1,4 +1,4 @@ -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError from odoo.tests import common @@ -63,7 +63,7 @@ class TestPmsBoardService(common.SavepointCase): "default_code": "CB", } ) - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): board_service_line = self.board_service_line = self.env[ "pms.board.service.line" ].create( diff --git a/pms/tests/test_pms_board_service_room_type_line.py b/pms/tests/test_pms_board_service_room_type_line.py index e711e9ac5..6311c8cc7 100644 --- a/pms/tests/test_pms_board_service_room_type_line.py +++ b/pms/tests/test_pms_board_service_room_type_line.py @@ -1,4 +1,4 @@ -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError from odoo.tests import common @@ -57,19 +57,21 @@ class TestPmsBoardServiceRoomTypeLine(common.SavepointCase): { "name": "Board Service", "default_code": "CB", + "pms_property_ids": self.property1, } ) self.room_type_class = self.env["pms.room.type.class"].create( { "name": "Room Type Class", - "pms_property_ids": self.property1, "default_code": "SIN1", + "pms_property_ids": self.property1, } ) self.room_type = self.env["pms.room.type"].create( { "name": "Room Type", "default_code": "Type1", + "pms_property_ids": self.property1, "class_id": self.room_type_class.id, } ) @@ -84,8 +86,7 @@ class TestPmsBoardServiceRoomTypeLine(common.SavepointCase): self.product = self.env["product.product"].create( {"name": "Product", "pms_property_ids": self.property2} ) - - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.env["pms.board.service.room.type.line"].create( { "pms_board_service_room_type_id": self.board_service_room_type.id, diff --git a/pms/tests/test_pms_folio.py b/pms/tests/test_pms_folio.py index a7f0df32d..7fdeef8c0 100644 --- a/pms/tests/test_pms_folio.py +++ b/pms/tests/test_pms_folio.py @@ -3,7 +3,7 @@ import datetime from freezegun import freeze_time from odoo import fields -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError from odoo.tests import common freeze_time("2000-02-02") @@ -279,7 +279,7 @@ class TestPmsFolio(common.SavepointCase): } ) - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.env["pms.folio"].create( { "pms_property_id": self.property3.id, diff --git a/pms/tests/test_pms_pricelist.py b/pms/tests/test_pms_pricelist.py index 401efda59..00b3edd9e 100644 --- a/pms/tests/test_pms_pricelist.py +++ b/pms/tests/test_pms_pricelist.py @@ -1,6 +1,6 @@ import datetime -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError, ValidationError from odoo.tests import common, tagged @@ -124,7 +124,7 @@ class TestPmsPricelist(common.SavepointCase): # ARRANGE self.create_common_scenario() # ACT & ASSERT - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.item1 = self.env["product.pricelist.item"].create( { "name": "item_1", @@ -149,7 +149,7 @@ class TestPmsPricelist(common.SavepointCase): } ) # ASSERT - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.item1 = self.env["product.pricelist.item"].create( { "name": "item_1", @@ -175,7 +175,7 @@ class TestPmsPricelist(common.SavepointCase): } ) # ASSERT - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): Pricelist.create( { "name": "Pricelist Test", @@ -189,7 +189,7 @@ class TestPmsPricelist(common.SavepointCase): self.availability_plan = self.env["pms.availability.plan"].create( {"name": "Availability Plan", "pms_property_ids": [self.property1.id]} ) - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.env["product.pricelist"].create( { "name": "Pricelist", diff --git a/pms/tests/test_pms_pricelist_priority.py b/pms/tests/test_pms_pricelist_priority.py index d81396952..544ba1289 100644 --- a/pms/tests/test_pms_pricelist_priority.py +++ b/pms/tests/test_pms_pricelist_priority.py @@ -122,7 +122,6 @@ class TestPmsPricelistRules(common.SavepointCase): "partner_id": self.partner1.id, } ) - # ACT n_days = (reservation.checkout - reservation.checkin).days expected_price = self.room.room_type_id.list_price * n_days @@ -147,7 +146,7 @@ class TestPmsPricelistRules(common.SavepointCase): # 5. id # - tie # - no [date_start|date_end|date_start_overnight|date_end_overnight] - + properties = self.room_type.product_id.pms_property_ids.ids test_cases = [ { "name": "sorting applied_on", @@ -164,6 +163,7 @@ class TestPmsPricelistRules(common.SavepointCase): "applied_on": "0_product_variant", "product_id": self.room_type.product_id.id, "fixed_price": 50.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -171,6 +171,7 @@ class TestPmsPricelistRules(common.SavepointCase): "product_id": self.room_type.product_id.id, "product_tmpl_id": self.product_template.id, "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -186,6 +187,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=2), "fixed_price": 60.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -195,6 +197,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=1), "fixed_price": 50.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -204,6 +207,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=3), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -219,6 +223,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end_overnight": datetime.datetime.now() + datetime.timedelta(days=6), "fixed_price": 60.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -228,6 +233,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end_overnight": datetime.datetime.now() + datetime.timedelta(days=10), "fixed_price": 50.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -237,6 +243,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end_overnight": datetime.datetime.now() + datetime.timedelta(days=3), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -249,6 +256,7 @@ class TestPmsPricelistRules(common.SavepointCase): "applied_on": "0_product_variant", "product_id": self.room_type.product_id.id, "fixed_price": 60.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -275,18 +283,21 @@ class TestPmsPricelistRules(common.SavepointCase): "applied_on": "0_product_variant", "product_id": self.room_type.product_id.id, "fixed_price": 60.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, "applied_on": "0_product_variant", "product_id": self.room_type.product_id.id, "fixed_price": 50.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, "applied_on": "0_product_variant", "product_id": self.room_type.product_id.id, "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -302,6 +313,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=2), "fixed_price": 60.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -312,6 +324,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=1), "fixed_price": 50.0, + "pms_property_ids": properties, }, ], }, @@ -327,6 +340,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=10), "fixed_price": 120.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -336,6 +350,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end_overnight": datetime.datetime.now() + datetime.timedelta(days=3), "fixed_price": 50.0, + "pms_property_ids": properties, }, ], }, @@ -351,6 +366,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end_overnight": datetime.datetime.now() + datetime.timedelta(days=3), "fixed_price": 120.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -376,6 +392,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end_overnight": datetime.datetime.now() + datetime.timedelta(days=3), "fixed_price": 120.0, + "pms_property_ids": properties, }, { "pricelist_id": self.pricelist.id, @@ -432,6 +449,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_end": datetime.datetime.now() + datetime.timedelta(days=1), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -445,6 +463,7 @@ class TestPmsPricelistRules(common.SavepointCase): "product_id": self.room_type.product_id.id, "date_start": datetime.datetime.now(), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -458,6 +477,7 @@ class TestPmsPricelistRules(common.SavepointCase): "product_id": self.room_type.product_id.id, "date_end_overnight": datetime.datetime.now(), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -471,6 +491,7 @@ class TestPmsPricelistRules(common.SavepointCase): "product_id": self.room_type.product_id.id, "date_start_overnight": datetime.datetime.now(), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, @@ -485,6 +506,7 @@ class TestPmsPricelistRules(common.SavepointCase): "date_start_overnight": datetime.datetime.now(), "date_end_overnight": datetime.datetime.now(), "fixed_price": 40.0, + "pms_property_ids": properties, }, ], }, diff --git a/pms/tests/test_pms_reservation.py b/pms/tests/test_pms_reservation.py index 4d45dc18f..c54c509da 100644 --- a/pms/tests/test_pms_reservation.py +++ b/pms/tests/test_pms_reservation.py @@ -951,7 +951,7 @@ class TestPmsReservations(common.SavepointCase): for test_case in test_cases: with self.subTest(k=test_case): - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.reservation_test.write(test_case) @freeze_time("1950-11-01") diff --git a/pms/tests/test_pms_room.py b/pms/tests/test_pms_room.py index a9db1550e..90d4768a9 100644 --- a/pms/tests/test_pms_room.py +++ b/pms/tests/test_pms_room.py @@ -1,6 +1,6 @@ from psycopg2 import IntegrityError -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError from odoo.tools import mute_logger from .common import TestPms @@ -38,9 +38,7 @@ class TestPmsRoom(TestPms): } ) # ACT & ARRANGE - with self.assertRaises( - ValidationError, msg="Room has been created and it should't" - ): + with self.assertRaises(UserError, msg="Room has been created and it should't"): self.env["pms.room"].create( { "name": "Room 101", @@ -60,9 +58,7 @@ class TestPmsRoom(TestPms): } ) # ACT & ARRANGE - with self.assertRaises( - ValidationError, msg="Room has been created and it should't" - ): + with self.assertRaises(UserError, msg="Room has been created and it should't"): self.env["pms.room"].create( { "name": "Room 101", diff --git a/pms/tests/test_pms_room_type.py b/pms/tests/test_pms_room_type.py index a2722fff1..997d6c669 100644 --- a/pms/tests/test_pms_room_type.py +++ b/pms/tests/test_pms_room_type.py @@ -1,7 +1,7 @@ # Copyright 2021 Eric Antones # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError, ValidationError from .common import TestPms @@ -68,7 +68,7 @@ class TestRoomType(TestPms): """ # ARRANGE & ACT & ASSERT with self.assertRaises( - ValidationError, msg="The room type has been created and it shouldn't" + UserError, msg="The room type has been created and it shouldn't" ): # room_type1 self.env["pms.room.type"].create( @@ -95,7 +95,7 @@ class TestRoomType(TestPms): """ # ARRANGE & ACT & ASSERT with self.assertRaises( - ValidationError, msg="The room type has been created and it shouldn't" + UserError, msg="The room type has been created and it shouldn't" ): # room_type1 self.env["pms.room.type"].create( @@ -728,7 +728,7 @@ class TestRoomType(TestPms): ) # ACT & ASSERT with self.assertRaises( - ValidationError, msg="Room Type has been created and it shouldn't" + UserError, msg="Room Type has been created and it shouldn't" ): room_type1 = self.env["pms.room.type"].create( { @@ -776,7 +776,7 @@ class TestRoomType(TestPms): self.amenity1 = self.env["pms.amenity"].create( {"name": "Amenity", "pms_property_ids": self.pms_property1} ) - with self.assertRaises(ValidationError): + with self.assertRaises(UserError): self.env["pms.room.type"].create( { "name": "Room Type", diff --git a/pms/tests/test_pms_wizard_folio.py b/pms/tests/test_pms_wizard_folio.py index cef426af4..10ec93937 100644 --- a/pms/tests/test_pms_wizard_folio.py +++ b/pms/tests/test_pms_wizard_folio.py @@ -268,7 +268,7 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): expected_price_total = days * price_today * num_double_rooms # set pricelist item for current day - product_tmpl_id = self.test_room_type_double.product_id.product_tmpl_id.id + product_tmpl = self.test_room_type_double.product_id.product_tmpl_id pricelist_item = self.env["product.pricelist.item"].create( { "pricelist_id": self.test_pricelist.id, @@ -276,9 +276,10 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): "date_end_overnight": checkin, "compute_price": "fixed", "applied_on": "1_product", - "product_tmpl_id": product_tmpl_id, + "product_tmpl_id": product_tmpl.id, "fixed_price": price_today, "min_quantity": 0, + "pms_property_ids": product_tmpl.pms_property_ids.ids, } ) pricelist_item.flush() diff --git a/pms/views/pms_board_service_room_type_views.xml b/pms/views/pms_board_service_room_type_views.xml index 3064644b5..f78cd3d2d 100644 --- a/pms/views/pms_board_service_room_type_views.xml +++ b/pms/views/pms_board_service_room_type_views.xml @@ -8,6 +8,7 @@ + + diff --git a/pms/views/pms_board_service_views.xml b/pms/views/pms_board_service_views.xml index cc6da3576..b178f8c05 100644 --- a/pms/views/pms_board_service_views.xml +++ b/pms/views/pms_board_service_views.xml @@ -18,6 +18,7 @@ + @@ -34,6 +35,7 @@ + diff --git a/pms/views/pms_checkin_partner_views.xml b/pms/views/pms_checkin_partner_views.xml index 0c039750c..0ae652ec4 100644 --- a/pms/views/pms_checkin_partner_views.xml +++ b/pms/views/pms_checkin_partner_views.xml @@ -41,6 +41,7 @@ + @@ -77,6 +78,7 @@ + @@ -103,6 +105,7 @@ attrs="{'invisible': [('state','!=','precheckin')]}" /> + @@ -142,6 +145,7 @@ + @@ -218,6 +222,10 @@ name="reservation_id" placeholder="Room Reservation" /> + + diff --git a/pms/views/pms_reservation_views.xml b/pms/views/pms_reservation_views.xml index a285a23dc..12603e3fa 100644 --- a/pms/views/pms_reservation_views.xml +++ b/pms/views/pms_reservation_views.xml @@ -443,6 +443,7 @@ name="cancel_discount" attrs="{'column_invisible': [('parent.state','!=','cancelled')]}" /> + diff --git a/pms/views/pms_room_type_views.xml b/pms/views/pms_room_type_views.xml index 3fe2bf91c..22965b472 100644 --- a/pms/views/pms_room_type_views.xml +++ b/pms/views/pms_room_type_views.xml @@ -65,6 +65,7 @@ +