mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
Merge branch 'commitsun/14.0' into 'OCA/14.0'
This commit is contained in:
@@ -20,10 +20,11 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- stage: test
|
- stage: test
|
||||||
env:
|
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
|
- stage: test
|
||||||
env:
|
env:
|
||||||
- TESTS=1 ODOO_REPO="OCA/OCB"
|
- TESTS=1 ODOO_REPO="OCA/OCB" OPTIONS="--load web,multi_pms_properties"
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- VERSION="14.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0"
|
- VERSION="14.0" TESTS="0" LINT_CHECK="0" MAKEPOT="0"
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ Available addons
|
|||||||
addon | version | summary
|
addon | version | summary
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
[pms](pms/) | 14.0.1.0.0 | A property management system
|
[pms](pms/) | 14.0.1.0.0 | A property management system
|
||||||
|
[multi_pms_properties](multi_pms_properties/) | 14.0.1.0.0 | Multiproperty support
|
||||||
|
[pms_housekeeping](pms_housekeeping/) | 14.0.1.0.0 | Housekeeping feature
|
||||||
|
[pms_l10n_es](pms_l10n_es/) | 14.0.1.0.0 | Spanish localization support
|
||||||
|
[pms_rooming_xls](pms_rooming_xls/) | 14.0.1.0.0 | Management rooming
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[//]: # (end addons)
|
[//]: # (end addons)
|
||||||
|
|
||||||
|
|||||||
97
multi_pms_properties/README.rst
Normal file
97
multi_pms_properties/README.rst
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
====================
|
||||||
|
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 multiproperty in property management system (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"``
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
* Use the standard 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``
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/pms/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 <https://github.com/OCA/pms/issues/new?body=module:%20multi_pms_properties%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Commit [Sun]
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* `Commit [Sun] <https://www.commitsun.com>`:
|
||||||
|
|
||||||
|
* Dario Lodeiros
|
||||||
|
* Eric Antones
|
||||||
|
* Sara Lago
|
||||||
|
|
||||||
|
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 <https://github.com/OCA/pms/tree/14.0/multi_pms_properties>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||||
64
multi_pms_properties/__init__.py
Normal file
64
multi_pms_properties/__init__.py
Normal file
@@ -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
|
||||||
17
multi_pms_properties/__manifest__.py
Normal file
17
multi_pms_properties/__manifest__.py
Normal file
@@ -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": "Commit [Sun], Odoo Community Association (OCA)",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"category": "Pms",
|
||||||
|
"depends": ["base"],
|
||||||
|
"auto_install": False,
|
||||||
|
"installable": True,
|
||||||
|
}
|
||||||
186
multi_pms_properties/models.py
Normal file
186
multi_pms_properties/models.py
Normal file
@@ -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
|
||||||
5
multi_pms_properties/readme/CONTRIBUTORS.rst
Normal file
5
multi_pms_properties/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
* `Commit [Sun] <https://www.commitsun.com>`:
|
||||||
|
|
||||||
|
* Dario Lodeiros
|
||||||
|
* Eric Antones
|
||||||
|
* Sara Lago
|
||||||
1
multi_pms_properties/readme/DESCRIPTION.rst
Normal file
1
multi_pms_properties/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Technical addon to support multiproperty in property management system (PMS).
|
||||||
7
multi_pms_properties/readme/INSTALL.rst
Normal file
7
multi_pms_properties/readme/INSTALL.rst
Normal file
@@ -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"``
|
||||||
5
multi_pms_properties/readme/USAGE.rst
Normal file
5
multi_pms_properties/readme/USAGE.rst
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
* Use the standard 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``
|
||||||
BIN
multi_pms_properties/static/description/icon.png
Normal file
BIN
multi_pms_properties/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
444
multi_pms_properties/static/description/index.html
Normal file
444
multi_pms_properties/static/description/index.html
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
|
||||||
|
<title>multi_pms_properties</title>
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
/*
|
||||||
|
:Author: David Goodger (goodger@python.org)
|
||||||
|
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
||||||
|
:Copyright: This stylesheet has been placed in the public domain.
|
||||||
|
|
||||||
|
Default cascading style sheet for the HTML output of Docutils.
|
||||||
|
|
||||||
|
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||||
|
customize this style sheet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* used to remove borders from tables and images */
|
||||||
|
.borderless, table.borderless td, table.borderless th {
|
||||||
|
border: 0 }
|
||||||
|
|
||||||
|
table.borderless td, table.borderless th {
|
||||||
|
/* Override padding for "table.docutils td" with "! important".
|
||||||
|
The right padding separates the table cells. */
|
||||||
|
padding: 0 0.5em 0 0 ! important }
|
||||||
|
|
||||||
|
.first {
|
||||||
|
/* Override more specific margin styles with "! important". */
|
||||||
|
margin-top: 0 ! important }
|
||||||
|
|
||||||
|
.last, .with-subtitle {
|
||||||
|
margin-bottom: 0 ! important }
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none }
|
||||||
|
|
||||||
|
.subscript {
|
||||||
|
vertical-align: sub;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
.superscript {
|
||||||
|
vertical-align: super;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
a.toc-backref {
|
||||||
|
text-decoration: none ;
|
||||||
|
color: black }
|
||||||
|
|
||||||
|
blockquote.epigraph {
|
||||||
|
margin: 2em 5em ; }
|
||||||
|
|
||||||
|
dl.docutils dd {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||||
|
dl.docutils dt {
|
||||||
|
font-weight: bold }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.abstract {
|
||||||
|
margin: 2em 5em }
|
||||||
|
|
||||||
|
div.abstract p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||||
|
div.hint, div.important, div.note, div.tip, div.warning {
|
||||||
|
margin: 2em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||||
|
div.important p.admonition-title, div.note p.admonition-title,
|
||||||
|
div.tip p.admonition-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||||
|
div.danger p.admonition-title, div.error p.admonition-title,
|
||||||
|
div.warning p.admonition-title, .code .error {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||||
|
compound paragraphs.
|
||||||
|
div.compound .compound-first, div.compound .compound-middle {
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
div.compound .compound-last, div.compound .compound-middle {
|
||||||
|
margin-top: 0.5em }
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.dedication {
|
||||||
|
margin: 2em 5em ;
|
||||||
|
text-align: center ;
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
div.dedication p.topic-title {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-style: normal }
|
||||||
|
|
||||||
|
div.figure {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
div.footer, div.header {
|
||||||
|
clear: both;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
div.line-block {
|
||||||
|
display: block ;
|
||||||
|
margin-top: 1em ;
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
div.line-block div.line-block {
|
||||||
|
margin-top: 0 ;
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-left: 1.5em }
|
||||||
|
|
||||||
|
div.sidebar {
|
||||||
|
margin: 0 0 0.5em 1em ;
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em ;
|
||||||
|
background-color: #ffffee ;
|
||||||
|
width: 40% ;
|
||||||
|
float: right ;
|
||||||
|
clear: right }
|
||||||
|
|
||||||
|
div.sidebar p.rubric {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-size: medium }
|
||||||
|
|
||||||
|
div.system-messages {
|
||||||
|
margin: 5em }
|
||||||
|
|
||||||
|
div.system-messages h1 {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
div.system-message {
|
||||||
|
border: medium outset ;
|
||||||
|
padding: 1em }
|
||||||
|
|
||||||
|
div.system-message p.system-message-title {
|
||||||
|
color: red ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
margin: 2em }
|
||||||
|
|
||||||
|
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||||
|
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||||
|
margin-top: 0.4em }
|
||||||
|
|
||||||
|
h1.title {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
h2.subtitle {
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
hr.docutils {
|
||||||
|
width: 75% }
|
||||||
|
|
||||||
|
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||||
|
clear: left ;
|
||||||
|
float: left ;
|
||||||
|
margin-right: 1em }
|
||||||
|
|
||||||
|
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||||
|
clear: right ;
|
||||||
|
float: right ;
|
||||||
|
margin-left: 1em }
|
||||||
|
|
||||||
|
img.align-center, .figure.align-center, object.align-center {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.align-center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left }
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
clear: both ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right }
|
||||||
|
|
||||||
|
/* reset inner alignment in figures */
|
||||||
|
div.align-right {
|
||||||
|
text-align: inherit }
|
||||||
|
|
||||||
|
/* div.align-center * { */
|
||||||
|
/* text-align: left } */
|
||||||
|
|
||||||
|
.align-top {
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
.align-middle {
|
||||||
|
vertical-align: middle }
|
||||||
|
|
||||||
|
.align-bottom {
|
||||||
|
vertical-align: bottom }
|
||||||
|
|
||||||
|
ol.simple, ul.simple {
|
||||||
|
margin-bottom: 1em }
|
||||||
|
|
||||||
|
ol.arabic {
|
||||||
|
list-style: decimal }
|
||||||
|
|
||||||
|
ol.loweralpha {
|
||||||
|
list-style: lower-alpha }
|
||||||
|
|
||||||
|
ol.upperalpha {
|
||||||
|
list-style: upper-alpha }
|
||||||
|
|
||||||
|
ol.lowerroman {
|
||||||
|
list-style: lower-roman }
|
||||||
|
|
||||||
|
ol.upperroman {
|
||||||
|
list-style: upper-roman }
|
||||||
|
|
||||||
|
p.attribution {
|
||||||
|
text-align: right ;
|
||||||
|
margin-left: 50% }
|
||||||
|
|
||||||
|
p.caption {
|
||||||
|
font-style: italic }
|
||||||
|
|
||||||
|
p.credits {
|
||||||
|
font-style: italic ;
|
||||||
|
font-size: smaller }
|
||||||
|
|
||||||
|
p.label {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
p.rubric {
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger ;
|
||||||
|
color: maroon ;
|
||||||
|
text-align: center }
|
||||||
|
|
||||||
|
p.sidebar-title {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold ;
|
||||||
|
font-size: larger }
|
||||||
|
|
||||||
|
p.sidebar-subtitle {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
p.topic-title {
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
pre.address {
|
||||||
|
margin-bottom: 0 ;
|
||||||
|
margin-top: 0 ;
|
||||||
|
font: inherit }
|
||||||
|
|
||||||
|
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||||
|
margin-left: 2em ;
|
||||||
|
margin-right: 2em }
|
||||||
|
|
||||||
|
pre.code .ln { color: grey; } /* line numbers */
|
||||||
|
pre.code, code { background-color: #eeeeee }
|
||||||
|
pre.code .comment, code .comment { color: #5C6576 }
|
||||||
|
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||||
|
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||||
|
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||||
|
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||||
|
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||||
|
|
||||||
|
span.classifier {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-style: oblique }
|
||||||
|
|
||||||
|
span.classifier-delimiter {
|
||||||
|
font-family: sans-serif ;
|
||||||
|
font-weight: bold }
|
||||||
|
|
||||||
|
span.interpreted {
|
||||||
|
font-family: sans-serif }
|
||||||
|
|
||||||
|
span.option {
|
||||||
|
white-space: nowrap }
|
||||||
|
|
||||||
|
span.pre {
|
||||||
|
white-space: pre }
|
||||||
|
|
||||||
|
span.problematic {
|
||||||
|
color: red }
|
||||||
|
|
||||||
|
span.section-subtitle {
|
||||||
|
/* font-size relative to parent (h1..h6 element) */
|
||||||
|
font-size: 80% }
|
||||||
|
|
||||||
|
table.citation {
|
||||||
|
border-left: solid 1px gray;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docinfo {
|
||||||
|
margin: 2em 4em }
|
||||||
|
|
||||||
|
table.docutils {
|
||||||
|
margin-top: 0.5em ;
|
||||||
|
margin-bottom: 0.5em }
|
||||||
|
|
||||||
|
table.footnote {
|
||||||
|
border-left: solid 1px black;
|
||||||
|
margin-left: 1px }
|
||||||
|
|
||||||
|
table.docutils td, table.docutils th,
|
||||||
|
table.docinfo td, table.docinfo th {
|
||||||
|
padding-left: 0.5em ;
|
||||||
|
padding-right: 0.5em ;
|
||||||
|
vertical-align: top }
|
||||||
|
|
||||||
|
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||||
|
font-weight: bold ;
|
||||||
|
text-align: left ;
|
||||||
|
white-space: nowrap ;
|
||||||
|
padding-left: 0 }
|
||||||
|
|
||||||
|
/* "booktabs" style (no vertical lines) */
|
||||||
|
table.docutils.booktabs {
|
||||||
|
border: 0px;
|
||||||
|
border-top: 2px solid;
|
||||||
|
border-bottom: 2px solid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs * {
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
table.docutils.booktabs th {
|
||||||
|
border-bottom: thin solid;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||||
|
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||||
|
font-size: 100% }
|
||||||
|
|
||||||
|
ul.auto-toc {
|
||||||
|
list-style-type: none }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="document" id="multi-pms-properties">
|
||||||
|
<h1 class="title">multi_pms_properties</h1>
|
||||||
|
|
||||||
|
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
|
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/pms/tree/14.0/multi_pms_properties"><img alt="OCA/pms" src="https://img.shields.io/badge/github-OCA%2Fpms-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pms-14-0/pms-14-0-multi_pms_properties"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/293/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||||
|
<p>Technical addon to support multiproperty in property management system (PMS).</p>
|
||||||
|
<p><strong>Table of contents</strong></p>
|
||||||
|
<div class="contents local topic" id="contents">
|
||||||
|
<ul class="simple">
|
||||||
|
<li><a class="reference internal" href="#installation" id="id1">Installation</a></li>
|
||||||
|
<li><a class="reference internal" href="#usage" id="id2">Usage</a></li>
|
||||||
|
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
|
||||||
|
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
|
||||||
|
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
|
||||||
|
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
|
||||||
|
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="installation">
|
||||||
|
<h1><a class="toc-backref" href="#id1">Installation</a></h1>
|
||||||
|
<p>To install this module, you only need to add it to your addons, and load it as
|
||||||
|
a server-wide module.</p>
|
||||||
|
<p>This can be done with the <tt class="docutils literal">server_wide_modules</tt> parameter in <tt class="docutils literal">/etc/odoo.conf</tt>
|
||||||
|
or with the <tt class="docutils literal"><span class="pre">--load</span></tt> command-line parameter</p>
|
||||||
|
<p><tt class="docutils literal">server_wide_modules = "multi_pms_properties"</tt></p>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="usage">
|
||||||
|
<h1><a class="toc-backref" href="#id2">Usage</a></h1>
|
||||||
|
<ul>
|
||||||
|
<li><p class="first">Use the standard multicompany guidelines applied to pms.property:</p>
|
||||||
|
<p><tt class="docutils literal">_check_pms_properties_auto like model attribute to autocheck on create/write</tt>
|
||||||
|
<tt class="docutils literal">check_pms_properties like field attribute to check relational record properties consistence</tt>
|
||||||
|
<tt class="docutils literal">This module not implement propety dependent fields</tt></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="bug-tracker">
|
||||||
|
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
|
||||||
|
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/pms/issues">GitHub Issues</a>.
|
||||||
|
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
|
||||||
|
<a class="reference external" href="https://github.com/OCA/pms/issues/new?body=module:%20multi_pms_properties%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||||
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="credits">
|
||||||
|
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
|
||||||
|
<div class="section" id="authors">
|
||||||
|
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li>Commit [Sun]</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="contributors">
|
||||||
|
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
|
||||||
|
<ul class="simple">
|
||||||
|
<li><cite>Commit [Sun] <https://www.commitsun.com></cite>:<ul>
|
||||||
|
<li>Dario Lodeiros</li>
|
||||||
|
<li>Eric Antones</li>
|
||||||
|
<li>Sara Lago</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="section" id="maintainers">
|
||||||
|
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
|
||||||
|
<p>This module is maintained by the OCA.</p>
|
||||||
|
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
|
||||||
|
<p>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.</p>
|
||||||
|
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/pms/tree/14.0/multi_pms_properties">OCA/pms</a> project on GitHub.</p>
|
||||||
|
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
4
multi_pms_properties/tests/__init__.py
Normal file
4
multi_pms_properties/tests/__init__.py
Normal file
@@ -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
|
||||||
20
multi_pms_properties/tests/common.py
Normal file
20
multi_pms_properties/tests/common.py
Normal file
@@ -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)
|
||||||
17
multi_pms_properties/tests/multi_pms_properties_tester.py
Normal file
17
multi_pms_properties/tests/multi_pms_properties_tester.py
Normal file
@@ -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)
|
||||||
41
multi_pms_properties/tests/test_multi_pms_properties.py
Normal file
41
multi_pms_properties/tests/test_multi_pms_properties.py
Normal file
@@ -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"})
|
||||||
@@ -1 +1,2 @@
|
|||||||
# See https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#oca_dependencies-txt
|
partner-contact
|
||||||
|
reporting-engine
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ PMS (Property Management System)
|
|||||||
|
|
||||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||||
|
|
||||||
This module is an all-in-one property management system (PMS) focused on medium-sized hotels
|
This module is an all-in-one property management system (PMS) focused on medium-sized properties
|
||||||
for managing every aspect of your property's daily operations.
|
for managing every aspect of your property's daily operations.
|
||||||
|
|
||||||
You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory,
|
You can manage properties with multi-property and multi-company support, including your rooms inventory,
|
||||||
reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities.
|
reservations, check-in, daily reports, board services, rate and availability plans among other property functionalities.
|
||||||
|
|
||||||
.. IMPORTANT::
|
.. IMPORTANT::
|
||||||
This is an alpha version, the data model and design can change at any time without warning.
|
This is an alpha version, the data model and design can change at any time without warning.
|
||||||
@@ -44,20 +44,20 @@ reservations, check-in, daily reports, board services, rate and restriction plan
|
|||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
This module depends on modules ``base``, ``sale_stock``, ``account_payment_return``, ``partner_firstname``,
|
This module depends on modules ``base``, ``mail``, ``sale`` and ``multi_pms_properties``.
|
||||||
and ``account_cancel``. Ensure yourself to have all them in your addons list.
|
Ensure yourself to have all them in your addons list.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
=============
|
=============
|
||||||
|
|
||||||
You will find the hotel settings in Settings > Users & Companies > Hotels > Your Hotel.
|
You will find the hotel settings in PMS Management > Configuration > Properties > Your Property.
|
||||||
|
|
||||||
This module required additional configuration for company, accounting, invoicing and user privileges.
|
This module required additional configuration for company, accounting, invoicing and user privileges.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
To use this module, please, read the complete user guide at https://roomdoo.com.
|
To use this module, please, read the complete user guide at `<roomdoo.com>`_.
|
||||||
|
|
||||||
Bug Tracker
|
Bug Tracker
|
||||||
===========
|
===========
|
||||||
@@ -75,18 +75,21 @@ Credits
|
|||||||
Authors
|
Authors
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* Dario Lodeiros
|
* Commit [Sun]
|
||||||
* Alexadre Diaz
|
|
||||||
* Pablo Quesada
|
|
||||||
* Jose Luis Algara
|
|
||||||
|
|
||||||
Contributors
|
Contributors
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
* Dario Lodeiros <dario@commitsun.com>
|
|
||||||
* Alexandre Díaz
|
* Alexandre Díaz
|
||||||
* Pablo Quesada
|
* Pablo Quesada
|
||||||
* Jose Luis Algara
|
* Jose Luis Algara
|
||||||
|
* `Commit [Sun] <https://www.commitsun.com>`:
|
||||||
|
|
||||||
|
* Dario Lodeiros
|
||||||
|
* Eric Antones
|
||||||
|
* Sara Lago
|
||||||
|
* Brais Abeijon
|
||||||
|
* Miguel Padin
|
||||||
|
|
||||||
Maintainers
|
Maintainers
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
from . import wizards
|
from . import wizards
|
||||||
from .init_hook import post_init_hook
|
from . import controllers
|
||||||
|
from .init_hook import pre_init_hook
|
||||||
|
|||||||
@@ -8,11 +8,7 @@
|
|||||||
"development_status": "Alpha",
|
"development_status": "Alpha",
|
||||||
"category": "Generic Modules/Property Management System",
|
"category": "Generic Modules/Property Management System",
|
||||||
"website": "https://github.com/OCA/pms",
|
"website": "https://github.com/OCA/pms",
|
||||||
"author": "Dario Lodeiros, "
|
"author": "Commit [Sun], Odoo Community Association (OCA)",
|
||||||
"Alexadre Diaz, "
|
|
||||||
"Pablo Quesada, "
|
|
||||||
"Jose Luis Algara, "
|
|
||||||
"Odoo Community Association (OCA)",
|
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"application": True,
|
"application": True,
|
||||||
"installable": True,
|
"installable": True,
|
||||||
@@ -23,6 +19,7 @@
|
|||||||
# "partner_firstname",
|
# "partner_firstname",
|
||||||
# "email_template_qweb",
|
# "email_template_qweb",
|
||||||
"sale",
|
"sale",
|
||||||
|
"multi_pms_properties",
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
"security/pms_security.xml",
|
"security/pms_security.xml",
|
||||||
@@ -33,29 +30,33 @@
|
|||||||
"report/pms_folio.xml",
|
"report/pms_folio.xml",
|
||||||
"report/pms_folio_templates.xml",
|
"report/pms_folio_templates.xml",
|
||||||
# "templates/pms_email_template.xml",
|
# "templates/pms_email_template.xml",
|
||||||
"views/general.xml",
|
|
||||||
"data/menus.xml",
|
"data/menus.xml",
|
||||||
|
"wizards/wizard_payment_folio.xml",
|
||||||
|
"wizards/folio_make_invoice_advance_views.xml",
|
||||||
|
"wizards/wizard_folio.xml",
|
||||||
|
"wizards/wizard_folio_changes.xml",
|
||||||
"views/pms_amenity_views.xml",
|
"views/pms_amenity_views.xml",
|
||||||
"views/pms_amenity_type_views.xml",
|
"views/pms_amenity_type_views.xml",
|
||||||
"views/pms_board_service_views.xml",
|
"views/pms_board_service_views.xml",
|
||||||
"views/pms_board_service_room_type_views.xml",
|
"views/pms_board_service_room_type_views.xml",
|
||||||
"views/pms_cancelation_rule_views.xml",
|
"views/pms_cancelation_rule_views.xml",
|
||||||
"views/pms_checkin_partner_views.xml",
|
"views/pms_checkin_partner_views.xml",
|
||||||
"views/pms_floor_views.xml",
|
"views/pms_ubication_views.xml",
|
||||||
"views/pms_folio_views.xml",
|
|
||||||
"views/pms_property_views.xml",
|
"views/pms_property_views.xml",
|
||||||
"views/pms_reservation_views.xml",
|
"views/pms_reservation_views.xml",
|
||||||
|
"views/pms_service_views.xml",
|
||||||
|
"views/pms_service_line_views.xml",
|
||||||
|
"views/pms_folio_views.xml",
|
||||||
"views/pms_room_type_views.xml",
|
"views/pms_room_type_views.xml",
|
||||||
"views/pms_room_views.xml",
|
"views/pms_room_views.xml",
|
||||||
"views/pms_room_closure_reason_views.xml",
|
"views/pms_room_closure_reason_views.xml",
|
||||||
"views/account_payment_views.xml",
|
"views/account_payment_views.xml",
|
||||||
"views/account_move_views.xml",
|
"views/account_move_views.xml",
|
||||||
|
"views/account_bank_statement_views.xml",
|
||||||
"views/res_users_views.xml",
|
"views/res_users_views.xml",
|
||||||
"views/pms_room_type_class_views.xml",
|
"views/pms_room_type_class_views.xml",
|
||||||
"views/pms_room_type_restriction_views.xml",
|
"views/pms_availability_plan_views.xml",
|
||||||
"views/pms_room_type_restriction_item_views.xml",
|
"views/pms_availability_plan_rule_views.xml",
|
||||||
"views/pms_service_views.xml",
|
|
||||||
"views/pms_service_line_views.xml",
|
|
||||||
"views/pms_shared_room_views.xml",
|
"views/pms_shared_room_views.xml",
|
||||||
"views/res_partner_views.xml",
|
"views/res_partner_views.xml",
|
||||||
"views/product_pricelist_views.xml",
|
"views/product_pricelist_views.xml",
|
||||||
@@ -63,8 +64,12 @@
|
|||||||
"views/pms_sale_channel.xml",
|
"views/pms_sale_channel.xml",
|
||||||
"views/product_template_views.xml",
|
"views/product_template_views.xml",
|
||||||
"views/webclient_templates.xml",
|
"views/webclient_templates.xml",
|
||||||
"views/ir_sequence_views.xml",
|
"views/account_journal_views.xml",
|
||||||
"wizards/wizard_reservation.xml",
|
"views/folio_portal_templates.xml",
|
||||||
|
"views/reservation_portal_templates.xml",
|
||||||
|
"wizards/wizard_split_join_swap_reservation.xml",
|
||||||
|
"wizards/wizard_massive_changes.xml",
|
||||||
|
"wizards/wizard_advanced_filters.xml",
|
||||||
],
|
],
|
||||||
"demo": [
|
"demo": [
|
||||||
"demo/pms_master_data.xml",
|
"demo/pms_master_data.xml",
|
||||||
@@ -73,6 +78,7 @@
|
|||||||
],
|
],
|
||||||
"qweb": [
|
"qweb": [
|
||||||
"static/src/xml/pms_base_templates.xml",
|
"static/src/xml/pms_base_templates.xml",
|
||||||
|
"static/src/xml/reservation_group_button_views.xml",
|
||||||
],
|
],
|
||||||
"post_init_hook": "post_init_hook",
|
"pre_init_hook": "pre_init_hook",
|
||||||
}
|
}
|
||||||
|
|||||||
1
pms/controllers/__init__.py
Normal file
1
pms/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import pms_portal
|
||||||
216
pms/controllers/pms_portal.py
Normal file
216
pms/controllers/pms_portal.py
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
from odoo import _, http
|
||||||
|
from odoo.exceptions import AccessError, MissingError
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
|
||||||
|
|
||||||
|
|
||||||
|
class PortalFolio(CustomerPortal):
|
||||||
|
def _prepare_home_portal_values(self, counters):
|
||||||
|
partner = request.env.user.partner_id
|
||||||
|
values = super()._prepare_home_portal_values(counters)
|
||||||
|
Folio = request.env["pms.folio"]
|
||||||
|
if "folio_count" in counters:
|
||||||
|
values["folio_count"] = (
|
||||||
|
Folio.search_count(
|
||||||
|
[
|
||||||
|
("partner_id", "=", partner.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if Folio.check_access_rights("read", raise_exception=False)
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def _folio_get_page_view_values(self, folio, access_token, **kwargs):
|
||||||
|
values = {"folio": folio, "token": access_token}
|
||||||
|
return self._get_page_view_values(
|
||||||
|
folio, access_token, values, "my_folios_history", False, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
["/my/folios", "/my/folios/page/<int:page>"],
|
||||||
|
type="http",
|
||||||
|
auth="user",
|
||||||
|
website=True,
|
||||||
|
)
|
||||||
|
def portal_my_folios(
|
||||||
|
self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw
|
||||||
|
):
|
||||||
|
partner = request.env.user.partner_id
|
||||||
|
values = self._prepare_portal_layout_values()
|
||||||
|
PmsFolio = request.env["pms.folio"]
|
||||||
|
values["folios"] = PmsFolio.search(
|
||||||
|
[
|
||||||
|
("partner_id", "child_of", partner.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
domain = [
|
||||||
|
("partner_id", "child_of", partner.id),
|
||||||
|
]
|
||||||
|
searchbar_sortings = {
|
||||||
|
"date": {"label": _("Order Date"), "folio": "date_order desc"},
|
||||||
|
"name": {"label": _("Reference"), "folio": "name"},
|
||||||
|
"stage": {"label": _("Stage"), "folio": "state"},
|
||||||
|
}
|
||||||
|
if not sortby:
|
||||||
|
sortby = "date"
|
||||||
|
sort_order = searchbar_sortings[sortby]["folio"]
|
||||||
|
|
||||||
|
if date_begin and date_end:
|
||||||
|
domain += [
|
||||||
|
("create_date", ">", date_begin),
|
||||||
|
("create_date", "<=", date_end),
|
||||||
|
]
|
||||||
|
folio_count = PmsFolio.search_count(domain)
|
||||||
|
pager = portal_pager(
|
||||||
|
url="/my/folios",
|
||||||
|
url_args={"date_begin": date_begin, "date_end": date_end, "sortby": sortby},
|
||||||
|
total=folio_count,
|
||||||
|
page=page,
|
||||||
|
step=self._items_per_page,
|
||||||
|
)
|
||||||
|
folios = PmsFolio.search(
|
||||||
|
domain, order=sort_order, limit=self._items_per_page, offset=pager["offset"]
|
||||||
|
)
|
||||||
|
request.session["my_folios_history"] = folios.ids[:100]
|
||||||
|
values.update(
|
||||||
|
{
|
||||||
|
"date": date_begin,
|
||||||
|
"folios": folios.sudo(),
|
||||||
|
"page_name": "folios",
|
||||||
|
"pager": pager,
|
||||||
|
"default_url": "/my/folios",
|
||||||
|
"searchbar_sortings": searchbar_sortings,
|
||||||
|
"sortby": sortby,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return request.render("pms.portal_my_folio", values)
|
||||||
|
|
||||||
|
@http.route(["/my/folios/<int:folio_id>"], type="http", auth="user", website=True)
|
||||||
|
def portal_my_folio_detail(
|
||||||
|
self, folio_id, access_token=None, report_type=None, download=False, **kw
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
folio_sudo = self._document_check_access(
|
||||||
|
"pms.folio",
|
||||||
|
folio_id,
|
||||||
|
access_token=access_token,
|
||||||
|
)
|
||||||
|
except (AccessError, MissingError):
|
||||||
|
return request.redirect("/my")
|
||||||
|
if report_type in ("html", "pdf", "text"):
|
||||||
|
return self._show_report(
|
||||||
|
model=folio_sudo,
|
||||||
|
report_type=report_type,
|
||||||
|
report_ref="pms.action_report_folio",
|
||||||
|
download=download,
|
||||||
|
)
|
||||||
|
values = self._folio_get_page_view_values(folio_sudo, access_token, **kw)
|
||||||
|
return request.render("pms.folio_portal_template", values)
|
||||||
|
|
||||||
|
|
||||||
|
class PortalReservation(CustomerPortal):
|
||||||
|
def _prepare_home_portal_values(self, counters):
|
||||||
|
partner = request.env.user.partner_id
|
||||||
|
values = super()._prepare_home_portal_values(counters)
|
||||||
|
Reservation = request.env["pms.reservation"]
|
||||||
|
if "reservation_count" in counters:
|
||||||
|
values["reservation_count"] = (
|
||||||
|
Reservation.search_count(
|
||||||
|
[
|
||||||
|
("partner_id", "=", partner.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if Reservation.check_access_rights("read", raise_exception=False)
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def _reservation_get_page_view_values(self, reservation, access_token, **kwargs):
|
||||||
|
values = {"reservation": reservation, "token": access_token}
|
||||||
|
return self._get_page_view_values(
|
||||||
|
reservation,
|
||||||
|
access_token,
|
||||||
|
values,
|
||||||
|
"my_reservations_history",
|
||||||
|
False,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
["/my/reservations", "/my/reservations/page/<int:page>"],
|
||||||
|
type="http",
|
||||||
|
auth="user",
|
||||||
|
website=True,
|
||||||
|
)
|
||||||
|
def portal_my_reservations(
|
||||||
|
self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw
|
||||||
|
):
|
||||||
|
partner = request.env.user.partner_id
|
||||||
|
values = self._prepare_portal_layout_values()
|
||||||
|
Reservation = request.env["pms.reservation"]
|
||||||
|
values["reservations"] = Reservation.search(
|
||||||
|
[
|
||||||
|
("partner_id", "child_of", partner.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
domain = [
|
||||||
|
("partner_id", "child_of", partner.id),
|
||||||
|
]
|
||||||
|
if date_begin and date_end:
|
||||||
|
domain += [
|
||||||
|
("create_date", ">", date_begin),
|
||||||
|
("create_date", "<=", date_end),
|
||||||
|
]
|
||||||
|
reservation_count = Reservation.search_count(domain)
|
||||||
|
pager = portal_pager(
|
||||||
|
url="/my/reservations",
|
||||||
|
url_args={"date_begin": date_begin, "date_end": date_end},
|
||||||
|
total=reservation_count,
|
||||||
|
page=page,
|
||||||
|
step=self._items_per_page,
|
||||||
|
)
|
||||||
|
reservations = Reservation.search(
|
||||||
|
domain, limit=self._items_per_page, offset=pager["offset"]
|
||||||
|
)
|
||||||
|
folios_dict = {}
|
||||||
|
for reservation in reservations:
|
||||||
|
folio = reservation.folio_id
|
||||||
|
folios_dict[folio] = ""
|
||||||
|
|
||||||
|
request.session["my_reservations_history"] = reservations.ids[:100]
|
||||||
|
values.update(
|
||||||
|
{
|
||||||
|
"date": date_begin,
|
||||||
|
"reservations": reservations.sudo(),
|
||||||
|
"page_name": "reservations",
|
||||||
|
"pager": pager,
|
||||||
|
"default_url": "/my/reservations",
|
||||||
|
"folios_dict": folios_dict,
|
||||||
|
"partner": partner,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return request.render("pms.portal_my_reservation", values)
|
||||||
|
|
||||||
|
@http.route(
|
||||||
|
["/my/reservations/<int:reservation_id>"],
|
||||||
|
type="http",
|
||||||
|
auth="user",
|
||||||
|
website=True,
|
||||||
|
)
|
||||||
|
def portal_my_reservation_detail(self, reservation_id, access_token=None, **kw):
|
||||||
|
try:
|
||||||
|
reservation_sudo = self._document_check_access(
|
||||||
|
"pms.reservation",
|
||||||
|
reservation_id,
|
||||||
|
access_token=access_token,
|
||||||
|
)
|
||||||
|
except (AccessError, MissingError):
|
||||||
|
return request.redirect("/my")
|
||||||
|
# for attachment in reservation_sudo.attachment_ids:
|
||||||
|
# attachment.generate_access_token()
|
||||||
|
values = self._reservation_get_page_view_values(
|
||||||
|
reservation_sudo, access_token, **kw
|
||||||
|
)
|
||||||
|
return request.render("pms.portal_my_reservation_detail", values)
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
name="nextcall"
|
name="nextcall"
|
||||||
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 09:00:00')"
|
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 09:00:00')"
|
||||||
/>
|
/>
|
||||||
<field name="code">model.auto_no_show()</field>
|
<field name="code">model.auto_arrival_delayed()</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- Set reservation like No Checout if checkout is not confirmed-->
|
<!-- Set reservation like No Checout if checkout is not confirmed-->
|
||||||
<record model="ir.cron" id="nocheckout_reservations">
|
<record model="ir.cron" id="nocheckout_reservations">
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
<field name="model_id" ref="model_pms_reservation" />
|
<field name="model_id" ref="model_pms_reservation" />
|
||||||
<field name="nextcall" eval="DateTime.now()" />
|
<field name="nextcall" eval="DateTime.now()" />
|
||||||
<field name="code">model.auto_no_checkout()</field>
|
<field name="code">model.auto_departure_delayed()</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- Scheduler For To Inform Guest About Reservation Before 24 Hours -->
|
<!-- Scheduler For To Inform Guest About Reservation Before 24 Hours -->
|
||||||
<record model="ir.cron" id="autocheckout_reservations">
|
<record model="ir.cron" id="autocheckout_reservations">
|
||||||
|
|||||||
@@ -5,33 +5,48 @@
|
|||||||
name="PMS Management"
|
name="PMS Management"
|
||||||
sequence="8"
|
sequence="8"
|
||||||
web_icon="pms,static/description/icon.png"
|
web_icon="pms,static/description/icon.png"
|
||||||
groups="pms.group_pms_user,pms.group_pms_call"
|
|
||||||
/>
|
/>
|
||||||
<menuitem
|
<menuitem
|
||||||
id="pms_configuration_menu"
|
id="menu_reservations"
|
||||||
name="Configuration"
|
name="Reservations"
|
||||||
|
parent="pms_management_menu"
|
||||||
|
sequence="5"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="pms_sales_menu"
|
||||||
|
name="Sales"
|
||||||
|
sequence="15"
|
||||||
|
parent="pms_management_menu"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="pms_contacts_menu"
|
||||||
|
name="Contacts"
|
||||||
sequence="20"
|
sequence="20"
|
||||||
parent="pms_management_menu"
|
parent="pms_management_menu"
|
||||||
groups="pms.group_pms_user"
|
groups="pms.group_pms_user"
|
||||||
/>
|
/>
|
||||||
<menuitem
|
<menuitem
|
||||||
id="pms_reports_menu"
|
id="revenue_management_menu"
|
||||||
name="Reports"
|
name="Revenue Management"
|
||||||
sequence="15"
|
sequence="25"
|
||||||
parent="pms_management_menu"
|
parent="pms_management_menu"
|
||||||
groups="pms.group_pms_user"
|
|
||||||
/>
|
/>
|
||||||
<menuitem
|
<menuitem
|
||||||
id="menu_account_finance_xls_reports"
|
id="pms_rooms_menu"
|
||||||
name="XLS Reports"
|
name="Rooms"
|
||||||
parent="pms.pms_reports_menu"
|
sequence="35"
|
||||||
sequence="50"
|
parent="pms_management_menu"
|
||||||
/>
|
/>
|
||||||
<menuitem
|
<menuitem
|
||||||
id="configuration_others"
|
id="pms_services_menu"
|
||||||
|
name="Services"
|
||||||
|
sequence="45"
|
||||||
|
parent="pms_management_menu"
|
||||||
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="pms_configuration_menu"
|
||||||
name="Configuration"
|
name="Configuration"
|
||||||
parent="pms.pms_configuration_menu"
|
sequence="55"
|
||||||
sequence="10"
|
parent="pms_management_menu"
|
||||||
groups="pms.group_pms_manager"
|
|
||||||
/>
|
/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -2,14 +2,13 @@
|
|||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data noupdate="1">
|
||||||
<!-- Basic pms -->
|
<!-- Basic pms -->
|
||||||
<record id="main_pms_room_type_restriction" model="pms.room.type.restriction">
|
<record id="main_pms_availability_plan" model="pms.availability.plan">
|
||||||
<field name="name">Restriction Plan</field>
|
<field name="name">Availability Plan</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="main_pms_property" model="pms.property">
|
<record id="main_pms_property" model="pms.property">
|
||||||
<field name="name">My Property</field>
|
<field name="name">My Property</field>
|
||||||
<field name="company_id" ref="base.main_company" />
|
<field name="company_id" ref="base.main_company" />
|
||||||
<field name="default_pricelist_id" ref="product.list0" />
|
<field name="default_pricelist_id" ref="product.list0" />
|
||||||
<field name="default_restriction_id" ref="main_pms_room_type_restriction" />
|
|
||||||
<field name="street">Rua Street Demo, s/n</field>
|
<field name="street">Rua Street Demo, s/n</field>
|
||||||
<field name="city">Commitsun city</field>
|
<field name="city">Commitsun city</field>
|
||||||
<field name="country_id" ref="base.es" />
|
<field name="country_id" ref="base.es" />
|
||||||
@@ -18,6 +17,8 @@
|
|||||||
<field name="email">commitsun@hootel.com</field>
|
<field name="email">commitsun@hootel.com</field>
|
||||||
<field name="website">https://www.commitsun.com</field>
|
<field name="website">https://www.commitsun.com</field>
|
||||||
<field name="folio_sequence_id" ref="pms.seq_pms_folio" />
|
<field name="folio_sequence_id" ref="pms.seq_pms_folio" />
|
||||||
|
<field name="checkin_sequence_id" ref="pms.seq_pms_checkin" />
|
||||||
|
<field name="reservation_sequence_id" ref="pms.seq_pms_reservation" />
|
||||||
</record>
|
</record>
|
||||||
<!-- pms.users -->
|
<!-- pms.users -->
|
||||||
<record id="base.user_root" model="res.users">
|
<record id="base.user_root" model="res.users">
|
||||||
|
|||||||
@@ -1,12 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data noupdate="1">
|
||||||
<!-- Sequences for pms folio -->
|
|
||||||
<record model="ir.sequence" id="seq_pms_folio">
|
<record model="ir.sequence" id="seq_pms_folio">
|
||||||
<field name="name">PMS Folio</field>
|
<field name="name">PMS Folio</field>
|
||||||
<field name="code">pms.folio</field>
|
<field name="code">pms.folio</field>
|
||||||
<field name="prefix">F/</field>
|
<field name="prefix">F/%(y)s</field>
|
||||||
<field name="padding">5</field>
|
<field name="suffix">%(sec)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.sequence" id="seq_pms_reservation">
|
||||||
|
<field name="name">PMS Reservation</field>
|
||||||
|
<field name="code">pms.reservation</field>
|
||||||
|
<field name="prefix">R/%(y)s</field>
|
||||||
|
<field name="suffix">%(sec)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.sequence" id="seq_pms_checkin">
|
||||||
|
<field name="name">PMS Checkin</field>
|
||||||
|
<field name="code">pms.checkin.partner</field>
|
||||||
|
<field name="prefix">C/%(y)s</field>
|
||||||
|
<field name="suffix">%(sec)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
<!-- reservation of 1 economic room for 1 person -->
|
<!-- reservation of 1 economic room for 1 person -->
|
||||||
<record id="pms_folio_0" model="pms.folio">
|
<record id="pms_folio_0" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_27" />
|
<field name="partner_id" ref="base.res_partner_address_27" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -21,6 +23,8 @@
|
|||||||
<!-- reservation of 1 triple room for 3 people on behalf on the company -->
|
<!-- reservation of 1 triple room for 3 people on behalf on the company -->
|
||||||
<record id="pms_folio_1" model="pms.folio">
|
<record id="pms_folio_1" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -37,6 +41,8 @@
|
|||||||
<!-- TODO: The third reservation is marked from State: Cancelled to Pending arrival at Folio creation -->
|
<!-- TODO: The third reservation is marked from State: Cancelled to Pending arrival at Folio creation -->
|
||||||
<record id="pms_folio_2" model="pms.folio">
|
<record id="pms_folio_2" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -69,6 +75,8 @@
|
|||||||
<!-- reservation of the conference room for 1 day on behalf of a company -->
|
<!-- reservation of the conference room for 1 day on behalf of a company -->
|
||||||
<record id="pms_folio_3" model="pms.folio">
|
<record id="pms_folio_3" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -84,7 +92,9 @@
|
|||||||
<!-- out of service room -->
|
<!-- out of service room -->
|
||||||
<record id="pms_folio_4" model="pms.folio">
|
<record id="pms_folio_4" model="pms.folio">
|
||||||
<field name="partner_id" ref="main_pms_property" />
|
<field name="partner_id" ref="main_pms_property" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="reservation_type">out</field>
|
<field name="reservation_type">out</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -103,6 +113,8 @@
|
|||||||
<!-- reservation of 1 double room for 1 day-->
|
<!-- reservation of 1 double room for 1 day-->
|
||||||
<record id="pms_folio_5" model="pms.folio">
|
<record id="pms_folio_5" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_15" />
|
<field name="partner_id" ref="base.res_partner_address_15" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -120,6 +132,8 @@
|
|||||||
<!-- reservation of 1 triple room for for 1 person and 1 day-->
|
<!-- reservation of 1 triple room for for 1 person and 1 day-->
|
||||||
<record id="pms_folio_6" model="pms.folio">
|
<record id="pms_folio_6" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_4" />
|
<field name="partner_id" ref="base.res_partner_4" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -136,6 +150,8 @@
|
|||||||
<!-- reservation of all rooms for 1 day on behalf of a company -->
|
<!-- reservation of all rooms for 1 day on behalf of a company -->
|
||||||
<record id="pms_folio_7" model="pms.folio">
|
<record id="pms_folio_7" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_10" />
|
<field name="partner_id" ref="base.res_partner_10" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -208,7 +224,9 @@
|
|||||||
<!-- out of service single-101 room -->
|
<!-- out of service single-101 room -->
|
||||||
<record id="pms_folio_8" model="pms.folio">
|
<record id="pms_folio_8" model="pms.folio">
|
||||||
<field name="partner_id" ref="main_pms_property" />
|
<field name="partner_id" ref="main_pms_property" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="reservation_type">out</field>
|
<field name="reservation_type">out</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -227,6 +245,8 @@
|
|||||||
<!--2 reservetions in diferent rooms and diferent days-->
|
<!--2 reservetions in diferent rooms and diferent days-->
|
||||||
<record id="pms_folio_9" model="pms.folio">
|
<record id="pms_folio_9" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_33" />
|
<field name="partner_id" ref="base.res_partner_address_33" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -251,6 +271,8 @@
|
|||||||
<!--reservation of the conference room for 3 days-->
|
<!--reservation of the conference room for 3 days-->
|
||||||
<record id="pms_folio_10" model="pms.folio">
|
<record id="pms_folio_10" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_main2" />
|
<field name="partner_id" ref="base.res_partner_main2" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -266,6 +288,8 @@
|
|||||||
<!--reservation of 2 single rooms and 1 triple room-->
|
<!--reservation of 2 single rooms and 1 triple room-->
|
||||||
<record id="pms_folio_11" model="pms.folio">
|
<record id="pms_folio_11" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -298,6 +322,8 @@
|
|||||||
<!--Reservation of economic room-->
|
<!--Reservation of economic room-->
|
||||||
<record id="pms_folio_12" model="pms.folio">
|
<record id="pms_folio_12" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_1" />
|
<field name="partner_id" ref="base.res_partner_1" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -313,7 +339,9 @@
|
|||||||
<!--Reservation of the conference room with VIP privacy closure reason-->
|
<!--Reservation of the conference room with VIP privacy closure reason-->
|
||||||
<record id="pms_folio_13" model="pms.folio">
|
<record id="pms_folio_13" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="reservation_type">normal</field>
|
<field name="reservation_type">normal</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -330,6 +358,8 @@
|
|||||||
<!--Reservation of triple room with 2 adults and 1 children-->
|
<!--Reservation of triple room with 2 adults and 1 children-->
|
||||||
<record id="pms_folio_14" model="pms.folio">
|
<record id="pms_folio_14" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -347,7 +377,9 @@
|
|||||||
<!-- TODO: The reservation is marked from State: Cancelled to Pending arrival at Folio creation -->
|
<!-- TODO: The reservation is marked from State: Cancelled to Pending arrival at Folio creation -->
|
||||||
<record id="pms_folio_15" model="pms.folio">
|
<record id="pms_folio_15" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_18" />
|
<field name="partner_id" ref="base.res_partner_18" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="reservation_type">normal</field>
|
<field name="reservation_type">normal</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -363,7 +395,9 @@
|
|||||||
<!--Reservation of double room without default hours-->
|
<!--Reservation of double room without default hours-->
|
||||||
<record id="pms_folio_16" model="pms.folio">
|
<record id="pms_folio_16" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_18" />
|
<field name="partner_id" ref="base.res_partner_18" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="reservation_type">normal</field>
|
<field name="reservation_type">normal</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -384,7 +418,9 @@
|
|||||||
<!-- TODO: The reservation is marked from State: Pre-reservation to Pending arrival at Folio creation -->
|
<!-- TODO: The reservation is marked from State: Pre-reservation to Pending arrival at Folio creation -->
|
||||||
<record id="pms_folio_17" model="pms.folio">
|
<record id="pms_folio_17" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_32" />
|
<field name="partner_id" ref="base.res_partner_address_32" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="reservation_type">normal</field>
|
<field name="reservation_type">normal</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -400,6 +436,8 @@
|
|||||||
<!--Reservation of 2 double rooms and 1 triple room whith board service-->
|
<!--Reservation of 2 double rooms and 1 triple room whith board service-->
|
||||||
<record id="pms_folio_18" model="pms.folio">
|
<record id="pms_folio_18" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_30" />
|
<field name="partner_id" ref="base.res_partner_address_30" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -436,6 +474,8 @@
|
|||||||
<!--Reservation of economic room and 3 single rooms whith differents board services-->
|
<!--Reservation of economic room and 3 single rooms whith differents board services-->
|
||||||
<record id="pms_folio_19" model="pms.folio">
|
<record id="pms_folio_19" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_18" />
|
<field name="partner_id" ref="base.res_partner_18" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -479,6 +519,8 @@
|
|||||||
<!--Reservation of 2 single rooms whith differents arrival and departure hours-->
|
<!--Reservation of 2 single rooms whith differents arrival and departure hours-->
|
||||||
<record id="pms_folio_20" model="pms.folio">
|
<record id="pms_folio_20" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_25" />
|
<field name="partner_id" ref="base.res_partner_address_25" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -507,6 +549,8 @@
|
|||||||
<!--Reservation of 2 double rooms whith differents arrival and departure hours and one room whith board service-->
|
<!--Reservation of 2 double rooms whith differents arrival and departure hours and one room whith board service-->
|
||||||
<record id="pms_folio_21" model="pms.folio">
|
<record id="pms_folio_21" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_32" />
|
<field name="partner_id" ref="base.res_partner_address_32" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -535,6 +579,8 @@
|
|||||||
<!--Reservation of conference room whith lunch and dinner services-->
|
<!--Reservation of conference room whith lunch and dinner services-->
|
||||||
<record id="pms_folio_22" model="pms.folio">
|
<record id="pms_folio_22" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_10" />
|
<field name="partner_id" ref="base.res_partner_10" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -554,6 +600,8 @@
|
|||||||
<!--Reservation of economic room for a week-->
|
<!--Reservation of economic room for a week-->
|
||||||
<record id="pms_folio_23" model="pms.folio">
|
<record id="pms_folio_23" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_4" />
|
<field name="partner_id" ref="base.res_partner_4" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -570,6 +618,8 @@
|
|||||||
<!--Reservation of 1 single room for a week-->
|
<!--Reservation of 1 single room for a week-->
|
||||||
<record id="pms_folio_24" model="pms.folio">
|
<record id="pms_folio_24" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_27" />
|
<field name="partner_id" ref="base.res_partner_address_27" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -587,6 +637,8 @@
|
|||||||
<!--Reservation of 1 single room for a week whith board service-->
|
<!--Reservation of 1 single room for a week whith board service-->
|
||||||
<record id="pms_folio_25" model="pms.folio">
|
<record id="pms_folio_25" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -604,6 +656,8 @@
|
|||||||
<!--Reservation of 2 double rooms for a week whith diferent checkouts-->
|
<!--Reservation of 2 double rooms for a week whith diferent checkouts-->
|
||||||
<record id="pms_folio_26" model="pms.folio">
|
<record id="pms_folio_26" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_27" />
|
<field name="partner_id" ref="base.res_partner_address_27" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0),
|
eval="[(5, 0),
|
||||||
@@ -628,6 +682,8 @@
|
|||||||
<!--Reservation of the conference room with VIP privacy closure reason and board service-->
|
<!--Reservation of the conference room with VIP privacy closure reason and board service-->
|
||||||
<record id="pms_folio_27" model="pms.folio">
|
<record id="pms_folio_27" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_3" />
|
<field name="partner_id" ref="base.res_partner_3" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -645,6 +701,8 @@
|
|||||||
<!--Reservation of 1 triple room for 1 adult and 2 children whith board service-->
|
<!--Reservation of 1 triple room for 1 adult and 2 children whith board service-->
|
||||||
<record id="pms_folio_28" model="pms.folio">
|
<record id="pms_folio_28" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_17" />
|
<field name="partner_id" ref="base.res_partner_address_17" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -661,6 +719,8 @@
|
|||||||
<!--Reservation of 1 triple room for 1 adult and 2 children whith specific arrival and departure hours and whith board service-->
|
<!--Reservation of 1 triple room for 1 adult and 2 children whith specific arrival and departure hours and whith board service-->
|
||||||
<record id="pms_folio_29" model="pms.folio">
|
<record id="pms_folio_29" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_25" />
|
<field name="partner_id" ref="base.res_partner_address_25" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -679,6 +739,8 @@
|
|||||||
<!-- reservation of 1 triple room for 1 day-->
|
<!-- reservation of 1 triple room for 1 day-->
|
||||||
<record id="pms_folio_30" model="pms.folio">
|
<record id="pms_folio_30" model="pms.folio">
|
||||||
<field name="partner_id" ref="base.res_partner_address_15" />
|
<field name="partner_id" ref="base.res_partner_address_15" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
<field
|
<field
|
||||||
name="reservation_ids"
|
name="reservation_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
|
|||||||
@@ -1,22 +1,125 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data noupdate="1">
|
||||||
|
<!-- Company -->
|
||||||
|
<record id="pms_company1" model="res.company">
|
||||||
|
<field name="name">Alda Company</field>
|
||||||
|
<field name="currency_id" ref="base.EUR" />
|
||||||
|
<field
|
||||||
|
name="favicon"
|
||||||
|
model="res.company"
|
||||||
|
eval="obj()._get_default_favicon(original=True)"
|
||||||
|
/>
|
||||||
|
</record>
|
||||||
|
<!--Availability Plan -->
|
||||||
|
<record id="demo_pms_availability" model="pms.availability.plan">
|
||||||
|
<field name="name">Availability Plan Demo</field>
|
||||||
|
</record>
|
||||||
|
<!-- Sequence -->
|
||||||
|
<record model="ir.sequence" id="seq_pms_folio2">
|
||||||
|
<field name="name">PMS Folio 2</field>
|
||||||
|
<field name="code">pms.folio</field>
|
||||||
|
<field name="prefix">F/%(y)s</field>
|
||||||
|
<field name="suffix">%(sec)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
<field name="company_id" ref="pms_company1" />
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.sequence" id="seq_pms_reservation2">
|
||||||
|
<field name="name">PMS Reservation 2</field>
|
||||||
|
<field name="code">pms.reservation</field>
|
||||||
|
<field name="prefix">R/%(y)s</field>
|
||||||
|
<field name="suffix">%(sec)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
<field name="company_id" ref="pms_company1" />
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.sequence" id="seq_pms_checkin2">
|
||||||
|
<field name="name">PMS Checkin 2</field>
|
||||||
|
<field name="code">pms.checkin.partner</field>
|
||||||
|
<field name="prefix">C/%(y)s</field>
|
||||||
|
<field name="suffix">%(sec)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
<field name="company_id" ref="pms_company1" />
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Properties-->
|
||||||
|
<record id="demo_pms_property" model="pms.property">
|
||||||
|
<field name="name">San Carlos</field>
|
||||||
|
<field name="company_id" ref="base.main_company" />
|
||||||
|
<field name="default_pricelist_id" ref="product.list0" />
|
||||||
|
<field name="folio_sequence_id" ref="pms.seq_pms_folio" />
|
||||||
|
<field name="checkin_sequence_id" ref="pms.seq_pms_checkin" />
|
||||||
|
<field name="reservation_sequence_id" ref="pms.seq_pms_reservation" />
|
||||||
|
</record>
|
||||||
|
<record id="demo_pms_property2" model="pms.property">
|
||||||
|
<field name="name">Agalia</field>
|
||||||
|
<field name="company_id" ref="base.main_company" />
|
||||||
|
<field name="default_pricelist_id" ref="product.list0" />
|
||||||
|
<field name="folio_sequence_id" ref="pms.seq_pms_folio" />
|
||||||
|
<field name="checkin_sequence_id" ref="pms.seq_pms_checkin" />
|
||||||
|
<field name="reservation_sequence_id" ref="pms.seq_pms_reservation" />
|
||||||
|
</record>
|
||||||
|
<record id="demo_pms_property3" model="pms.property">
|
||||||
|
<field name="name">Pilgrim Leon</field>
|
||||||
|
<field name="company_id" ref="pms_company1" />
|
||||||
|
<field name="default_pricelist_id" ref="product.list0" />
|
||||||
|
<field name="folio_sequence_id" ref="pms.seq_pms_folio2" />
|
||||||
|
<field name="checkin_sequence_id" ref="pms.seq_pms_checkin2" />
|
||||||
|
<field name="reservation_sequence_id" ref="pms.seq_pms_reservation2" />
|
||||||
|
</record>
|
||||||
<!-- users -->
|
<!-- users -->
|
||||||
|
<record id="base.user_root" model="res.users">
|
||||||
|
<field name="company_ids" eval="[(4, ref('pms.pms_company1'))]" />
|
||||||
|
<field
|
||||||
|
name="pms_property_ids"
|
||||||
|
eval="[
|
||||||
|
(4, ref('demo_pms_property')),
|
||||||
|
(4, ref('demo_pms_property2')),
|
||||||
|
(4, ref('demo_pms_property3')),
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</record>
|
||||||
|
<record id="base.user_admin" model="res.users">
|
||||||
|
<field name="company_ids" eval="[(4, ref('pms.pms_company1'))]" />
|
||||||
|
<field
|
||||||
|
name="pms_property_ids"
|
||||||
|
eval="[
|
||||||
|
(4, ref('demo_pms_property')),
|
||||||
|
(4, ref('demo_pms_property2')),
|
||||||
|
(4, ref('demo_pms_property3')),
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</record>
|
||||||
<record id="base.user_demo" model="res.users">
|
<record id="base.user_demo" model="res.users">
|
||||||
<field name="groups_id" eval="[(4,ref('pms.group_pms_user'))]" />
|
<field name="groups_id" eval="[(4,ref('pms.group_pms_user'))]" />
|
||||||
<field name="company_id" ref="base.main_company" />
|
<field name="company_id" ref="base.main_company" />
|
||||||
<field name="company_ids" eval="[(4, ref('base.main_company'))]" />
|
<field
|
||||||
|
name="company_ids"
|
||||||
|
eval="[
|
||||||
|
(4, ref('base.main_company')),
|
||||||
|
(4, ref('pms.pms_company1')),
|
||||||
|
]"
|
||||||
|
/>
|
||||||
<field name="pms_property_id" ref="main_pms_property" />
|
<field name="pms_property_id" ref="main_pms_property" />
|
||||||
<field name="pms_property_ids" eval="[(4, ref('main_pms_property'))]" />
|
<field
|
||||||
|
name="pms_property_ids"
|
||||||
|
eval="[
|
||||||
|
(4, ref('main_pms_property')),
|
||||||
|
(4, ref('demo_pms_property')),
|
||||||
|
(4, ref('demo_pms_property2')),
|
||||||
|
(4, ref('demo_pms_property3')),
|
||||||
|
]"
|
||||||
|
/>
|
||||||
</record>
|
</record>
|
||||||
<!-- pms.floor -->
|
<!-- pms.ubication -->
|
||||||
<record id="pms_floor_0" model="pms.floor">
|
<record id="pms_ubication_0" model="pms.ubication">
|
||||||
<field name="name">Ground Floor</field>
|
<field name="name">Ground Floor</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_floor_1" model="pms.floor">
|
<record id="pms_ubication_1" model="pms.ubication">
|
||||||
<field name="name">First Floor</field>
|
<field name="name">First Floor</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_floor_2" model="pms.floor">
|
<record id="pms_ubication_2" model="pms.ubication">
|
||||||
<field name="name">Second Floor</field>
|
<field name="name">Second Floor</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- pms.amenity.type -->
|
<!-- pms.amenity.type -->
|
||||||
@@ -32,50 +135,52 @@
|
|||||||
<!-- pms.amenity -->
|
<!-- pms.amenity -->
|
||||||
<record id="pms_amenity_0" model="pms.amenity">
|
<record id="pms_amenity_0" model="pms.amenity">
|
||||||
<field name="name">Shampoo and Soap</field>
|
<field name="name">Shampoo and Soap</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_0" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_0" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_amenity_1" model="pms.amenity">
|
<record id="pms_amenity_1" model="pms.amenity">
|
||||||
<field name="name">High-quality Shampoo and Soap Essential Herbs</field>
|
<field name="name">High-quality Shampoo and Soap Essential Herbs</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_0" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_0" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_amenity_2" model="pms.amenity">
|
<record id="pms_amenity_2" model="pms.amenity">
|
||||||
<field name="name">Hair Dryer</field>
|
<field name="name">Hair Dryer</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_0" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_0" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_amenity_3" model="pms.amenity">
|
<record id="pms_amenity_3" model="pms.amenity">
|
||||||
<field name="name">High speed Wired Internet access</field>
|
<field name="name">High speed Wired Internet access</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_1" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_1" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_amenity_4" model="pms.amenity">
|
<record id="pms_amenity_4" model="pms.amenity">
|
||||||
<field name="name">Wi-Fi</field>
|
<field name="name">Wi-Fi</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_1" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_1" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_amenity_5" model="pms.amenity">
|
<record id="pms_amenity_5" model="pms.amenity">
|
||||||
<field name="name">Microwave oven</field>
|
<field name="name">Microwave oven</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_2" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_2" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_amenity_6" model="pms.amenity">
|
<record id="pms_amenity_6" model="pms.amenity">
|
||||||
<field name="name">Half-sized Refrigerator</field>
|
<field name="name">Half-sized Refrigerator</field>
|
||||||
<field name="room_amenity_type_id" ref="pms_amenity_type_2" />
|
<field name="pms_amenity_type_id" ref="pms_amenity_type_2" />
|
||||||
</record>
|
</record>
|
||||||
<!-- pms.room.type.class -->
|
<!-- pms.room.type.class -->
|
||||||
<record id="pms_room_type_class_0" model="pms.room.type.class">
|
<record id="pms_room_type_class_0" model="pms.room.type.class">
|
||||||
<field name="name">Room</field>
|
<field name="name">Room</field>
|
||||||
|
<field name="default_code">RO</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_type_class_1" model="pms.room.type.class">
|
<record id="pms_room_type_class_1" model="pms.room.type.class">
|
||||||
<field name="name">Conference</field>
|
<field name="name">Conference</field>
|
||||||
|
<field name="default_code">CO</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- pms.room.type -->
|
<!-- pms.room.type -->
|
||||||
<record id="pms_room_type_0" model="pms.room.type">
|
<record id="pms_room_type_0" model="pms.room.type">
|
||||||
<field name="name">Economic</field>
|
<field name="name">Economic</field>
|
||||||
<field name="code_type">ECO</field>
|
<field name="default_code">ECO</field>
|
||||||
<field name="list_price">21.00</field>
|
<field name="list_price">21.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_0" />
|
<field name="class_id" ref="pms_room_type_class_0" />
|
||||||
<field name="room_amenity_ids" eval="[(4, ref('pms_amenity_0'))]" />
|
<field name="room_amenity_ids" eval="[(4, ref('pms_amenity_0'))]" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_type_1" model="pms.room.type">
|
<record id="pms_room_type_1" model="pms.room.type">
|
||||||
<field name="name">Single</field>
|
<field name="name">Single</field>
|
||||||
<field name="code_type">SNG</field>
|
<field name="default_code">SNG</field>
|
||||||
<field name="list_price">20.00</field>
|
<field name="list_price">20.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_0" />
|
<field name="class_id" ref="pms_room_type_class_0" />
|
||||||
<field
|
<field
|
||||||
@@ -85,7 +190,7 @@
|
|||||||
</record>
|
</record>
|
||||||
<record id="pms_room_type_2" model="pms.room.type">
|
<record id="pms_room_type_2" model="pms.room.type">
|
||||||
<field name="name">Double</field>
|
<field name="name">Double</field>
|
||||||
<field name="code_type">DBL</field>
|
<field name="default_code">DBL</field>
|
||||||
<field name="list_price">25.00</field>
|
<field name="list_price">25.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_0" />
|
<field name="class_id" ref="pms_room_type_class_0" />
|
||||||
<field
|
<field
|
||||||
@@ -95,7 +200,7 @@
|
|||||||
</record>
|
</record>
|
||||||
<record id="pms_room_type_3" model="pms.room.type">
|
<record id="pms_room_type_3" model="pms.room.type">
|
||||||
<field name="name">Triple</field>
|
<field name="name">Triple</field>
|
||||||
<field name="code_type">TRP</field>
|
<field name="default_code">TRP</field>
|
||||||
<field name="list_price">35.00</field>
|
<field name="list_price">35.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_0" />
|
<field name="class_id" ref="pms_room_type_class_0" />
|
||||||
<field
|
<field
|
||||||
@@ -105,7 +210,7 @@
|
|||||||
</record>
|
</record>
|
||||||
<record id="pms_room_type_4" model="pms.room.type">
|
<record id="pms_room_type_4" model="pms.room.type">
|
||||||
<field name="name">Conference Room</field>
|
<field name="name">Conference Room</field>
|
||||||
<field name="code_type">CFR</field>
|
<field name="default_code">CFR</field>
|
||||||
<field name="list_price">80.00</field>
|
<field name="list_price">80.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_1" />
|
<field name="class_id" ref="pms_room_type_class_1" />
|
||||||
<field
|
<field
|
||||||
@@ -117,51 +222,59 @@
|
|||||||
<record id="pms_room_0" model="pms.room">
|
<record id="pms_room_0" model="pms.room">
|
||||||
<field name="name">Economic-101</field>
|
<field name="name">Economic-101</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="floor_id" ref="pms_floor_1" />
|
<field name="ubication_id" ref="pms_ubication_1" />
|
||||||
<field name="capacity">2</field>
|
<field name="capacity">2</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_1" model="pms.room">
|
<record id="pms_room_1" model="pms.room">
|
||||||
<field name="name">Single-101</field>
|
<field name="name">Single-101</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="floor_id" ref="pms_floor_1" />
|
<field name="ubication_id" ref="pms_ubication_1" />
|
||||||
<field name="capacity">1</field>
|
<field name="capacity">1</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_2" model="pms.room">
|
<record id="pms_room_2" model="pms.room">
|
||||||
<field name="name">Single-102</field>
|
<field name="name">Single-102</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="floor_id" ref="pms_floor_1" />
|
<field name="ubication_id" ref="pms_ubication_1" />
|
||||||
<field name="capacity">1</field>
|
<field name="capacity">1</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_3" model="pms.room">
|
<record id="pms_room_3" model="pms.room">
|
||||||
<field name="name">Single-103</field>
|
<field name="name">Single-103</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="floor_id" ref="pms_floor_1" />
|
<field name="ubication_id" ref="pms_ubication_1" />
|
||||||
<field name="capacity">1</field>
|
<field name="capacity">1</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_4" model="pms.room">
|
<record id="pms_room_4" model="pms.room">
|
||||||
<field name="name">Double-201</field>
|
<field name="name">Double-201</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="floor_id" ref="pms_floor_2" />
|
<field name="ubication_id" ref="pms_ubication_2" />
|
||||||
<field name="capacity">2</field>
|
<field name="capacity">2</field>
|
||||||
<field name="extra_beds_allowed">1</field>
|
<field name="extra_beds_allowed">1</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_5" model="pms.room">
|
<record id="pms_room_5" model="pms.room">
|
||||||
<field name="name">Double-202</field>
|
<field name="name">Double-202</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="floor_id" ref="pms_floor_2" />
|
<field name="ubication_id" ref="pms_ubication_2" />
|
||||||
<field name="capacity">2</field>
|
<field name="capacity">2</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_6" model="pms.room">
|
<record id="pms_room_6" model="pms.room">
|
||||||
<field name="name">Triple-203</field>
|
<field name="name">Triple-203</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_3" />
|
<field name="room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="floor_id" ref="pms_floor_2" />
|
<field name="ubication_id" ref="pms_ubication_2" />
|
||||||
<field name="capacity">3</field>
|
<field name="capacity">3</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_room_7" model="pms.room">
|
<record id="pms_room_7" model="pms.room">
|
||||||
<field name="name">Open Talk Away Room</field>
|
<field name="name">Open Talk Away Room</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_4" />
|
<field name="room_type_id" ref="pms_room_type_4" />
|
||||||
<field name="floor_id" ref="pms_floor_0" />
|
<field name="ubication_id" ref="pms_ubication_0" />
|
||||||
<field name="capacity">1</field>
|
<field name="capacity">10</field>
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<!-- product.product for pms services -->
|
<!-- product.product for pms services -->
|
||||||
<record id="pms_service_0" model="product.product">
|
<record id="pms_service_0" model="product.product">
|
||||||
@@ -171,6 +284,7 @@
|
|||||||
<field name="purchase_ok">False</field>
|
<field name="purchase_ok">False</field>
|
||||||
<field name="per_day">True</field>
|
<field name="per_day">True</field>
|
||||||
<field name="per_person">True</field>
|
<field name="per_person">True</field>
|
||||||
|
<field name="consumed_on">after</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_service_1" model="product.product">
|
<record id="pms_service_1" model="product.product">
|
||||||
<field name="name">Extra Bed</field>
|
<field name="name">Extra Bed</field>
|
||||||
@@ -181,7 +295,6 @@
|
|||||||
<field name="per_person">False</field>
|
<field name="per_person">False</field>
|
||||||
<field name="daily_limit">1</field>
|
<field name="daily_limit">1</field>
|
||||||
<field name="is_extra_bed">True</field>
|
<field name="is_extra_bed">True</field>
|
||||||
<field name="show_in_calendar">True</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_service_3" model="product.product">
|
<record id="pms_service_3" model="product.product">
|
||||||
<field name="name">Late Check-out</field>
|
<field name="name">Late Check-out</field>
|
||||||
@@ -218,16 +331,17 @@
|
|||||||
<!-- pms.board.service -->
|
<!-- pms.board.service -->
|
||||||
<record id="pms_board_service_0" model="pms.board.service">
|
<record id="pms_board_service_0" model="pms.board.service">
|
||||||
<field name="name">BreakFast</field>
|
<field name="name">BreakFast</field>
|
||||||
|
<field name="default_code">BB</field>
|
||||||
<field
|
<field
|
||||||
name="board_service_line_ids"
|
name="board_service_line_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
'product_id': ref('pms_service_0'),
|
'product_id': ref('pms_service_0'),
|
||||||
'amount': 3})]"
|
'amount': 3})]"
|
||||||
/>
|
/>
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_board_service_1" model="pms.board.service">
|
<record id="pms_board_service_1" model="pms.board.service">
|
||||||
<field name="name">Half Board</field>
|
<field name="name">Half Board</field>
|
||||||
|
<field name="default_code">HB</field>
|
||||||
<field
|
<field
|
||||||
name="board_service_line_ids"
|
name="board_service_line_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -237,10 +351,10 @@
|
|||||||
'amount': 8})
|
'amount': 8})
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_board_service_2" model="pms.board.service">
|
<record id="pms_board_service_2" model="pms.board.service">
|
||||||
<field name="name">FullBoard</field>
|
<field name="name">FullBoard</field>
|
||||||
|
<field name="default_code">FB</field>
|
||||||
<field
|
<field
|
||||||
name="board_service_line_ids"
|
name="board_service_line_ids"
|
||||||
eval="[(5, 0), (0, 0, {
|
eval="[(5, 0), (0, 0, {
|
||||||
@@ -252,36 +366,29 @@
|
|||||||
'amount': 8})
|
'amount': 8})
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<!-- pms.board.service.room.type -->
|
<!-- pms.board.service.room.type -->
|
||||||
<!--Room 0 Economic-->
|
<!--Room 0 Economic-->
|
||||||
<record id="pms_board_service_room_0" model="pms.board.service.room.type">
|
<record id="pms_board_service_room_0" model="pms.board.service.room.type">
|
||||||
<field name="pms_board_service_id" ref="pms_board_service_0" />
|
<field name="pms_board_service_id" ref="pms_board_service_0" />
|
||||||
<field name="pms_room_type_id" ref="pms_room_type_0" />
|
<field name="pms_room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_board_service_room_1" model="pms.board.service.room.type">
|
<record id="pms_board_service_room_1" model="pms.board.service.room.type">
|
||||||
<field name="pms_board_service_id" ref="pms_board_service_1" />
|
<field name="pms_board_service_id" ref="pms_board_service_1" />
|
||||||
<field name="pms_room_type_id" ref="pms_room_type_0" />
|
<field name="pms_room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_board_service_room_2" model="pms.board.service.room.type">
|
<record id="pms_board_service_room_2" model="pms.board.service.room.type">
|
||||||
<field name="pms_board_service_id" ref="pms_board_service_1" />
|
<field name="pms_board_service_id" ref="pms_board_service_2" />
|
||||||
<field name="pms_room_type_id" ref="pms_room_type_0" />
|
<field name="pms_room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<!--Room 3 Triple-->
|
<!--Room 3 Triple-->
|
||||||
<record id="pms_board_service_room_3" model="pms.board.service.room.type">
|
<record id="pms_board_service_room_3" model="pms.board.service.room.type">
|
||||||
<field name="pms_board_service_id" ref="pms_board_service_0" />
|
<field name="pms_board_service_id" ref="pms_board_service_0" />
|
||||||
<field name="pms_room_type_id" ref="pms_room_type_3" />
|
<field name="pms_room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_board_service_room_4" model="pms.board.service.room.type">
|
<record id="pms_board_service_room_4" model="pms.board.service.room.type">
|
||||||
<field name="pms_board_service_id" ref="pms_board_service_2" />
|
<field name="pms_board_service_id" ref="pms_board_service_2" />
|
||||||
<field name="pms_room_type_id" ref="pms_room_type_3" />
|
<field name="pms_room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="price_type">fixed</field>
|
|
||||||
</record>
|
</record>
|
||||||
<!-- room.closure.reason -->
|
<!-- room.closure.reason -->
|
||||||
<record id="pms_room_closure_reason_0" model="room.closure.reason">
|
<record id="pms_room_closure_reason_0" model="room.closure.reason">
|
||||||
@@ -297,21 +404,11 @@
|
|||||||
Used for closing of rooms for extra privacy.
|
Used for closing of rooms for extra privacy.
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- Multi pms Demo -->
|
|
||||||
<record id="demo_pms_room_type_restriction" model="pms.room.type.restriction">
|
|
||||||
<field name="name">Restriction Plan Demo</field>
|
|
||||||
</record>
|
|
||||||
<record id="demo_pms_property" model="pms.property">
|
|
||||||
<field name="name">My pms Demo</field>
|
|
||||||
<field name="company_id" ref="base.main_company" />
|
|
||||||
<field name="default_pricelist_id" ref="product.list0" />
|
|
||||||
<field name="default_restriction_id" ref="demo_pms_room_type_restriction" />
|
|
||||||
</record>
|
|
||||||
<!-- pms.room.type -->
|
<!-- pms.room.type -->
|
||||||
<record id="demo_pms_room_type_0" model="pms.room.type">
|
<record id="demo_pms_room_type_0" model="pms.room.type">
|
||||||
<field name="pms_property_ids" eval="[(4, ref('pms.demo_pms_property'))]" />
|
<field name="pms_property_ids" eval="[(4, ref('pms.demo_pms_property'))]" />
|
||||||
<field name="name">Prop. Demo Suite</field>
|
<field name="name">Prop. Demo Suite</field>
|
||||||
<field name="code_type">SUI</field>
|
<field name="default_code">SUI</field>
|
||||||
<field name="list_price">21.00</field>
|
<field name="list_price">21.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_0" />
|
<field name="class_id" ref="pms_room_type_class_0" />
|
||||||
<field name="room_amenity_ids" eval="[(4, ref('pms_amenity_0'))]" />
|
<field name="room_amenity_ids" eval="[(4, ref('pms_amenity_0'))]" />
|
||||||
@@ -319,7 +416,7 @@
|
|||||||
<record id="demo_pms_room_type_1" model="pms.room.type">
|
<record id="demo_pms_room_type_1" model="pms.room.type">
|
||||||
<field name="pms_property_ids" eval="[(4, ref('pms.demo_pms_property'))]" />
|
<field name="pms_property_ids" eval="[(4, ref('pms.demo_pms_property'))]" />
|
||||||
<field name="name">Prop. Demo Views</field>
|
<field name="name">Prop. Demo Views</field>
|
||||||
<field name="code_type">VIE</field>
|
<field name="default_code">VIE</field>
|
||||||
<field name="list_price">20.00</field>
|
<field name="list_price">20.00</field>
|
||||||
<field name="class_id" ref="pms_room_type_class_0" />
|
<field name="class_id" ref="pms_room_type_class_0" />
|
||||||
<field
|
<field
|
||||||
|
|||||||
@@ -4,23 +4,28 @@
|
|||||||
<!-- Economic -->
|
<!-- Economic -->
|
||||||
<record id="pms_reservation_0" model="pms.reservation">
|
<record id="pms_reservation_0" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_27" />
|
<field name="partner_id" ref="base.res_partner_address_27" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(8)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(8)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(9)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(9)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_1" model="pms.reservation">
|
<record id="pms_reservation_1" model="pms.reservation">
|
||||||
<field name="partner_id" ref="main_pms_property" />
|
<field name="partner_id" ref="main_pms_property" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="reservation_type">"out"</field>
|
<field name="reservation_type">"out"</field>
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(-3)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(-3)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(-1)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(-1)" />
|
||||||
<field name="closure_reason_id" ref="pms_room_closure_reason_1" />
|
<field name="closure_reason_id" ref="pms_room_closure_reason_1" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_2" model="pms.reservation">
|
<record id="pms_reservation_2" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_28" />
|
<field name="partner_id" ref="base.res_partner_address_28" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
@@ -41,34 +46,42 @@
|
|||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(1)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(1)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_3" model="pms.reservation">
|
<record id="pms_reservation_3" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_13" />
|
<field name="partner_id" ref="base.res_partner_address_13" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(1)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(1)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_4" model="pms.reservation">
|
<record id="pms_reservation_4" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_16" />
|
<field name="partner_id" ref="base.res_partner_address_16" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(5)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(5)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_5" model="pms.reservation">
|
<record id="pms_reservation_5" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_33" />
|
<field name="partner_id" ref="base.res_partner_address_33" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_0" />
|
<field name="room_type_id" ref="pms_room_type_0" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(5)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(5)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(7)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(7)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<!-- Single -->
|
<!-- Single -->
|
||||||
<record id="pms_reservation_6" model="pms.reservation">
|
<record id="pms_reservation_6" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_27" />
|
<field name="partner_id" ref="base.res_partner_address_27" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
@@ -84,9 +97,11 @@
|
|||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(3)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(3)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_7" model="pms.reservation">
|
<record id="pms_reservation_7" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
@@ -101,62 +116,76 @@
|
|||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(3)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(3)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_8" model="pms.reservation">
|
<record id="pms_reservation_8" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_13" />
|
<field name="partner_id" ref="base.res_partner_address_13" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(7)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(7)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_9" model="pms.reservation">
|
<record id="pms_reservation_9" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_15" />
|
<field name="partner_id" ref="base.res_partner_address_15" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(9)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(9)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_10" model="pms.reservation">
|
<record id="pms_reservation_10" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="children">1</field>
|
<field name="children">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(13)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(13)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(14)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(14)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_11" model="pms.reservation">
|
<record id="pms_reservation_11" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_12" model="pms.reservation">
|
<record id="pms_reservation_12" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_1" />
|
<field name="room_type_id" ref="pms_room_type_1" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(10)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(10)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<!-- Double-->
|
<!-- Double-->
|
||||||
<record id="pms_reservation_13" model="pms.reservation">
|
<record id="pms_reservation_13" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_14" model="pms.reservation">
|
<record id="pms_reservation_14" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_33" />
|
<field name="partner_id" ref="base.res_partner_address_33" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
@@ -169,9 +198,11 @@
|
|||||||
/>
|
/>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_15" model="pms.reservation">
|
<record id="pms_reservation_15" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
@@ -179,26 +210,32 @@
|
|||||||
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_3" />
|
<field name="board_service_room_id" ref="pms_board_service_room_3" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_16" model="pms.reservation">
|
<record id="pms_reservation_16" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_13" />
|
<field name="partner_id" ref="base.res_partner_address_13" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_17" model="pms.reservation">
|
<record id="pms_reservation_17" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_15" />
|
<field name="partner_id" ref="base.res_partner_address_15" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_18" model="pms.reservation">
|
<record id="pms_reservation_18" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
@@ -211,54 +248,66 @@
|
|||||||
/>
|
/>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_19" model="pms.reservation">
|
<record id="pms_reservation_19" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_32" />
|
<field name="partner_id" ref="base.res_partner_address_32" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_20" model="pms.reservation">
|
<record id="pms_reservation_20" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_15" />
|
<field name="partner_id" ref="base.res_partner_address_15" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
<field name="children">1</field>
|
<field name="children">1</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_21" model="pms.reservation">
|
<record id="pms_reservation_21" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_3" />
|
<field name="partner_id" ref="base.res_partner_3" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_22" model="pms.reservation">
|
<record id="pms_reservation_22" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_2" />
|
<field name="room_type_id" ref="pms_room_type_2" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<!-- Triple -->
|
<!-- Triple -->
|
||||||
<record id="pms_reservation_23" model="pms.reservation">
|
<record id="pms_reservation_23" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_4" />
|
<field name="partner_id" ref="base.res_partner_4" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_3" />
|
<field name="room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="adults">3</field>
|
<field name="adults">3</field>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_24" model="pms.reservation">
|
<record id="pms_reservation_24" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_3" />
|
<field name="room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="adults">3</field>
|
<field name="adults">3</field>
|
||||||
@@ -272,9 +321,11 @@
|
|||||||
/>
|
/>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_25" model="pms.reservation">
|
<record id="pms_reservation_25" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_28" />
|
<field name="partner_id" ref="base.res_partner_address_28" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_3" />
|
<field name="room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="adults">1</field>
|
<field name="adults">1</field>
|
||||||
@@ -288,9 +339,11 @@
|
|||||||
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_3" />
|
<field name="board_service_room_id" ref="pms_board_service_room_3" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_26" model="pms.reservation">
|
<record id="pms_reservation_26" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_16" />
|
<field name="partner_id" ref="base.res_partner_address_16" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_3" />
|
<field name="room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="adults">3</field>
|
<field name="adults">3</field>
|
||||||
@@ -304,9 +357,11 @@
|
|||||||
/>
|
/>
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_27" model="pms.reservation">
|
<record id="pms_reservation_27" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_12" />
|
<field name="partner_id" ref="base.res_partner_12" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_3" />
|
<field name="room_type_id" ref="pms_room_type_3" />
|
||||||
<field name="adults">2</field>
|
<field name="adults">2</field>
|
||||||
@@ -328,28 +383,35 @@
|
|||||||
<field name="checkin" eval="DateTime.today()" />
|
<field name="checkin" eval="DateTime.today()" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<!-- Open talk away -->
|
<!-- Open talk away -->
|
||||||
<record id="pms_reservation_28" model="pms.reservation">
|
<record id="pms_reservation_28" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_27" />
|
<field name="partner_id" ref="base.res_partner_address_27" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_4" />
|
<field name="room_type_id" ref="pms_room_type_4" />
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(12)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(12)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_29" model="pms.reservation">
|
<record id="pms_reservation_29" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_32" />
|
<field name="partner_id" ref="base.res_partner_address_32" />
|
||||||
|
<field name="user_id" ref="base.user_demo" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_4" />
|
<field name="room_type_id" ref="pms_room_type_4" />
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(1)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(1)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_30" model="pms.reservation">
|
<record id="pms_reservation_30" model="pms.reservation">
|
||||||
<field name="partner_id" ref="base.res_partner_address_33" />
|
<field name="partner_id" ref="base.res_partner_address_33" />
|
||||||
|
<field name="user_id" ref="base.user_admin" />
|
||||||
<field name="pricelist_id" ref="product.list0" />
|
<field name="pricelist_id" ref="product.list0" />
|
||||||
<field name="room_type_id" ref="pms_room_type_4" />
|
<field name="room_type_id" ref="pms_room_type_4" />
|
||||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||||
|
<field name="pms_property_id" ref="pms.main_pms_property" />
|
||||||
</record>
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
12778
pms/i18n/es.po
12778
pms/i18n/es.po
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,15 @@ from odoo import SUPERUSER_ID
|
|||||||
from odoo.api import Environment
|
from odoo.api import Environment
|
||||||
|
|
||||||
|
|
||||||
def post_init_hook(cr, _):
|
def pre_init_hook(cr):
|
||||||
with Environment.manage():
|
with Environment.manage():
|
||||||
env = Environment(cr, SUPERUSER_ID, {})
|
env = Environment(cr, SUPERUSER_ID, {})
|
||||||
|
ResConfig = env["res.config.settings"]
|
||||||
|
default_values = ResConfig.default_get(list(ResConfig.fields_get()))
|
||||||
|
default_values.update(
|
||||||
|
{"group_product_pricelist": True, "group_sale_pricelist": True}
|
||||||
|
)
|
||||||
|
ResConfig.sudo().create(default_values).execute()
|
||||||
env["ir.config_parameter"].sudo().set_param(
|
env["ir.config_parameter"].sudo().set_param(
|
||||||
"product.product_pricelist_setting", "advanced"
|
"product.product_pricelist_setting", "advanced"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from . import ir_http
|
from . import ir_http
|
||||||
from . import ir_sequence
|
|
||||||
from . import ir_config_parameter
|
from . import ir_config_parameter
|
||||||
|
|
||||||
# from . import payment_return
|
# from . import payment_return
|
||||||
from . import pms_board_service_room_type
|
from . import pms_board_service_room_type
|
||||||
from . import pms_property
|
from . import pms_property
|
||||||
from . import res_users
|
from . import res_users
|
||||||
from . import pms_floor
|
from . import pms_ubication
|
||||||
from . import pms_folio
|
from . import pms_folio
|
||||||
from . import pms_reservation
|
from . import pms_reservation
|
||||||
from . import pms_room
|
from . import pms_room
|
||||||
@@ -21,11 +20,11 @@ from . import pms_room_type
|
|||||||
from . import pms_service
|
from . import pms_service
|
||||||
from . import account_move
|
from . import account_move
|
||||||
from . import product_template
|
from . import product_template
|
||||||
|
from . import product_product
|
||||||
from . import res_company
|
from . import res_company
|
||||||
from . import account_payment
|
from . import account_payment
|
||||||
from . import pms_room_type_availability
|
from . import pms_availability_plan
|
||||||
from . import pms_room_type_restriction
|
from . import pms_availability_plan_rule
|
||||||
from . import pms_room_type_restriction_item
|
|
||||||
from . import pms_reservation_line
|
from . import pms_reservation_line
|
||||||
from . import pms_checkin_partner
|
from . import pms_checkin_partner
|
||||||
from . import product_pricelist
|
from . import product_pricelist
|
||||||
@@ -42,3 +41,8 @@ from . import pms_board_service_room_type_line
|
|||||||
from . import pms_board_service_line
|
from . import pms_board_service_line
|
||||||
from . import account_move_line
|
from . import account_move_line
|
||||||
from . import pms_cancelation_rule
|
from . import pms_cancelation_rule
|
||||||
|
from . import folio_sale_line
|
||||||
|
from . import account_bank_statement_line
|
||||||
|
from . import account_bank_statement
|
||||||
|
from . import account_journal
|
||||||
|
from . import pms_availability
|
||||||
|
|||||||
16
pms/models/account_bank_statement.py
Normal file
16
pms/models/account_bank_statement.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountBankStatement(models.Model):
|
||||||
|
_inherit = "account.bank.statement"
|
||||||
|
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Properties with access to the element",
|
||||||
|
copy=False,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
string="Company",
|
||||||
|
help="The company for Account Bank Statement",
|
||||||
|
)
|
||||||
46
pms/models/account_bank_statement_line.py
Normal file
46
pms/models/account_bank_statement_line.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountBankStatementLine(models.Model):
|
||||||
|
_inherit = "account.bank.statement.line"
|
||||||
|
|
||||||
|
folio_ids = fields.Many2many(
|
||||||
|
string="Folios",
|
||||||
|
comodel_name="pms.folio",
|
||||||
|
ondelete="cascade",
|
||||||
|
relation="account_bank_statement_folio_rel",
|
||||||
|
column1="account_journal_id",
|
||||||
|
column2="folio_id",
|
||||||
|
)
|
||||||
|
reservation_ids = fields.Many2many(
|
||||||
|
string="Reservations",
|
||||||
|
help="Reservations in which the Account Bank Statement Lines are included",
|
||||||
|
comodel_name="pms.reservation",
|
||||||
|
ondelete="cascade",
|
||||||
|
relation="account_bank_statement_reservation_rel",
|
||||||
|
column1="account_bank_statement_id",
|
||||||
|
column2="reservation_id",
|
||||||
|
)
|
||||||
|
service_ids = fields.Many2many(
|
||||||
|
string="Services",
|
||||||
|
help="Services in which the Account Bank Statement Lines are included",
|
||||||
|
comodel_name="pms.service",
|
||||||
|
ondelete="cascade",
|
||||||
|
relation="account_bank_statement_service_rel",
|
||||||
|
column1="account_bank_statement_id",
|
||||||
|
column2="service_id",
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _prepare_move_line_default_vals(self, counterpart_account_id=None):
|
||||||
|
line_vals_list = super(
|
||||||
|
AccountBankStatementLine, self
|
||||||
|
)._prepare_move_line_default_vals(counterpart_account_id)
|
||||||
|
if self.folio_ids:
|
||||||
|
for line in line_vals_list:
|
||||||
|
line.update(
|
||||||
|
{
|
||||||
|
"folio_ids": [(6, 0, self.folio_ids.ids)],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return line_vals_list
|
||||||
21
pms/models/account_journal.py
Normal file
21
pms/models/account_journal.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class AccountJournal(models.Model):
|
||||||
|
_inherit = "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",
|
||||||
|
)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
string="Company",
|
||||||
|
help="The company for Account Jouarnal",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
|
||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||||
import json
|
import json
|
||||||
@@ -12,10 +11,19 @@ class AccountMove(models.Model):
|
|||||||
|
|
||||||
# Field Declarations
|
# Field Declarations
|
||||||
folio_ids = fields.Many2many(
|
folio_ids = fields.Many2many(
|
||||||
comodel_name="pms.folio", compute="_compute_folio_origin"
|
string="Folios",
|
||||||
|
help="Folios where the account move are included",
|
||||||
|
comodel_name="pms.folio",
|
||||||
|
compute="_compute_folio_origin",
|
||||||
|
relation="account_move_folio_ids_rel",
|
||||||
|
column1="account_move_id",
|
||||||
|
column2="folio_ids_id",
|
||||||
|
)
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Property with access to the element",
|
||||||
|
comodel_name="pms.property",
|
||||||
)
|
)
|
||||||
pms_property_id = fields.Many2one("pms.property")
|
|
||||||
from_folio = fields.Boolean(compute="_compute_folio_origin")
|
|
||||||
outstanding_folios_debits_widget = fields.Text(
|
outstanding_folios_debits_widget = fields.Text(
|
||||||
compute="_compute_get_outstanding_folios_JSON"
|
compute="_compute_get_outstanding_folios_JSON"
|
||||||
)
|
)
|
||||||
@@ -23,38 +31,13 @@ class AccountMove(models.Model):
|
|||||||
compute="_compute_get_outstanding_folios_JSON"
|
compute="_compute_get_outstanding_folios_JSON"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compute and Search methods
|
|
||||||
|
|
||||||
def _compute_folio_origin(self):
|
def _compute_folio_origin(self):
|
||||||
for inv in self:
|
for inv in self:
|
||||||
inv.from_folio = False
|
|
||||||
inv.folio_ids = False
|
inv.folio_ids = False
|
||||||
folios = inv.mapped("invoice_line_ids.reservation_ids.folio_id")
|
folios = inv.mapped("invoice_line_ids.folio_ids")
|
||||||
folios |= inv.mapped("invoice_line_ids.service_ids.folio_id")
|
|
||||||
if folios:
|
if folios:
|
||||||
inv.from_folio = True
|
|
||||||
inv.folio_ids = [(6, 0, folios.ids)]
|
inv.folio_ids = [(6, 0, folios.ids)]
|
||||||
|
|
||||||
# Action methods
|
|
||||||
|
|
||||||
def action_folio_payments(self):
|
|
||||||
self.ensure_one()
|
|
||||||
sales = self.mapped("invoice_line_ids.sale_line_ids.order_id")
|
|
||||||
folios = self.env["pms.folio"].search([("order_id.id", "in", sales.ids)])
|
|
||||||
payments_obj = self.env["account.payment"]
|
|
||||||
payments = payments_obj.search([("folio_id", "in", folios.ids)])
|
|
||||||
payment_ids = payments.mapped("id")
|
|
||||||
return {
|
|
||||||
"name": _("Payments"),
|
|
||||||
"view_type": "form",
|
|
||||||
"view_mode": "tree,form",
|
|
||||||
"res_model": "account.payment",
|
|
||||||
"target": "new",
|
|
||||||
"type": "ir.actions.act_window",
|
|
||||||
"domain": [("id", "in", payment_ids)],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Business methods
|
|
||||||
def _compute_get_outstanding_folios_JSON(self):
|
def _compute_get_outstanding_folios_JSON(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
self.outstanding_folios_debits_widget = json.dumps(False)
|
self.outstanding_folios_debits_widget = json.dumps(False)
|
||||||
@@ -124,3 +107,20 @@ class AccountMove(models.Model):
|
|||||||
info["title"] = type_payment
|
info["title"] = type_payment
|
||||||
self.outstanding_folios_debits_widget = json.dumps(info)
|
self.outstanding_folios_debits_widget = json.dumps(info)
|
||||||
self.has_folio_outstanding = True
|
self.has_folio_outstanding = True
|
||||||
|
|
||||||
|
def action_folio_payments(self):
|
||||||
|
self.ensure_one()
|
||||||
|
sales = self.mapped("invoice_line_ids.sale_line_ids.order_id")
|
||||||
|
folios = self.env["pms.folio"].search([("order_id.id", "in", sales.ids)])
|
||||||
|
payments_obj = self.env["account.payment"]
|
||||||
|
payments = payments_obj.search([("folio_id", "in", folios.ids)])
|
||||||
|
payment_ids = payments.mapped("id")
|
||||||
|
return {
|
||||||
|
"name": _("Payments"),
|
||||||
|
"view_type": "form",
|
||||||
|
"view_mode": "tree,form",
|
||||||
|
"res_model": "account.payment",
|
||||||
|
"target": "new",
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"domain": [("id", "in", payment_ids)],
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,37 +1,75 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
# Copyright 2017 Alexandre Díaz
|
||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||||
from odoo import fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class AccountMoveLine(models.Model):
|
class AccountMoveLine(models.Model):
|
||||||
_inherit = "account.move.line"
|
_inherit = "account.move.line"
|
||||||
|
|
||||||
# Fields declaration
|
# Fields declaration
|
||||||
reservation_ids = fields.Many2many(
|
# TODO: REVIEW why not a Many2one?
|
||||||
"pms.reservation",
|
folio_line_ids = fields.Many2many(
|
||||||
"reservation_move_rel",
|
string="Folio Lines",
|
||||||
"move_line_id",
|
help="The folio lines in the account move lines",
|
||||||
"reservation_id",
|
|
||||||
string="Reservations",
|
|
||||||
readonly=True,
|
|
||||||
copy=False,
|
copy=False,
|
||||||
|
comodel_name="folio.sale.line",
|
||||||
|
relation="folio_sale_line_invoice_rel",
|
||||||
|
column1="invoice_line_id",
|
||||||
|
column2="sale_line_id",
|
||||||
)
|
)
|
||||||
service_ids = fields.Many2many(
|
folio_ids = fields.Many2many(
|
||||||
"pms.service",
|
string="Folios",
|
||||||
"service_line_move_rel",
|
comodel_name="pms.folio",
|
||||||
"move_line_id",
|
relation="payment_folio_rel",
|
||||||
"service_id",
|
column1="move_id",
|
||||||
string="Services",
|
column2="folio_id",
|
||||||
readonly=True,
|
|
||||||
copy=False,
|
|
||||||
)
|
)
|
||||||
reservation_line_ids = fields.Many2many(
|
name_changed_by_user = fields.Boolean(
|
||||||
"pms.reservation.line",
|
string="Custom label",
|
||||||
"reservation_line_move_rel",
|
readonly=False,
|
||||||
"move_line_id",
|
default=False,
|
||||||
"reservation_line_id",
|
store=True,
|
||||||
string="Reservation Lines",
|
compute="_compute_name_changed_by_user",
|
||||||
readonly=True,
|
|
||||||
copy=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@api.depends("name")
|
||||||
|
def _compute_name_changed_by_user(self):
|
||||||
|
for record in self:
|
||||||
|
# if not record._context.get("auto_name"):
|
||||||
|
if not self._context.get("auto_name"):
|
||||||
|
record.name_changed_by_user = True
|
||||||
|
else:
|
||||||
|
record.name_changed_by_user = False
|
||||||
|
|
||||||
|
name = fields.Char(
|
||||||
|
compute="_compute_name",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends("quantity")
|
||||||
|
def _compute_name(self):
|
||||||
|
for record in self:
|
||||||
|
record.name = self.env["folio.sale.line"].generate_folio_sale_name(
|
||||||
|
record.folio_line_ids.reservation_id,
|
||||||
|
record.product_id,
|
||||||
|
record.folio_line_ids.service_id,
|
||||||
|
record.folio_line_ids.reservation_line_ids,
|
||||||
|
record.folio_line_ids.service_line_ids,
|
||||||
|
qty=record.quantity,
|
||||||
|
)
|
||||||
|
# TODO: check why this code doesn't work
|
||||||
|
# if not record.name_changed_by_user:
|
||||||
|
# record.with_context(auto_name=True).name = self
|
||||||
|
# .env["folio.sale.line"].generate_folio_sale_name(
|
||||||
|
# record.folio_line_ids.service_id,
|
||||||
|
# record.folio_line_ids.reservation_line_ids,
|
||||||
|
# record.product_id,
|
||||||
|
# qty=record.quantity)
|
||||||
|
# record.with_context(auto_name=True)
|
||||||
|
# ._compute_name_changed_by_user()
|
||||||
|
|
||||||
|
def _copy_data_extend_business_fields(self, values):
|
||||||
|
super(AccountMoveLine, self)._copy_data_extend_business_fields(values)
|
||||||
|
values["folio_line_ids"] = [(6, None, self.folio_line_ids.ids)]
|
||||||
|
|||||||
@@ -1,86 +1,17 @@
|
|||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, fields, models
|
||||||
from odoo.exceptions import except_orm
|
|
||||||
|
|
||||||
|
|
||||||
class AccountPayment(models.Model):
|
class AccountPayment(models.Model):
|
||||||
_inherit = "account.payment"
|
_inherit = "account.payment"
|
||||||
|
|
||||||
# Fields declaration
|
# Fields declaration
|
||||||
folio_id = fields.Many2one("pms.folio", string="Folio Reference")
|
folio_id = fields.Many2one(
|
||||||
amount_total_folio = fields.Float(
|
string="Folio Reference",
|
||||||
compute="_compute_folio_amount",
|
help="Folio in account payment",
|
||||||
store=True,
|
comodel_name="pms.folio",
|
||||||
string="Total amount in folio",
|
|
||||||
)
|
)
|
||||||
save_amount = fields.Monetary(string="onchange_amount")
|
|
||||||
save_date = fields.Date()
|
|
||||||
save_journal_id = fields.Integer()
|
|
||||||
|
|
||||||
# Compute and Search methods
|
|
||||||
|
|
||||||
@api.depends("state")
|
|
||||||
def _compute_folio_amount(self):
|
|
||||||
# FIXME: Finalize method
|
|
||||||
res = []
|
|
||||||
fol = ()
|
|
||||||
for payment in self:
|
|
||||||
if payment.folio_id:
|
|
||||||
fol = payment.env["pms.folio"].search(
|
|
||||||
[("id", "=", payment.folio_id.id)]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
if not any(fol):
|
|
||||||
return
|
|
||||||
if len(fol) > 1:
|
|
||||||
raise except_orm(
|
|
||||||
_("Warning"),
|
|
||||||
_(
|
|
||||||
"This pay is related with \
|
|
||||||
more than one Reservation."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
fol.compute_amount()
|
|
||||||
return res
|
|
||||||
|
|
||||||
# Constraints and onchanges
|
|
||||||
# @api.onchange("amount", "payment_date", "journal_id")
|
|
||||||
# def onchange_amount(self):
|
|
||||||
# if self._origin:
|
|
||||||
# self.save_amount = self._origin.amount
|
|
||||||
# self.save_journal_id = self._origin.journal_id.id
|
|
||||||
# self.save_date = self._origin.payment_date
|
|
||||||
|
|
||||||
# Action methods
|
|
||||||
# def return_payment_folio(self):
|
|
||||||
# journal = self.journal_id
|
|
||||||
# partner = self.partner_id
|
|
||||||
# amount = self.amount
|
|
||||||
# reference = self.communication
|
|
||||||
# account_move_lines = self.move_line_ids.filtered(
|
|
||||||
# lambda x: (x.account_id.internal_type == "receivable")
|
|
||||||
# )
|
|
||||||
# return_line_vals = {
|
|
||||||
# "move_line_ids": [(6, False, [x.id for x in account_move_lines])],
|
|
||||||
# "partner_id": partner.id,
|
|
||||||
# "amount": amount,
|
|
||||||
# "reference": reference,
|
|
||||||
# }
|
|
||||||
# return_vals = {
|
|
||||||
# "journal_id": journal.id,
|
|
||||||
# "line_ids": [(0, 0, return_line_vals)],
|
|
||||||
# }
|
|
||||||
# return_pay = self.env["payment.return"].create(return_vals)
|
|
||||||
# if self.save_amount:
|
|
||||||
# self.amount = self.save_amount
|
|
||||||
# if self.save_date:
|
|
||||||
# self.payment_date = self.save_date
|
|
||||||
# if self.save_journal_id:
|
|
||||||
# self.journal_id = self.env["account.journal"].browse(self.save_journal_id)
|
|
||||||
# return_pay.action_confirm()
|
|
||||||
|
|
||||||
# Business methods
|
# Business methods
|
||||||
|
|
||||||
|
|||||||
1013
pms/models/folio_sale_line.py
Normal file
1013
pms/models/folio_sale_line.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,10 +27,7 @@ class IrHttp(models.AbstractModel):
|
|||||||
for property in user.pms_property_ids
|
for property in user.pms_property_ids
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"display_switch_pms_property_menu": user.has_group(
|
"display_switch_pms_property_menu": len(user.pms_property_ids) > 1,
|
||||||
"base.group_multi_company"
|
|
||||||
)
|
|
||||||
and len(user.pms_property_ids) > 1,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# TODO: This user context update should be placed in other function ¿?
|
# TODO: This user context update should be placed in other function ¿?
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# Copyright 2017 Dario Lodeiros
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
from odoo import fields, models
|
|
||||||
|
|
||||||
|
|
||||||
class IrSequence(models.Model):
|
|
||||||
_inherit = "ir.sequence"
|
|
||||||
|
|
||||||
pms_property_id = fields.Many2one("pms.property", string="Property")
|
|
||||||
@@ -7,12 +7,22 @@ class PaymentReturn(models.Model):
|
|||||||
_inherit = "payment.return"
|
_inherit = "payment.return"
|
||||||
|
|
||||||
# Fields declaration
|
# Fields declaration
|
||||||
folio_id = fields.Many2one("pms.folio", string="Folio")
|
folio_id = fields.Many2one(
|
||||||
pms_property_id = fields.Many2one(
|
string="Folio", help="Folio in payment return", comodel_name="pms.folio"
|
||||||
"pms.property", store=True, readonly=True, related="folio_id.pms_property_id"
|
)
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Property with access to the element",
|
||||||
|
store=True,
|
||||||
|
readonly=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
related="folio_id.pms_property_id",
|
||||||
|
)
|
||||||
|
company_id = fields.Many2one(
|
||||||
|
string="Company",
|
||||||
|
help="The company for Payment Return",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Business methods
|
|
||||||
|
|
||||||
def action_confirm(self):
|
def action_confirm(self):
|
||||||
pay = super(PaymentReturn, self).action_confirm()
|
pay = super(PaymentReturn, self).action_confirm()
|
||||||
|
|||||||
@@ -6,15 +6,37 @@ from odoo import fields, models
|
|||||||
|
|
||||||
class PmsRoomAmenity(models.Model):
|
class PmsRoomAmenity(models.Model):
|
||||||
_name = "pms.amenity"
|
_name = "pms.amenity"
|
||||||
_description = "Room amenities"
|
_description = "Room amenity"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
active = fields.Boolean(
|
||||||
name = fields.Char("Amenity Name", translate=True, required=True)
|
string="Active",
|
||||||
pms_property_ids = fields.Many2many(
|
help="Determines if amenity is active",
|
||||||
"pms.property", string="Properties", required=False, ondelete="restrict"
|
default=True,
|
||||||
|
)
|
||||||
|
name = fields.Char(
|
||||||
|
string="Amenity Name",
|
||||||
|
help="Amenity Name",
|
||||||
|
required=True,
|
||||||
|
translate=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",
|
||||||
|
ondelete="restrict",
|
||||||
|
relation="pms_amenity_pms_property_rel",
|
||||||
|
column1="amenity_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",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
default_code = fields.Char(
|
||||||
|
string="Internal Reference", help="Internal unique identifier of the amenity"
|
||||||
)
|
)
|
||||||
room_amenity_type_id = fields.Many2one("pms.amenity.type", "Amenity Category")
|
|
||||||
default_code = fields.Char("Internal Reference")
|
|
||||||
active = fields.Boolean("Active", default=True)
|
|
||||||
|
|
||||||
# TODO: Constrain coherence pms_property_ids with amenity types pms_property_ids
|
|
||||||
|
|||||||
@@ -6,16 +6,35 @@ from odoo import fields, models
|
|||||||
|
|
||||||
class PmsRoomAmenityType(models.Model):
|
class PmsRoomAmenityType(models.Model):
|
||||||
_name = "pms.amenity.type"
|
_name = "pms.amenity.type"
|
||||||
_description = "Amenities Type"
|
_description = "Amenity Type"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
active = fields.Boolean(
|
||||||
name = fields.Char("Amenity Type Name", translate=True, required=True)
|
string="Active",
|
||||||
|
help="Determines if amenity type is active",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
name = fields.Char(
|
||||||
|
string="Amenity Type Name",
|
||||||
|
help="Amenity Type Name",
|
||||||
|
required=True,
|
||||||
|
translate=True,
|
||||||
|
)
|
||||||
pms_property_ids = fields.Many2many(
|
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",
|
||||||
|
ondelete="restrict",
|
||||||
|
relation="pms_amenity_type_pms_property_rel",
|
||||||
|
column1="amenity_type_id",
|
||||||
|
column2="pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
room_amenity_ids = fields.One2many(
|
pms_amenity_ids = fields.One2many(
|
||||||
"pms.amenity", "room_amenity_type_id", "Amenities in this category"
|
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,
|
||||||
)
|
)
|
||||||
active = fields.Boolean("Active", default=True)
|
|
||||||
|
|
||||||
# TODO: Constrain coherence pms_property_ids with amenities pms_property_ids
|
|
||||||
|
|||||||
103
pms/models/pms_availability.py
Normal file
103
pms/models/pms_availability.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Copyright 2017 Alexandre Díaz
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
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",
|
||||||
|
help="Room type for which availability is indicated",
|
||||||
|
readonly=True,
|
||||||
|
required=True,
|
||||||
|
comodel_name="pms.room.type",
|
||||||
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
date = fields.Date(
|
||||||
|
string="Date",
|
||||||
|
help="Date for which availability applies",
|
||||||
|
readonly=True,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Property to which the availability is directed",
|
||||||
|
readonly=True,
|
||||||
|
required=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
reservation_line_ids = fields.One2many(
|
||||||
|
string="Reservation Lines",
|
||||||
|
help="They are the lines of the reservation into a reservation,"
|
||||||
|
"they corresponds to the nights",
|
||||||
|
readonly=True,
|
||||||
|
comodel_name="pms.reservation.line",
|
||||||
|
inverse_name="avail_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
real_avail = fields.Integer(
|
||||||
|
string="Real Avail",
|
||||||
|
help="",
|
||||||
|
store=True,
|
||||||
|
readonly=True,
|
||||||
|
compute="_compute_real_avail",
|
||||||
|
)
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
(
|
||||||
|
"room_type_registry_unique",
|
||||||
|
"unique(room_type_id, date, pms_property_id)",
|
||||||
|
"Only can exists one availability in the same \
|
||||||
|
day for the same room type!",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
"reservation_line_ids",
|
||||||
|
"reservation_line_ids.occupies_availability",
|
||||||
|
"room_type_id.total_rooms_count",
|
||||||
|
)
|
||||||
|
def _compute_real_avail(self):
|
||||||
|
for record in self:
|
||||||
|
Rooms = self.env["pms.room"]
|
||||||
|
RoomLines = self.env["pms.reservation.line"]
|
||||||
|
total_rooms = Rooms.search_count(
|
||||||
|
[
|
||||||
|
("room_type_id", "=", record.room_type_id.id),
|
||||||
|
("pms_property_id", "=", record.pms_property_id.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
room_ids = record.room_type_id.mapped("room_ids.id")
|
||||||
|
rooms_not_avail = RoomLines.search_count(
|
||||||
|
[
|
||||||
|
("date", "=", record.date),
|
||||||
|
("room_id", "in", room_ids),
|
||||||
|
("pms_property_id", "=", record.pms_property_id.id),
|
||||||
|
("occupies_availability", "=", True),
|
||||||
|
# ("id", "not in", current_lines if current_lines else []),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
record.real_avail = total_rooms - rooms_not_avail
|
||||||
|
|
||||||
|
@api.constrains(
|
||||||
|
"room_type_id",
|
||||||
|
"pms_property_id",
|
||||||
|
)
|
||||||
|
def _check_property_integrity(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.pms_property_id and rec.room_type_id:
|
||||||
|
if (
|
||||||
|
rec.room_type_id.pms_property_ids.ids
|
||||||
|
and rec.pms_property_id.id
|
||||||
|
not in rec.room_type_id.pms_property_ids.ids
|
||||||
|
):
|
||||||
|
raise ValidationError(
|
||||||
|
_("Property not allowed on availability day compute")
|
||||||
|
)
|
||||||
332
pms/models/pms_availability_plan.py
Normal file
332
pms/models/pms_availability_plan.py
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
# Copyright 2017 Alexandre Díaz
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||||
|
|
||||||
|
|
||||||
|
class PmsAvailabilityPlan(models.Model):
|
||||||
|
"""The room type availability is used as a daily availability plan for room types
|
||||||
|
and therefore is related only with one property."""
|
||||||
|
|
||||||
|
_name = "pms.availability.plan"
|
||||||
|
_description = "Reservation availability plan"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_default_pms_property(self):
|
||||||
|
return self.env.user.get_active_property_ids()[0] or None
|
||||||
|
|
||||||
|
name = fields.Char(
|
||||||
|
string="Availability Plan Name", help="Name of availability plan", required=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",
|
||||||
|
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(
|
||||||
|
string="Availability Rules",
|
||||||
|
help="Rules in a availability plan",
|
||||||
|
comodel_name="pms.availability.plan.rule",
|
||||||
|
inverse_name="availability_plan_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
active = fields.Boolean(
|
||||||
|
string="Active",
|
||||||
|
help="If unchecked, it will allow you to hide the "
|
||||||
|
"Availability plan without removing it.",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def any_rule_applies(cls, checkin, checkout, item):
|
||||||
|
reservation_len = (checkout - checkin).days
|
||||||
|
return any(
|
||||||
|
[
|
||||||
|
(0 < item.max_stay < reservation_len),
|
||||||
|
(0 < item.min_stay > reservation_len),
|
||||||
|
(0 < item.max_stay_arrival < reservation_len and checkin == item.date),
|
||||||
|
(0 < item.min_stay_arrival > reservation_len and checkin == item.date),
|
||||||
|
item.closed,
|
||||||
|
(item.closed_arrival and checkin == item.date),
|
||||||
|
(item.closed_departure and checkout == item.date),
|
||||||
|
(item.quota == 0 or item.max_avail == 0),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def rooms_available(
|
||||||
|
self,
|
||||||
|
checkin,
|
||||||
|
checkout,
|
||||||
|
room_type_id=False,
|
||||||
|
current_lines=False,
|
||||||
|
pricelist_id=False,
|
||||||
|
pms_property_id=False,
|
||||||
|
):
|
||||||
|
if current_lines and not isinstance(current_lines, list):
|
||||||
|
current_lines = [current_lines]
|
||||||
|
free_rooms = self.get_real_free_rooms(
|
||||||
|
checkin, checkout, room_type_id, current_lines, pms_property_id
|
||||||
|
)
|
||||||
|
domain_rules = [
|
||||||
|
("date", ">=", checkin),
|
||||||
|
(
|
||||||
|
"date",
|
||||||
|
"<=",
|
||||||
|
checkout,
|
||||||
|
), # TODO: only closed_departure take account checkout date!
|
||||||
|
]
|
||||||
|
if pms_property_id:
|
||||||
|
domain_rules.append(("pms_property_id", "=", pms_property_id))
|
||||||
|
|
||||||
|
if room_type_id:
|
||||||
|
domain_rules.append(("room_type_id", "=", room_type_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:
|
||||||
|
room_types_to_remove = []
|
||||||
|
for item in rule_items:
|
||||||
|
if self.any_rule_applies(checkin, checkout, item):
|
||||||
|
room_types_to_remove.append(item.room_type_id.id)
|
||||||
|
free_rooms = free_rooms.filtered(
|
||||||
|
lambda x: x.room_type_id.id not in room_types_to_remove
|
||||||
|
)
|
||||||
|
elif not pricelist:
|
||||||
|
raise ValidationError(_("Pricelist not found"))
|
||||||
|
return free_rooms.sorted(key=lambda r: r.sequence)
|
||||||
|
|
||||||
|
def get_real_free_rooms(
|
||||||
|
self,
|
||||||
|
checkin,
|
||||||
|
checkout,
|
||||||
|
room_type_id=False,
|
||||||
|
current_lines=False,
|
||||||
|
pms_property_id=False,
|
||||||
|
):
|
||||||
|
Avail = self.env["pms.availability"]
|
||||||
|
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()
|
||||||
|
domain = [
|
||||||
|
("date", ">=", checkin),
|
||||||
|
("date", "<=", checkout - datetime.timedelta(1)),
|
||||||
|
]
|
||||||
|
if not current_lines:
|
||||||
|
current_lines = []
|
||||||
|
rooms_not_avail = (
|
||||||
|
Avail.search(domain)
|
||||||
|
.reservation_line_ids.filtered(lambda l: l.id and l.id not in current_lines)
|
||||||
|
.room_id.ids
|
||||||
|
)
|
||||||
|
domain_rooms = []
|
||||||
|
if rooms_not_avail:
|
||||||
|
domain_rooms = [
|
||||||
|
("id", "not in", rooms_not_avail),
|
||||||
|
]
|
||||||
|
if pms_property_id:
|
||||||
|
domain_rooms.append(("pms_property_id", "=", pms_property_id))
|
||||||
|
if room_type_id:
|
||||||
|
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,
|
||||||
|
checkin,
|
||||||
|
checkout,
|
||||||
|
room_type_id=False,
|
||||||
|
current_lines=False,
|
||||||
|
pricelist=False,
|
||||||
|
pms_property_id=False,
|
||||||
|
):
|
||||||
|
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 date_iterator in [
|
||||||
|
checkin + datetime.timedelta(days=x)
|
||||||
|
for x in range(0, (checkout - checkin).days)
|
||||||
|
]:
|
||||||
|
rooms_avail = self.rooms_available(
|
||||||
|
checkin=date_iterator,
|
||||||
|
checkout=date_iterator + datetime.timedelta(1),
|
||||||
|
room_type_id=room_type_id,
|
||||||
|
current_lines=current_lines,
|
||||||
|
pricelist_id=pricelist.id,
|
||||||
|
pms_property_id=pms_property_id,
|
||||||
|
)
|
||||||
|
if len(rooms_avail) < 1:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def update_quota(self, pricelist_id, room_type_id, date, line):
|
||||||
|
if pricelist_id and room_type_id and date:
|
||||||
|
rule = self.env["pms.availability.plan.rule"].search(
|
||||||
|
[
|
||||||
|
("availability_plan_id.pms_pricelist_ids", "=", pricelist_id.id),
|
||||||
|
("room_type_id", "=", room_type_id.id),
|
||||||
|
("date", "=", date),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# applies a rule
|
||||||
|
if rule:
|
||||||
|
rule.ensure_one()
|
||||||
|
if rule and rule.quota != -1 and rule.quota > 0:
|
||||||
|
|
||||||
|
# the line has no rule item applied before
|
||||||
|
if not line.impacts_quota:
|
||||||
|
rule.quota -= 1
|
||||||
|
return rule.id
|
||||||
|
|
||||||
|
# the line has a rule item applied before
|
||||||
|
elif line.impacts_quota != rule.id:
|
||||||
|
|
||||||
|
# decrement quota on current rule item
|
||||||
|
rule.quota -= 1
|
||||||
|
|
||||||
|
# check old rule item
|
||||||
|
old_rule = self.env["pms.availability.plan.rule"].search(
|
||||||
|
[("id", "=", line.impacts_quota)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# restore quota in old rule item
|
||||||
|
if old_rule:
|
||||||
|
old_rule.quota += 1
|
||||||
|
|
||||||
|
return rule.id
|
||||||
|
|
||||||
|
# in any case, check old rule item
|
||||||
|
if line.impacts_quota:
|
||||||
|
old_rule = self.env["pms.availability.plan.rule"].search(
|
||||||
|
[("id", "=", line.impacts_quota)]
|
||||||
|
)
|
||||||
|
# and restore quota in old rule item
|
||||||
|
if old_rule:
|
||||||
|
old_rule.quota += 1
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Action methods
|
||||||
|
def open_massive_changes_wizard(self):
|
||||||
|
|
||||||
|
if self.ensure_one():
|
||||||
|
return {
|
||||||
|
"view_type": "form",
|
||||||
|
"view_mode": "form",
|
||||||
|
"name": "Massive changes on Availability Plan: " + self.name,
|
||||||
|
"res_model": "pms.massive.changes.wizard",
|
||||||
|
"target": "new",
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"context": {
|
||||||
|
"availability_plan_id": self.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
192
pms/models/pms_availability_plan_rule.py
Normal file
192
pms/models/pms_availability_plan_rule.py
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
# Copyright 2017 Alexandre Díaz
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
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",
|
||||||
|
help="The availability plan that include the Availabilty Rule",
|
||||||
|
index=True,
|
||||||
|
comodel_name="pms.availability.plan",
|
||||||
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
room_type_id = fields.Many2one(
|
||||||
|
string="Room Type",
|
||||||
|
help="Room type for which availability rule is applied",
|
||||||
|
required=True,
|
||||||
|
comodel_name="pms.room.type",
|
||||||
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
date = fields.Date(
|
||||||
|
string="Date",
|
||||||
|
help="Date for which availability rule applies",
|
||||||
|
)
|
||||||
|
|
||||||
|
min_stay = fields.Integer(
|
||||||
|
string="Min. Stay",
|
||||||
|
help="Minimum stay",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
min_stay_arrival = fields.Integer(
|
||||||
|
string="Min. Stay Arrival",
|
||||||
|
help="Minimum stay if checkin is today",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
max_stay = fields.Integer(
|
||||||
|
string="Max. Stay",
|
||||||
|
help="Maximum stay",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
max_stay_arrival = fields.Integer(
|
||||||
|
string="Max. Stay Arrival",
|
||||||
|
help="Maximum stay if checkin is today",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
closed = fields.Boolean(
|
||||||
|
string="Closed",
|
||||||
|
help="Indicate if property is closed or not",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
closed_departure = fields.Boolean(
|
||||||
|
string="Closed Departure",
|
||||||
|
help="",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
closed_arrival = fields.Boolean(
|
||||||
|
string="Closed Arrival",
|
||||||
|
help="",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
quota = fields.Integer(
|
||||||
|
string="Quota",
|
||||||
|
help="Generic Quota assigned.",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_quota",
|
||||||
|
)
|
||||||
|
max_avail = fields.Integer(
|
||||||
|
string="Max. Availability",
|
||||||
|
help="Maximum simultaneous availability on own Booking Engine",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_max_avail",
|
||||||
|
)
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Properties with access to the element",
|
||||||
|
ondelete="restrict",
|
||||||
|
required=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
avail_id = fields.Many2one(
|
||||||
|
string="Avail record",
|
||||||
|
comodel_name="pms.availability",
|
||||||
|
compute="_compute_avail_id",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
real_avail = fields.Integer(
|
||||||
|
string="Real availability",
|
||||||
|
related="avail_id.real_avail",
|
||||||
|
store="True",
|
||||||
|
)
|
||||||
|
plan_avail = fields.Integer(
|
||||||
|
compute="_compute_plan_avail",
|
||||||
|
store="True",
|
||||||
|
)
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
(
|
||||||
|
"room_type_registry_unique",
|
||||||
|
"unique(availability_plan_id, room_type_id, date, pms_property_id)",
|
||||||
|
"Only can exists one availability rule in the same \
|
||||||
|
day for the same room type!",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
@api.depends("room_type_id", "date", "pms_property_id")
|
||||||
|
def _compute_avail_id(self):
|
||||||
|
for record in self:
|
||||||
|
if record.room_type_id and record.pms_property_id and record.date:
|
||||||
|
avail = self.env["pms.availability"].search(
|
||||||
|
[
|
||||||
|
("date", "=", record.date),
|
||||||
|
("room_type_id", "=", record.room_type_id.id),
|
||||||
|
("pms_property_id", "=", record.pms_property_id.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if avail:
|
||||||
|
record.avail_id = avail.id
|
||||||
|
else:
|
||||||
|
record.avail_id = self.env["pms.availability"].create(
|
||||||
|
{
|
||||||
|
"date": record.date,
|
||||||
|
"room_type_id": record.room_type_id.id,
|
||||||
|
"pms_property_id": record.pms_property_id.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
record.avail_id = False
|
||||||
|
|
||||||
|
@api.depends("quota", "max_avail", "real_avail")
|
||||||
|
def _compute_plan_avail(self):
|
||||||
|
for record in self.filtered("real_avail"):
|
||||||
|
real_avail = record.real_avail
|
||||||
|
plan_avail = min(
|
||||||
|
[
|
||||||
|
record.max_avail if record.max_avail >= 0 else real_avail,
|
||||||
|
record.quota if record.quota >= 0 else real_avail,
|
||||||
|
real_avail,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if not record.plan_avail or record.plan_avail != plan_avail:
|
||||||
|
record.plan_avail = plan_avail
|
||||||
|
|
||||||
|
@api.depends("room_type_id")
|
||||||
|
def _compute_quota(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.quota:
|
||||||
|
record.quota = record.room_type_id.default_quota
|
||||||
|
|
||||||
|
@api.depends("room_type_id")
|
||||||
|
def _compute_max_avail(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.max_avail:
|
||||||
|
record.max_avail = record.room_type_id.default_max_avail
|
||||||
|
|
||||||
|
@api.constrains("min_stay", "min_stay_arrival", "max_stay", "max_stay_arrival")
|
||||||
|
def _check_min_max_stay(self):
|
||||||
|
for record in self:
|
||||||
|
if record.min_stay < 0:
|
||||||
|
raise ValidationError(_("Min. Stay can't be less than zero"))
|
||||||
|
elif record.min_stay_arrival < 0:
|
||||||
|
raise ValidationError(_("Min. Stay Arrival can't be less than zero"))
|
||||||
|
elif record.max_stay < 0:
|
||||||
|
raise ValidationError(_("Max. Stay can't be less than zero"))
|
||||||
|
elif record.max_stay_arrival < 0:
|
||||||
|
raise ValidationError(_("Max. Stay Arrival can't be less than zero"))
|
||||||
|
elif (
|
||||||
|
record.min_stay != 0
|
||||||
|
and record.max_stay != 0
|
||||||
|
and record.min_stay > record.max_stay
|
||||||
|
):
|
||||||
|
raise ValidationError(_("Max. Stay can't be less than Min. Stay"))
|
||||||
|
elif (
|
||||||
|
record.min_stay_arrival != 0
|
||||||
|
and record.max_stay_arrival != 0
|
||||||
|
and record.min_stay_arrival > record.max_stay_arrival
|
||||||
|
):
|
||||||
|
raise ValidationError(
|
||||||
|
_("Max. Stay Arrival can't be less than Min. Stay Arrival")
|
||||||
|
)
|
||||||
@@ -1,34 +1,66 @@
|
|||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
from odoo import api, fields, models
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class PmsBoardService(models.Model):
|
class PmsBoardService(models.Model):
|
||||||
_name = "pms.board.service"
|
_name = "pms.board.service"
|
||||||
_description = "Board Services"
|
_description = "Board Services"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
name = fields.Char(
|
||||||
name = fields.Char("Board Name", translate=True, size=64, required=True, index=True)
|
string="Board Service Name",
|
||||||
board_service_line_ids = fields.One2many(
|
help="Board Service Name",
|
||||||
"pms.board.service.line", "pms_board_service_id"
|
required=True,
|
||||||
|
index=True,
|
||||||
|
size=64,
|
||||||
|
translate=True,
|
||||||
)
|
)
|
||||||
pms_property_ids = fields.Many2many(
|
default_code = fields.Char(
|
||||||
"pms.property", string="Properties", required=False, ondelete="restrict"
|
string="Board Service Code",
|
||||||
)
|
help="Unique Board Service identification code per property",
|
||||||
pms_board_service_room_type_ids = fields.One2many(
|
|
||||||
"pms.board.service.room.type", "pms_board_service_id"
|
|
||||||
)
|
|
||||||
price_type = fields.Selection(
|
|
||||||
[("fixed", "Fixed"), ("percent", "Percent")],
|
|
||||||
string="Type",
|
|
||||||
default="fixed",
|
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
|
board_service_line_ids = fields.One2many(
|
||||||
|
string="Board Service Lines",
|
||||||
|
help="Services included in this Board Service",
|
||||||
|
comodel_name="pms.board.service.line",
|
||||||
|
inverse_name="pms_board_service_id",
|
||||||
|
)
|
||||||
|
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_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",
|
||||||
|
help="Board Services Room Type corresponding to this Board Service,"
|
||||||
|
"One board service for several room types",
|
||||||
|
comodel_name="pms.board.service.room.type",
|
||||||
|
inverse_name="pms_board_service_id",
|
||||||
|
)
|
||||||
amount = fields.Float(
|
amount = fields.Float(
|
||||||
"Amount", digits=("Product Price"), compute="_compute_board_amount", store=True
|
string="Amount",
|
||||||
|
help="Price for this Board Service. "
|
||||||
|
"It corresponds to the sum of his board service lines",
|
||||||
|
store=True,
|
||||||
|
digits=("Product Price"),
|
||||||
|
compute="_compute_board_amount",
|
||||||
|
)
|
||||||
|
|
||||||
|
show_detail_report = fields.Boolean(
|
||||||
|
string="Show Detail Report",
|
||||||
|
help="True if you want that board service detail to be shown on the report",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compute and Search methods
|
|
||||||
@api.depends("board_service_line_ids.amount")
|
@api.depends("board_service_line_ids.amount")
|
||||||
def _compute_board_amount(self):
|
def _compute_board_amount(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -36,3 +68,66 @@ class PmsBoardService(models.Model):
|
|||||||
for service in record.board_service_line_ids:
|
for service in record.board_service_line_ids:
|
||||||
total += service.amount
|
total += service.amount
|
||||||
record.update({"amount": total})
|
record.update({"amount": total})
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_unique_by_property_code(self, pms_property_id, default_code=None):
|
||||||
|
"""
|
||||||
|
:param pms_property_id: property ID
|
||||||
|
:param default_code: board service code (optional)
|
||||||
|
:return: - recordset of
|
||||||
|
- all the pms.board.service of the pms_property_id
|
||||||
|
if default_code not defined
|
||||||
|
- one or 0 pms.board.service if default_code defined
|
||||||
|
- ValidationError if more than one default_code found by
|
||||||
|
the same pms_property_id
|
||||||
|
"""
|
||||||
|
# TODO: similiar code as room.type -> unify
|
||||||
|
domain = []
|
||||||
|
if default_code:
|
||||||
|
domain += ["&", ("default_code", "=", default_code)]
|
||||||
|
domain += [
|
||||||
|
"|",
|
||||||
|
("pms_property_ids", "in", pms_property_id),
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
]
|
||||||
|
records = self.search(domain)
|
||||||
|
res, res_priority = {}, {}
|
||||||
|
for rec in records:
|
||||||
|
res_priority.setdefault(rec.default_code, -1)
|
||||||
|
priority = rec.pms_property_ids and 1 or 0
|
||||||
|
if priority > res_priority[rec.default_code]:
|
||||||
|
res.setdefault(rec.default_code, rec.id)
|
||||||
|
res[rec.default_code], res_priority[rec.default_code] = rec.id, priority
|
||||||
|
elif priority == res_priority[rec.default_code]:
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"Integrity error: There's multiple board services "
|
||||||
|
"with the same code %s and properties"
|
||||||
|
)
|
||||||
|
% rec.default_code
|
||||||
|
)
|
||||||
|
return self.browse(list(res.values()))
|
||||||
|
|
||||||
|
@api.constrains("default_code", "pms_property_ids")
|
||||||
|
def _check_code_property_uniqueness(self):
|
||||||
|
# TODO: similiar code as room.type -> unify
|
||||||
|
msg = _(
|
||||||
|
"Already exists another Board Service with the same code and properties"
|
||||||
|
)
|
||||||
|
for rec in self:
|
||||||
|
if not rec.pms_property_ids:
|
||||||
|
if self.search(
|
||||||
|
[
|
||||||
|
("id", "!=", rec.id),
|
||||||
|
("default_code", "=", rec.default_code),
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
]
|
||||||
|
):
|
||||||
|
raise ValidationError(msg)
|
||||||
|
else:
|
||||||
|
for pms_property in rec.pms_property_ids:
|
||||||
|
other = rec.get_unique_by_property_code(
|
||||||
|
pms_property.id, rec.default_code
|
||||||
|
)
|
||||||
|
if other and other != rec:
|
||||||
|
raise ValidationError(msg)
|
||||||
|
|||||||
@@ -6,26 +6,77 @@ from odoo import api, fields, models
|
|||||||
class PmsBoardServiceLine(models.Model):
|
class PmsBoardServiceLine(models.Model):
|
||||||
_name = "pms.board.service.line"
|
_name = "pms.board.service.line"
|
||||||
_description = "Services on Board Service included"
|
_description = "Services on Board Service included"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
|
pms_board_service_id = fields.Many2one(
|
||||||
|
string="Board Service",
|
||||||
|
help="Board Service in which this line is included",
|
||||||
|
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",
|
||||||
|
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",
|
||||||
|
help="Price for this Board Service Line/Product",
|
||||||
|
default=lambda self: self._get_default_price(),
|
||||||
|
digits=("Product Price"),
|
||||||
|
)
|
||||||
|
|
||||||
# Default methods
|
|
||||||
def _get_default_price(self):
|
def _get_default_price(self):
|
||||||
if self.product_id:
|
if self.product_id:
|
||||||
return self.product_id.list_price
|
return self.product_id.list_price
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
pms_board_service_id = fields.Many2one(
|
|
||||||
"pms.board.service", "Board Service", ondelete="cascade", required=True
|
|
||||||
)
|
|
||||||
product_id = fields.Many2one("product.product", string="Product", required=True)
|
|
||||||
pms_property_ids = fields.Many2many(
|
|
||||||
"pms.property", related="pms_board_service_id.pms_property_ids"
|
|
||||||
)
|
|
||||||
amount = fields.Float(
|
|
||||||
"Amount", digits=("Product Price"), default=_get_default_price
|
|
||||||
)
|
|
||||||
|
|
||||||
# Constraints and onchanges
|
|
||||||
@api.onchange("product_id")
|
@api.onchange("product_id")
|
||||||
def onchange_product_id(self):
|
def onchange_product_id(self):
|
||||||
if self.product_id:
|
if self.product_id:
|
||||||
self.update({"amount": self.product_id.list_price})
|
self.update({"amount": self.product_id.list_price})
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|||||||
@@ -10,57 +10,58 @@ class PmsBoardServiceRoomType(models.Model):
|
|||||||
_rec_name = "pms_board_service_id"
|
_rec_name = "pms_board_service_id"
|
||||||
_log_access = False
|
_log_access = False
|
||||||
_description = "Board Service included in Room"
|
_description = "Board Service included in Room"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Default Methods ang Gets
|
|
||||||
|
|
||||||
def name_get(self):
|
|
||||||
result = []
|
|
||||||
for res in self:
|
|
||||||
if res.pricelist_id:
|
|
||||||
name = u"{} ({})".format(
|
|
||||||
res.pms_board_service_id.name,
|
|
||||||
res.pricelist_id.name,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
name = u"{} ({})".format(res.pms_board_service_id.name, _("Generic"))
|
|
||||||
result.append((res.id, name))
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
pms_board_service_id = fields.Many2one(
|
pms_board_service_id = fields.Many2one(
|
||||||
"pms.board.service",
|
|
||||||
string="Board Service",
|
string="Board Service",
|
||||||
index=True,
|
help="Board Service corresponding to this Board Service Room Type",
|
||||||
ondelete="cascade",
|
|
||||||
required=True,
|
required=True,
|
||||||
|
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",
|
||||||
|
required=False,
|
||||||
|
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(
|
pms_room_type_id = fields.Many2one(
|
||||||
"pms.room.type",
|
|
||||||
string="Room Type",
|
string="Room Type",
|
||||||
index=True,
|
help="Room Type for which this Board Service is available",
|
||||||
ondelete="cascade",
|
|
||||||
required=True,
|
required=True,
|
||||||
)
|
index=True,
|
||||||
pricelist_id = fields.Many2one(
|
comodel_name="pms.room.type",
|
||||||
"product.pricelist", string="Pricelist", required=False
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
board_service_line_ids = fields.One2many(
|
board_service_line_ids = fields.One2many(
|
||||||
"pms.board.service.room.type.line", "pms_board_service_room_type_id"
|
string="Board Service Lines",
|
||||||
)
|
help="Services included in this Board Service",
|
||||||
pms_property_id = fields.Many2one(
|
comodel_name="pms.board.service.room.type.line",
|
||||||
"pms.property",
|
inverse_name="pms_board_service_room_type_id",
|
||||||
)
|
|
||||||
price_type = fields.Selection(
|
|
||||||
[("fixed", "Fixed"), ("percent", "Percent")],
|
|
||||||
string="Type",
|
|
||||||
default="fixed",
|
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
amount = fields.Float(
|
amount = fields.Float(
|
||||||
"Amount", digits=("Product Price"), compute="_compute_board_amount", store=True
|
string="Amount",
|
||||||
|
help="Price for this Board Service. "
|
||||||
|
"It corresponds to the sum of his board service lines",
|
||||||
|
store=True,
|
||||||
|
digits=("Product Price"),
|
||||||
|
compute="_compute_board_amount",
|
||||||
|
)
|
||||||
|
by_default = fields.Boolean(
|
||||||
|
string="Apply by Default",
|
||||||
|
help="Indicates if this board service is applied by default in the room type",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compute and Search methods
|
|
||||||
@api.depends("board_service_line_ids.amount")
|
@api.depends("board_service_line_ids.amount")
|
||||||
def _compute_board_amount(self):
|
def _compute_board_amount(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -69,44 +70,22 @@ class PmsBoardServiceRoomType(models.Model):
|
|||||||
total += service.amount
|
total += service.amount
|
||||||
record.update({"amount": total})
|
record.update({"amount": total})
|
||||||
|
|
||||||
# Constraints and onchanges
|
@api.constrains("by_default")
|
||||||
@api.constrains("pricelist_id")
|
def constrains_duplicated_board_defaul(self):
|
||||||
def constrains_pricelist_id(self):
|
|
||||||
for record in self:
|
for record in self:
|
||||||
if self.pricelist_id:
|
default_boards = (
|
||||||
board_pricelist = self.env["pms.board.service.room.type"].search(
|
record.pms_room_type_id.board_service_room_type_ids.filtered(
|
||||||
[
|
"by_default"
|
||||||
("pricelist_id", "=", record.pricelist_id.id),
|
|
||||||
("pms_room_type_id", "=", record.pms_room_type_id.id),
|
|
||||||
("pms_board_service_id", "=", record.pms_board_service_id.id),
|
|
||||||
("id", "!=", record.id),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
if board_pricelist:
|
)
|
||||||
raise UserError(
|
# TODO Check properties (with different propertys is allowed)
|
||||||
_("This Board Service in this Room can't repeat pricelist")
|
if any(default_boards.filtered(lambda l: l.id != record.id)):
|
||||||
)
|
raise UserError(_("""Only can set one default board service"""))
|
||||||
else:
|
|
||||||
board_pricelist = self.env["pms.board.service.room.type"].search(
|
|
||||||
[
|
|
||||||
("pricelist_id", "=", False),
|
|
||||||
("pms_room_type_id", "=", record.pms_room_type_id.id),
|
|
||||||
("pms_board_service_id", "=", record.pms_board_service_id.id),
|
|
||||||
("id", "!=", record.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if board_pricelist:
|
|
||||||
raise UserError(
|
|
||||||
_(
|
|
||||||
"This Board Service in this Room \
|
|
||||||
can't repeat without pricelist"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Action methods
|
|
||||||
|
|
||||||
def open_board_lines_form(self):
|
def open_board_lines_form(self):
|
||||||
action = self.env.ref("pms.action_pms_board_service_room_type_view").read()[0]
|
action = (
|
||||||
|
self.env.ref("pms.action_pms_board_service_room_type_view").sudo().read()[0]
|
||||||
|
)
|
||||||
action["views"] = [
|
action["views"] = [
|
||||||
(self.env.ref("pms.pms_board_service_room_type_form").id, "form")
|
(self.env.ref("pms.pms_board_service_room_type_form").id, "form")
|
||||||
]
|
]
|
||||||
@@ -114,25 +93,42 @@ class PmsBoardServiceRoomType(models.Model):
|
|||||||
action["target"] = "new"
|
action["target"] = "new"
|
||||||
return action
|
return action
|
||||||
|
|
||||||
# ORM Overrides
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self._cr.execute(
|
self._cr.execute(
|
||||||
"SELECT indexname FROM pg_indexes WHERE indexname = %s",
|
"SELECT indexname FROM pg_indexes WHERE indexname = %s",
|
||||||
("pms_board_service_id_pms_room_type_id_pricelist_id",),
|
("pms_board_service_id_pms_room_type_id",),
|
||||||
)
|
)
|
||||||
if not self._cr.fetchone():
|
if not self._cr.fetchone():
|
||||||
self._cr.execute(
|
self._cr.execute(
|
||||||
"CREATE INDEX pms_board_service_id_pms_room_type_id_pricelist_id \
|
"CREATE INDEX pms_board_service_id_pms_room_type_id \
|
||||||
ON pms_board_service_room_type_rel \
|
ON pms_board_service_room_type_rel \
|
||||||
(pms_board_service_id, pms_room_type_id, pricelist_id)"
|
(pms_board_service_id, pms_room_type_id)"
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
|
properties = False
|
||||||
if "pms_board_service_id" in vals:
|
if "pms_board_service_id" in vals:
|
||||||
vals.update(
|
vals.update(
|
||||||
self.prepare_board_service_reservation_ids(vals["pms_board_service_id"])
|
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)
|
return super(PmsBoardServiceRoomType, self).create(vals)
|
||||||
|
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
@@ -142,7 +138,6 @@ class PmsBoardServiceRoomType(models.Model):
|
|||||||
)
|
)
|
||||||
return super(PmsBoardServiceRoomType, self).write(vals)
|
return super(PmsBoardServiceRoomType, self).write(vals)
|
||||||
|
|
||||||
# Business methods
|
|
||||||
@api.model
|
@api.model
|
||||||
def prepare_board_service_reservation_ids(self, board_service_id):
|
def prepare_board_service_reservation_ids(self, board_service_id):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,21 +1,73 @@
|
|||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
from odoo import fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class PmsBoardServiceRoomTypeLine(models.Model):
|
class PmsBoardServiceRoomTypeLine(models.Model):
|
||||||
_name = "pms.board.service.room.type.line"
|
_name = "pms.board.service.room.type.line"
|
||||||
_description = "Services on Board Service included in Room"
|
_description = "Services on Board Service included in Room"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
# Fields declaration
|
||||||
pms_board_service_room_type_id = fields.Many2one(
|
pms_board_service_room_type_id = fields.Many2one(
|
||||||
"pms.board.service.room.type",
|
string="Board Service Room",
|
||||||
"Board Service Room",
|
help="Board Service Room Type in which this line is included",
|
||||||
ondelete="cascade",
|
|
||||||
required=True,
|
required=True,
|
||||||
|
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(
|
product_id = fields.Many2one(
|
||||||
"product.product", "Product", required=True, readonly=True
|
string="Product",
|
||||||
|
help="Product associated with this board service room type line",
|
||||||
|
comodel_name="product.product",
|
||||||
|
readonly=True,
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
# TODO def default_amount "amount of service"
|
# TODO def default_amount "amount of service"
|
||||||
amount = fields.Float("Amount", digits=("Product Price"), default=0.0)
|
amount = fields.Float(
|
||||||
|
string="Amount",
|
||||||
|
help="Price for this Board Service Room Type Line/Product",
|
||||||
|
default=0.0,
|
||||||
|
digits=("Product Price"),
|
||||||
|
)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|||||||
@@ -4,36 +4,85 @@
|
|||||||
from odoo import fields, models
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
# TODO: refactoring to cancellation.rule
|
|
||||||
class PmsCancelationRule(models.Model):
|
class PmsCancelationRule(models.Model):
|
||||||
_name = "pms.cancelation.rule"
|
_name = "pms.cancelation.rule"
|
||||||
_description = "Cancelation Rules"
|
_description = "Cancelation Rules"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
name = fields.Char(
|
||||||
name = fields.Char("Amenity Name", translate=True, required=True)
|
string="Cancelation Rule",
|
||||||
|
required=True,
|
||||||
|
translate=True,
|
||||||
|
)
|
||||||
pricelist_ids = fields.One2many(
|
pricelist_ids = fields.One2many(
|
||||||
"product.pricelist", "cancelation_rule_id", "Pricelist that use this rule"
|
string="Pricelist",
|
||||||
|
help="Pricelist that use this rule",
|
||||||
|
comodel_name="product.pricelist",
|
||||||
|
inverse_name="cancelation_rule_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
pms_property_ids = fields.Many2many(
|
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="pms_cancelation_rule_pms_property_rel",
|
||||||
|
column1="pms_cancelation_rule_id",
|
||||||
|
column2="pms_property_id",
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
active = fields.Boolean(
|
||||||
|
string="Active", help="Determines if cancelation rule is active", default=True
|
||||||
)
|
)
|
||||||
active = fields.Boolean("Active", default=True)
|
|
||||||
days_intime = fields.Integer(
|
days_intime = fields.Integer(
|
||||||
"Days Late", help="Maximum number of days for free cancellation before Checkin"
|
string="Days Late",
|
||||||
|
help="Maximum number of days for free cancellation before Checkin",
|
||||||
|
)
|
||||||
|
penalty_late = fields.Integer(
|
||||||
|
string="% Penalty Late",
|
||||||
|
help="Percentage of the total price that partner has "
|
||||||
|
"to pay in case of late arrival",
|
||||||
|
default="100",
|
||||||
)
|
)
|
||||||
penalty_late = fields.Integer("% Penalty Late", default="100")
|
|
||||||
apply_on_late = fields.Selection(
|
apply_on_late = fields.Selection(
|
||||||
[("first", "First Day"), ("all", "All Days"), ("days", "Specify days")],
|
string="Late apply on",
|
||||||
"Late apply on",
|
help="Days on which the cancelation rule applies when "
|
||||||
|
"the reason is late arrival. "
|
||||||
|
"Can be first, all days or specify the days.",
|
||||||
default="first",
|
default="first",
|
||||||
|
selection=[
|
||||||
|
("first", "First Day"),
|
||||||
|
("all", "All Days"),
|
||||||
|
("days", "Specify days"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
days_late = fields.Integer(
|
||||||
|
string="Late first days",
|
||||||
|
help="Is number of days late in the cancelation rule "
|
||||||
|
"if the value of the apply_on_late field is specify days.",
|
||||||
|
default="2",
|
||||||
|
)
|
||||||
|
penalty_noshow = fields.Integer(
|
||||||
|
string="% Penalty No Show",
|
||||||
|
help="Percentage of the total price that partner has to pay in case of no show",
|
||||||
|
default="100",
|
||||||
)
|
)
|
||||||
days_late = fields.Integer("Late first days", default="2")
|
|
||||||
penalty_noshow = fields.Integer("% Penalty No Show", default="100")
|
|
||||||
apply_on_noshow = fields.Selection(
|
apply_on_noshow = fields.Selection(
|
||||||
[("first", "First Day"), ("all", "All Days"), ("days", "Specify days")],
|
string="No Show apply on",
|
||||||
"No Show apply on",
|
help="Days on which the cancelation rule applies when"
|
||||||
|
" the reason is no show. Can be first, all days or specify the days.",
|
||||||
|
selection=[
|
||||||
|
("first", "First Day"),
|
||||||
|
("all", "All Days"),
|
||||||
|
("days", "Specify days"),
|
||||||
|
],
|
||||||
default="all",
|
default="all",
|
||||||
)
|
)
|
||||||
days_noshow = fields.Integer("NoShow first days", default="2")
|
days_noshow = fields.Integer(
|
||||||
|
string="NoShow first days",
|
||||||
# TODO: Constrain coherence pms_property_ids pricelist and cancelation_rules
|
help="Is number of days no show in the cancelation rule "
|
||||||
|
"if the value of the apply_on_show field is specify days.",
|
||||||
|
default="2",
|
||||||
|
)
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
# Copyright 2018 Alexandre Diaz
|
# Copyright 2018 Alexandre Diaz
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
@@ -9,43 +12,100 @@ from odoo.exceptions import ValidationError
|
|||||||
class PmsCheckinPartner(models.Model):
|
class PmsCheckinPartner(models.Model):
|
||||||
_name = "pms.checkin.partner"
|
_name = "pms.checkin.partner"
|
||||||
_description = "Partner Checkins"
|
_description = "Partner Checkins"
|
||||||
|
_rec_name = "identifier"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _get_default_pms_property(self):
|
|
||||||
# TODO: Change by property env variable (like company)
|
|
||||||
return self.env.user.pms_property_id
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
identifier = fields.Char(
|
identifier = fields.Char(
|
||||||
"Identifier",
|
string="Identifier",
|
||||||
compute="_compute_identifier",
|
help="Checkin Partner Id",
|
||||||
readonly=False,
|
readonly=True,
|
||||||
store=True,
|
index=True,
|
||||||
|
default=lambda self: _("New"),
|
||||||
)
|
)
|
||||||
partner_id = fields.Many2one(
|
partner_id = fields.Many2one(
|
||||||
"res.partner",
|
string="Partner",
|
||||||
|
help="Partner associated with checkin partner",
|
||||||
|
comodel_name="res.partner",
|
||||||
domain="[('is_company', '=', False)]",
|
domain="[('is_company', '=', False)]",
|
||||||
)
|
)
|
||||||
reservation_id = fields.Many2one("pms.reservation")
|
reservation_id = fields.Many2one(
|
||||||
|
string="Reservation",
|
||||||
|
help="Reservation to which checkin partners belong",
|
||||||
|
comodel_name="pms.reservation",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
folio_id = fields.Many2one(
|
folio_id = fields.Many2one(
|
||||||
"pms.folio",
|
string="Folio",
|
||||||
compute="_compute_folio_id",
|
help="Folio to which reservation of checkin partner belongs",
|
||||||
store=True,
|
store=True,
|
||||||
|
comodel_name="pms.folio",
|
||||||
|
compute="_compute_folio_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
pms_property_id = fields.Many2one(
|
pms_property_id = fields.Many2one(
|
||||||
"pms.property", default=_get_default_pms_property, required=True
|
string="Property",
|
||||||
|
help="Property to which the folio associated belongs",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
related="folio_id.pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
name = fields.Char(
|
||||||
|
string="Name",
|
||||||
|
help="Checkin partner name",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_name",
|
||||||
|
)
|
||||||
|
email = fields.Char(
|
||||||
|
string="E-mail",
|
||||||
|
help="Checkin Partner Email",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_email",
|
||||||
|
)
|
||||||
|
mobile = fields.Char(
|
||||||
|
string="Mobile",
|
||||||
|
help="Checkin Partner Mobile",
|
||||||
|
compute="_compute_mobile",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
|
)
|
||||||
|
image_128 = fields.Image(
|
||||||
|
string="Image",
|
||||||
|
help="Checkin Partner Image, it corresponds with Partner Image associated",
|
||||||
|
related="partner_id.image_128",
|
||||||
)
|
)
|
||||||
name = fields.Char("Name", related="partner_id.name")
|
|
||||||
email = fields.Char("E-mail", related="partner_id.email")
|
|
||||||
mobile = fields.Char("Mobile", related="partner_id.mobile")
|
|
||||||
image_128 = fields.Image(related="partner_id.image_128")
|
|
||||||
segmentation_ids = fields.Many2many(
|
segmentation_ids = fields.Many2many(
|
||||||
|
string="Segmentation",
|
||||||
|
help="Segmentation tags to classify checkin partners",
|
||||||
related="reservation_id.segmentation_ids",
|
related="reservation_id.segmentation_ids",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
arrival = fields.Datetime("Enter")
|
checkin = fields.Date(
|
||||||
departure = fields.Datetime("Exit")
|
string="Checkin",
|
||||||
|
help="Checkin date",
|
||||||
|
store=True,
|
||||||
|
related="reservation_id.checkin",
|
||||||
|
depends=["reservation_id.checkin"],
|
||||||
|
)
|
||||||
|
checkout = fields.Date(
|
||||||
|
string="Checkout",
|
||||||
|
help="Checkout date",
|
||||||
|
store=True,
|
||||||
|
related="reservation_id.checkout",
|
||||||
|
depends=["reservation_id.checkout"],
|
||||||
|
)
|
||||||
|
arrival = fields.Datetime("Enter", help="Checkin partner arrival date and time")
|
||||||
|
departure = fields.Datetime(
|
||||||
|
string="Exit", help="Checkin partner departure date and time"
|
||||||
|
)
|
||||||
state = fields.Selection(
|
state = fields.Selection(
|
||||||
|
string="State",
|
||||||
|
help="Status of the checkin partner regarding the reservation",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
selection=[
|
selection=[
|
||||||
("draft", "Unkown Guest"),
|
("draft", "Unkown Guest"),
|
||||||
("precheckin", "Pending arrival"),
|
("precheckin", "Pending arrival"),
|
||||||
@@ -53,10 +113,7 @@ class PmsCheckinPartner(models.Model):
|
|||||||
("done", "Out"),
|
("done", "Out"),
|
||||||
("cancelled", "Cancelled"),
|
("cancelled", "Cancelled"),
|
||||||
],
|
],
|
||||||
string="State",
|
|
||||||
compute="_compute_state",
|
compute="_compute_state",
|
||||||
store=True,
|
|
||||||
readonly=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compute
|
# Compute
|
||||||
@@ -98,14 +155,28 @@ class PmsCheckinPartner(models.Model):
|
|||||||
else:
|
else:
|
||||||
record.state = "precheckin"
|
record.state = "precheckin"
|
||||||
|
|
||||||
@api.model
|
@api.depends(
|
||||||
def _checkin_mandatory_fields(self, depends=False):
|
"partner_id",
|
||||||
# api.depends need "reservation_id.state" in de lambda function
|
"partner_id.name",
|
||||||
if depends:
|
"reservation_id",
|
||||||
return ["reservation_id.state", "name"]
|
"reservation_id.preferred_room_id",
|
||||||
return ["name"]
|
)
|
||||||
|
def _compute_name(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.name:
|
||||||
|
record.name = record.partner_id.name
|
||||||
|
|
||||||
# Constraints and onchanges
|
@api.depends("partner_id", "partner_id.email")
|
||||||
|
def _compute_email(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.email:
|
||||||
|
record.email = record.partner_id.email
|
||||||
|
|
||||||
|
@api.depends("partner_id", "partner_id.mobile")
|
||||||
|
def _compute_mobile(self):
|
||||||
|
for record in self:
|
||||||
|
if not record.mobile:
|
||||||
|
record.mobile = record.partner_id.mobile
|
||||||
|
|
||||||
@api.constrains("departure", "arrival")
|
@api.constrains("departure", "arrival")
|
||||||
def _check_departure(self):
|
def _check_departure(self):
|
||||||
@@ -129,23 +200,138 @@ class PmsCheckinPartner(models.Model):
|
|||||||
_("This guest is already registered in the room")
|
_("This guest is already registered in the room")
|
||||||
)
|
)
|
||||||
|
|
||||||
# CRUD
|
@api.constrains("email")
|
||||||
|
def check_email_pattern(self):
|
||||||
|
for record in self:
|
||||||
|
if record.email:
|
||||||
|
if not re.search(
|
||||||
|
r"^[a-zA-Z0-9]([a-zA-z0-9\-\_]*[\.]?[a-zA-Z0-9\-\_]+)*"
|
||||||
|
r"@([a-zA-z0-9\-]+([\.][a-zA-Z0-9\-\_]+)?\.[a-zA-Z0-9]+)+$",
|
||||||
|
record.email,
|
||||||
|
):
|
||||||
|
raise ValidationError(_("'%s' is not a valid email", record.email))
|
||||||
|
|
||||||
|
@api.constrains("mobile")
|
||||||
|
def check_phone_pattern(self):
|
||||||
|
|
||||||
|
for record in self:
|
||||||
|
if record.mobile:
|
||||||
|
|
||||||
|
if not re.search(
|
||||||
|
r"^(\d{3}[\-\s]?\d{2}[\-\s]?\d{2}[\-\s]?\d{2}[\-\s]?|"
|
||||||
|
r"\d{3}[\-\s]?\d{3}[\-\s]?\d{3})$",
|
||||||
|
str(record.mobile),
|
||||||
|
):
|
||||||
|
raise ValidationError(_("'%s' is not a valid phone", record.mobile))
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
# The checkin records are created automatically from adult depends
|
# The checkin records are created automatically from adult depends
|
||||||
# if you try to create one manually, we update one unassigned checkin
|
# if you try to create one manually, we update one unassigned checkin
|
||||||
if not self._context.get("auto_create_checkin"):
|
reservation_id = vals.get("reservation_id")
|
||||||
reservation_id = vals.get("reservation_id")
|
if reservation_id:
|
||||||
if reservation_id:
|
reservation = self.env["pms.reservation"].browse(reservation_id)
|
||||||
reservation = self.env["pms.reservation"].browse(reservation_id)
|
else:
|
||||||
draft_checkins = reservation.checkin_partner_ids.filtered(
|
raise ValidationError(
|
||||||
lambda c: c.state == "draft"
|
_("Is mandatory indicate the reservation on the checkin")
|
||||||
|
)
|
||||||
|
draft_checkins = reservation.checkin_partner_ids.filtered(
|
||||||
|
lambda c: c.state == "draft"
|
||||||
|
)
|
||||||
|
if len(reservation.checkin_partner_ids) < reservation.adults:
|
||||||
|
if vals.get("identifier", _("New")) == _("New") or "identifier" not in vals:
|
||||||
|
pms_property_id = (
|
||||||
|
self.env.user.get_active_property_ids()[0]
|
||||||
|
if "pms_property_id" not in vals
|
||||||
|
else vals["pms_property_id"]
|
||||||
)
|
)
|
||||||
if len(draft_checkins) > 0 and vals.get("partner_id"):
|
pms_property = self.env["pms.property"].browse(pms_property_id)
|
||||||
draft_checkins[0].sudo().unlink()
|
vals["identifier"] = pms_property.folio_sequence_id._next_do()
|
||||||
return super(PmsCheckinPartner, self).create(vals)
|
return super(PmsCheckinPartner, self).create(vals)
|
||||||
|
if len(draft_checkins) > 0:
|
||||||
|
draft_checkins[0].write(vals)
|
||||||
|
return draft_checkins[0]
|
||||||
|
raise ValidationError(
|
||||||
|
_("Is not possible to create the proposed check-in in this reservation")
|
||||||
|
)
|
||||||
|
|
||||||
# Action methods
|
def write(self, vals):
|
||||||
|
res = super(PmsCheckinPartner, self).write(vals)
|
||||||
|
ResPartner = self.env["res.partner"]
|
||||||
|
if any(field in vals for field in ResPartner._get_key_fields()):
|
||||||
|
# Create Partner if get key field in the checkin
|
||||||
|
for record in self:
|
||||||
|
key = False
|
||||||
|
partner = False
|
||||||
|
if not record.partner_id:
|
||||||
|
partner_vals = {}
|
||||||
|
for field in self._checkin_partner_fields():
|
||||||
|
if getattr(record, field):
|
||||||
|
partner_vals[field] = getattr(record, field)
|
||||||
|
if field in ResPartner._get_key_fields() and partner_vals.get(
|
||||||
|
field
|
||||||
|
):
|
||||||
|
key = True
|
||||||
|
# REVIEW: if partner exist, we can merge?
|
||||||
|
partner = ResPartner.search(
|
||||||
|
[(field, "=", getattr(record, field))]
|
||||||
|
)
|
||||||
|
if key:
|
||||||
|
if not partner:
|
||||||
|
partner = ResPartner.create(partner_vals)
|
||||||
|
record.partner_id = partner
|
||||||
|
|
||||||
|
if any(field in vals for field in self._checkin_partner_fields()):
|
||||||
|
# Update partner when the checkin partner field is not set on the partner
|
||||||
|
for record in self:
|
||||||
|
if record.partner_id:
|
||||||
|
partner_vals = {}
|
||||||
|
for field in self._checkin_partner_fields():
|
||||||
|
if not getattr(record.partner_id, field):
|
||||||
|
partner_vals[field] = getattr(record, field)
|
||||||
|
record.partner_id.write(partner_vals)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def unlink(self):
|
||||||
|
reservations = self.mapped("reservation_id")
|
||||||
|
res = super().unlink()
|
||||||
|
reservations._compute_checkin_partner_ids()
|
||||||
|
return res
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _checkin_mandatory_fields(self, depends=False):
|
||||||
|
# api.depends need "reservation_id.state" in the lambda function
|
||||||
|
if depends:
|
||||||
|
return ["reservation_id.state", "name"]
|
||||||
|
return ["name"]
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _checkin_partner_fields(self):
|
||||||
|
# api.depends need "reservation_id.state" in the lambda function
|
||||||
|
checkin_fields = self._checkin_mandatory_fields()
|
||||||
|
checkin_fields.extend(["mobile", "email"])
|
||||||
|
return checkin_fields
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def import_room_list_json(self, roomlist_json):
|
||||||
|
roomlist_json = json.loads(roomlist_json)
|
||||||
|
for checkin_dict in roomlist_json:
|
||||||
|
identifier = checkin_dict["identifier"]
|
||||||
|
reservation_id = checkin_dict["reservation_id"]
|
||||||
|
checkin = self.env["pms.checkin.partner"].search(
|
||||||
|
[("identifier", "=", identifier)]
|
||||||
|
)
|
||||||
|
reservation = self.env["pms.reservation"].browse(reservation_id)
|
||||||
|
if not checkin:
|
||||||
|
raise ValidationError(
|
||||||
|
_("%s not found in checkins (%s)"), identifier, reservation.name
|
||||||
|
)
|
||||||
|
checkin_vals = {}
|
||||||
|
for key, value in checkin_dict.items():
|
||||||
|
if key in ("reservation_id", "folio_id", "identifier"):
|
||||||
|
continue
|
||||||
|
checkin_vals[key] = value
|
||||||
|
checkin.write(checkin_vals)
|
||||||
|
|
||||||
def action_on_board(self):
|
def action_on_board(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -153,12 +339,16 @@ class PmsCheckinPartner(models.Model):
|
|||||||
raise ValidationError(_("It is not yet checkin day!"))
|
raise ValidationError(_("It is not yet checkin day!"))
|
||||||
if record.reservation_id.checkout <= fields.Date.today():
|
if record.reservation_id.checkout <= fields.Date.today():
|
||||||
raise ValidationError(_("Its too late to checkin"))
|
raise ValidationError(_("Its too late to checkin"))
|
||||||
|
if any(
|
||||||
|
not getattr(record, field) for field in self._checkin_mandatory_fields()
|
||||||
|
):
|
||||||
|
raise ValidationError(_("Personal data is missing for check-in"))
|
||||||
vals = {
|
vals = {
|
||||||
"state": "onboard",
|
"state": "onboard",
|
||||||
"arrival": fields.Datetime.now(),
|
"arrival": fields.Datetime.now(),
|
||||||
}
|
}
|
||||||
record.update(vals)
|
record.update(vals)
|
||||||
if record.reservation_id.left_for_checkin:
|
if record.reservation_id.allowed_checkin:
|
||||||
record.reservation_id.state = "onboard"
|
record.reservation_id.state = "onboard"
|
||||||
|
|
||||||
def action_done(self):
|
def action_done(self):
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
# Copyright 2017 Dario Lodeiros
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
from odoo import fields, models
|
|
||||||
|
|
||||||
|
|
||||||
class PmsFloor(models.Model):
|
|
||||||
_name = "pms.floor"
|
|
||||||
_description = "Ubication"
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
name = fields.Char(
|
|
||||||
"Ubication Name", translate=True, size=64, required=True, index=True
|
|
||||||
)
|
|
||||||
pms_property_ids = fields.Many2many(
|
|
||||||
"pms.property", string="Properties", required=False, ondelete="restrict"
|
|
||||||
)
|
|
||||||
sequence = fields.Integer("Sequence")
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -18,56 +18,77 @@ class PmsProperty(models.Model):
|
|||||||
_inherits = {"res.partner": "partner_id"}
|
_inherits = {"res.partner": "partner_id"}
|
||||||
_check_company_auto = True
|
_check_company_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
partner_id = fields.Many2one(
|
partner_id = fields.Many2one(
|
||||||
"res.partner", "Property", required=True, delegate=True, ondelete="cascade"
|
string="Property",
|
||||||
|
help="Current property",
|
||||||
|
comodel_name="res.partner",
|
||||||
|
required=True,
|
||||||
|
ondelete="cascade",
|
||||||
)
|
)
|
||||||
company_id = fields.Many2one(
|
company_id = fields.Many2one(
|
||||||
"res.company",
|
string="Company",
|
||||||
required=True,
|
|
||||||
help="The company that owns or operates this property.",
|
help="The company that owns or operates this property.",
|
||||||
|
comodel_name="res.company",
|
||||||
|
required=True,
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
user_ids = fields.Many2many(
|
user_ids = fields.Many2many(
|
||||||
"res.users",
|
|
||||||
"pms_property_users_rel",
|
|
||||||
"pms_property_id",
|
|
||||||
"user_id",
|
|
||||||
string="Accepted Users",
|
string="Accepted Users",
|
||||||
|
help="Field related to res.users. Allowed users on the property",
|
||||||
|
comodel_name="res.users",
|
||||||
|
relation="pms_property_users_rel",
|
||||||
|
column1="pms_property_id",
|
||||||
|
column2="user_id",
|
||||||
|
)
|
||||||
|
room_ids = fields.One2many(
|
||||||
|
string="Rooms",
|
||||||
|
help="Rooms that a property has.",
|
||||||
|
comodel_name="pms.room",
|
||||||
|
inverse_name="pms_property_id",
|
||||||
)
|
)
|
||||||
room_ids = fields.One2many("pms.room", "pms_property_id", "Rooms")
|
|
||||||
default_pricelist_id = fields.Many2one(
|
default_pricelist_id = fields.Many2one(
|
||||||
"product.pricelist",
|
|
||||||
string="Product Pricelist",
|
string="Product Pricelist",
|
||||||
required=True,
|
|
||||||
help="The default pricelist used in this property.",
|
help="The default pricelist used in this property.",
|
||||||
)
|
comodel_name="product.pricelist",
|
||||||
default_restriction_id = fields.Many2one(
|
|
||||||
"pms.room.type.restriction",
|
|
||||||
"Restriction Plan",
|
|
||||||
required=True,
|
required=True,
|
||||||
help="The default restriction plan used in this property.",
|
default=lambda self: self.env.ref("product.list0").id,
|
||||||
)
|
)
|
||||||
default_arrival_hour = fields.Char(
|
default_arrival_hour = fields.Char(
|
||||||
"Arrival Hour (GMT)", help="HH:mm Format", default="14:00"
|
string="Arrival Hour", help="HH:mm Format", default="14:00"
|
||||||
)
|
)
|
||||||
default_departure_hour = fields.Char(
|
default_departure_hour = fields.Char(
|
||||||
"Departure Hour (GMT)", help="HH:mm Format", default="12:00"
|
string="Departure Hour", help="HH:mm Format", default="12:00"
|
||||||
)
|
)
|
||||||
default_cancel_policy_days = fields.Integer("Cancellation Days")
|
|
||||||
default_cancel_policy_percent = fields.Float("Percent to pay")
|
|
||||||
folio_sequence_id = fields.Many2one(
|
folio_sequence_id = fields.Many2one(
|
||||||
"ir.sequence", "Folio Sequence", check_company=True, copy=False
|
string="Folio Sequence",
|
||||||
|
help="The sequence that formed the name of the folio.",
|
||||||
|
check_company=True,
|
||||||
|
copy=False,
|
||||||
|
comodel_name="ir.sequence",
|
||||||
)
|
)
|
||||||
tz = fields.Selection(
|
reservation_sequence_id = fields.Many2one(
|
||||||
_tz_get,
|
string="Reservation Sequence",
|
||||||
string="Timezone",
|
help="The sequence that formed the name of the reservation.",
|
||||||
required=True,
|
check_company=True,
|
||||||
default=lambda self: self.env.user.tz or "UTC",
|
copy=False,
|
||||||
help="This field is used in order to define \
|
comodel_name="ir.sequence",
|
||||||
in which timezone the arrival/departure will work.",
|
)
|
||||||
|
checkin_sequence_id = fields.Many2one(
|
||||||
|
string="Checkin Sequence",
|
||||||
|
help="Field used to create the name of the checkin partner",
|
||||||
|
check_company=True,
|
||||||
|
copy=False,
|
||||||
|
comodel_name="ir.sequence",
|
||||||
|
)
|
||||||
|
|
||||||
|
tz = fields.Selection(
|
||||||
|
string="Timezone",
|
||||||
|
help="This field is used to determine de timezone of the property.",
|
||||||
|
required=True,
|
||||||
|
default=lambda self: self.env.user.tz or "UTC",
|
||||||
|
selection=_tz_get,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Constraints and onchanges
|
|
||||||
@api.constrains("default_arrival_hour")
|
@api.constrains("default_arrival_hour")
|
||||||
def _check_arrival_hour(self):
|
def _check_arrival_hour(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -96,12 +117,75 @@ class PmsProperty(models.Model):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def date_property_timezone(self, date):
|
def date_property_timezone(self, dt):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
tz_property = self.tz
|
tz_property = self.tz
|
||||||
date = pytz.timezone(tz_property).localize(date)
|
dt = pytz.timezone(tz_property).localize(dt)
|
||||||
date = date.replace(tzinfo=None)
|
dt = dt.replace(tzinfo=None)
|
||||||
date = pytz.timezone(self.env.user.tz).localize(date)
|
dt = pytz.timezone(self.env.user.tz).localize(dt)
|
||||||
date = date.astimezone(pytz.utc)
|
dt = dt.astimezone(pytz.utc)
|
||||||
date = date.replace(tzinfo=None)
|
dt = dt.replace(tzinfo=None)
|
||||||
return date
|
return dt
|
||||||
|
|
||||||
|
def _get_payment_methods(self):
|
||||||
|
self.ensure_one()
|
||||||
|
payment_methods = self.env["account.journal"].search(
|
||||||
|
[
|
||||||
|
"&",
|
||||||
|
("type", "in", ["cash", "bank"]),
|
||||||
|
"|",
|
||||||
|
("pms_property_ids", "in", self.id),
|
||||||
|
"|",
|
||||||
|
"&",
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
("company_id", "=", self.company_id.id),
|
||||||
|
"&",
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
("company_id", "=", False),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return payment_methods
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
name = vals.get("name")
|
||||||
|
if "folio_sequence_id" not in vals or not vals.get("folio_sequence_id"):
|
||||||
|
folio_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Folio " + name,
|
||||||
|
"code": "pms.folio",
|
||||||
|
"prefix": "F/%(y)s",
|
||||||
|
"suffix": "%(sec)s",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": vals.get("company_id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
vals.update({"folio_sequence_id": folio_sequence.id})
|
||||||
|
if "reservation_sequence_id" not in vals or not vals.get(
|
||||||
|
"reservation_sequence_id"
|
||||||
|
):
|
||||||
|
reservation_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Reservation " + name,
|
||||||
|
"code": "pms.reservation",
|
||||||
|
"prefix": "R/%(y)s",
|
||||||
|
"suffix": "%(sec)s",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": vals.get("company_id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
vals.update({"reservation_sequence_id": reservation_sequence.id})
|
||||||
|
if "checkin_sequence_id" not in vals or not vals.get("checkin_sequence_id"):
|
||||||
|
checkin_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Checkin " + name,
|
||||||
|
"code": "pms.checkin.partner",
|
||||||
|
"prefix": "C/%(y)s",
|
||||||
|
"suffix": "%(sec)s",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": vals.get("company_id"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
vals.update({"checkin_sequence_id": checkin_sequence.id})
|
||||||
|
record = super(PmsProperty, self).create(vals)
|
||||||
|
return record
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -14,71 +14,103 @@ class PmsReservationLine(models.Model):
|
|||||||
_name = "pms.reservation.line"
|
_name = "pms.reservation.line"
|
||||||
_description = "Reservations by day"
|
_description = "Reservations by day"
|
||||||
_order = "date"
|
_order = "date"
|
||||||
|
_check_company_auto = True
|
||||||
|
|
||||||
# Default Methods ang Gets
|
|
||||||
|
|
||||||
def name_get(self):
|
|
||||||
result = []
|
|
||||||
for res in self:
|
|
||||||
date = fields.Date.from_string(res.date)
|
|
||||||
name = u"{}/{}".format(date.day, date.month)
|
|
||||||
result.append((res.id, name))
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
reservation_id = fields.Many2one(
|
reservation_id = fields.Many2one(
|
||||||
"pms.reservation",
|
|
||||||
string="Reservation",
|
string="Reservation",
|
||||||
ondelete="cascade",
|
help="It is the reservation in a reservation line",
|
||||||
required=True,
|
required=True,
|
||||||
copy=False,
|
copy=False,
|
||||||
|
comodel_name="pms.reservation",
|
||||||
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
room_id = fields.Many2one(
|
room_id = fields.Many2one(
|
||||||
"pms.room",
|
|
||||||
string="Room",
|
string="Room",
|
||||||
ondelete="restrict",
|
help="The room of a reservation. ",
|
||||||
compute="_compute_room_id",
|
|
||||||
store=True,
|
|
||||||
readonly=False,
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_room_id",
|
||||||
|
comodel_name="pms.room",
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
move_line_ids = fields.Many2many(
|
|
||||||
"account.move.line",
|
sale_line_ids = fields.Many2many(
|
||||||
"reservation_line_move_rel",
|
string="Sales Lines",
|
||||||
"reservation_line_id",
|
|
||||||
"move_line_id",
|
|
||||||
string="Invoice Lines",
|
|
||||||
readonly=True,
|
readonly=True,
|
||||||
copy=False,
|
copy=False,
|
||||||
|
comodel_name="folio.sale.line",
|
||||||
|
relation="reservation_line_sale_line_rel",
|
||||||
|
column1="reservation_line_id",
|
||||||
|
column2="sale_line_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
pms_property_id = fields.Many2one(
|
pms_property_id = fields.Many2one(
|
||||||
"pms.property",
|
string="Property",
|
||||||
store=True,
|
help="Property with access to the element;"
|
||||||
|
" if not set, all properties can access",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
related="reservation_id.pms_property_id",
|
related="reservation_id.pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
date = fields.Date(
|
||||||
|
string="Date",
|
||||||
|
help="The date of the reservation in reservation line",
|
||||||
|
)
|
||||||
|
state = fields.Selection(
|
||||||
|
string="State",
|
||||||
|
help="State of the reservation line.",
|
||||||
|
related="reservation_id.state",
|
||||||
|
store=True,
|
||||||
)
|
)
|
||||||
date = fields.Date("Date")
|
|
||||||
state = fields.Selection(related="reservation_id.state")
|
|
||||||
price = fields.Float(
|
price = fields.Float(
|
||||||
string="Price",
|
string="Price",
|
||||||
|
help="The price in a reservation line",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
digits=("Product Price"),
|
digits=("Product Price"),
|
||||||
compute="_compute_price",
|
compute="_compute_price",
|
||||||
store=True,
|
|
||||||
readonly=False,
|
|
||||||
)
|
)
|
||||||
cancel_discount = fields.Float(
|
cancel_discount = fields.Float(
|
||||||
string="Cancel Discount (%)",
|
string="Cancelation Discount (%)",
|
||||||
digits=("Discount"),
|
help="",
|
||||||
default=0.0,
|
|
||||||
compute="_compute_cancel_discount",
|
|
||||||
store=True,
|
|
||||||
readonly=False,
|
readonly=False,
|
||||||
|
default=0.0,
|
||||||
|
store=True,
|
||||||
|
digits=("Discount"),
|
||||||
|
compute="_compute_cancel_discount",
|
||||||
|
)
|
||||||
|
avail_id = fields.Many2one(
|
||||||
|
string="Availability Day",
|
||||||
|
help="",
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.availability",
|
||||||
|
ondelete="restrict",
|
||||||
|
compute="_compute_avail_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
discount = fields.Float(
|
||||||
|
string="Discount (%)",
|
||||||
|
help="",
|
||||||
|
default=0.0,
|
||||||
|
digits=("Discount"),
|
||||||
)
|
)
|
||||||
discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0)
|
|
||||||
occupies_availability = fields.Boolean(
|
occupies_availability = fields.Boolean(
|
||||||
string="Occupies",
|
string="Occupies",
|
||||||
compute="_compute_occupies_availability",
|
|
||||||
store=True,
|
|
||||||
help="This record is taken into account to calculate availability",
|
help="This record is taken into account to calculate availability",
|
||||||
|
store=True,
|
||||||
|
compute="_compute_occupies_availability",
|
||||||
|
)
|
||||||
|
impacts_quota = fields.Integer(
|
||||||
|
string="Impacts quota",
|
||||||
|
help="This line has been taken into account in the avail quota",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_impact_quota",
|
||||||
)
|
)
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
@@ -90,66 +122,122 @@ class PmsReservationLine(models.Model):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Compute and Search methods
|
def name_get(self):
|
||||||
@api.depends("reservation_id.room_type_id")
|
result = []
|
||||||
|
for res in self:
|
||||||
|
date = fields.Date.from_string(res.date)
|
||||||
|
name = u"{}/{}".format(date.day, date.month)
|
||||||
|
result.append((res.id, name))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_display_price(self, product):
|
||||||
|
if self.reservation_id.pricelist_id.discount_policy == "with_discount":
|
||||||
|
return product.with_context(
|
||||||
|
pricelist=self.reservation_id.pricelist_id.id
|
||||||
|
).price
|
||||||
|
product_context = dict(
|
||||||
|
self.env.context,
|
||||||
|
partner_id=self.reservation_id.partner_id.id,
|
||||||
|
date=self.date,
|
||||||
|
uom=product.uom_id.id,
|
||||||
|
)
|
||||||
|
final_price, rule_id = self.reservation_id.pricelist_id.with_context(
|
||||||
|
product_context
|
||||||
|
).get_product_price_rule(product, 1.0, self.reservation_id.partner_id)
|
||||||
|
base_price, currency = self.with_context(
|
||||||
|
product_context
|
||||||
|
)._get_real_price_currency(
|
||||||
|
product, rule_id, 1, product.uom_id, self.reservation_id.pricelist_id.id
|
||||||
|
)
|
||||||
|
if currency != self.reservation_id.pricelist_id.currency_id:
|
||||||
|
base_price = currency._convert(
|
||||||
|
base_price,
|
||||||
|
self.reservation_id.pricelist_id.currency_id,
|
||||||
|
self.reservation_id.company_id or self.env.company,
|
||||||
|
fields.Date.today(),
|
||||||
|
)
|
||||||
|
# negative discounts (= surcharge) are included in the display price
|
||||||
|
return max(base_price, final_price)
|
||||||
|
|
||||||
|
@api.depends("reservation_id.room_type_id", "reservation_id.preferred_room_id")
|
||||||
def _compute_room_id(self):
|
def _compute_room_id(self):
|
||||||
for line in self.sorted(key=lambda r: (r.reservation_id, r.date)):
|
for line in self.filtered("reservation_id.room_type_id").sorted(
|
||||||
|
key=lambda r: (r.reservation_id, r.date)
|
||||||
# if the reservation has a room type and no room id
|
):
|
||||||
if line.reservation_id.room_type_id and not line.room_id:
|
reservation = line.reservation_id
|
||||||
|
if (
|
||||||
|
reservation.preferred_room_id
|
||||||
|
and reservation.preferred_room_id != line.room_id
|
||||||
|
) or (
|
||||||
|
(reservation.preferred_room_id or reservation.room_type_id)
|
||||||
|
and not line.room_id
|
||||||
|
):
|
||||||
|
free_room_select = True if reservation.preferred_room_id else False
|
||||||
# we get the rooms available for the entire stay
|
# we get the rooms available for the entire stay
|
||||||
rooms_available = self.env[
|
rooms_available = self.env["pms.availability.plan"].rooms_available(
|
||||||
"pms.room.type.availability"
|
|
||||||
].rooms_available(
|
|
||||||
checkin=line.reservation_id.checkin,
|
checkin=line.reservation_id.checkin,
|
||||||
checkout=line.reservation_id.checkout,
|
checkout=line.reservation_id.checkout,
|
||||||
room_type_id=line.reservation_id.room_type_id.id,
|
room_type_id=reservation.room_type_id.id
|
||||||
current_lines=line._origin.reservation_id.reservation_line_ids.ids,
|
if not free_room_select
|
||||||
|
else False,
|
||||||
|
current_lines=line.reservation_id.reservation_line_ids.ids,
|
||||||
|
pricelist_id=line.reservation_id.pricelist_id.id,
|
||||||
|
pms_property_id=line.pms_property_id.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# if there is availability for the entire stay
|
# if there is availability for the entire stay
|
||||||
if rooms_available:
|
if rooms_available:
|
||||||
|
|
||||||
# if the reservation has a preferred room
|
# if the reservation has a preferred room
|
||||||
if line.reservation_id.preferred_room_id:
|
if reservation.preferred_room_id:
|
||||||
|
|
||||||
# if the preferred room is available
|
# if the preferred room is available
|
||||||
if line.reservation_id.preferred_room_id in rooms_available:
|
if reservation.preferred_room_id in rooms_available:
|
||||||
line.room_id = line.reservation_id.preferred_room_id
|
line.room_id = reservation.preferred_room_id
|
||||||
|
|
||||||
# if the preferred room is NOT available
|
# if the preferred room is NOT available
|
||||||
else:
|
else:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("%s: No room available.")
|
_("%s: No room available.")
|
||||||
% (line.reservation_id.preferred_room_id.name)
|
% (reservation.preferred_room_id.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
# otherwise we assign the first of those
|
# otherwise we assign the first of those
|
||||||
# available for the entire stay
|
# available for the entire stay
|
||||||
else:
|
else:
|
||||||
line.room_id = rooms_available[0]
|
line.room_id = rooms_available[0]
|
||||||
|
# check that the reservation cannot be allocated even by dividing it
|
||||||
|
elif not self.env["pms.availability.plan"].splitted_availability(
|
||||||
|
checkin=line.reservation_id.checkin,
|
||||||
|
checkout=line.reservation_id.checkout,
|
||||||
|
room_type_id=line.reservation_id.room_type_id.id,
|
||||||
|
current_lines=line._origin.reservation_id.reservation_line_ids.ids,
|
||||||
|
pricelist=line.reservation_id.pricelist_id,
|
||||||
|
pms_property_id=line.pms_property_id.id,
|
||||||
|
):
|
||||||
|
raise ValidationError(
|
||||||
|
_("%s: No room type available")
|
||||||
|
% (line.reservation_id.room_type_id.name)
|
||||||
|
)
|
||||||
|
|
||||||
# if there is no availability for the entire stay without
|
# the reservation can be allocated into several rooms
|
||||||
# changing rooms (we assume a split reservation)
|
|
||||||
else:
|
else:
|
||||||
rooms_ranking = dict()
|
rooms_ranking = dict()
|
||||||
|
|
||||||
# we go through the rooms of the type
|
# we go through the rooms of the type
|
||||||
for room in self.env["pms.room"].search(
|
for room in self.env["pms.room"].search(
|
||||||
[("room_type_id", "=", line.reservation_id.room_type_id.id)]
|
[
|
||||||
|
("room_type_id", "=", reservation.room_type_id.id),
|
||||||
|
("pms_property_id", "=", reservation.pms_property_id.id),
|
||||||
|
]
|
||||||
):
|
):
|
||||||
|
|
||||||
# we iterate the dates from the date of the line to the checkout
|
# we iterate the dates from the date of the line to the checkout
|
||||||
for date_iterator in [
|
for date_iterator in [
|
||||||
line.date + datetime.timedelta(days=x)
|
line.date + datetime.timedelta(days=x)
|
||||||
for x in range(
|
for x in range(0, (reservation.checkout - line.date).days)
|
||||||
0, (line.reservation_id.checkout - line.date).days
|
|
||||||
)
|
|
||||||
]:
|
]:
|
||||||
# if the room is already assigned for
|
# if the room is already assigned for
|
||||||
# a date we go to the next room
|
# a date we go to the next room
|
||||||
ids = line.reservation_id.reservation_line_ids.ids
|
ids = reservation.reservation_line_ids.ids
|
||||||
if (
|
if (
|
||||||
self.env["pms.reservation.line"].search_count(
|
self.env["pms.reservation.line"].search_count(
|
||||||
[
|
[
|
||||||
@@ -170,12 +258,8 @@ class PmsReservationLine(models.Model):
|
|||||||
if room.id not in rooms_ranking
|
if room.id not in rooms_ranking
|
||||||
else rooms_ranking[room.id] + 1
|
else rooms_ranking[room.id] + 1
|
||||||
)
|
)
|
||||||
if len(rooms_ranking) == 0:
|
|
||||||
raise ValidationError(
|
if len(rooms_ranking) > 0:
|
||||||
_("%s: No room type available")
|
|
||||||
% (line.reservation_id.room_type_id.name)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# we get the best score in the ranking
|
# we get the best score in the ranking
|
||||||
best = max(rooms_ranking.values())
|
best = max(rooms_ranking.values())
|
||||||
|
|
||||||
@@ -194,7 +278,7 @@ class PmsReservationLine(models.Model):
|
|||||||
line_past_night = self.env["pms.reservation.line"].search(
|
line_past_night = self.env["pms.reservation.line"].search(
|
||||||
[
|
[
|
||||||
("date", "=", date_last_night),
|
("date", "=", date_last_night),
|
||||||
("reservation_id", "=", line.reservation_id.id),
|
("reservation_id", "=", reservation.id),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
# if there is the night before and if the room
|
# if there is the night before and if the room
|
||||||
@@ -215,9 +299,19 @@ class PmsReservationLine(models.Model):
|
|||||||
# no matter what it is
|
# no matter what it is
|
||||||
line.room_id = list(bests.keys())[0]
|
line.room_id = list(bests.keys())[0]
|
||||||
|
|
||||||
|
@api.depends("reservation_id.room_type_id", "reservation_id.pricelist_id")
|
||||||
|
def _compute_impact_quota(self):
|
||||||
|
for line in self:
|
||||||
|
reservation = line.reservation_id
|
||||||
|
line.impacts_quota = self.env["pms.availability.plan"].update_quota(
|
||||||
|
pricelist_id=reservation.pricelist_id,
|
||||||
|
room_type_id=reservation.room_type_id,
|
||||||
|
date=line.date,
|
||||||
|
line=line,
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"reservation_id",
|
"reservation_id",
|
||||||
"reservation_id.pricelist_id",
|
|
||||||
"reservation_id.room_type_id",
|
"reservation_id.room_type_id",
|
||||||
"reservation_id.reservation_type",
|
"reservation_id.reservation_type",
|
||||||
"reservation_id.pms_property_id",
|
"reservation_id.pms_property_id",
|
||||||
@@ -231,7 +325,7 @@ class PmsReservationLine(models.Model):
|
|||||||
or not reservation.pms_property_id
|
or not reservation.pms_property_id
|
||||||
):
|
):
|
||||||
line.price = 0
|
line.price = 0
|
||||||
elif line._recompute_price():
|
elif not line.price or self._context.get("force_recompute"):
|
||||||
room_type_id = reservation.room_type_id.id
|
room_type_id = reservation.room_type_id.id
|
||||||
product = self.env["pms.room.type"].browse(room_type_id).product_id
|
product = self.env["pms.room.type"].browse(room_type_id).product_id
|
||||||
partner = self.env["res.partner"].browse(reservation.partner_id.id)
|
partner = self.env["res.partner"].browse(reservation.partner_id.id)
|
||||||
@@ -239,7 +333,8 @@ class PmsReservationLine(models.Model):
|
|||||||
lang=partner.lang,
|
lang=partner.lang,
|
||||||
partner=partner.id,
|
partner=partner.id,
|
||||||
quantity=1,
|
quantity=1,
|
||||||
date=line.date,
|
date=line.reservation_id.date_order,
|
||||||
|
consumption_date=line.date,
|
||||||
pricelist=reservation.pricelist_id.id,
|
pricelist=reservation.pricelist_id.id,
|
||||||
uom=product.uom_id.id,
|
uom=product.uom_id.id,
|
||||||
property=reservation.pms_property_id.id,
|
property=reservation.pms_property_id.id,
|
||||||
@@ -251,8 +346,6 @@ class PmsReservationLine(models.Model):
|
|||||||
line.reservation_id.company_id,
|
line.reservation_id.company_id,
|
||||||
)
|
)
|
||||||
# TODO: Out of service 0 amount
|
# TODO: Out of service 0 amount
|
||||||
else:
|
|
||||||
line.price = line._origin.price
|
|
||||||
|
|
||||||
@api.depends("reservation_id.state", "reservation_id.overbooking")
|
@api.depends("reservation_id.state", "reservation_id.overbooking")
|
||||||
def _compute_occupies_availability(self):
|
def _compute_occupies_availability(self):
|
||||||
@@ -265,38 +358,15 @@ class PmsReservationLine(models.Model):
|
|||||||
else:
|
else:
|
||||||
line.occupies_availability = True
|
line.occupies_availability = True
|
||||||
|
|
||||||
def _recompute_price(self):
|
|
||||||
# REVIEW: Conditional to avoid overriding already calculated prices,
|
|
||||||
# I'm not sure it's the best way
|
|
||||||
self.ensure_one()
|
|
||||||
origin = self._origin.reservation_id
|
|
||||||
new = self.reservation_id
|
|
||||||
price_fields = [
|
|
||||||
"pricelist_id",
|
|
||||||
"room_type_id",
|
|
||||||
"reservation_type",
|
|
||||||
"pms_property_id",
|
|
||||||
]
|
|
||||||
if (
|
|
||||||
any(origin[field] != new[field] for field in price_fields)
|
|
||||||
or self._origin.price == 0
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
# TODO: Refact method and allowed cancelled single days
|
# TODO: Refact method and allowed cancelled single days
|
||||||
@api.depends("reservation_id.cancelled_reason")
|
@api.depends("reservation_id.cancelled_reason")
|
||||||
def _compute_cancel_discount(self):
|
def _compute_cancel_discount(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
line.cancel_discount = 0
|
line.cancel_discount = 0
|
||||||
|
# TODO: Review cancel logic
|
||||||
# reservation = line.reservation_id
|
# reservation = line.reservation_id
|
||||||
# pricelist = reservation.pricelist_id
|
# pricelist = reservation.pricelist_id
|
||||||
# if reservation.state == "cancelled":
|
# if reservation.state == "cancelled":
|
||||||
# # TODO: Set 0 qty on cancel room services change to compute day_qty
|
|
||||||
# # (view constrain service_line_days)
|
|
||||||
# for service in reservation.service_ids:
|
|
||||||
# service.service_line_ids.write({"day_qty": 0})
|
|
||||||
# service._compute_days_qty()
|
|
||||||
# if (
|
# if (
|
||||||
# reservation.cancelled_reason
|
# reservation.cancelled_reason
|
||||||
# and pricelist
|
# and pricelist
|
||||||
@@ -344,6 +414,30 @@ class PmsReservationLine(models.Model):
|
|||||||
# else:
|
# else:
|
||||||
# reservation.reservation_line_ids.update({"cancel_discount": 0})
|
# reservation.reservation_line_ids.update({"cancel_discount": 0})
|
||||||
|
|
||||||
|
@api.depends("room_id", "pms_property_id", "date", "occupies_availability")
|
||||||
|
def _compute_avail_id(self):
|
||||||
|
for record in self:
|
||||||
|
if record.room_id.room_type_id and record.date and record.pms_property_id:
|
||||||
|
avail = self.env["pms.availability"].search(
|
||||||
|
[
|
||||||
|
("date", "=", record.date),
|
||||||
|
("room_type_id", "=", record.room_id.room_type_id.id),
|
||||||
|
("pms_property_id", "=", record.pms_property_id.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if avail:
|
||||||
|
record.avail_id = avail.id
|
||||||
|
else:
|
||||||
|
record.avail_id = self.env["pms.availability"].create(
|
||||||
|
{
|
||||||
|
"date": record.date,
|
||||||
|
"room_type_id": record.room_id.room_type_id.id,
|
||||||
|
"pms_property_id": record.pms_property_id.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
record.avail_id = False
|
||||||
|
|
||||||
# Constraints and onchanges
|
# Constraints and onchanges
|
||||||
@api.constrains("date")
|
@api.constrains("date")
|
||||||
def constrains_duplicated_date(self):
|
def constrains_duplicated_date(self):
|
||||||
@@ -365,35 +459,6 @@ class PmsReservationLine(models.Model):
|
|||||||
)
|
)
|
||||||
cancel_lines.day_qty = 0
|
cancel_lines.day_qty = 0
|
||||||
|
|
||||||
def _get_display_price(self, product):
|
|
||||||
if self.reservation_id.pricelist_id.discount_policy == "with_discount":
|
|
||||||
return product.with_context(
|
|
||||||
pricelist=self.reservation_id.pricelist_id.id
|
|
||||||
).price
|
|
||||||
product_context = dict(
|
|
||||||
self.env.context,
|
|
||||||
partner_id=self.reservation_id.partner_id.id,
|
|
||||||
date=self.date,
|
|
||||||
uom=product.uom_id.id,
|
|
||||||
)
|
|
||||||
final_price, rule_id = self.reservation_id.pricelist_id.with_context(
|
|
||||||
product_context
|
|
||||||
).get_product_price_rule(product, 1.0, self.reservation_id.partner_id)
|
|
||||||
base_price, currency = self.with_context(
|
|
||||||
product_context
|
|
||||||
)._get_real_price_currency(
|
|
||||||
product, rule_id, 1, product.uom_id, self.reservation_id.pricelist_id.id
|
|
||||||
)
|
|
||||||
if currency != self.reservation_id.pricelist_id.currency_id:
|
|
||||||
base_price = currency._convert(
|
|
||||||
base_price,
|
|
||||||
self.reservation_id.pricelist_id.currency_id,
|
|
||||||
self.reservation_id.company_id or self.env.company,
|
|
||||||
fields.Date.today(),
|
|
||||||
)
|
|
||||||
# negative discounts (= surcharge) are included in the display price
|
|
||||||
return max(base_price, final_price)
|
|
||||||
|
|
||||||
@api.constrains("room_id")
|
@api.constrains("room_id")
|
||||||
def _check_adults(self):
|
def _check_adults(self):
|
||||||
for record in self.filtered("room_id"):
|
for record in self.filtered("room_id"):
|
||||||
|
|||||||
@@ -15,46 +15,87 @@ class PmsRoom(models.Model):
|
|||||||
_name = "pms.room"
|
_name = "pms.room"
|
||||||
_description = "Property Room"
|
_description = "Property Room"
|
||||||
_order = "sequence, room_type_id, name"
|
_order = "sequence, room_type_id, name"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
|
name = fields.Char(
|
||||||
|
string="Room Name",
|
||||||
|
help="Room Name",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
active = fields.Boolean(
|
||||||
|
string="Active", help="Determines if room is active", default=True
|
||||||
|
)
|
||||||
|
sequence = fields.Integer(
|
||||||
|
string="Sequence",
|
||||||
|
help="Field used to change the position of the rooms in tree view."
|
||||||
|
"Changing the position changes the sequence",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Properties with access to the element;"
|
||||||
|
" if not set, all properties can access",
|
||||||
|
required=True,
|
||||||
|
default=lambda self: self.env.user.get_active_property_ids()[0],
|
||||||
|
comodel_name="pms.property",
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
room_type_id = fields.Many2one(
|
||||||
|
string="Property Room Type",
|
||||||
|
help="Unique room type for the rooms",
|
||||||
|
required=True,
|
||||||
|
comodel_name="pms.room.type",
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
# TODO: design shared rooms
|
||||||
|
shared_room_id = fields.Many2one(
|
||||||
|
string="Shared Room",
|
||||||
|
help="The room can be sold by beds",
|
||||||
|
default=False,
|
||||||
|
comodel_name="pms.shared.room",
|
||||||
|
)
|
||||||
|
ubication_id = fields.Many2one(
|
||||||
|
string="Ubication",
|
||||||
|
help="At which ubication the room is located.",
|
||||||
|
comodel_name="pms.ubication",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
capacity = fields.Integer(
|
||||||
|
string="Capacity", help="The maximum number of people that can occupy a room"
|
||||||
|
)
|
||||||
|
extra_beds_allowed = fields.Integer(
|
||||||
|
string="Extra Beds Allowed",
|
||||||
|
help="Number of extra beds allowed in room",
|
||||||
|
required=True,
|
||||||
|
default="0",
|
||||||
|
)
|
||||||
|
description_sale = fields.Text(
|
||||||
|
string="Sale Description",
|
||||||
|
help="A description of the Product that you want to communicate to "
|
||||||
|
" your customers. This description will be copied to every Sales "
|
||||||
|
" Order, Delivery Order and Customer Invoice/Credit Note",
|
||||||
|
translate=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
(
|
||||||
|
"room_property_unique",
|
||||||
|
"unique(name, pms_property_id)",
|
||||||
|
"you cannot have more than one room "
|
||||||
|
"with the same name in the same property",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
# Defaults and Gets
|
|
||||||
def name_get(self):
|
def name_get(self):
|
||||||
result = []
|
result = []
|
||||||
for room in self:
|
for room in self:
|
||||||
name = room.name
|
name = room.name
|
||||||
if room.room_type_id:
|
if room.room_type_id:
|
||||||
name += " [%s]" % room.room_type_id.code_type
|
name += " [%s]" % room.room_type_id.default_code
|
||||||
result.append((room.id, name))
|
result.append((room.id, name))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
name = fields.Char("Room Name", required=True)
|
|
||||||
pms_property_id = fields.Many2one(
|
|
||||||
"pms.property",
|
|
||||||
store=True,
|
|
||||||
readonly=True,
|
|
||||||
)
|
|
||||||
room_type_id = fields.Many2one(
|
|
||||||
"pms.room.type", "Property Room Type", required=True, ondelete="restrict"
|
|
||||||
)
|
|
||||||
shared_room_id = fields.Many2one("pms.shared.room", "Shared Room", default=False)
|
|
||||||
floor_id = fields.Many2one(
|
|
||||||
"pms.floor", "Ubication", help="At which floor the room is located."
|
|
||||||
)
|
|
||||||
capacity = fields.Integer("Capacity")
|
|
||||||
to_be_cleaned = fields.Boolean("To be Cleaned", default=False)
|
|
||||||
extra_beds_allowed = fields.Integer(
|
|
||||||
"Extra beds allowed", default="0", required=True
|
|
||||||
)
|
|
||||||
description_sale = fields.Text(
|
|
||||||
"Sale Description",
|
|
||||||
translate=True,
|
|
||||||
help="A description of the Product that you want to communicate to "
|
|
||||||
" your customers. This description will be copied to every Sales "
|
|
||||||
" Order, Delivery Order and Customer Invoice/Credit Note",
|
|
||||||
)
|
|
||||||
active = fields.Boolean("Active", default=True)
|
|
||||||
sequence = fields.Integer("Sequence", default=0)
|
|
||||||
|
|
||||||
# Constraints and onchanges
|
# Constraints and onchanges
|
||||||
@api.constrains("capacity")
|
@api.constrains("capacity")
|
||||||
def _check_capacity(self):
|
def _check_capacity(self):
|
||||||
@@ -70,6 +111,11 @@ class PmsRoom(models.Model):
|
|||||||
# Business methods
|
# Business methods
|
||||||
|
|
||||||
def get_capacity(self, extra_bed=0):
|
def get_capacity(self, extra_bed=0):
|
||||||
if not self.shared_room_id:
|
for record in self:
|
||||||
return self.capacity + extra_bed
|
if not record.shared_room_id:
|
||||||
return self.capacity
|
if extra_bed > record.extra_beds_allowed:
|
||||||
|
raise ValidationError(
|
||||||
|
_("Extra beds can't be greater than allowed beds for this room")
|
||||||
|
)
|
||||||
|
return record.capacity + extra_bed
|
||||||
|
return record.capacity
|
||||||
|
|||||||
@@ -7,9 +7,24 @@ class RoomClosureReason(models.Model):
|
|||||||
_name = "room.closure.reason"
|
_name = "room.closure.reason"
|
||||||
_description = "Cause of out of service"
|
_description = "Cause of out of service"
|
||||||
|
|
||||||
# Fields declaration
|
name = fields.Char(
|
||||||
name = fields.Char("Name", translate=True, required=True)
|
string="Name",
|
||||||
pms_property_ids = fields.Many2many(
|
help="The name that identifies the room closure reason",
|
||||||
"pms.property", string="Properties", required=False, ondelete="restrict"
|
required=True,
|
||||||
|
translate=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",
|
||||||
|
relation="pms_room_closure_reason_pms_property_rel",
|
||||||
|
column1="room_closure_reason_type_id",
|
||||||
|
column2="pms_property_id",
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
description = fields.Text(
|
||||||
|
string="Description",
|
||||||
|
help="Explanation of the reason for closing a room",
|
||||||
|
translate=True,
|
||||||
)
|
)
|
||||||
description = fields.Text("Description", translate=True)
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
# Copyright 2017 Alexandre Díaz
|
||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
|
# Copyright 2021 Eric Antones <eantones@nuobit.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
@@ -15,82 +16,180 @@ class PmsRoomType(models.Model):
|
|||||||
_name = "pms.room.type"
|
_name = "pms.room.type"
|
||||||
_description = "Room Type"
|
_description = "Room Type"
|
||||||
_inherits = {"product.product": "product_id"}
|
_inherits = {"product.product": "product_id"}
|
||||||
_order = "sequence, code_type, name"
|
_order = "sequence,default_code,name"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
sequence = fields.Integer(
|
||||||
|
string="Sequence",
|
||||||
|
help="Field used to change the position of the room types in tree view.",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
product_id = fields.Many2one(
|
product_id = fields.Many2one(
|
||||||
"product.product",
|
string="Product Room Type",
|
||||||
"Product Room Type",
|
help="Product identifier associated with room type",
|
||||||
|
comodel_name="product.product",
|
||||||
required=True,
|
required=True,
|
||||||
delegate=True,
|
delegate=True,
|
||||||
ondelete="cascade",
|
ondelete="cascade",
|
||||||
)
|
)
|
||||||
pms_property_ids = fields.Many2many(
|
room_ids = fields.One2many(
|
||||||
"pms.property",
|
string="Rooms",
|
||||||
"pms_property_room_type_rel",
|
help="Rooms that belong to room type.",
|
||||||
"room_type_id",
|
comodel_name="pms.room",
|
||||||
"pms_property_id",
|
inverse_name="room_type_id",
|
||||||
ondelete="restrict",
|
check_pms_properties=True,
|
||||||
string="Properties",
|
)
|
||||||
|
class_id = fields.Many2one(
|
||||||
|
string="Property Type Class",
|
||||||
|
help="Class to which the room type belongs",
|
||||||
|
comodel_name="pms.room.type.class",
|
||||||
|
required=True,
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
room_ids = fields.One2many("pms.room", "room_type_id", "Rooms")
|
|
||||||
class_id = fields.Many2one("pms.room.type.class", "Property Type Class")
|
|
||||||
board_service_room_type_ids = fields.One2many(
|
board_service_room_type_ids = fields.One2many(
|
||||||
"pms.board.service.room.type", "pms_room_type_id", string="Board Services"
|
string="Board Services",
|
||||||
|
help="Board Service included in room type",
|
||||||
|
comodel_name="pms.board.service.room.type",
|
||||||
|
inverse_name="pms_room_type_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
room_amenity_ids = fields.Many2many(
|
room_amenity_ids = fields.Many2many(
|
||||||
"pms.amenity",
|
|
||||||
"pms_room_type_aminity_rel",
|
|
||||||
"room_type_ids",
|
|
||||||
"amenity_ids",
|
|
||||||
string="Room Type Amenities",
|
string="Room Type Amenities",
|
||||||
help="List of Amenities.",
|
help="List of amenities included in room type",
|
||||||
|
comodel_name="pms.amenity",
|
||||||
|
relation="pms_room_type_amenity_rel",
|
||||||
|
column1="room_type_id",
|
||||||
|
column2="amenity_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
code_type = fields.Char(
|
default_code = fields.Char(
|
||||||
"Code",
|
string="Code",
|
||||||
|
help="Identification code for a room type",
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
shared_room = fields.Boolean(
|
# TODO: Session review to define shared room and "sales rooms packs"
|
||||||
"Shared Room", default=False, help="This room type is reservation by beds"
|
is_shared_room = fields.Boolean(
|
||||||
|
string="Shared Room",
|
||||||
|
help="This room type is reservation by beds",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
total_rooms_count = fields.Integer(
|
||||||
|
string="Total Rooms Count",
|
||||||
|
help="The number of rooms in a room type",
|
||||||
|
compute="_compute_total_rooms_count",
|
||||||
|
store=True,
|
||||||
)
|
)
|
||||||
total_rooms_count = fields.Integer(compute="_compute_total_rooms", store=True)
|
|
||||||
active = fields.Boolean("Active", default=True)
|
|
||||||
sequence = fields.Integer("Sequence", default=0)
|
|
||||||
default_max_avail = fields.Integer(
|
default_max_avail = fields.Integer(
|
||||||
"Max. Availability",
|
string="Default Max. Availability",
|
||||||
default=-1,
|
|
||||||
help="Maximum simultaneous availability on own Booking Engine "
|
help="Maximum simultaneous availability on own Booking Engine "
|
||||||
"given no availability rules. "
|
"given no availability rules. "
|
||||||
"Use `-1` for using maximum simultaneous availability.",
|
"Use `-1` for using maximum simultaneous availability.",
|
||||||
|
default=-1,
|
||||||
)
|
)
|
||||||
default_quota = fields.Integer(
|
default_quota = fields.Integer(
|
||||||
"Default Quota",
|
string="Default Quota",
|
||||||
default=-1,
|
|
||||||
help="Quota assigned to the own Booking Engine given no availability rules. "
|
help="Quota assigned to the own Booking Engine given no availability rules. "
|
||||||
"Use `-1` for managing no quota.",
|
"Use `-1` for managing no quota.",
|
||||||
|
default=-1,
|
||||||
)
|
)
|
||||||
|
|
||||||
_sql_constraints = [
|
def name_get(self):
|
||||||
(
|
result = []
|
||||||
"code_type_pms_unique",
|
for room_type in self:
|
||||||
"unique(code_type)",
|
name = room_type.name
|
||||||
"Room Type Code must be unique",
|
if self._context.get("checkin") and self._context.get("checkout"):
|
||||||
),
|
avail = self.env["pms.availability.plan"].get_count_rooms_available(
|
||||||
]
|
checkin=self._context.get("checkin"),
|
||||||
|
checkout=self._context.get("checkout"),
|
||||||
|
room_type_id=room_type.id,
|
||||||
|
pms_property_id=self._context.get("pms_property_id") or False,
|
||||||
|
pricelist_id=self._context.get("pricelist_id") or False,
|
||||||
|
)
|
||||||
|
name += " (%s)" % avail
|
||||||
|
result.append((room_type.id, name))
|
||||||
|
return result
|
||||||
|
|
||||||
# Constraints and onchanges
|
|
||||||
@api.depends("room_ids", "room_ids.active")
|
@api.depends("room_ids", "room_ids.active")
|
||||||
def _compute_total_rooms(self):
|
def _compute_total_rooms_count(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.total_rooms_count = len(record.room_ids)
|
record.total_rooms_count = len(record.room_ids)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_room_types_by_property(self, pms_property_id, default_code=None):
|
||||||
|
"""
|
||||||
|
:param pms_property_id: property ID
|
||||||
|
:param default_code: room type code (optional)
|
||||||
|
:return: - recordset of
|
||||||
|
- all the pms.room.type of the pms_property_id
|
||||||
|
if default_code not defined
|
||||||
|
- one or 0 pms.room.type if default_code defined
|
||||||
|
- ValidationError if more than one default_code found by
|
||||||
|
the same pms_property_id
|
||||||
|
"""
|
||||||
|
domain = []
|
||||||
|
if default_code:
|
||||||
|
domain += ["&", ("default_code", "=", default_code)]
|
||||||
|
company_id = self.env["pms.property"].browse(pms_property_id).company_id.id
|
||||||
|
domain += [
|
||||||
|
"|",
|
||||||
|
("pms_property_ids", "in", pms_property_id),
|
||||||
|
"|",
|
||||||
|
"&",
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
("company_id", "=", company_id),
|
||||||
|
"&",
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
("company_id", "=", False),
|
||||||
|
]
|
||||||
|
records = self.search(domain)
|
||||||
|
res, res_priority = {}, {}
|
||||||
|
for rec in records:
|
||||||
|
res_priority.setdefault(rec.default_code, -1)
|
||||||
|
priority = (rec.pms_property_ids and 2) or (rec.company_id and 1 or 0)
|
||||||
|
if priority > res_priority[rec.default_code]:
|
||||||
|
res.setdefault(rec.default_code, rec.id)
|
||||||
|
res[rec.default_code], res_priority[rec.default_code] = rec.id, priority
|
||||||
|
elif priority == res_priority[rec.default_code]:
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"Integrity error: There's multiple room types "
|
||||||
|
"with the same code %s and properties"
|
||||||
|
)
|
||||||
|
% rec.default_code
|
||||||
|
)
|
||||||
|
return self.browse(list(res.values()))
|
||||||
|
|
||||||
|
@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")
|
||||||
|
for rec in self:
|
||||||
|
if not rec.pms_property_ids:
|
||||||
|
if self.search(
|
||||||
|
[
|
||||||
|
("id", "!=", rec.id),
|
||||||
|
("default_code", "=", rec.default_code),
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
("company_id", "=", rec.company_id.id),
|
||||||
|
]
|
||||||
|
):
|
||||||
|
raise ValidationError(msg)
|
||||||
|
else:
|
||||||
|
for pms_property in rec.pms_property_ids:
|
||||||
|
other = rec.get_room_types_by_property(
|
||||||
|
pms_property.id, rec.default_code
|
||||||
|
)
|
||||||
|
if other and other != rec:
|
||||||
|
raise ValidationError(msg)
|
||||||
|
|
||||||
# ORM Overrides
|
# ORM Overrides
|
||||||
|
# TODO: Review Check product fields default values to room
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
""" Add room types as not purchase services. """
|
""" Add room types as not purchase services. """
|
||||||
vals.update(
|
vals.update(
|
||||||
{
|
{
|
||||||
"purchase_ok": False,
|
"purchase_ok": False,
|
||||||
|
"sale_ok": False,
|
||||||
"type": "service",
|
"type": "service",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -101,68 +200,7 @@ class PmsRoomType(models.Model):
|
|||||||
record.product_id.unlink()
|
record.product_id.unlink()
|
||||||
return super().unlink()
|
return super().unlink()
|
||||||
|
|
||||||
# Business methods
|
|
||||||
|
|
||||||
def get_capacity(self):
|
def get_capacity(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
capacities = self.room_ids.mapped("capacity")
|
capacities = self.room_ids.mapped("capacity")
|
||||||
return min(capacities) if any(capacities) else 0
|
return min(capacities) if any(capacities) else 0
|
||||||
|
|
||||||
@api.model
|
|
||||||
def get_rate_room_types(self, **kwargs):
|
|
||||||
"""
|
|
||||||
room_type_ids: Ids from room types to get rate, optional, if you
|
|
||||||
not use this param, the method return all room_types
|
|
||||||
from: Date from, mandatory
|
|
||||||
days: Number of days, mandatory
|
|
||||||
pricelist_id: Pricelist to use, optional
|
|
||||||
partner_id: Partner, optional
|
|
||||||
Return Dict Code Room Types: subdict with day, discount, price
|
|
||||||
"""
|
|
||||||
vals = {}
|
|
||||||
# room_type_ids = kwargs.get("room_type_ids", False)
|
|
||||||
# room_types = (
|
|
||||||
# self.env["pms.room.type"].browse(room_type_ids)
|
|
||||||
# if room_type_ids
|
|
||||||
# else self.env["pms.room.type"].search([])
|
|
||||||
# )
|
|
||||||
date_from = kwargs.get("date_from", False)
|
|
||||||
days = kwargs.get("days", False)
|
|
||||||
discount = kwargs.get("discount", False)
|
|
||||||
if not date_from or not days:
|
|
||||||
raise ValidationError(_("Date From and days are mandatory"))
|
|
||||||
partner_id = kwargs.get("partner_id", False)
|
|
||||||
# partner = self.env["res.partner"].browse(partner_id)
|
|
||||||
# pricelist_id = kwargs.get(
|
|
||||||
# "pricelist_id",
|
|
||||||
# partner.property_product_pricelist.id
|
|
||||||
# and partner.property_product_pricelist.id
|
|
||||||
# or self.env.user.pms_property_id.default_pricelist_id.id,
|
|
||||||
# )
|
|
||||||
vals.update(
|
|
||||||
{
|
|
||||||
"partner_id": partner_id if partner_id else False,
|
|
||||||
"discount": discount,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
rate_vals = {}
|
|
||||||
# TODO: Now it is computed field, We need other way to return rates
|
|
||||||
# for room_type in room_types:
|
|
||||||
# vals.update({"room_type_id": room_type.id})
|
|
||||||
# room_vals = self.env["pms.reservation"].prepare_reservation_lines(
|
|
||||||
# date_from,
|
|
||||||
# days,
|
|
||||||
# pricelist_id=pricelist_id,
|
|
||||||
# vals=vals,
|
|
||||||
# update_old_prices=False,
|
|
||||||
# )
|
|
||||||
# rate_vals.update(
|
|
||||||
# {
|
|
||||||
# room_type.id: [
|
|
||||||
# item[2] for item in room_vals[
|
|
||||||
# "reservation_line_ids"
|
|
||||||
# ] if item[2]
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
return rate_vals
|
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
|
||||||
# Copyright 2017 Dario Lodeiros
|
|
||||||
# Copyright 2018 Pablo Quesada
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from odoo import api, fields, models
|
|
||||||
|
|
||||||
|
|
||||||
class PmsRoomTypeAvailability(models.Model):
|
|
||||||
_name = "pms.room.type.availability"
|
|
||||||
_description = "Availability"
|
|
||||||
_inherit = "mail.thread"
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _default_max_avail(self):
|
|
||||||
return self.room_type_id.default_max_avail
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _default_quota(self):
|
|
||||||
return self.room_type_id.default_quota
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
room_type_id = fields.Many2one(
|
|
||||||
"pms.room.type", "Room Type", required=True, ondelete="cascade"
|
|
||||||
)
|
|
||||||
date = fields.Date(
|
|
||||||
"Date",
|
|
||||||
required=True,
|
|
||||||
tracking=True,
|
|
||||||
)
|
|
||||||
quota = fields.Integer(
|
|
||||||
"Quota",
|
|
||||||
default=_default_quota,
|
|
||||||
tracking=True,
|
|
||||||
help="Generic Quota assigned.",
|
|
||||||
)
|
|
||||||
max_avail = fields.Integer(
|
|
||||||
"Max. Availability",
|
|
||||||
default=-1,
|
|
||||||
readonly=True,
|
|
||||||
tracking=True,
|
|
||||||
help="Maximum simultaneous availability on own Booking Engine.",
|
|
||||||
)
|
|
||||||
no_web = fields.Boolean(
|
|
||||||
"No Web",
|
|
||||||
default=False,
|
|
||||||
tracking=True,
|
|
||||||
help="Set zero availability to the own Booking Engine "
|
|
||||||
"even when the availability is positive,",
|
|
||||||
)
|
|
||||||
|
|
||||||
_sql_constraints = [
|
|
||||||
(
|
|
||||||
"unique_availability_room_type_rule_date",
|
|
||||||
"unique(room_type_id, date)",
|
|
||||||
"The availability rule for this date in this room type already exists, "
|
|
||||||
"modify it instead of trying to create a new one",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Business Methods
|
|
||||||
@api.model
|
|
||||||
def rooms_available(
|
|
||||||
self, checkin, checkout, room_type_id=False, current_lines=False
|
|
||||||
):
|
|
||||||
domain = self._get_domain_reservations_occupation(
|
|
||||||
dfrom=checkin,
|
|
||||||
dto=checkout - timedelta(1),
|
|
||||||
current_lines=current_lines,
|
|
||||||
)
|
|
||||||
reservation_lines = self.env["pms.reservation.line"].search(domain)
|
|
||||||
reservations_rooms = reservation_lines.mapped("room_id.id")
|
|
||||||
free_rooms = self.env["pms.room"].search([("id", "not in", reservations_rooms)])
|
|
||||||
if room_type_id:
|
|
||||||
rooms_linked = (
|
|
||||||
self.env["pms.room.type"].search([("id", "=", room_type_id)]).room_ids
|
|
||||||
)
|
|
||||||
free_rooms = free_rooms & rooms_linked
|
|
||||||
return free_rooms.sorted(key=lambda r: r.sequence)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _get_domain_reservations_occupation(self, dfrom, dto, current_lines=False):
|
|
||||||
if current_lines and not isinstance(current_lines, list):
|
|
||||||
current_lines = [current_lines]
|
|
||||||
domain = [
|
|
||||||
("date", ">=", dfrom),
|
|
||||||
("date", "<=", dto),
|
|
||||||
("occupies_availability", "=", True),
|
|
||||||
("id", "not in", current_lines),
|
|
||||||
]
|
|
||||||
return domain
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
# Copyright 2017 Alexandre Díaz
|
||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
|
# Copyright 2021 Eric Antones <eantones@nuobit.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
from odoo import fields, models
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
class PmsRoomTypeClass(models.Model):
|
class PmsRoomTypeClass(models.Model):
|
||||||
@@ -13,19 +15,108 @@ class PmsRoomTypeClass(models.Model):
|
|||||||
|
|
||||||
_name = "pms.room.type.class"
|
_name = "pms.room.type.class"
|
||||||
_description = "Room Type Class"
|
_description = "Room Type Class"
|
||||||
_order = "sequence, name, code_class"
|
_order = "sequence, name, default_code"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
name = fields.Char(
|
||||||
name = fields.Char("Class Name", required=True, translate=True)
|
string="Class Name",
|
||||||
# Relationship between models
|
help="Name of the room type class",
|
||||||
pms_property_ids = fields.Many2many(
|
required=True,
|
||||||
"pms.property", string="Properties", required=False, ondelete="restrict"
|
translate=True,
|
||||||
|
)
|
||||||
|
active = fields.Boolean(
|
||||||
|
string="Active",
|
||||||
|
help="If unchecked, it will allow you to hide the room type",
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
sequence = fields.Integer(
|
||||||
|
string="Sequence",
|
||||||
|
help="Field used to change the position of the room type classes in tree view.",
|
||||||
|
default=0,
|
||||||
|
)
|
||||||
|
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_room_type_class_property_rel",
|
||||||
|
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,
|
||||||
)
|
)
|
||||||
room_type_ids = fields.One2many("pms.room.type", "class_id", "Types")
|
|
||||||
code_class = fields.Char("Code")
|
|
||||||
active = fields.Boolean("Active", default=True)
|
|
||||||
sequence = fields.Integer("Sequence", default=0)
|
|
||||||
|
|
||||||
_sql_constraints = [
|
@api.model
|
||||||
("code_class_unique", "unique(code_class)", "Room Class Code must be unique!")
|
def get_unique_by_property_code(self, pms_property_id, default_code=None):
|
||||||
]
|
"""
|
||||||
|
:param pms_property_id: property ID
|
||||||
|
:param default_code: room type code (optional)
|
||||||
|
:return: - recordset of
|
||||||
|
- all the pms.room.type.class of the pms_property_id
|
||||||
|
if default_code not defined
|
||||||
|
- one or 0 pms.room.type.class if default_code defined
|
||||||
|
- ValidationError if more than one default_code found by
|
||||||
|
the same pms_property_id
|
||||||
|
"""
|
||||||
|
# TODO: similiar code as room.type -> unify
|
||||||
|
domain = []
|
||||||
|
if default_code:
|
||||||
|
domain += ["&", ("default_code", "=", default_code)]
|
||||||
|
domain += [
|
||||||
|
"|",
|
||||||
|
("pms_property_ids", "in", pms_property_id),
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
]
|
||||||
|
records = self.search(domain)
|
||||||
|
res, res_priority = {}, {}
|
||||||
|
for rec in records:
|
||||||
|
res_priority.setdefault(rec.default_code, -1)
|
||||||
|
priority = rec.pms_property_ids and 1 or 0
|
||||||
|
if priority > res_priority[rec.default_code]:
|
||||||
|
res.setdefault(rec.default_code, rec.id)
|
||||||
|
res[rec.default_code], res_priority[rec.default_code] = rec.id, priority
|
||||||
|
elif priority == res_priority[rec.default_code]:
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"Integrity error: There's multiple room types "
|
||||||
|
"with the same code %s and properties"
|
||||||
|
)
|
||||||
|
% rec.default_code
|
||||||
|
)
|
||||||
|
return self.browse(list(res.values()))
|
||||||
|
|
||||||
|
@api.constrains("default_code", "pms_property_ids")
|
||||||
|
def _check_code_property_uniqueness(self):
|
||||||
|
# TODO: similiar code as room.type -> unify
|
||||||
|
msg = _(
|
||||||
|
"Already exists another room type class with the same code and properties"
|
||||||
|
)
|
||||||
|
for rec in self:
|
||||||
|
if not rec.pms_property_ids:
|
||||||
|
if self.search(
|
||||||
|
[
|
||||||
|
("id", "!=", rec.id),
|
||||||
|
("default_code", "=", rec.default_code),
|
||||||
|
("pms_property_ids", "=", False),
|
||||||
|
]
|
||||||
|
):
|
||||||
|
raise ValidationError(msg)
|
||||||
|
else:
|
||||||
|
for pms_property in rec.pms_property_ids:
|
||||||
|
other = rec.get_unique_by_property_code(
|
||||||
|
pms_property.id, rec.default_code
|
||||||
|
)
|
||||||
|
if other and other != rec:
|
||||||
|
raise ValidationError(msg)
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
from odoo import api, fields, models
|
|
||||||
|
|
||||||
|
|
||||||
class PmsRoomTypeRestriction(models.Model):
|
|
||||||
"""The room type restriction is used as a daily restriction plan for room types
|
|
||||||
and therefore is related only with one property."""
|
|
||||||
|
|
||||||
_name = "pms.room.type.restriction"
|
|
||||||
_description = "Reservation restriction plan"
|
|
||||||
|
|
||||||
# Default methods
|
|
||||||
@api.model
|
|
||||||
def _get_default_pms_property(self):
|
|
||||||
return self.env.user.pms_property_id or None
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
name = fields.Char("Restriction Plan Name", required=True)
|
|
||||||
pms_property_id = fields.Many2one(
|
|
||||||
"pms.property",
|
|
||||||
"Property",
|
|
||||||
ondelete="restrict",
|
|
||||||
default=_get_default_pms_property,
|
|
||||||
)
|
|
||||||
item_ids = fields.One2many(
|
|
||||||
"pms.room.type.restriction.item",
|
|
||||||
"restriction_id",
|
|
||||||
string="Restriction Items",
|
|
||||||
copy=True,
|
|
||||||
)
|
|
||||||
active = fields.Boolean(
|
|
||||||
"Active",
|
|
||||||
default=True,
|
|
||||||
help="If unchecked, it will allow you to hide the "
|
|
||||||
"restriction plan without removing it.",
|
|
||||||
)
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Copyright 2017 Alexandre Díaz
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
from odoo import _, api, fields, models
|
|
||||||
from odoo.exceptions import ValidationError
|
|
||||||
|
|
||||||
|
|
||||||
class PmsRoomTypeRestrictionItem(models.Model):
|
|
||||||
_name = "pms.room.type.restriction.item"
|
|
||||||
_description = "Reservation restriction by day"
|
|
||||||
|
|
||||||
# Field Declarations
|
|
||||||
restriction_id = fields.Many2one(
|
|
||||||
"pms.room.type.restriction", "Restriction Plan", ondelete="cascade", index=True
|
|
||||||
)
|
|
||||||
room_type_id = fields.Many2one(
|
|
||||||
"pms.room.type", "Room Type", required=True, ondelete="cascade"
|
|
||||||
)
|
|
||||||
date = fields.Date("Date")
|
|
||||||
|
|
||||||
min_stay = fields.Integer("Min. Stay")
|
|
||||||
min_stay_arrival = fields.Integer("Min. Stay Arrival")
|
|
||||||
max_stay = fields.Integer("Max. Stay")
|
|
||||||
max_stay_arrival = fields.Integer("Max. Stay Arrival")
|
|
||||||
closed = fields.Boolean("Closed")
|
|
||||||
closed_departure = fields.Boolean("Closed Departure")
|
|
||||||
closed_arrival = fields.Boolean("Closed Arrival")
|
|
||||||
|
|
||||||
_sql_constraints = [
|
|
||||||
(
|
|
||||||
"room_type_registry_unique",
|
|
||||||
"unique(restriction_id, room_type_id, date)",
|
|
||||||
"Only can exists one restriction in the same \
|
|
||||||
day for the same room type!",
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Constraints and onchanges
|
|
||||||
|
|
||||||
@api.constrains("min_stay", "min_stay_arrival", "max_stay", "max_stay_arrival")
|
|
||||||
def _check_min_stay(self):
|
|
||||||
for record in self:
|
|
||||||
if record.min_stay < 0:
|
|
||||||
raise ValidationError(_("Min. Stay can't be less than zero"))
|
|
||||||
elif record.min_stay_arrival < 0:
|
|
||||||
raise ValidationError(_("Min. Stay Arrival can't be less than zero"))
|
|
||||||
elif record.max_stay < 0:
|
|
||||||
raise ValidationError(_("Max. Stay can't be less than zero"))
|
|
||||||
elif record.max_stay_arrival < 0:
|
|
||||||
raise ValidationError(_("Max. Stay Arrival can't be less than zero"))
|
|
||||||
@@ -4,9 +4,37 @@ from odoo import fields, models
|
|||||||
class PmsSaleChannel(models.Model):
|
class PmsSaleChannel(models.Model):
|
||||||
_name = "pms.sale.channel"
|
_name = "pms.sale.channel"
|
||||||
_description = "Sales Channel"
|
_description = "Sales Channel"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
name = fields.Text(string="Sale Channel Name", help="The name of the sale channel")
|
||||||
name = fields.Text(string="Sale Channel Name")
|
|
||||||
channel_type = fields.Selection(
|
channel_type = fields.Selection(
|
||||||
[("direct", "Direct"), ("indirect", "Indirect")], string="Sale Channel Type"
|
string="Sale Channel Type",
|
||||||
|
help="Type of sale channel; it can be 'direct'(if there is"
|
||||||
|
"no intermediary) or 'indirect'(if there are"
|
||||||
|
"intermediaries between partner and property",
|
||||||
|
selection=[("direct", "Direct"), ("indirect", "Indirect")],
|
||||||
|
)
|
||||||
|
is_on_line = fields.Boolean(
|
||||||
|
string="On Line", help="Indicates if the sale channel is on-line"
|
||||||
|
)
|
||||||
|
product_pricelist_ids = fields.Many2many(
|
||||||
|
string="Pricelists",
|
||||||
|
help="Pricelists for a sale channel",
|
||||||
|
comodel_name="product.pricelist",
|
||||||
|
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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import logging
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.tools import float_compare, float_is_zero
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -13,156 +12,247 @@ _logger = logging.getLogger(__name__)
|
|||||||
class PmsService(models.Model):
|
class PmsService(models.Model):
|
||||||
_name = "pms.service"
|
_name = "pms.service"
|
||||||
_description = "Services and its charges"
|
_description = "Services and its charges"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Default methods
|
|
||||||
|
|
||||||
def name_get(self):
|
|
||||||
result = []
|
|
||||||
for rec in self:
|
|
||||||
name = []
|
|
||||||
name.append("{name}".format(name=rec.name))
|
|
||||||
if rec.reservation_id.name:
|
|
||||||
name.append("{name}".format(name=rec.reservation_id.name))
|
|
||||||
result.append((rec.id, ", ".join(name)))
|
|
||||||
return result
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _default_reservation_id(self):
|
|
||||||
if self.env.context.get("reservation_ids"):
|
|
||||||
ids = [item[1] for item in self.env.context["reservation_ids"]]
|
|
||||||
return self.env["pms.reservation"].browse([(ids)], limit=1)
|
|
||||||
elif self.env.context.get("default_reservation_id"):
|
|
||||||
return self.env.context.get("default_reservation_id")
|
|
||||||
return False
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _default_folio_id(self):
|
|
||||||
if "folio_id" in self._context:
|
|
||||||
return self._context["folio_id"]
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
name = fields.Char(
|
name = fields.Char(
|
||||||
"Service description",
|
string="Service description",
|
||||||
compute="_compute_name",
|
help="Service description",
|
||||||
store=True,
|
|
||||||
readonly=False,
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_name",
|
||||||
)
|
)
|
||||||
product_id = fields.Many2one(
|
product_id = fields.Many2one(
|
||||||
"product.product", "Service", ondelete="restrict", required=True
|
string="Service",
|
||||||
|
help="Product associated with this service",
|
||||||
|
required=True,
|
||||||
|
comodel_name="product.product",
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
folio_id = fields.Many2one(
|
folio_id = fields.Many2one(
|
||||||
"pms.folio", "Folio", ondelete="cascade", default=_default_folio_id
|
string="Folio",
|
||||||
|
help="Folio in which the service is included",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.folio",
|
||||||
|
compute="_compute_folio_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
sale_line_ids = fields.One2many(
|
||||||
|
string="Sale Lines",
|
||||||
|
help="",
|
||||||
|
copy=False,
|
||||||
|
comodel_name="folio.sale.line",
|
||||||
|
inverse_name="service_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
reservation_id = fields.Many2one(
|
reservation_id = fields.Many2one(
|
||||||
"pms.reservation", "Room", default=_default_reservation_id
|
string="Room",
|
||||||
|
help="Reservation in which the service is included",
|
||||||
|
default=lambda self: self._default_reservation_id(),
|
||||||
|
comodel_name="pms.reservation",
|
||||||
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
service_line_ids = fields.One2many(
|
service_line_ids = fields.One2many(
|
||||||
"pms.service.line",
|
string="Service Lines",
|
||||||
"service_id",
|
help="Subservices included in this service",
|
||||||
compute="_compute_service_line_ids",
|
|
||||||
store=True,
|
|
||||||
readonly=False,
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.service.line",
|
||||||
|
inverse_name="service_id",
|
||||||
|
compute="_compute_service_line_ids",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
company_id = fields.Many2one(
|
company_id = fields.Many2one(
|
||||||
related="folio_id.company_id", string="Company", store=True, readonly=True
|
string="Company",
|
||||||
|
help="Company to which the service belongs",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
related="folio_id.company_id",
|
||||||
)
|
)
|
||||||
pms_property_id = fields.Many2one(
|
pms_property_id = fields.Many2one(
|
||||||
"pms.property", store=True, readonly=True, related="folio_id.pms_property_id"
|
string="Property",
|
||||||
|
help="Property to which the service belongs",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
related="folio_id.pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
tax_ids = fields.Many2many(
|
tax_ids = fields.Many2many(
|
||||||
"account.tax",
|
|
||||||
string="Taxes",
|
string="Taxes",
|
||||||
compute="_compute_tax_ids",
|
help="Taxes applied in the service",
|
||||||
store=True,
|
|
||||||
readonly=False,
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
comodel_name="account.tax",
|
||||||
domain=["|", ("active", "=", False), ("active", "=", True)],
|
domain=["|", ("active", "=", False), ("active", "=", True)],
|
||||||
|
compute="_compute_tax_ids",
|
||||||
)
|
)
|
||||||
move_line_ids = fields.Many2many(
|
analytic_tag_ids = fields.Many2many(
|
||||||
"account.move.line",
|
string="Analytic Tags",
|
||||||
"service_line_move_rel",
|
help="",
|
||||||
"service_id",
|
comodel_name="account.analytic.tag",
|
||||||
"move_line_id",
|
|
||||||
string="move Lines",
|
|
||||||
copy=False,
|
|
||||||
)
|
)
|
||||||
analytic_tag_ids = fields.Many2many("account.analytic.tag", string="Analytic Tags")
|
|
||||||
currency_id = fields.Many2one(
|
currency_id = fields.Many2one(
|
||||||
related="folio_id.currency_id", store=True, string="Currency", readonly=True
|
string="Currency",
|
||||||
)
|
help="The currency used in relation to the folio",
|
||||||
sequence = fields.Integer(string="Sequence", default=10)
|
readonly=True,
|
||||||
state = fields.Selection(related="folio_id.state")
|
|
||||||
per_day = fields.Boolean(related="product_id.per_day", related_sudo=True)
|
|
||||||
product_qty = fields.Integer(
|
|
||||||
"Quantity",
|
|
||||||
compute="_compute_product_qty",
|
|
||||||
store=True,
|
store=True,
|
||||||
readonly=False,
|
related="folio_id.currency_id",
|
||||||
|
)
|
||||||
|
sequence = fields.Integer(string="Sequence", help="", default=10)
|
||||||
|
state = fields.Selection(
|
||||||
|
string="State",
|
||||||
|
help="Service status, it corresponds with folio status",
|
||||||
|
related="folio_id.state",
|
||||||
|
)
|
||||||
|
per_day = fields.Boolean(
|
||||||
|
string="Per Day",
|
||||||
|
help="Indicates if service is sold by days",
|
||||||
|
related="product_id.per_day",
|
||||||
|
related_sudo=True,
|
||||||
|
)
|
||||||
|
product_qty = fields.Integer(
|
||||||
|
string="Quantity",
|
||||||
|
help="Number of services that were sold",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_product_qty",
|
||||||
|
)
|
||||||
|
is_board_service = fields.Boolean(
|
||||||
|
string="Is Board Service",
|
||||||
|
help="Indicates if the service is part of a board service",
|
||||||
)
|
)
|
||||||
is_board_service = fields.Boolean()
|
|
||||||
# Non-stored related field to allow portal user to
|
# Non-stored related field to allow portal user to
|
||||||
# see the image of the product he has ordered
|
# see the image of the product he has ordered
|
||||||
product_image = fields.Binary(
|
product_image = fields.Binary(
|
||||||
"Product Image", related="product_id.image_1024", store=False, related_sudo=True
|
string="Product Image",
|
||||||
|
help="Image of the service",
|
||||||
|
store=False,
|
||||||
|
related="product_id.image_1024",
|
||||||
|
related_sudo=True,
|
||||||
)
|
)
|
||||||
invoice_status = fields.Selection(
|
invoice_status = fields.Selection(
|
||||||
[
|
string="Invoice Status",
|
||||||
|
help="State in which the service is with respect to invoices."
|
||||||
|
"It can be 'invoiced', 'to_invoice' or 'no'",
|
||||||
|
readonly=True,
|
||||||
|
default="no",
|
||||||
|
store=True,
|
||||||
|
compute="_compute_invoice_status",
|
||||||
|
selection=[
|
||||||
("invoiced", "Fully Invoiced"),
|
("invoiced", "Fully Invoiced"),
|
||||||
("to invoice", "To Invoice"),
|
("to invoice", "To Invoice"),
|
||||||
("no", "Nothing to Invoice"),
|
("no", "Nothing to Invoice"),
|
||||||
],
|
],
|
||||||
string="Invoice Status",
|
|
||||||
compute="_compute_invoice_status",
|
|
||||||
store=True,
|
|
||||||
readonly=True,
|
|
||||||
default="no",
|
|
||||||
)
|
)
|
||||||
channel_type = fields.Selection(
|
channel_type = fields.Selection(
|
||||||
[
|
string="Sales Channel",
|
||||||
|
help="sales channel through which the service was sold."
|
||||||
|
"It can be 'door', 'mail', 'phone', 'call' or 'web'",
|
||||||
|
selection=[
|
||||||
("door", "Door"),
|
("door", "Door"),
|
||||||
("mail", "Mail"),
|
("mail", "Mail"),
|
||||||
("phone", "Phone"),
|
("phone", "Phone"),
|
||||||
("call", "Call Center"),
|
("call", "Call Center"),
|
||||||
("web", "Web"),
|
("web", "Web"),
|
||||||
],
|
],
|
||||||
string="Sales Channel",
|
|
||||||
)
|
|
||||||
price_unit = fields.Float(
|
|
||||||
"Unit Price",
|
|
||||||
digits=("Product Price"),
|
|
||||||
compute="_compute_price_unit",
|
|
||||||
store=True,
|
|
||||||
readonly=False,
|
|
||||||
)
|
|
||||||
discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0)
|
|
||||||
qty_to_invoice = fields.Float(
|
|
||||||
compute="_compute_get_to_invoice_qty",
|
|
||||||
string="To Invoice",
|
|
||||||
store=True,
|
|
||||||
readonly=True,
|
|
||||||
digits=("Product Unit of Measure"),
|
|
||||||
)
|
|
||||||
qty_invoiced = fields.Float(
|
|
||||||
compute="_compute_get_invoice_qty",
|
|
||||||
string="Invoiced",
|
|
||||||
store=True,
|
|
||||||
readonly=True,
|
|
||||||
digits=("Product Unit of Measure"),
|
|
||||||
)
|
)
|
||||||
price_subtotal = fields.Monetary(
|
price_subtotal = fields.Monetary(
|
||||||
string="Subtotal", readonly=True, store=True, compute="_compute_amount_service"
|
string="Subtotal",
|
||||||
|
help="Subtotal price without taxes",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_amount_service",
|
||||||
)
|
)
|
||||||
price_total = fields.Monetary(
|
price_total = fields.Monetary(
|
||||||
string="Total", readonly=True, store=True, compute="_compute_amount_service"
|
string="Total",
|
||||||
|
help="Total price without taxes",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_amount_service",
|
||||||
)
|
)
|
||||||
price_tax = fields.Float(
|
price_tax = fields.Float(
|
||||||
string="Taxes Amount",
|
string="Taxes Amount",
|
||||||
|
help="Total of taxes in service",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
store=True,
|
store=True,
|
||||||
compute="_compute_amount_service",
|
compute="_compute_amount_service",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compute and Search methods
|
# Compute and Search methods
|
||||||
|
@api.depends("product_id")
|
||||||
|
def _compute_tax_ids(self):
|
||||||
|
for service in self:
|
||||||
|
service.tax_ids = service.product_id.taxes_id.filtered(
|
||||||
|
lambda r: not service.company_id or r.company_id == service.company_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends("service_line_ids", "service_line_ids.day_qty")
|
||||||
|
def _compute_product_qty(self):
|
||||||
|
self.product_qty = 0
|
||||||
|
for service in self.filtered("service_line_ids"):
|
||||||
|
qty = sum(service.service_line_ids.mapped("day_qty"))
|
||||||
|
service.product_qty = qty
|
||||||
|
|
||||||
|
@api.depends("reservation_id", "reservation_id.folio_id")
|
||||||
|
def _compute_folio_id(self):
|
||||||
|
for record in self:
|
||||||
|
if record.reservation_id:
|
||||||
|
record.folio_id = record.reservation_id.folio_id
|
||||||
|
elif not record.folio_id:
|
||||||
|
record.folio_id = False
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
"sale_line_ids",
|
||||||
|
"sale_line_ids.invoice_status",
|
||||||
|
)
|
||||||
|
def _compute_invoice_status(self):
|
||||||
|
"""
|
||||||
|
Compute the invoice status of a Reservation. Possible statuses:
|
||||||
|
Base on folio sale line invoice status
|
||||||
|
"""
|
||||||
|
for line in self:
|
||||||
|
states = list(set(line.sale_line_ids.mapped("invoice_status")))
|
||||||
|
if len(states) == 1:
|
||||||
|
line.invoice_status = states[0]
|
||||||
|
elif len(states) >= 1:
|
||||||
|
if "to_invoice" in states:
|
||||||
|
line.invoice_status = "to_invoice"
|
||||||
|
elif "invoiced" in states:
|
||||||
|
line.invoice_status = "invoiced"
|
||||||
|
else:
|
||||||
|
line.invoice_status = "no"
|
||||||
|
else:
|
||||||
|
line.invoice_status = "no"
|
||||||
|
|
||||||
|
@api.depends("service_line_ids.price_day_total")
|
||||||
|
def _compute_amount_service(self):
|
||||||
|
for service in self:
|
||||||
|
if service.service_line_ids:
|
||||||
|
service.update(
|
||||||
|
{
|
||||||
|
"price_tax": sum(
|
||||||
|
service.service_line_ids.mapped("price_day_tax")
|
||||||
|
),
|
||||||
|
"price_total": sum(
|
||||||
|
service.service_line_ids.mapped("price_day_total")
|
||||||
|
),
|
||||||
|
"price_subtotal": sum(
|
||||||
|
service.service_line_ids.mapped("price_day_subtotal")
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
service.update(
|
||||||
|
{
|
||||||
|
"price_tax": 0,
|
||||||
|
"price_total": 0,
|
||||||
|
"price_subtotal": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends("product_id")
|
@api.depends("product_id")
|
||||||
def _compute_name(self):
|
def _compute_name(self):
|
||||||
self.name = False
|
self.name = False
|
||||||
@@ -188,7 +278,12 @@ class PmsService(models.Model):
|
|||||||
name += "\n" + product.description_sale
|
name += "\n" + product.description_sale
|
||||||
service.name = name
|
service.name = name
|
||||||
|
|
||||||
@api.depends("reservation_id.checkin", "reservation_id.checkout", "product_id")
|
@api.depends(
|
||||||
|
"reservation_id.checkin",
|
||||||
|
"reservation_id.checkout",
|
||||||
|
"product_id",
|
||||||
|
"reservation_id.adults",
|
||||||
|
)
|
||||||
def _compute_service_line_ids(self):
|
def _compute_service_line_ids(self):
|
||||||
for service in self:
|
for service in self:
|
||||||
if service.product_id:
|
if service.product_id:
|
||||||
@@ -205,13 +300,21 @@ class PmsService(models.Model):
|
|||||||
if consumed_on == "after":
|
if consumed_on == "after":
|
||||||
i += 1
|
i += 1
|
||||||
idate = reservation.checkin + timedelta(days=i)
|
idate = reservation.checkin + timedelta(days=i)
|
||||||
old_line = service._search_old_lines(idate)
|
old_line = service.service_line_ids.filtered(
|
||||||
if idate in [
|
lambda r: r.date == idate
|
||||||
line.date for line in service.service_line_ids
|
)
|
||||||
]:
|
price_unit = service._get_price_unit_line(idate)
|
||||||
# REVIEW: If the date is already
|
if old_line and old_line.auto_qty:
|
||||||
# cached (otherwise double the date)
|
lines.append(
|
||||||
pass
|
(
|
||||||
|
1,
|
||||||
|
old_line.id,
|
||||||
|
{
|
||||||
|
"day_qty": day_qty,
|
||||||
|
"auto_qty": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
elif not old_line:
|
elif not old_line:
|
||||||
lines.append(
|
lines.append(
|
||||||
(
|
(
|
||||||
@@ -220,11 +323,11 @@ class PmsService(models.Model):
|
|||||||
{
|
{
|
||||||
"date": idate,
|
"date": idate,
|
||||||
"day_qty": day_qty,
|
"day_qty": day_qty,
|
||||||
|
"auto_qty": True,
|
||||||
|
"price_unit": price_unit,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
lines.append((4, old_line.id))
|
|
||||||
move_day = 0
|
move_day = 0
|
||||||
if consumed_on == "after":
|
if consumed_on == "after":
|
||||||
move_day = 1
|
move_day = 1
|
||||||
@@ -245,12 +348,10 @@ class PmsService(models.Model):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_logger.info(service)
|
|
||||||
_logger.info(lines)
|
|
||||||
service.service_line_ids = lines
|
service.service_line_ids = lines
|
||||||
else:
|
else:
|
||||||
# TODO: Review (business logic refact) no per_day logic service
|
|
||||||
if not service.service_line_ids:
|
if not service.service_line_ids:
|
||||||
|
price_unit = service._get_price_unit_line()
|
||||||
service.service_line_ids = [
|
service.service_line_ids = [
|
||||||
(
|
(
|
||||||
0,
|
0,
|
||||||
@@ -258,13 +359,13 @@ class PmsService(models.Model):
|
|||||||
{
|
{
|
||||||
"date": fields.Date.today(),
|
"date": fields.Date.today(),
|
||||||
"day_qty": day_qty,
|
"day_qty": day_qty,
|
||||||
|
"price_unit": price_unit,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
# TODO: Service without reservation(room) but with folio¿?
|
|
||||||
# example: tourist tour in group
|
|
||||||
if not service.service_line_ids:
|
if not service.service_line_ids:
|
||||||
|
price_unit = service._get_price_unit_line()
|
||||||
service.service_line_ids = [
|
service.service_line_ids = [
|
||||||
(
|
(
|
||||||
0,
|
0,
|
||||||
@@ -272,231 +373,37 @@ class PmsService(models.Model):
|
|||||||
{
|
{
|
||||||
"date": fields.Date.today(),
|
"date": fields.Date.today(),
|
||||||
"day_qty": day_qty,
|
"day_qty": day_qty,
|
||||||
|
"price_unit": price_unit,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
service.service_line_ids = False
|
service.service_line_ids = False
|
||||||
|
|
||||||
def _search_old_lines(self, date):
|
# Default methods
|
||||||
self.ensure_one()
|
|
||||||
if isinstance(self._origin.id, int):
|
def name_get(self):
|
||||||
old_line = self._origin.service_line_ids.filtered(lambda r: r.date == date)
|
result = []
|
||||||
return old_line
|
for rec in self:
|
||||||
|
name = []
|
||||||
|
name.append("{name}".format(name=rec.name))
|
||||||
|
if rec.reservation_id.name:
|
||||||
|
name.append("{name}".format(name=rec.reservation_id.name))
|
||||||
|
result.append((rec.id, ", ".join(name)))
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_reservation_id(self):
|
||||||
|
if self.env.context.get("reservation_ids"):
|
||||||
|
ids = [item[1] for item in self.env.context["reservation_ids"]]
|
||||||
|
return self.env["pms.reservation"].browse([(ids)], limit=1)
|
||||||
|
elif self.env.context.get("default_reservation_id"):
|
||||||
|
return self.env.context.get("default_reservation_id")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@api.depends("product_id")
|
|
||||||
def _compute_tax_ids(self):
|
|
||||||
for service in self:
|
|
||||||
service.tax_ids = service.product_id.taxes_id.filtered(
|
|
||||||
lambda r: not service.company_id or r.company_id == service.company_id
|
|
||||||
)
|
|
||||||
|
|
||||||
@api.depends("service_line_ids", "service_line_ids.day_qty")
|
|
||||||
def _compute_product_qty(self):
|
|
||||||
self.product_qty = 0
|
|
||||||
for service in self.filtered("service_line_ids"):
|
|
||||||
qty = sum(service.service_line_ids.mapped("day_qty"))
|
|
||||||
service.product_qty = qty
|
|
||||||
|
|
||||||
@api.depends(
|
|
||||||
"product_id",
|
|
||||||
"service_line_ids",
|
|
||||||
"reservation_id.pricelist_id",
|
|
||||||
"reservation_id.pms_property_id",
|
|
||||||
"pms_property_id",
|
|
||||||
)
|
|
||||||
def _compute_price_unit(self):
|
|
||||||
for service in self:
|
|
||||||
folio = service.folio_id
|
|
||||||
reservation = service.reservation_id
|
|
||||||
origin = reservation if reservation else folio
|
|
||||||
if origin:
|
|
||||||
if service._recompute_price():
|
|
||||||
partner = origin.partner_id
|
|
||||||
pricelist = origin.pricelist_id
|
|
||||||
if reservation and service.is_board_service:
|
|
||||||
board_room_type = reservation.board_service_room_id
|
|
||||||
if board_room_type.price_type == "fixed":
|
|
||||||
service.price_unit = (
|
|
||||||
self.env["pms.board.service.room.type.line"]
|
|
||||||
.search(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"pms_board_service_room_type_id",
|
|
||||||
"=",
|
|
||||||
board_room_type.id,
|
|
||||||
),
|
|
||||||
("product_id", "=", service.product_id.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
.amount
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
service.price_unit = (
|
|
||||||
reservation.price_total
|
|
||||||
* self.env["pms.board.service.room.type.line"]
|
|
||||||
.search(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"pms_board_service_room_type_id",
|
|
||||||
"=",
|
|
||||||
board_room_type.id,
|
|
||||||
),
|
|
||||||
("product_id", "=", service.product_id.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
.amount
|
|
||||||
) / 100
|
|
||||||
else:
|
|
||||||
product = service.product_id.with_context(
|
|
||||||
lang=partner.lang,
|
|
||||||
partner=partner.id,
|
|
||||||
quantity=service.product_qty,
|
|
||||||
date=folio.date_order if folio else fields.Date.today(),
|
|
||||||
pricelist=pricelist.id,
|
|
||||||
uom=service.product_id.uom_id.id,
|
|
||||||
fiscal_position=False,
|
|
||||||
property=service.pms_property_id.id,
|
|
||||||
)
|
|
||||||
service.price_unit = self.env[
|
|
||||||
"account.tax"
|
|
||||||
]._fix_tax_included_price_company(
|
|
||||||
service._get_display_price(product),
|
|
||||||
product.taxes_id,
|
|
||||||
service.tax_ids,
|
|
||||||
origin.company_id,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
service.price_unit = service._origin.price_unit
|
|
||||||
else:
|
|
||||||
service.price_unit = 0
|
|
||||||
|
|
||||||
def _recompute_price(self):
|
|
||||||
# REVIEW: Conditional to avoid overriding already calculated prices,
|
|
||||||
# I'm not sure it's the best way
|
|
||||||
self.ensure_one()
|
|
||||||
# folio/reservation origin service
|
|
||||||
folio_origin = self._origin.folio_id
|
|
||||||
reservation_origin = self._origin.reservation_id
|
|
||||||
origin = reservation_origin if reservation_origin else folio_origin
|
|
||||||
# folio/reservation new service
|
|
||||||
folio_new = self.folio_id
|
|
||||||
reservation_new = self.reservation_id
|
|
||||||
new = reservation_new if reservation_new else folio_new
|
|
||||||
price_fields = [
|
|
||||||
"pricelist_id",
|
|
||||||
"reservation_type",
|
|
||||||
"pms_property_id",
|
|
||||||
]
|
|
||||||
if (
|
|
||||||
any(origin[field] != new[field] for field in price_fields)
|
|
||||||
or self._origin.price_unit == 0
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@api.depends("qty_invoiced", "product_qty", "folio_id.state")
|
|
||||||
def _compute_get_to_invoice_qty(self):
|
|
||||||
"""
|
|
||||||
Compute the quantity to invoice. If the invoice policy is order,
|
|
||||||
the quantity to invoice is calculated from the ordered quantity.
|
|
||||||
Otherwise, the quantity delivered is used.
|
|
||||||
"""
|
|
||||||
for line in self:
|
|
||||||
if line.folio_id.state not in ["draft"]:
|
|
||||||
line.qty_to_invoice = line.product_qty - line.qty_invoiced
|
|
||||||
else:
|
|
||||||
line.qty_to_invoice = 0
|
|
||||||
|
|
||||||
@api.depends("move_line_ids.move_id.state", "move_line_ids.quantity")
|
|
||||||
def _compute_get_invoice_qty(self):
|
|
||||||
"""
|
|
||||||
Compute the quantity invoiced. If case of a refund,
|
|
||||||
the quantity invoiced is decreased. Note that this is the case only
|
|
||||||
if the refund is generated from the Folio and that is intentional: if
|
|
||||||
a refund made would automatically decrease the invoiced quantity,
|
|
||||||
then there is a risk of reinvoicing it automatically, which may
|
|
||||||
not be wanted at all. That's why the refund has to be
|
|
||||||
created from the Folio
|
|
||||||
"""
|
|
||||||
for line in self:
|
|
||||||
qty_invoiced = 0.0
|
|
||||||
for invoice_line in line.move_line_ids:
|
|
||||||
if invoice_line.move_id.state != "cancel":
|
|
||||||
if invoice_line.move_id.type == "out_invoice":
|
|
||||||
qty_invoiced += invoice_line.uom_id._compute_quantity(
|
|
||||||
invoice_line.quantity, line.product_id.uom_id
|
|
||||||
)
|
|
||||||
elif invoice_line.move_id.type == "out_refund":
|
|
||||||
qty_invoiced -= invoice_line.uom_id._compute_quantity(
|
|
||||||
invoice_line.quantity, line.product_id.uom_id
|
|
||||||
)
|
|
||||||
line.qty_invoiced = qty_invoiced
|
|
||||||
|
|
||||||
@api.depends("product_qty", "qty_to_invoice", "qty_invoiced")
|
|
||||||
def _compute_invoice_status(self):
|
|
||||||
"""
|
|
||||||
Compute the invoice status of a SO line. Possible statuses:
|
|
||||||
- no: if the SO is not in status 'sale' or 'done',
|
|
||||||
we consider that there is nothing to invoice.
|
|
||||||
This is also hte default value if the conditions of no other
|
|
||||||
status is met.
|
|
||||||
- to invoice: we refer to the quantity to invoice of the line.
|
|
||||||
Refer to method `_compute_get_to_invoice_qty()` for more information on
|
|
||||||
how this quantity is calculated.
|
|
||||||
- upselling: this is possible only for a product invoiced on ordered
|
|
||||||
quantities for which we delivered more than expected.
|
|
||||||
The could arise if, for example, a project took more time than
|
|
||||||
expected but we decided not to invoice the extra cost to the
|
|
||||||
client. This occurs onyl in state 'sale', so that when a Folio
|
|
||||||
is set to done, the upselling opportunity is removed from the list.
|
|
||||||
- invoiced: the quantity invoiced is larger or equal to the
|
|
||||||
quantity ordered.
|
|
||||||
"""
|
|
||||||
precision = self.env["decimal.precision"].precision_get(
|
|
||||||
"Product Unit of Measure"
|
|
||||||
)
|
|
||||||
for line in self:
|
|
||||||
state = line.folio_id.state or "draft"
|
|
||||||
if state == "draft":
|
|
||||||
line.invoice_status = "no"
|
|
||||||
elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
|
|
||||||
line.invoice_status = "to invoice"
|
|
||||||
elif (
|
|
||||||
float_compare(
|
|
||||||
line.qty_invoiced, line.product_qty, precision_digits=precision
|
|
||||||
)
|
|
||||||
>= 0
|
|
||||||
):
|
|
||||||
line.invoice_status = "invoiced"
|
|
||||||
else:
|
|
||||||
line.invoice_status = "no"
|
|
||||||
|
|
||||||
@api.depends("product_qty", "discount", "price_unit", "tax_ids")
|
|
||||||
def _compute_amount_service(self):
|
|
||||||
for service in self:
|
|
||||||
folio = service.folio_id
|
|
||||||
reservation = service.reservation_id
|
|
||||||
currency = folio.currency_id if folio else reservation.currency_id
|
|
||||||
product = service.product_id
|
|
||||||
price = service.price_unit * (1 - (service.discount or 0.0) * 0.01)
|
|
||||||
taxes = service.tax_ids.compute_all(
|
|
||||||
price, currency, service.product_qty, product=product
|
|
||||||
)
|
|
||||||
service.update(
|
|
||||||
{
|
|
||||||
"price_tax": sum(
|
|
||||||
t.get("amount", 0.0) for t in taxes.get("taxes", [])
|
|
||||||
),
|
|
||||||
"price_total": taxes["total_included"],
|
|
||||||
"price_subtotal": taxes["total_excluded"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Action methods
|
# Action methods
|
||||||
def open_service_ids(self):
|
def open_service_ids(self):
|
||||||
action = self.env.ref("pms.action_pms_services_form").read()[0]
|
action = self.env.ref("pms.action_pms_services_form").sudo().read()[0]
|
||||||
action["views"] = [(self.env.ref("pms.pms_service_view_form").id, "form")]
|
action["views"] = [(self.env.ref("pms.pms_service_view_form").id, "form")]
|
||||||
action["res_id"] = self.id
|
action["res_id"] = self.id
|
||||||
action["target"] = "new"
|
action["target"] = "new"
|
||||||
@@ -522,20 +429,12 @@ class PmsService(models.Model):
|
|||||||
reservation = self.reservation_id
|
reservation = self.reservation_id
|
||||||
origin = folio if folio else reservation
|
origin = folio if folio else reservation
|
||||||
if origin.pricelist_id.discount_policy == "with_discount":
|
if origin.pricelist_id.discount_policy == "with_discount":
|
||||||
return product.with_context(pricelist=origin.pricelist_id.id).price
|
return product.price
|
||||||
product_context = dict(
|
|
||||||
self.env.context,
|
|
||||||
partner_id=origin.partner_id.id,
|
|
||||||
date=folio.date_order if folio else fields.Date.today(),
|
|
||||||
uom=self.product_id.uom_id.id,
|
|
||||||
)
|
|
||||||
final_price, rule_id = origin.pricelist_id.with_context(
|
final_price, rule_id = origin.pricelist_id.with_context(
|
||||||
product_context
|
product._context
|
||||||
).get_product_price_rule(
|
).get_product_price_rule(product, self.product_qty or 1.0, origin.partner_id)
|
||||||
self.product_id, self.product_qty or 1.0, origin.partner_id
|
|
||||||
)
|
|
||||||
base_price, currency_id = self.with_context(
|
base_price, currency_id = self.with_context(
|
||||||
product_context
|
product._context
|
||||||
)._get_real_price_currency(
|
)._get_real_price_currency(
|
||||||
product,
|
product,
|
||||||
rule_id,
|
rule_id,
|
||||||
@@ -547,12 +446,74 @@ class PmsService(models.Model):
|
|||||||
base_price = (
|
base_price = (
|
||||||
self.env["res.currency"]
|
self.env["res.currency"]
|
||||||
.browse(currency_id)
|
.browse(currency_id)
|
||||||
.with_context(product_context)
|
.with_context(product._context)
|
||||||
.compute(base_price, origin.pricelist_id.currency_id)
|
.compute(base_price, origin.pricelist_id.currency_id)
|
||||||
)
|
)
|
||||||
# negative discounts (= surcharge) are included in the display price
|
# negative discounts (= surcharge) are included in the display price
|
||||||
return max(base_price, final_price)
|
return max(base_price, final_price)
|
||||||
|
|
||||||
|
def _get_real_price_currency(self, product, rule_id, qty, uom, pricelist_id):
|
||||||
|
"""Retrieve the price before applying the pricelist
|
||||||
|
:param obj product: object of current product record
|
||||||
|
:parem float qty: total quantity of product
|
||||||
|
:param tuple price_and_rule: tuple(price, suitable_rule)
|
||||||
|
coming from pricelist computation
|
||||||
|
:param obj uom: unit of measure of current order line
|
||||||
|
:param integer pricelist_id: pricelist id of sales order"""
|
||||||
|
PricelistItem = self.env["product.pricelist.item"]
|
||||||
|
field_name = "lst_price"
|
||||||
|
currency_id = None
|
||||||
|
product_currency = product.currency_id
|
||||||
|
if rule_id:
|
||||||
|
pricelist_item = PricelistItem.browse(rule_id)
|
||||||
|
if pricelist_item.pricelist_id.discount_policy == "without_discount":
|
||||||
|
while (
|
||||||
|
pricelist_item.base == "pricelist"
|
||||||
|
and pricelist_item.base_pricelist_id
|
||||||
|
and pricelist_item.base_pricelist_id.discount_policy
|
||||||
|
== "without_discount"
|
||||||
|
):
|
||||||
|
price, rule_id = pricelist_item.base_pricelist_id.with_context(
|
||||||
|
uom=uom.id
|
||||||
|
).get_product_price_rule(product, qty, self.order_id.partner_id)
|
||||||
|
pricelist_item = PricelistItem.browse(rule_id)
|
||||||
|
|
||||||
|
if pricelist_item.base == "standard_price":
|
||||||
|
field_name = "standard_price"
|
||||||
|
product_currency = product.cost_currency_id
|
||||||
|
elif (
|
||||||
|
pricelist_item.base == "pricelist" and pricelist_item.base_pricelist_id
|
||||||
|
):
|
||||||
|
field_name = "price"
|
||||||
|
product = product.with_context(
|
||||||
|
pricelist=pricelist_item.base_pricelist_id.id
|
||||||
|
)
|
||||||
|
product_currency = pricelist_item.base_pricelist_id.currency_id
|
||||||
|
currency_id = pricelist_item.pricelist_id.currency_id
|
||||||
|
|
||||||
|
if not currency_id:
|
||||||
|
currency_id = product_currency
|
||||||
|
cur_factor = 1.0
|
||||||
|
else:
|
||||||
|
if currency_id.id == product_currency.id:
|
||||||
|
cur_factor = 1.0
|
||||||
|
else:
|
||||||
|
cur_factor = currency_id._get_conversion_rate(
|
||||||
|
product_currency,
|
||||||
|
currency_id,
|
||||||
|
self.company_id or self.env.company,
|
||||||
|
self.folio_id.date_order or fields.Date.today(),
|
||||||
|
)
|
||||||
|
|
||||||
|
product_uom = self.env.context.get("uom") or product.uom_id.id
|
||||||
|
if uom and uom.id != product_uom:
|
||||||
|
# the unit price is in a different uom
|
||||||
|
uom_factor = uom._compute_price(1.0, product.uom_id)
|
||||||
|
else:
|
||||||
|
uom_factor = 1.0
|
||||||
|
|
||||||
|
return product[field_name] * uom_factor * cur_factor, currency_id
|
||||||
|
|
||||||
# Businness Methods
|
# Businness Methods
|
||||||
def _service_day_qty(self):
|
def _service_day_qty(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -563,3 +524,38 @@ class PmsService(models.Model):
|
|||||||
if self.product_id.per_person:
|
if self.product_id.per_person:
|
||||||
qty = self.reservation_id.adults
|
qty = self.reservation_id.adults
|
||||||
return qty
|
return qty
|
||||||
|
|
||||||
|
def _get_price_unit_line(self, date=False):
|
||||||
|
self.ensure_one()
|
||||||
|
folio = self.folio_id
|
||||||
|
reservation = self.reservation_id
|
||||||
|
origin = reservation if reservation else folio
|
||||||
|
if origin:
|
||||||
|
partner = origin.partner_id
|
||||||
|
pricelist = origin.pricelist_id
|
||||||
|
board_room_type = False
|
||||||
|
product_context = dict(
|
||||||
|
self.env.context,
|
||||||
|
lang=partner.lang,
|
||||||
|
partner=partner.id,
|
||||||
|
quantity=self.product_qty,
|
||||||
|
date=folio.date_order if folio else fields.Date.today(),
|
||||||
|
pricelist=pricelist.id,
|
||||||
|
board_service=board_room_type.id if board_room_type else False,
|
||||||
|
uom=self.product_id.uom_id.id,
|
||||||
|
fiscal_position=False,
|
||||||
|
property=self.pms_property_id.id,
|
||||||
|
)
|
||||||
|
if date:
|
||||||
|
product_context["date_overnight"] = date
|
||||||
|
if reservation and self.is_board_service:
|
||||||
|
product_context["board_service"] = reservation.board_service_room_id.id
|
||||||
|
product = self.product_id.with_context(product_context)
|
||||||
|
return self.env["account.tax"]._fix_tax_included_price_company(
|
||||||
|
self._get_display_price(product),
|
||||||
|
product.taxes_id,
|
||||||
|
self.tax_ids,
|
||||||
|
origin.company_id,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|||||||
@@ -9,53 +9,230 @@ class PmsServiceLine(models.Model):
|
|||||||
_name = "pms.service.line"
|
_name = "pms.service.line"
|
||||||
_description = "Service by day"
|
_description = "Service by day"
|
||||||
_order = "date"
|
_order = "date"
|
||||||
|
_rec_name = "service_id"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
service_id = fields.Many2one(
|
service_id = fields.Many2one(
|
||||||
"pms.service",
|
|
||||||
string="Service Room",
|
string="Service Room",
|
||||||
ondelete="cascade",
|
help="Service identifier",
|
||||||
required=True,
|
required=True,
|
||||||
copy=False,
|
copy=False,
|
||||||
|
comodel_name="pms.service",
|
||||||
|
ondelete="cascade",
|
||||||
|
)
|
||||||
|
is_board_service = fields.Boolean(
|
||||||
|
string="Is Board Service",
|
||||||
|
help="Indicates if the service line is part of a board service",
|
||||||
|
store=True,
|
||||||
|
related="service_id.is_board_service",
|
||||||
|
)
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
string="Product",
|
||||||
|
help="Product associated with this service line",
|
||||||
|
store=True,
|
||||||
|
related="service_id.product_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
product_id = fields.Many2one(related="service_id.product_id", store=True)
|
|
||||||
tax_ids = fields.Many2many(
|
tax_ids = fields.Many2many(
|
||||||
"account.tax", string="Taxes", related="service_id.tax_ids", readonly="True"
|
string="Taxes",
|
||||||
|
help="Taxes applied in the service line",
|
||||||
|
readonly="True",
|
||||||
|
comodel_name="account.tax",
|
||||||
|
related="service_id.tax_ids",
|
||||||
)
|
)
|
||||||
pms_property_id = fields.Many2one(
|
pms_property_id = fields.Many2one(
|
||||||
"pms.property", store=True, readonly=True, related="service_id.pms_property_id"
|
string="Property",
|
||||||
|
help="Property to which the service belongs",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
related="service_id.pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
date = fields.Date("Date")
|
date = fields.Date(
|
||||||
day_qty = fields.Integer("Units")
|
string="Date",
|
||||||
price_total = fields.Float(
|
help="Sate on which the product is to be consumed",
|
||||||
"Price Total", compute="_compute_price_total", store=True
|
)
|
||||||
|
day_qty = fields.Integer(
|
||||||
|
string="Units",
|
||||||
|
help="Amount to be consumed per day",
|
||||||
)
|
)
|
||||||
price_unit = fields.Float(
|
price_unit = fields.Float(
|
||||||
"Unit Price", related="service_id.price_unit", readonly=True, store=True
|
string="Unit Price",
|
||||||
|
help="Price per unit of service",
|
||||||
|
digits=("Product Price"),
|
||||||
)
|
)
|
||||||
room_id = fields.Many2one(
|
price_day_subtotal = fields.Monetary(
|
||||||
string="Room", related="service_id.reservation_id", readonly=True, store=True
|
string="Subtotal",
|
||||||
|
help="Subtotal price without taxes",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_day_amount_service",
|
||||||
|
)
|
||||||
|
price_day_total = fields.Monetary(
|
||||||
|
string="Total",
|
||||||
|
help="Total price without taxes",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_day_amount_service",
|
||||||
|
)
|
||||||
|
price_day_tax = fields.Float(
|
||||||
|
string="Taxes Amount",
|
||||||
|
help="",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_day_amount_service",
|
||||||
|
)
|
||||||
|
currency_id = fields.Many2one(
|
||||||
|
string="Currency",
|
||||||
|
help="The currency used in relation to the service where it's included",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
related="service_id.currency_id",
|
||||||
|
)
|
||||||
|
reservation_id = fields.Many2one(
|
||||||
|
string="Reservation",
|
||||||
|
help="Room to which the services will be applied",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
related="service_id.reservation_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
discount = fields.Float(
|
discount = fields.Float(
|
||||||
"Discount", related="service_id.discount", readonly=True, store=True
|
string="Discount (%)",
|
||||||
|
help="Discount in the price of the service.",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
default=0.0,
|
||||||
|
digits=("Discount"),
|
||||||
|
compute="_compute_discount",
|
||||||
)
|
)
|
||||||
cancel_discount = fields.Float(
|
cancel_discount = fields.Float(
|
||||||
"Discount cancel", compute="_compute_cancel_discount"
|
string="Cancelation Discount",
|
||||||
|
help="",
|
||||||
|
compute="_compute_cancel_discount",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
auto_qty = fields.Boolean(
|
||||||
|
string="Qty automated setted",
|
||||||
|
help="Show if the day qty was calculated automatically",
|
||||||
|
compute="_compute_auto_qty",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compute and Search methods
|
@api.depends("day_qty", "discount", "price_unit", "tax_ids")
|
||||||
@api.depends("day_qty", "service_id.price_total")
|
def _compute_day_amount_service(self):
|
||||||
def _compute_price_total(self):
|
for line in self:
|
||||||
|
amount_service = line.price_unit
|
||||||
|
if amount_service > 0:
|
||||||
|
currency = line.service_id.currency_id
|
||||||
|
product = line.product_id
|
||||||
|
price = amount_service * (1 - (line.discount or 0.0) * 0.01)
|
||||||
|
# REVIEW: line.day_qty is not the total qty (the total is on service_id)
|
||||||
|
taxes = line.tax_ids.compute_all(
|
||||||
|
price, currency, line.day_qty, product=product
|
||||||
|
)
|
||||||
|
line.update(
|
||||||
|
{
|
||||||
|
"price_day_tax": sum(
|
||||||
|
t.get("amount", 0.0) for t in taxes.get("taxes", [])
|
||||||
|
),
|
||||||
|
"price_day_total": taxes["total_included"],
|
||||||
|
"price_day_subtotal": taxes["total_excluded"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
line.update(
|
||||||
|
{
|
||||||
|
"price_day_tax": 0,
|
||||||
|
"price_day_total": 0,
|
||||||
|
"price_day_subtotal": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends("service_id.reservation_id", "service_id.reservation_id.discount")
|
||||||
|
def _compute_discount(self):
|
||||||
"""
|
"""
|
||||||
Used to reports
|
On board service the line discount is always
|
||||||
|
equal to reservation line discount
|
||||||
"""
|
"""
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.service_id.product_qty != 0:
|
if record.is_board_service:
|
||||||
record.price_total = (
|
record.discount = (
|
||||||
record.service_id.price_total * record.day_qty
|
record.service_id.reservation_id.reservation_line_ids.filtered(
|
||||||
) / record.service_id.product_qty
|
lambda l: l.date == record.date
|
||||||
else:
|
).discount
|
||||||
record.price_total = 0
|
)
|
||||||
|
elif not record.discount:
|
||||||
|
record.discount = 0
|
||||||
|
|
||||||
|
# TODO: Refact method and allowed cancelled single days
|
||||||
|
@api.depends("service_id.reservation_id.cancelled_reason")
|
||||||
|
def _compute_cancel_discount(self):
|
||||||
|
for line in self:
|
||||||
|
line.cancel_discount = 0
|
||||||
|
# TODO: Review cancel logic
|
||||||
|
# reservation = line.reservation_id.reservation_id
|
||||||
|
# pricelist = reservation.pricelist_id
|
||||||
|
# if reservation.state == "cancelled":
|
||||||
|
# if (
|
||||||
|
# reservation.cancelled_reason
|
||||||
|
# and pricelist
|
||||||
|
# and pricelist.cancelation_rule_id
|
||||||
|
# ):
|
||||||
|
# date_start_dt = fields.Date.from_string(
|
||||||
|
# reservation.checkin
|
||||||
|
# )
|
||||||
|
# date_end_dt = fields.Date.from_string(
|
||||||
|
# reservation.checkout
|
||||||
|
# )
|
||||||
|
# days = abs((date_end_dt - date_start_dt).days)
|
||||||
|
# rule = pricelist.cancelation_rule_id
|
||||||
|
# if reservation.cancelled_reason == "late":
|
||||||
|
# discount = 100 - rule.penalty_late
|
||||||
|
# if rule.apply_on_late == "first":
|
||||||
|
# days = 1
|
||||||
|
# elif rule.apply_on_late == "days":
|
||||||
|
# days = rule.days_late
|
||||||
|
# elif reservation.cancelled_reason == "noshow":
|
||||||
|
# discount = 100 - rule.penalty_noshow
|
||||||
|
# if rule.apply_on_noshow == "first":
|
||||||
|
# days = 1
|
||||||
|
# elif rule.apply_on_noshow == "days":
|
||||||
|
# days = rule.days_late - 1
|
||||||
|
# elif reservation.cancelled_reason == "intime":
|
||||||
|
# discount = 100
|
||||||
|
|
||||||
|
# checkin = reservation.checkin
|
||||||
|
# dates = []
|
||||||
|
# for i in range(0, days):
|
||||||
|
# dates.append(
|
||||||
|
# (
|
||||||
|
# fields.Date.from_string(checkin) + timedelta(days=i)
|
||||||
|
# ).strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||||
|
# )
|
||||||
|
# reservation.reservation_line_ids.filtered(
|
||||||
|
# lambda r: r.date in dates
|
||||||
|
# ).update({"cancel_discount": discount})
|
||||||
|
# reservation.reservation_line_ids.filtered(
|
||||||
|
# lambda r: r.date not in dates
|
||||||
|
# ).update({"cancel_discount": 100})
|
||||||
|
# else:
|
||||||
|
# reservation.reservation_line_ids.update({"cancel_discount": 0})
|
||||||
|
# else:
|
||||||
|
# reservation.reservation_line_ids.update({"cancel_discount": 0})
|
||||||
|
|
||||||
|
@api.depends("day_qty")
|
||||||
|
def _compute_auto_qty(self):
|
||||||
|
"""
|
||||||
|
Set auto_qty = False if the service is no linked to room or
|
||||||
|
if the day_qty was set manually
|
||||||
|
(See autogeneration of service lines in
|
||||||
|
_compute_service_line_ids -pms.service-)
|
||||||
|
"""
|
||||||
|
self.auto_qty = False
|
||||||
|
|
||||||
# Constraints and onchanges
|
# Constraints and onchanges
|
||||||
@api.constrains("day_qty")
|
@api.constrains("day_qty")
|
||||||
|
|||||||
@@ -10,43 +10,63 @@ class PmsSharedRoom(models.Model):
|
|||||||
_name = "pms.shared.room"
|
_name = "pms.shared.room"
|
||||||
_description = "Shared Room"
|
_description = "Shared Room"
|
||||||
_order = "room_type_id, name"
|
_order = "room_type_id, name"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
name = fields.Char(
|
||||||
name = fields.Char("Room Name", required=True)
|
string="Room Name", help="Name of the shared room", required=True
|
||||||
|
)
|
||||||
|
active = fields.Boolean(
|
||||||
|
string="Active", help="Determines if shared room is active", default=True
|
||||||
|
)
|
||||||
|
sequence = fields.Integer(
|
||||||
|
string="Sequence",
|
||||||
|
help="Field used to change the position of the shared rooms in tree view."
|
||||||
|
"Changing the position changes the sequence",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
room_type_id = fields.Many2one(
|
room_type_id = fields.Many2one(
|
||||||
"pms.room.type",
|
string="Room Type",
|
||||||
"Room Type",
|
help="Room type which the shared room belongs",
|
||||||
|
comodel_name="pms.room.type",
|
||||||
required=True,
|
required=True,
|
||||||
ondelete="restrict",
|
ondelete="restrict",
|
||||||
domain=[("shared_room", "=", True)],
|
domain=[("shared_room", "=", True)],
|
||||||
)
|
)
|
||||||
# TODO: properties relation
|
# TODO: properties relation
|
||||||
pms_property_ids = fields.Many2many(
|
pms_property_ids = fields.Many2many(
|
||||||
"pms.property",
|
string="Properties",
|
||||||
|
help="Properties with access to the element;"
|
||||||
|
" if not set, all properties can access",
|
||||||
|
comodel_name="pms.property",
|
||||||
|
relation="pms_shared_room_pms_property_rel",
|
||||||
|
column1="shared_room_id",
|
||||||
|
column2="pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
floor_id = fields.Many2one(
|
ubication_id = fields.Many2one(
|
||||||
"pms.floor",
|
string="Ubication",
|
||||||
"Ubication",
|
help="At which ubication the room is located.",
|
||||||
|
comodel_name="pms.ubication",
|
||||||
ondelete="restrict",
|
ondelete="restrict",
|
||||||
help="At which floor the room is located.",
|
|
||||||
)
|
)
|
||||||
bed_ids = fields.One2many(
|
bed_ids = fields.One2many(
|
||||||
"pms.room",
|
string="Beds",
|
||||||
"shared_room_id",
|
help="Beds in one room",
|
||||||
|
comodel_name="pms.room",
|
||||||
|
inverse_name="shared_room_id",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
)
|
)
|
||||||
active = fields.Boolean("Active", default=True)
|
beds = fields.Integer(
|
||||||
sequence = fields.Integer("Sequence", required=True)
|
string="Number Of Beds", help="Number of beds in a shared room"
|
||||||
beds = fields.Integer("Beds")
|
)
|
||||||
description_sale = fields.Text(
|
description_sale = fields.Text(
|
||||||
"Sale Description",
|
string="Sale Description",
|
||||||
translate=True,
|
|
||||||
help="A description of the Product that you want to communicate to "
|
help="A description of the Product that you want to communicate to "
|
||||||
" your customers. This description will be copied to every Sales "
|
" your customers. This description will be copied to every Sales "
|
||||||
" Order, Delivery Order and Customer Invoice/Credit Note",
|
" Order, Delivery Order and Customer Invoice/Credit Note",
|
||||||
|
translate=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Constraints and onchanges
|
|
||||||
@api.constrains("beds")
|
@api.constrains("beds")
|
||||||
def _constrain_beds(self):
|
def _constrain_beds(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -75,7 +95,7 @@ class PmsSharedRoom(models.Model):
|
|||||||
"capacity": 1,
|
"capacity": 1,
|
||||||
"room_type_id": self.room_type_id.id,
|
"room_type_id": self.room_type_id.id,
|
||||||
"sequence": self.sequence,
|
"sequence": self.sequence,
|
||||||
"floor_id": self.floor_id.id if self.floor_id else False,
|
"ubication_id": self.ubication_id.id if self.ubication_id else False,
|
||||||
"shared_room_id": self.id,
|
"shared_room_id": self.id,
|
||||||
}
|
}
|
||||||
beds.append((0, False, bed_vals))
|
beds.append((0, False, bed_vals))
|
||||||
@@ -98,11 +118,11 @@ class PmsSharedRoom(models.Model):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.constrains("floor_id")
|
@api.constrains("ubication_id")
|
||||||
def _constrain_floor_id(self):
|
def _constrain_ubication_id(self):
|
||||||
self.bed_ids.write(
|
self.bed_ids.write(
|
||||||
{
|
{
|
||||||
"floor_id": self.floor_id.id,
|
"ubication_id": self.ubication_id.id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
50
pms/models/pms_ubication.py
Normal file
50
pms/models/pms_ubication.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
class PmsUbication(models.Model):
|
||||||
|
_name = "pms.ubication"
|
||||||
|
_description = "Ubication"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
|
name = fields.Char(
|
||||||
|
string="Ubication Name",
|
||||||
|
help="Ubication Name",
|
||||||
|
required=True,
|
||||||
|
translate=True,
|
||||||
|
)
|
||||||
|
sequence = fields.Integer(
|
||||||
|
string="Sequence",
|
||||||
|
help="Field used to change the position of the ubications in tree view."
|
||||||
|
"Changing the position changes the sequence",
|
||||||
|
)
|
||||||
|
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_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(
|
||||||
|
"pms_property_ids",
|
||||||
|
"pms_room_ids",
|
||||||
|
)
|
||||||
|
def _check_property_integrity(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.pms_property_ids and rec.pms_room_ids:
|
||||||
|
if rec.pms_room_ids.pms_property_id not in rec.pms_property_ids:
|
||||||
|
raise ValidationError(_("Property not allowed"))
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
# Copyright 2017 Alexandre Díaz, Pablo Quesada, Darío Lodeiros
|
# Copyright 2017 Alexandre Díaz, Pablo Quesada, Darío Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
import logging
|
||||||
|
|
||||||
from odoo import fields, models
|
from odoo import fields, models
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ProductPricelist(models.Model):
|
class ProductPricelist(models.Model):
|
||||||
"""Before creating a 'daily' pricelist, you need to consider the following:
|
"""Before creating a 'daily' pricelist, you need to consider the following:
|
||||||
@@ -10,17 +14,127 @@ class ProductPricelist(models.Model):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_inherit = "product.pricelist"
|
_inherit = "product.pricelist"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
# Fields declaration
|
# Fields declaration
|
||||||
pms_property_ids = fields.Many2many(
|
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(
|
||||||
|
string="Company",
|
||||||
|
help="Company to which the pricelist belongs",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
cancelation_rule_id = fields.Many2one(
|
cancelation_rule_id = fields.Many2one(
|
||||||
"pms.cancelation.rule", string="Cancelation Policy"
|
string="Cancelation Policy",
|
||||||
|
help="Cancelation Policy included in the room",
|
||||||
|
comodel_name="pms.cancelation.rule",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
pricelist_type = fields.Selection(
|
pricelist_type = fields.Selection(
|
||||||
[("daily", "Daily Plan")], string="Pricelist Type", default="daily"
|
string="Pricelist Type",
|
||||||
|
help="Pricelist types, it can be Daily Plan",
|
||||||
|
default="daily",
|
||||||
|
selection=[("daily", "Daily Plan")],
|
||||||
)
|
)
|
||||||
|
pms_sale_channel_ids = fields.Many2many(
|
||||||
|
string="Available Channels",
|
||||||
|
help="Sale channel for which the pricelist is included",
|
||||||
|
comodel_name="pms.sale.channel",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
availability_plan_id = fields.Many2one(
|
||||||
|
string="Availability Plan",
|
||||||
|
help="Availability Plan for which the pricelist is included",
|
||||||
|
comodel_name="pms.availability.plan",
|
||||||
|
ondelete="restrict",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
item_ids = fields.One2many(
|
||||||
|
string="Items",
|
||||||
|
help="Items for which the pricelist is made up",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _compute_price_rule_get_items(
|
||||||
|
self, products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
"property" in self._context
|
||||||
|
and self._context["property"]
|
||||||
|
and self._context.get("consumption_date")
|
||||||
|
):
|
||||||
|
self.env.cr.execute(
|
||||||
|
"""
|
||||||
|
SELECT item.id
|
||||||
|
FROM product_pricelist_item item
|
||||||
|
LEFT JOIN product_category categ
|
||||||
|
ON item.categ_id = categ.id
|
||||||
|
LEFT JOIN product_pricelist_pms_property_rel cab
|
||||||
|
ON item.pricelist_id = cab.product_pricelist_id
|
||||||
|
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
|
||||||
|
WHERE (lin.pms_property_id = %s OR lin.pms_property_id IS NULL)
|
||||||
|
AND (cab.pms_property_id = %s OR cab.pms_property_id IS NULL)
|
||||||
|
AND (item.product_tmpl_id IS NULL
|
||||||
|
OR item.product_tmpl_id = ANY(%s))
|
||||||
|
AND (item.product_id IS NULL OR item.product_id = ANY(%s))
|
||||||
|
AND (item.categ_id IS NULL OR item.categ_id = ANY(%s))
|
||||||
|
AND (item.pricelist_id = %s)
|
||||||
|
AND (item.date_start IS NULL OR item.date_start <=%s)
|
||||||
|
AND (item.date_end IS NULL OR item.date_end >=%s)
|
||||||
|
AND (item.date_start_overnight IS NULL
|
||||||
|
OR item.date_start_overnight <=%s)
|
||||||
|
AND (item.date_end_overnight IS NULL
|
||||||
|
OR item.date_end_overnight >=%s)
|
||||||
|
GROUP BY item.id
|
||||||
|
ORDER BY item.applied_on,
|
||||||
|
/* REVIEW: priotrity date sale / date overnight */
|
||||||
|
item.date_end - item.date_start ASC,
|
||||||
|
item.date_end_overnight - item.date_start_overnight ASC,
|
||||||
|
NULLIF((SELECT COUNT(1)
|
||||||
|
FROM product_pricelist_item_pms_property_rel l
|
||||||
|
WHERE item.id = l.product_pricelist_item_id)
|
||||||
|
+ (SELECT COUNT(1)
|
||||||
|
FROM product_pricelist_pms_property_rel c
|
||||||
|
WHERE item.pricelist_id = c.product_pricelist_id),0)
|
||||||
|
NULLS LAST,
|
||||||
|
item.id DESC;
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
self._context["property"],
|
||||||
|
self._context["property"],
|
||||||
|
prod_tmpl_ids,
|
||||||
|
prod_ids,
|
||||||
|
categ_ids,
|
||||||
|
# on_board_service_bool,
|
||||||
|
# board_service_id,
|
||||||
|
self.id,
|
||||||
|
date,
|
||||||
|
date,
|
||||||
|
self._context["consumption_date"],
|
||||||
|
self._context["consumption_date"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
item_ids = [x[0] for x in self.env.cr.fetchall()]
|
||||||
|
items = self.env["product.pricelist.item"].browse(item_ids)
|
||||||
|
else:
|
||||||
|
items = super(ProductPricelist, self)._compute_price_rule_get_items(
|
||||||
|
products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids
|
||||||
|
)
|
||||||
|
return items
|
||||||
|
|
||||||
# Constraints and onchanges
|
# Constraints and onchanges
|
||||||
# @api.constrains("pricelist_type", "pms_property_ids")
|
# @api.constrains("pricelist_type", "pms_property_ids")
|
||||||
@@ -52,23 +166,17 @@ class ProductPricelist(models.Model):
|
|||||||
# )
|
# )
|
||||||
# )
|
# )
|
||||||
|
|
||||||
def _compute_price_rule_get_items(
|
def open_massive_changes_wizard(self):
|
||||||
self, products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids
|
|
||||||
):
|
if self.ensure_one():
|
||||||
items = super(ProductPricelist, self)._compute_price_rule_get_items(
|
return {
|
||||||
products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids
|
"view_type": "form",
|
||||||
)
|
"view_mode": "form",
|
||||||
# Discard the rules with defined properties other than the context,
|
"name": "Massive changes on Pricelist: " + self.name,
|
||||||
# and we reorder the rules to return the most concrete property rule first
|
"res_model": "pms.massive.changes.wizard",
|
||||||
if "property" in self._context:
|
"target": "new",
|
||||||
items_filtered = items.filtered(
|
"type": "ir.actions.act_window",
|
||||||
lambda i: not i.pms_property_ids
|
"context": {
|
||||||
or self._context["property"] in i.pms_property_ids.ids
|
"pricelist_id": self.id,
|
||||||
)
|
},
|
||||||
return items_filtered.sorted(
|
}
|
||||||
key=lambda s: (
|
|
||||||
(s.applied_on),
|
|
||||||
((s.date_end - s.date_start).days),
|
|
||||||
((not s.pms_property_ids, s), len(s.pms_property_ids)),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -5,7 +5,53 @@ from odoo import fields, models
|
|||||||
|
|
||||||
class ProductPricelistItem(models.Model):
|
class ProductPricelistItem(models.Model):
|
||||||
_inherit = "product.pricelist.item"
|
_inherit = "product.pricelist.item"
|
||||||
|
_check_pms_properties_auto = True
|
||||||
|
|
||||||
pms_property_ids = fields.Many2many(
|
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",
|
||||||
|
help="Start date to apply daily pricelist items",
|
||||||
|
)
|
||||||
|
date_end_overnight = fields.Date(
|
||||||
|
string="End Date Overnight",
|
||||||
|
help="End date to apply daily pricelist items",
|
||||||
|
)
|
||||||
|
on_board_service = fields.Boolean(
|
||||||
|
string="On Board Service",
|
||||||
|
help="Those included in Board Services",
|
||||||
|
)
|
||||||
|
board_service_room_type_ids = fields.Many2many(
|
||||||
|
string="Board Services",
|
||||||
|
help="""Specify a Board services on Room Types.""",
|
||||||
|
comodel_name="pms.board.service.room.type",
|
||||||
|
relation="board_service_pricelist_item_rel",
|
||||||
|
column1="pricelist_item_id",
|
||||||
|
column2="board_service_id",
|
||||||
|
ondelete="cascade",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
pricelist_id = fields.Many2one(
|
||||||
|
string="Pricelist",
|
||||||
|
help="Pricelist in which this item is included",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
product_id = fields.Many2one(
|
||||||
|
string="Product",
|
||||||
|
help="Product associated with the item",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
|
product_tmpl_id = fields.Many2one(
|
||||||
|
string="Product Template",
|
||||||
|
help="Product template associated with the item",
|
||||||
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
|
|||||||
43
pms/models/product_product.py
Normal file
43
pms/models/product_product.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ProductProduct(models.Model):
|
||||||
|
_inherit = "product.product"
|
||||||
|
|
||||||
|
board_price = fields.Float(
|
||||||
|
string="Board Service Price",
|
||||||
|
help="Get price on board service",
|
||||||
|
digits="Product Price",
|
||||||
|
compute="_compute_board_price",
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends_context("consumption_date")
|
||||||
|
def _compute_product_price(self):
|
||||||
|
super(ProductProduct, self)._compute_product_price()
|
||||||
|
|
||||||
|
def _compute_board_price(self):
|
||||||
|
for record in self:
|
||||||
|
if self._context.get("board_service"):
|
||||||
|
record.board_price = (
|
||||||
|
self.env["pms.board.service.room.type.line"]
|
||||||
|
.search(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"pms_board_service_room_type_id",
|
||||||
|
"=",
|
||||||
|
self._context.get("board_service"),
|
||||||
|
),
|
||||||
|
("product_id", "=", record.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.amount
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
record.board_price = False
|
||||||
|
|
||||||
|
def price_compute(self, price_type, uom=False, currency=False, company=None):
|
||||||
|
if self._context.get("board_service"):
|
||||||
|
price_type = "board_price"
|
||||||
|
return super(ProductProduct, self).price_compute(
|
||||||
|
price_type, uom, currency, company
|
||||||
|
)
|
||||||
@@ -8,19 +8,39 @@ class ProductTemplate(models.Model):
|
|||||||
_inherit = "product.template"
|
_inherit = "product.template"
|
||||||
|
|
||||||
pms_property_ids = fields.Many2many(
|
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_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",
|
||||||
|
help="Indicates that the product is sold by days",
|
||||||
|
)
|
||||||
|
per_person = fields.Boolean(
|
||||||
|
string="Unit increment per person",
|
||||||
|
help="Indicates that the product is sold per person",
|
||||||
)
|
)
|
||||||
per_day = fields.Boolean("Unit increment per day")
|
|
||||||
per_person = fields.Boolean("Unit increment per person")
|
|
||||||
consumed_on = fields.Selection(
|
consumed_on = fields.Selection(
|
||||||
[("before", "Before night"), ("after", "After night")],
|
string="Consumed",
|
||||||
"Consumed",
|
help="Indicates when the product is consumed",
|
||||||
|
selection=[("before", "Before night"), ("after", "After night")],
|
||||||
default="before",
|
default="before",
|
||||||
)
|
)
|
||||||
daily_limit = fields.Integer("Daily limit")
|
daily_limit = fields.Integer(
|
||||||
is_extra_bed = fields.Boolean("Is extra bed", default=False)
|
string="Daily limit", help="Indicates how much products can consumed in one day"
|
||||||
show_in_calendar = fields.Boolean(
|
)
|
||||||
"Show in Calendar",
|
is_extra_bed = fields.Boolean(
|
||||||
default=False,
|
string="Is extra bed",
|
||||||
help="Specifies if the product is shown in the calendar information.",
|
help="Indicates if that product is a extra bed, add +1 capacity in the room",
|
||||||
|
default=False,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,20 +7,9 @@ from odoo import fields, models
|
|||||||
class ResCompany(models.Model):
|
class ResCompany(models.Model):
|
||||||
_inherit = "res.company"
|
_inherit = "res.company"
|
||||||
|
|
||||||
# Fields declaration
|
pms_property_ids = fields.One2many(
|
||||||
pms_property_ids = fields.One2many("pms.property", "company_id", "Properties")
|
string="Properties",
|
||||||
# TODO: need extra explanation or remove otherwise
|
help="Properties with access to the element",
|
||||||
# additional_hours = fields.Integer('Additional Hours',
|
comodel_name="pms.property",
|
||||||
# help="Provide the min hours value for \
|
inverse_name="company_id",
|
||||||
# check in, checkout days, whatever \
|
)
|
||||||
# the hours will be provided here based \
|
|
||||||
# on that extra days will be \
|
|
||||||
# calculated.")
|
|
||||||
# TODO: move the text to the default template for confirmed reservations
|
|
||||||
# cardex_warning = fields.Text(
|
|
||||||
# 'Warning in Cardex',
|
|
||||||
# default="Time to access rooms: 14: 00h. Departure time: \
|
|
||||||
# 12: 00h. If the accommodation is not left at that time, \
|
|
||||||
# the establishment will charge a day's stay according to \
|
|
||||||
# current rate that day",
|
|
||||||
# help="Notice under the signature on the traveler's ticket.")
|
|
||||||
|
|||||||
@@ -11,35 +11,60 @@ _logger = logging.getLogger(__name__)
|
|||||||
class ResPartner(models.Model):
|
class ResPartner(models.Model):
|
||||||
_inherit = "res.partner"
|
_inherit = "res.partner"
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
main_partner_id = fields.Many2one(
|
|
||||||
"res.partner", string="Destination Partner fusion"
|
|
||||||
)
|
|
||||||
reservations_count = fields.Integer(
|
reservations_count = fields.Integer(
|
||||||
"Reservations", compute="_compute_reservations_count"
|
string="Reservations",
|
||||||
|
help="Number of reservations of the partner",
|
||||||
|
compute="_compute_reservations_count",
|
||||||
|
)
|
||||||
|
folios_count = fields.Integer(
|
||||||
|
string="Folios",
|
||||||
|
help="Number of folios of the partner",
|
||||||
|
compute="_compute_folios_count",
|
||||||
|
)
|
||||||
|
is_agency = fields.Boolean(
|
||||||
|
string="Is Agency", help="Indicates if the partner is an agency"
|
||||||
)
|
)
|
||||||
folios_count = fields.Integer("Folios", compute="_compute_folios_count")
|
|
||||||
unconfirmed = fields.Boolean("Unconfirmed", default=True)
|
|
||||||
is_agency = fields.Boolean("Is Agency")
|
|
||||||
sale_channel_id = fields.Many2one(
|
sale_channel_id = fields.Many2one(
|
||||||
"pms.sale.channel",
|
|
||||||
string="Sale Channel",
|
string="Sale Channel",
|
||||||
ondelete="restrict",
|
help="The sale channel of the partner",
|
||||||
|
comodel_name="pms.sale.channel",
|
||||||
domain=[("channel_type", "=", "indirect")],
|
domain=[("channel_type", "=", "indirect")],
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
default_commission = fields.Integer(string="Commission", help="Default commission")
|
||||||
|
apply_pricelist = fields.Boolean(
|
||||||
|
string="Apply Pricelist",
|
||||||
|
help="Indicates if agency pricelist is applied to his reservations",
|
||||||
|
)
|
||||||
|
invoice_to_agency = fields.Boolean(
|
||||||
|
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,
|
||||||
)
|
)
|
||||||
default_commission = fields.Integer("Commission")
|
|
||||||
apply_pricelist = fields.Boolean("Apply Pricelist")
|
|
||||||
invoice_agency = fields.Boolean("Invoice Agency")
|
|
||||||
|
|
||||||
# Compute and Search methods
|
|
||||||
def _compute_reservations_count(self):
|
def _compute_reservations_count(self):
|
||||||
|
# TODO: recuperar las reservas de los folios del partner
|
||||||
pms_reservation_obj = self.env["pms.reservation"]
|
pms_reservation_obj = self.env["pms.reservation"]
|
||||||
for record in self:
|
for record in self:
|
||||||
record.reservations_count = pms_reservation_obj.search_count(
|
record.reservations_count = pms_reservation_obj.search_count(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
"partner_id.id",
|
"partner_id.id",
|
||||||
"=",
|
"child_of",
|
||||||
record.id if isinstance(record.id, int) else False,
|
record.id if isinstance(record.id, int) else False,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -58,17 +83,14 @@ class ResPartner(models.Model):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# ORM Overrides
|
|
||||||
@api.model
|
@api.model
|
||||||
def name_search(self, name, args=None, operator="ilike", limit=100):
|
def name_search(self, name, args=None, operator="ilike", limit=100):
|
||||||
if not args:
|
if not args:
|
||||||
args = []
|
args = []
|
||||||
domain = [
|
domain = [
|
||||||
"|",
|
|
||||||
"|",
|
"|",
|
||||||
("phone", operator, name),
|
("phone", operator, name),
|
||||||
("mobile", operator, name),
|
("mobile", operator, name),
|
||||||
("email", operator, name),
|
|
||||||
]
|
]
|
||||||
partners = self.search(
|
partners = self.search(
|
||||||
domain + args,
|
domain + args,
|
||||||
@@ -93,3 +115,34 @@ class ResPartner(models.Model):
|
|||||||
raise models.ValidationError(_("Sale Channel must be entered"))
|
raise models.ValidationError(_("Sale Channel must be entered"))
|
||||||
if not record.is_agency and record.sale_channel_id:
|
if not record.is_agency and record.sale_channel_id:
|
||||||
record.sale_channel_id = None
|
record.sale_channel_id = None
|
||||||
|
|
||||||
|
# REVIEW: problems with odoo demo data
|
||||||
|
# @api.constrains("mobile", "email")
|
||||||
|
# def _check_duplicated(self):
|
||||||
|
# for record in self:
|
||||||
|
# partner, field = record._search_duplicated()
|
||||||
|
# if partner:
|
||||||
|
# raise models.ValidationError(
|
||||||
|
# _(
|
||||||
|
# "Partner %s found with same %s (%s)",
|
||||||
|
# partner.name,
|
||||||
|
# partner._fields[field].string,
|
||||||
|
# getattr(record, field),
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
def _search_duplicated(self):
|
||||||
|
self.ensure_one()
|
||||||
|
partner = False
|
||||||
|
for field in self._get_key_fields():
|
||||||
|
if getattr(self, field):
|
||||||
|
partner = self.search(
|
||||||
|
[(field, "=", getattr(self, field)), ("id", "!=", self.id)]
|
||||||
|
)
|
||||||
|
if partner:
|
||||||
|
field = field
|
||||||
|
return partner, field
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_key_fields(self):
|
||||||
|
return []
|
||||||
|
|||||||
@@ -1,33 +1,28 @@
|
|||||||
# Copyright 2019 Pablo Quesada
|
# Copyright 2019 Pablo Quesada
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import AccessError
|
from odoo.exceptions import ValidationError
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
class ResUsers(models.Model):
|
class ResUsers(models.Model):
|
||||||
_inherit = "res.users"
|
_inherit = "res.users"
|
||||||
|
|
||||||
# Default Methods ang Gets
|
|
||||||
@api.model
|
|
||||||
def _get_default_pms_property(self):
|
|
||||||
return self.env.user.pms_property_id
|
|
||||||
|
|
||||||
# Fields declaration
|
|
||||||
pms_property_id = fields.Many2one(
|
pms_property_id = fields.Many2one(
|
||||||
"pms.property",
|
string="Default Property",
|
||||||
string="Property",
|
help="The property that is selected within " "those allowed for the user",
|
||||||
default=_get_default_pms_property,
|
comodel_name="pms.property",
|
||||||
help="The property this user is currently working for.",
|
domain="[('id','in',pms_property_ids)]",
|
||||||
context={"user_preference": True},
|
context={"user_preference": True},
|
||||||
)
|
)
|
||||||
pms_property_ids = fields.Many2many(
|
pms_property_ids = fields.Many2many(
|
||||||
"pms.property",
|
|
||||||
"pms_property_users_rel",
|
|
||||||
"user_id",
|
|
||||||
"pms_property_id",
|
|
||||||
string="Properties",
|
string="Properties",
|
||||||
default=_get_default_pms_property,
|
help="The properties allowed for this user",
|
||||||
|
comodel_name="pms.property",
|
||||||
|
relation="pms_property_users_rel",
|
||||||
|
column1="user_id",
|
||||||
|
column2="pms_property_id",
|
||||||
|
domain="[('company_id','in',company_ids)]",
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
@@ -39,7 +34,24 @@ class ResUsers(models.Model):
|
|||||||
active_property_ids = list(
|
active_property_ids = list(
|
||||||
map(int, request.httprequest.cookies.get("pms_pids", "").split(","))
|
map(int, request.httprequest.cookies.get("pms_pids", "").split(","))
|
||||||
)
|
)
|
||||||
if any(pid not in user_property_ids for pid in active_property_ids):
|
active_property_ids = [
|
||||||
raise AccessError(_("Access to unauthorized or invalid properties."))
|
pid for pid in active_property_ids if pid in user_property_ids
|
||||||
|
]
|
||||||
return self.env["pms.property"].browse(active_property_ids).ids
|
return self.env["pms.property"].browse(active_property_ids).ids
|
||||||
return user_property_ids
|
return user_property_ids
|
||||||
|
|
||||||
|
@api.constrains("pms_property_id", "pms_property_ids")
|
||||||
|
def _check_property_in_allowed_properties(self):
|
||||||
|
if any(user.pms_property_id not in user.pms_property_ids for user in self):
|
||||||
|
raise ValidationError(
|
||||||
|
_("The chosen property is not in the allowed properties for this user")
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.constrains("pms_property_ids", "company_id")
|
||||||
|
def _check_company_in_property_ids(self):
|
||||||
|
for record in self:
|
||||||
|
for pms_property in record.pms_property_ids:
|
||||||
|
if pms_property.company_id not in record.company_ids:
|
||||||
|
raise ValidationError(
|
||||||
|
_("Some properties do not belong to the allowed companies")
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
You will find the hotel settings in Settings > Users & Companies > Hotels > Your Hotel.
|
You will find the hotel settings in PMS Management > Configuration > Properties > Your Property.
|
||||||
|
|
||||||
This module required additional configuration for company, accounting, invoicing and user privileges.
|
This module required additional configuration for company, accounting, invoicing and user privileges.
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
* Dario Lodeiros <dario@commitsun.com>
|
|
||||||
* Alexandre Díaz
|
* Alexandre Díaz
|
||||||
* Pablo Quesada
|
* Pablo Quesada
|
||||||
* Jose Luis Algara
|
* Jose Luis Algara
|
||||||
|
* `Commit [Sun] <https://www.commitsun.com>`:
|
||||||
|
|
||||||
|
* Dario Lodeiros
|
||||||
|
* Eric Antones
|
||||||
|
* Sara Lago
|
||||||
|
* Brais Abeijon
|
||||||
|
* Miguel Padin
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
This module is an all-in-one property management system (PMS) focused on medium-sized hotels
|
This module is an all-in-one property management system (PMS) focused on medium-sized properties
|
||||||
for managing every aspect of your property's daily operations.
|
for managing every aspect of your property's daily operations.
|
||||||
|
|
||||||
You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory,
|
You can manage properties with multi-property and multi-company support, including your rooms inventory,
|
||||||
reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities.
|
reservations, check-in, daily reports, board services, rate and availability plans among other property functionalities.
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
This module depends on modules ``base``, ``sale_stock``, ``account_payment_return``, ``partner_firstname``,
|
This module depends on modules ``base``, ``mail``, ``sale`` and ``multi_pms_properties``.
|
||||||
and ``account_cancel``. Ensure yourself to have all them in your addons list.
|
Ensure yourself to have all them in your addons list.
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
To use this module, please, read the complete user guide at https://roomdoo.com.
|
To use this module, please, read the complete user guide at `<roomdoo.com>`_.
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<template id="report_folio_document">
|
<template id="report_folio_document">
|
||||||
<t t-call="web.external_layout">
|
<t t-call="web.external_layout">
|
||||||
<t t-set="doc" t-value="doc.with_context({'lang':doc.partner_id.lang})" />
|
<t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)" />
|
||||||
<div class="page">
|
<t t-set="address">
|
||||||
<div class="oe_structure" />
|
<div class="row">
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
<t t-if="doc.partner_invoice_id != doc.partner_id">
|
<t
|
||||||
|
t-if="len(doc.partner_invoice_ids)==1 and doc.partner_id != doc.partner_invoice_ids[0]"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
t-field="doc.partner_invoice_id"
|
t-field="doc.partner_invoice_ids[0]"
|
||||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'
|
t-options='{"widget": "contact", "fields": ["address", "name", "phone"], "no_marker": True, "phone_icons": True}'
|
||||||
/>
|
/>
|
||||||
</t>
|
</t>
|
||||||
@@ -25,308 +26,286 @@
|
|||||||
<span t-field="doc.partner_id.vat" />
|
<span t-field="doc.partner_id.vat" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
<div class="page">
|
||||||
|
<div class="oe_structure" />
|
||||||
|
|
||||||
|
<h2 class="mt16">
|
||||||
|
<span t-if="doc.state not in ['draft','sent']">Order #</span>
|
||||||
|
<span t-if="doc.state in ['draft','sent']">Quotation #</span>
|
||||||
|
<span t-field="doc.name" />
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="row mt32 mb32" id="informations">
|
||||||
|
<div t-if="doc.client_order_ref" class="col-auto col-3 mw-100 mb-2">
|
||||||
|
<strong>Your Reference:</strong>
|
||||||
|
<p class="m-0" t-field="doc.client_order_ref" />
|
||||||
</div>
|
</div>
|
||||||
<h2>
|
<div
|
||||||
<span t-if="doc.state not in ['draft','sent']">Order #</span>
|
t-if="doc.date_order and doc.state not in ['draft','sent']"
|
||||||
<span t-if="doc.state in ['draft','sent']">Quotation #</span>
|
class="col-auto col-3 mw-100 mb-2"
|
||||||
<span t-field="doc.name" />
|
|
||||||
</h2>
|
|
||||||
<div class="row mt32 mb32" id="informations">
|
|
||||||
<div t-if="doc.client_order_ref" class="col-xs-3">
|
|
||||||
<strong>Your Reference:</strong>
|
|
||||||
<p t-field="doc.client_order_ref" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
t-if="doc.confirmation_date and doc.state not in ['draft','sent']"
|
|
||||||
class="col-xs-3"
|
|
||||||
>
|
>
|
||||||
<strong>Date Ordered:</strong>
|
<strong>Order Date:</strong>
|
||||||
<p t-field="doc.confirmation_date" />
|
<p class="m-0" t-field="doc.date_order" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
t-if="doc.date_order and doc.state in ['draft','sent']"
|
t-if="doc.date_order and doc.state in ['draft','sent']"
|
||||||
class="col-xs-3"
|
class="col-auto col-3 mw-100 mb-2"
|
||||||
>
|
>
|
||||||
<strong>Quotation Date:</strong>
|
<strong>Quotation Date:</strong>
|
||||||
<p t-field="doc.date_order" />
|
<p
|
||||||
</div>
|
class="m-0"
|
||||||
<div t-if="doc.user_id.name" class="col-xs-3">
|
t-field="doc.date_order"
|
||||||
<strong>Salesperson:</strong>
|
t-options='{"widget": "date"}'
|
||||||
<p t-field="doc.user_id" />
|
/>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
name="payment_term"
|
|
||||||
t-if="doc.payment_term_id"
|
|
||||||
class="col-xs-3"
|
|
||||||
>
|
|
||||||
<strong>Payment Terms:</strong>
|
|
||||||
<p t-field="doc.payment_term_id" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- Is there a discount on at least one line? -->
|
<div t-if="doc.user_id.name" class="col-auto col-3 mw-100 mb-2">
|
||||||
<t
|
<strong>Salesperson:</strong>
|
||||||
|
<p class="m-0" t-field="doc.user_id" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Is there a discount on at least one line? -->
|
||||||
|
<t
|
||||||
t-set="display_discount"
|
t-set="display_discount"
|
||||||
t-value="any([l.discount for l in doc.reservation_ids])"
|
t-value="any(l.discount for l in doc.sale_line_ids)"
|
||||||
/>
|
/>
|
||||||
<table class="table table-condensed">
|
|
||||||
<thead>
|
<table class="table table-sm o_main_table">
|
||||||
<tr>
|
<!-- In case we want to repeat the header, remove "display: table-row-group" -->
|
||||||
<th>Description</th>
|
<thead style="display: table-row-group">
|
||||||
<th class="text-right">Quantity</th>
|
<tr>
|
||||||
<th
|
<th name="th_description" class="text-left">Description</th>
|
||||||
|
<th name="th_quantity" class="text-right">Quantity</th>
|
||||||
|
<th name="th_priceunit" class="text-right">Unit Price</th>
|
||||||
|
<th
|
||||||
|
name="th_discount"
|
||||||
t-if="display_discount"
|
t-if="display_discount"
|
||||||
class="text-right"
|
class="text-right"
|
||||||
groups="sale.group_discount_per_so_line"
|
|
||||||
>
|
>
|
||||||
Disc.(%)
|
<span>Disc.%</span>
|
||||||
</th>
|
</th>
|
||||||
<th class="text-right">Taxes</th>
|
<th name="th_taxes" class="text-right">Taxes</th>
|
||||||
<th
|
<th name="th_subtotal" class="text-right">
|
||||||
class="text-right"
|
<span
|
||||||
groups="sale.group_show_price_subtotal"
|
groups="account.group_show_line_subtotals_tax_excluded"
|
||||||
|
>Amount</span>
|
||||||
|
<span
|
||||||
|
groups="account.group_show_line_subtotals_tax_included"
|
||||||
|
>Total Price</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="sale_tbody">
|
||||||
|
|
||||||
|
<t t-set="current_subtotal" t-value="0" />
|
||||||
|
|
||||||
|
<t t-foreach="doc.sale_line_ids" t-as="line">
|
||||||
|
|
||||||
|
<t
|
||||||
|
t-set="current_subtotal"
|
||||||
|
t-value="current_subtotal + line.price_subtotal"
|
||||||
|
groups="account.group_show_line_subtotals_tax_excluded"
|
||||||
|
/>
|
||||||
|
<t
|
||||||
|
t-set="current_subtotal"
|
||||||
|
t-value="current_subtotal + line.price_total"
|
||||||
|
groups="account.group_show_line_subtotals_tax_included"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<tr
|
||||||
|
t-att-class="'bg-200 font-weight-bold o_line_section' if line.display_type == 'line_section' else 'font-italic o_line_note' if line.display_type == 'line_note' else ''"
|
||||||
>
|
>
|
||||||
Amount
|
<t t-if="not line.display_type">
|
||||||
</th>
|
<t t-set="price" t-value="line.price_unit" />
|
||||||
<th
|
|
||||||
class="text-right price_tax_included"
|
<t t-if="line.reservation_id">
|
||||||
groups="sale.group_show_price_total"
|
<t
|
||||||
>
|
t-set="print_board_service"
|
||||||
Total Price
|
t-value="line.reservation_id.board_service_room_id.pms_board_service_id.show_detail_report"
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="sale_tbody">
|
|
||||||
<!-- Lines associated -->
|
|
||||||
<t t-foreach="doc.reservation_ids" t-as="l">
|
|
||||||
<t t-if="l.price_total > 0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span t-field="l.name" />
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<span t-field="l.nights" />
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
t-if="display_discount"
|
|
||||||
class="text-right"
|
|
||||||
groups="sale.group_discount_per_so_line"
|
|
||||||
>
|
|
||||||
<span t-field="l.discount" />
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<span
|
|
||||||
t-esc="', '.join(map(lambda x: (x.description or x.name), l.tax_ids))"
|
|
||||||
/>
|
/>
|
||||||
</td>
|
<t t-if="not print_board_service">
|
||||||
<td
|
<t
|
||||||
class="text-right"
|
t-foreach="line.reservation_id.service_ids"
|
||||||
groups="sale.group_show_price_subtotal"
|
t-as="service"
|
||||||
>
|
>
|
||||||
<span
|
<t t-if="service.is_board_service">
|
||||||
t-field="l.price_subtotal"
|
<t
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
t-set="price"
|
||||||
/>
|
t-value="service.product_qty/line.price_total*(1-(service.reservation_id.discount or 0.0)*0.01) + price"
|
||||||
</td>
|
/>
|
||||||
<td
|
</t>
|
||||||
class="text-right"
|
</t>
|
||||||
groups="sale.group_show_price_total"
|
</t>
|
||||||
>
|
</t>
|
||||||
<span
|
|
||||||
t-field="l.price_total"
|
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
<t t-foreach="doc.service_ids" t-as="l">
|
|
||||||
<t t-if="l.price_total > 0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span t-field="l.name" />
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<span t-field="l.product_qty" />
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
t-if="display_discount"
|
|
||||||
class="text-right"
|
|
||||||
groups="sale.group_discount_per_so_line"
|
|
||||||
>
|
|
||||||
<span t-field="l.discount" />
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<span
|
|
||||||
t-esc="', '.join(map(lambda x: (x.description or x.name), l.tax_ids))"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="text-right"
|
|
||||||
groups="sale.group_show_price_subtotal"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
t-field="l.price_subtotal"
|
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="text-right"
|
|
||||||
groups="sale.group_show_price_total"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
t-field="l.price_total"
|
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</t>
|
|
||||||
</t>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="clearfix">
|
|
||||||
<div class="row" name="total">
|
|
||||||
<div class="col-xs-4 pull-right">
|
|
||||||
<table
|
|
||||||
class="table table-condensed"
|
|
||||||
style="min-width: 200px;max-width: 350px;"
|
|
||||||
>
|
|
||||||
<tr
|
|
||||||
class="border-black"
|
|
||||||
style="border-bottom:1px solid #dddddd;"
|
|
||||||
>
|
|
||||||
<td>
|
|
||||||
<strong>Subtotal</strong>
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<span
|
|
||||||
t-field="doc.amount_untaxed"
|
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
-
|
|
||||||
<t
|
<t
|
||||||
|
t-if="not(not print_board_service and line.service_id.is_board_service)"
|
||||||
|
>
|
||||||
|
<td name="td_name"><span
|
||||||
|
t-field="line.name"
|
||||||
|
/></td>
|
||||||
|
<td name="td_quantity" class="text-right">
|
||||||
|
<span t-field="line.product_uom_qty" />
|
||||||
|
<span
|
||||||
|
t-field="line.product_uom"
|
||||||
|
groups="uom.group_uom"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td name="td_priceunit" class="text-right">
|
||||||
|
<span
|
||||||
|
t-esc="price"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td t-if="display_discount" class="text-right">
|
||||||
|
<span t-field="line.discount" />
|
||||||
|
</td>
|
||||||
|
<td name="td_taxes" class="text-right">
|
||||||
|
<span
|
||||||
|
t-esc="', '.join(map(lambda x: (x.description or x.name), line.tax_ids))"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
name="td_subtotal"
|
||||||
|
class="text-right o_price_total"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
t-esc="price*(1-(line.discount or 0.0)*0.01)* line.product_uom_qty"
|
||||||
|
groups="account.group_show_line_subtotals_tax_excluded"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
t-esc="price *(1-(line.discount or 0.0)*0.01)* line.product_uom_qty"
|
||||||
|
groups="account.group_show_line_subtotals_tax_included"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
<t t-if="line.display_type == 'line_section'">
|
||||||
|
<td name="td_section_line" colspan="99">
|
||||||
|
<span t-field="line.name" />
|
||||||
|
</td>
|
||||||
|
<t t-set="current_section" t-value="line" />
|
||||||
|
<t t-set="current_subtotal" t-value="0" />
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-if="line.display_type == 'line_note'">
|
||||||
|
<td name="td_note_line" colspan="99">
|
||||||
|
<span t-field="line.name" />
|
||||||
|
</td>
|
||||||
|
</t>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<t
|
||||||
|
t-if="current_section and (line_last or doc.sale_line_ids[line_index+1].display_type == 'line_section')"
|
||||||
|
>
|
||||||
|
<tr class="is-subtotal text-right">
|
||||||
|
<td name="td_section_subtotal" colspan="99">
|
||||||
|
<strong class="mr16">Subtotal</strong>
|
||||||
|
<span
|
||||||
|
t-esc="current_subtotal"
|
||||||
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="clearfix" name="so_total_summary">
|
||||||
|
<div id="total" class="row" name="total">
|
||||||
|
<div
|
||||||
|
t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto"
|
||||||
|
>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tr class="border-black o_subtotal" style="">
|
||||||
|
<td name="td_amount_untaxed_label"><strong
|
||||||
|
>Subtotal</strong></td>
|
||||||
|
<td name="td_amount_untaxed" class="text-right">
|
||||||
|
<span t-field="doc.amount_untaxed" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<t
|
||||||
t-foreach="doc._get_tax_amount_by_group()"
|
t-foreach="doc._get_tax_amount_by_group()"
|
||||||
t-as="amount_by_group"
|
t-as="amount_by_group"
|
||||||
>
|
>
|
||||||
<tr style="border-bottom:1px solid #dddddd;">
|
<tr style="">
|
||||||
<t
|
<t
|
||||||
t-if="amount_by_group[3] == 1 and doc.amount_untaxed == amount_by_group[2]"
|
t-if="amount_by_group[3] == 1 and doc.amount_untaxed == amount_by_group[2]"
|
||||||
>
|
>
|
||||||
<td>
|
<td name="td_amount_by_group_label_3">
|
||||||
<span t-esc="amount_by_group[0]" />
|
<span t-esc="amount_by_group[0]" />
|
||||||
<span>&nbsp;<span>
|
<span>&nbsp;<span>on</span>&nbsp;<t
|
||||||
on
|
|
||||||
</span>&nbsp;<t
|
|
||||||
t-esc="amount_by_group[2]"
|
t-esc="amount_by_group[2]"
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
/></span>
|
/></span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td
|
||||||
<span
|
name="td_amount_by_group_3"
|
||||||
|
class="text-right o_price_total"
|
||||||
|
>
|
||||||
|
<span
|
||||||
t-esc="amount_by_group[1]"
|
t-esc="amount_by_group[1]"
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</t>
|
</t>
|
||||||
<t t-else="">
|
<t t-else="">
|
||||||
<td>
|
<td name="td_amount_by_group_label">
|
||||||
<span t-esc="amount_by_group[0]" />
|
<span t-esc="amount_by_group[0]" />
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td
|
||||||
<span
|
name="td_amount_by_group"
|
||||||
|
class="text-right o_price_total"
|
||||||
|
>
|
||||||
|
<span
|
||||||
t-esc="amount_by_group[1]"
|
t-esc="amount_by_group[1]"
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</t>
|
</t>
|
||||||
</tr>
|
|
||||||
</t>
|
|
||||||
<tr class="border-black">
|
|
||||||
<td>
|
|
||||||
<strong>Total</strong>
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<span
|
|
||||||
t-field="doc.amount_total"
|
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="border-black">
|
</t>
|
||||||
<td>
|
<tr class="border-black o_total">
|
||||||
<strong>Pending Payment</strong>
|
<td name="td_amount_total_label"><strong
|
||||||
</td>
|
>Total</strong></td>
|
||||||
<td class="text-right">
|
<td name="td_amount_total" class="text-right">
|
||||||
<span
|
<span t-field="doc.amount_total" />
|
||||||
t-field="doc.pending_amount"
|
</td>
|
||||||
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
|
</tr>
|
||||||
/>
|
</table>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<span t-if="doc.payment_ids">
|
|
||||||
<table style="width:80%;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Payment Ref.</th>
|
|
||||||
<th>Payment Date</th>
|
|
||||||
<th>Payment Method</th>
|
|
||||||
<th>Paid Amount</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr t-foreach="doc.payment_ids" t-as="l">
|
|
||||||
<td t-esc="l.name" />
|
|
||||||
<td t-esc="l.payment_date" />
|
|
||||||
<td t-esc="l.journal_id.name" />
|
|
||||||
<td t-esc="l.amount" />
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</span>
|
|
||||||
<!-- <span t-if="doc.return_ids">
|
|
||||||
<table style="width:80%;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Return Ref.</th>
|
|
||||||
<th>Return Date</th>
|
|
||||||
<th>Return Method</th>
|
|
||||||
<th>Return Amount</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr t-foreach="doc.return_ids" t-as="r">
|
|
||||||
<td t-esc="r.name" />
|
|
||||||
<td t-esc="r.date" />
|
|
||||||
<td t-esc="r.journal_id.name" />
|
|
||||||
<t
|
|
||||||
t-set="total_amount"
|
|
||||||
t-value="sum(l.amount for l in r.line_ids)"
|
|
||||||
/>
|
|
||||||
<td t-esc="total_amount" />
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</span> -->
|
|
||||||
</div>
|
|
||||||
<p t-field="doc.note" />
|
|
||||||
<p t-if="doc.payment_term_id.note">
|
|
||||||
<span t-field="doc.payment_term_id.note" />
|
|
||||||
</p>
|
|
||||||
<div class="oe_structure" />
|
|
||||||
</div>
|
</div>
|
||||||
</t>
|
|
||||||
</template>
|
<div class="oe_structure" />
|
||||||
<template id="report_folio">
|
|
||||||
|
<p t-field="doc.note" />
|
||||||
|
<p t-if="doc.payment_term_id.note">
|
||||||
|
<span t-field="doc.payment_term_id.note" />
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
id="fiscal_position_remark"
|
||||||
|
t-if="doc.fiscal_position_id and doc.fiscal_position_id.sudo().note"
|
||||||
|
>
|
||||||
|
<strong>Fiscal Position Remark:</strong>
|
||||||
|
<span t-field="doc.fiscal_position_id.sudo().note" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<template id="report_folio">
|
||||||
<t t-call="web.html_container">
|
<t t-call="web.html_container">
|
||||||
<t t-foreach="docs" t-as="doc">
|
<t t-foreach="docs" t-as="doc">
|
||||||
<t t-call="pms.report_folio_document" t-lang="doc.partner_id.lang" />
|
<t t-call="pms.report_folio_document" t-lang="doc.partner_id.lang" />
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
user_access_pms_floor,user_access_pms_floor,model_pms_floor,pms.group_pms_user,1,0,0,0
|
user_access_pms_ubication,user_access_pms_ubication,model_pms_ubication,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_amenity,user_access_pms_amenity,model_pms_amenity,pms.group_pms_user,1,0,0,0
|
user_access_pms_amenity,user_access_pms_amenity,model_pms_amenity,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_amenity_type,user_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_user,1,0,0,0
|
user_access_pms_amenity_type,user_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_service,user_access_pms_service,model_pms_service,pms.group_pms_user,1,1,1,1
|
user_access_pms_service,user_access_pms_service,model_pms_service,pms.group_pms_user,1,1,1,1
|
||||||
user_access_pms_room_type_restriction,user_access_pms_room_type_restriction,model_pms_room_type_restriction,pms.group_pms_user,1,0,0,0
|
|
||||||
user_access_pms_reservation_line,user_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_user,1,1,1,1
|
user_access_pms_reservation_line,user_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_user,1,1,1,1
|
||||||
user_access_room_closure_reason,user_access_room_closure_reason,model_room_closure_reason,pms.group_pms_user,1,0,0,0
|
user_access_room_closure_reason,user_access_room_closure_reason,model_room_closure_reason,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_service_line,user_access_pms_service_line,model_pms_service_line,pms.group_pms_user,1,1,1,1
|
user_access_pms_service_line,user_access_pms_service_line,model_pms_service_line,pms.group_pms_user,1,1,1,1
|
||||||
user_access_pms_board_service,user_access_pms_board_service,model_pms_board_service,pms.group_pms_user,1,0,0,0
|
user_access_pms_board_service,user_access_pms_board_service,model_pms_board_service,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_checkin_partner,user_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_user,1,1,1,0
|
user_access_pms_checkin_partner,user_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_user,1,1,1,1
|
||||||
user_access_pms_room_type_class,user_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_user,1,0,0,0
|
user_access_pms_room_type_class,user_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_room,user_access_pms_room,model_pms_room,pms.group_pms_user,1,0,0,0
|
user_access_pms_room,user_access_pms_room,model_pms_room,pms.group_pms_user,1,0,0,0
|
||||||
user_access_shared_pms_room,user_access_pms_shared_room,model_pms_shared_room,pms.group_pms_user,1,0,0,0
|
user_access_shared_pms_room,user_access_pms_shared_room,model_pms_shared_room,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_room_type_restriction_item,user_access_pms_room_type_restriction_item,model_pms_room_type_restriction_item,pms.group_pms_user,1,0,0,0
|
user_access_pms_availability_plan_rule,user_access_pms_availability_plan_rule,model_pms_availability_plan_rule,pms.group_pms_user,1,0,0,0
|
||||||
|
user_access_pms_availability,user_access_pms_availability,model_pms_availability,pms.group_pms_user,1,1,1,0
|
||||||
user_access_pms_reservation,user_access_pms_reservation,model_pms_reservation,pms.group_pms_user,1,1,1,1
|
user_access_pms_reservation,user_access_pms_reservation,model_pms_reservation,pms.group_pms_user,1,1,1,1
|
||||||
user_access_pms_folio,user_access_pms_folio,model_pms_folio,pms.group_pms_user,1,1,1,1
|
user_access_pms_folio,user_access_pms_folio,model_pms_folio,pms.group_pms_user,1,1,1,1
|
||||||
user_access_pms_room_type,user_access_pms_room_type,model_pms_room_type,pms.group_pms_user,1,0,0,0
|
user_access_pms_room_type,user_access_pms_room_type,model_pms_room_type,pms.group_pms_user,1,0,0,0
|
||||||
@@ -23,23 +23,23 @@ user_access_account_partial_reconcile,user_access_account_partial_reconcile,acco
|
|||||||
user_access_pms_cancelation_rule,user_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_user,1,0,0,0
|
user_access_pms_cancelation_rule,user_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_user,1,0,0,0
|
||||||
user_access_account_full_reconcile,user_access_account_full_reconcile,account.model_account_full_reconcile,pms.group_pms_user,1,1,1,1
|
user_access_account_full_reconcile,user_access_account_full_reconcile,account.model_account_full_reconcile,pms.group_pms_user,1,1,1,1
|
||||||
user_access_property,user_access_property,model_pms_property,pms.group_pms_user,1,0,0,0
|
user_access_property,user_access_property,model_pms_property,pms.group_pms_user,1,0,0,0
|
||||||
user_access_availability,user_access_availability,model_pms_room_type_availability,pms.group_pms_user,1,0,0,0
|
user_access_availability,user_access_availability,model_pms_availability_plan,pms.group_pms_user,1,0,0,0
|
||||||
user_access_pms_sale_channel,user_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_user,1,0,0,0
|
user_access_pms_sale_channel,user_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_user,1,0,0,0
|
||||||
manager_access_pms_floor,manager_access_pms_floor,model_pms_floor,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_ubication,manager_access_pms_ubication,model_pms_ubication,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_amenity,manager_access_pms_amenity,model_pms_amenity,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_amenity,manager_access_pms_amenity,model_pms_amenity,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_amenity_type,manager_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_amenity_type,manager_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_service,manager_access_pms_service,model_pms_service,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_service,manager_access_pms_service,model_pms_service,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_room_type_restriction,manager_access_pms_room_type_restriction,model_pms_room_type_restriction,pms.group_pms_manager,1,1,1,1
|
|
||||||
manager_access_pms_reservation_line,manager_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_reservation_line,manager_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_room_closure_reason,manager_access_room_closure_reason,model_room_closure_reason,pms.group_pms_manager,1,1,1,1
|
manager_access_room_closure_reason,manager_access_room_closure_reason,model_room_closure_reason,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_service_line,manager_access_pms_service_line,model_pms_service_line,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_service_line,manager_access_pms_service_line,model_pms_service_line,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_board_service,manager_access_pms_board_service,model_pms_board_service,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_board_service,manager_access_pms_board_service,model_pms_board_service,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_checkin_partner,manager_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_manager,1,1,1,0
|
manager_access_pms_checkin_partner,manager_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_room_type_class,manager_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_room_type_class,manager_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_room,manager_access_pms_room,model_pms_room,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_room,manager_access_pms_room,model_pms_room,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_shared_room,manager_access_pms_shared_room,model_pms_shared_room,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_shared_room,manager_access_pms_shared_room,model_pms_shared_room,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_room_type_restriction_item,manager_access_pms_room_type_restriction_item,model_pms_room_type_restriction_item,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_availability_plan_rule,manager_access_pms_availability_plan_rule,model_pms_availability_plan_rule,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_reservation,manager_access_pms_reservation,model_pms_reservation,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_reservation,manager_access_pms_reservation,model_pms_reservation,pms.group_pms_manager,1,1,1,1
|
||||||
|
manager_access_pms_availability,manager_access_pms_availability,model_pms_availability,pms.group_pms_manager,1,1,1,0
|
||||||
manager_access_pms_folio,manager_access_pms_folio,model_pms_folio,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_folio,manager_access_pms_folio,model_pms_folio,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_room_type,manager_access_pms_room_type,model_pms_room_type,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_room_type,manager_access_pms_room_type,model_pms_room_type,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_board_service_room_type,manager_access_pms_board_service_room_type,model_pms_board_service_room_type,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_board_service_room_type,manager_access_pms_board_service_room_type,model_pms_board_service_room_type,pms.group_pms_manager,1,1,1,1
|
||||||
@@ -47,6 +47,18 @@ manager_access_pms_board_service_room_type_line,manager_access_pms_board_service
|
|||||||
manager_access_pms_board_service_line,manager_access_pms_board_service_line,model_pms_board_service_line,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_board_service_line,manager_access_pms_board_service_line,model_pms_board_service_line,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_property,manager_access_property,model_pms_property,pms.group_pms_manager,1,1,1,1
|
manager_access_property,manager_access_property,model_pms_property,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_availability,manager_access_availability,model_pms_room_type_availability,pms.group_pms_manager,1,1,1,1
|
manager_access_availability,manager_access_availability,model_pms_availability_plan,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_sale_channel,manager_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_sale_channel,manager_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_manager,1,1,1,1
|
||||||
user_access_pms_reservation_wizard,user_access_pms_reservation_wizard,model_pms_reservation_wizard,pms.group_pms_user,1,1,1,1
|
user_access_pms_reservation_split_join_swap_wizard,user_access_pms_reservation_split_join_swap_wizard,model_pms_reservation_split_join_swap_wizard,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_wizard_reservation_lines_split,user_access_pms_wizard_reservation_lines_split,model_pms_wizard_reservation_lines_split,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_massive_changes_wizard,user_access_pms_massive_changes_wizard,model_pms_massive_changes_wizard,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_advanced_filters_wizard,user_access_pms_advanced_filters_wizard,model_pms_advanced_filters_wizard,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_folio_wizard,user_access_pms_folio_wizard,model_pms_folio_wizard,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_folio_availability_wizard,user_access_pms_folio_availability_wizard,model_pms_folio_availability_wizard,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_num_rooms_selection,user_access_pms_num_rooms_selection,model_pms_num_rooms_selection,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_folio_sale_line,user_access_pms_folio_sale_line,model_folio_sale_line,pms.group_pms_user,1,0,0,0
|
||||||
|
user_access_folio_make_invoice_advance,user_access_folio_make_invoice_advance,model_folio_advance_payment_inv,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_wizard_payment_folio,user_access_wizard_payment_folio,model_wizard_payment_folio,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_wizard_folio_changes,user_access_wizard_folio_changes,model_wizard_folio_changes,pms.group_pms_user,1,1,1,1
|
||||||
|
user_access_pms_folio_portal,user_access_pms_folio_portal,model_pms_folio,base.group_portal,1,0,0,0
|
||||||
|
user_access_pms_reservation_portal,user_access_pms_reservation_portal,model_pms_reservation,base.group_portal,1,0,0,0
|
||||||
|
|||||||
|
@@ -35,7 +35,7 @@
|
|||||||
</record>
|
</record>
|
||||||
<!-- Property Rules -->
|
<!-- Property Rules -->
|
||||||
<record id="pms_folio_property_rule" model="ir.rule">
|
<record id="pms_folio_property_rule" model="ir.rule">
|
||||||
<field name="name">PMS Folio Company Rule</field>
|
<field name="name">PMS Folio Property Rule</field>
|
||||||
<field name="model_id" ref="model_pms_folio" />
|
<field name="model_id" ref="model_pms_folio" />
|
||||||
<field name="global" eval="True" />
|
<field name="global" eval="True" />
|
||||||
<field name="domain_force">
|
<field name="domain_force">
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="pms_reservation_property_rule" model="ir.rule">
|
<record id="pms_reservation_property_rule" model="ir.rule">
|
||||||
<field name="name">PMS Reservation Company Rule</field>
|
<field name="name">PMS Reservation Property Rule</field>
|
||||||
<field name="model_id" ref="model_pms_reservation" />
|
<field name="model_id" ref="model_pms_reservation" />
|
||||||
<field name="global" eval="True" />
|
<field name="global" eval="True" />
|
||||||
<field name="domain_force">
|
<field name="domain_force">
|
||||||
@@ -52,5 +52,190 @@
|
|||||||
user.get_active_property_ids())]
|
user.get_active_property_ids())]
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record id="pms_amenity_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Amenity Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_amenity" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_amenity_type_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Amenity Type Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_amenity_type" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_board_service_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Board Service Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_board_service" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_board_service_line_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Board Service Line Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_board_service_line" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_board_service_room_type_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Board Service Room Type Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_board_service_room_type" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_cancelation_rule_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Cancelation Rule Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_cancelation_rule" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_ubication_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Ubication Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_ubication" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_reservation_line_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Reservation Line Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_reservation_line" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_id','=',False),('pms_property_id', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_clousure_reason_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Clousure Reason Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_room_closure_reason" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_room_type_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Room Type Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_room_type" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_availability_plan_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Room Type Availability Plan Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_availability_plan" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_availability_plan_rule_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Room Type Availability Rule Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_availability_plan_rule" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_id','=',False),('pms_property_id', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_service_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Service Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_service" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_id','=',False),('pms_property_id', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_service_line_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Service Line Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_service_line" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_id','=',False),('pms_property_id', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_product_pricelist_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Product Pricelist Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_product_pricelist" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_product_pricelist_item_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Room Type Class Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_pms_availability_plan" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_res_users_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Users Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_res_users" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_account_move_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Account Move Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_account_move" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_id','=',False),('pms_property_id', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_account_journal_property_rule" model="ir.rule">
|
||||||
|
<field name="name">PMS Account Journal Property Rule</field>
|
||||||
|
<field name="model_id" ref="model_account_journal" />
|
||||||
|
<field name="global" eval="True" />
|
||||||
|
<field name="domain_force">
|
||||||
|
['|',('pms_property_ids','=',False),('pms_property_ids', 'in',
|
||||||
|
user.get_active_property_ids())]
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="pms_folio_rule_portal" model="ir.rule">
|
||||||
|
<field name="name">Portal Personal Folios</field>
|
||||||
|
<field name="model_id" ref="model_pms_folio" />
|
||||||
|
<field name="domain_force">[]</field>
|
||||||
|
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
|
||||||
|
<field name="perm_read" eval="True" />
|
||||||
|
</record>
|
||||||
|
<record id="pms_reservation_rule_portal" model="ir.rule">
|
||||||
|
<field name="name">Portal Personal Reservation</field>
|
||||||
|
<field name="model_id" ref="model_pms_reservation" />
|
||||||
|
<field name="domain_force">[]</field>
|
||||||
|
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
|
||||||
|
<field name="perm_read" eval="True" />
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 24 KiB |
@@ -3,7 +3,7 @@
|
|||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
|
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
|
||||||
<title>PMS (Property Management System)</title>
|
<title>PMS (Property Management System)</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
||||||
@@ -368,10 +368,10 @@ ul.auto-toc {
|
|||||||
!! changes will be overwritten. !!
|
!! changes will be overwritten. !!
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/pms/tree/14.0/pms"><img alt="OCA/pms" src="https://img.shields.io/badge/github-OCA%2Fpms-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pms-14-0/pms-14-0-pms"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/293/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/pms/tree/14.0/pms"><img alt="OCA/pms" src="https://img.shields.io/badge/github-OCA%2Fpms-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/pms-14-0/pms-14-0-pms"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/293/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||||
<p>This module is an all-in-one property management system (PMS) focused on medium-sized hotels
|
<p>This module is an all-in-one property management system (PMS) focused on medium-sized properties
|
||||||
for managing every aspect of your property’s daily operations.</p>
|
for managing every aspect of your property’s daily operations.</p>
|
||||||
<p>You can manage hotel properties with multi-hotel and multi-company support, including your rooms inventory,
|
<p>You can manage properties with multi-property and multi-company support, including your rooms inventory,
|
||||||
reservations, check-in, daily reports, board services, rate and restriction plans among other hotel functionalities.</p>
|
reservations, check-in, daily reports, board services, rate and availability plans among other property functionalities.</p>
|
||||||
<div class="admonition important">
|
<div class="admonition important">
|
||||||
<p class="first admonition-title">Important</p>
|
<p class="first admonition-title">Important</p>
|
||||||
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
|
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
|
||||||
@@ -395,17 +395,17 @@ Only for development or testing purpose, do not use in production.
|
|||||||
</div>
|
</div>
|
||||||
<div class="section" id="installation">
|
<div class="section" id="installation">
|
||||||
<h1><a class="toc-backref" href="#id1">Installation</a></h1>
|
<h1><a class="toc-backref" href="#id1">Installation</a></h1>
|
||||||
<p>This module depends on modules <tt class="docutils literal">base</tt>, <tt class="docutils literal">sale_stock</tt>, <tt class="docutils literal">account_payment_return</tt>, <tt class="docutils literal">partner_firstname</tt>,
|
<p>This module depends on modules <tt class="docutils literal">base</tt>, <tt class="docutils literal">mail</tt>, <tt class="docutils literal">sale</tt> and <tt class="docutils literal">multi_pms_properties</tt>.
|
||||||
and <tt class="docutils literal">account_cancel</tt>. Ensure yourself to have all them in your addons list.</p>
|
Ensure yourself to have all them in your addons list.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="configuration">
|
<div class="section" id="configuration">
|
||||||
<h1><a class="toc-backref" href="#id2">Configuration</a></h1>
|
<h1><a class="toc-backref" href="#id2">Configuration</a></h1>
|
||||||
<p>You will find the hotel settings in Settings > Users & Companies > Hotels > Your Hotel.</p>
|
<p>You will find the hotel settings in PMS Management > Configuration > Properties > Your Property.</p>
|
||||||
<p>This module required additional configuration for company, accounting, invoicing and user privileges.</p>
|
<p>This module required additional configuration for company, accounting, invoicing and user privileges.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="usage">
|
<div class="section" id="usage">
|
||||||
<h1><a class="toc-backref" href="#id3">Usage</a></h1>
|
<h1><a class="toc-backref" href="#id3">Usage</a></h1>
|
||||||
<p>To use this module, please, read the complete user guide at <a class="reference external" href="https://roomdoo.com">https://roomdoo.com</a>.</p>
|
<p>To use this module, please, read the complete user guide at <a class="reference external" href="roomdoo.com">roomdoo.com</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="bug-tracker">
|
<div class="section" id="bug-tracker">
|
||||||
<h1><a class="toc-backref" href="#id4">Bug Tracker</a></h1>
|
<h1><a class="toc-backref" href="#id4">Bug Tracker</a></h1>
|
||||||
@@ -420,19 +420,23 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
|
|||||||
<div class="section" id="authors">
|
<div class="section" id="authors">
|
||||||
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Dario Lodeiros</li>
|
<li>Commit [Sun]</li>
|
||||||
<li>Alexadre Diaz</li>
|
|
||||||
<li>Pablo Quesada</li>
|
|
||||||
<li>Jose Luis Algara</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="contributors">
|
<div class="section" id="contributors">
|
||||||
<h2><a class="toc-backref" href="#id7">Contributors</a></h2>
|
<h2><a class="toc-backref" href="#id7">Contributors</a></h2>
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
<li>Dario Lodeiros <<a class="reference external" href="mailto:dario@commitsun.com">dario@commitsun.com</a>></li>
|
|
||||||
<li>Alexandre Díaz</li>
|
<li>Alexandre Díaz</li>
|
||||||
<li>Pablo Quesada</li>
|
<li>Pablo Quesada</li>
|
||||||
<li>Jose Luis Algara</li>
|
<li>Jose Luis Algara</li>
|
||||||
|
<li><cite>Commit [Sun] <https://www.commitsun.com></cite>:<ul>
|
||||||
|
<li>Dario Lodeiros</li>
|
||||||
|
<li>Eric Antones</li>
|
||||||
|
<li>Sara Lago</li>
|
||||||
|
<li>Brais Abeijon</li>
|
||||||
|
<li>Miguel Padin</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="maintainers">
|
<div class="section" id="maintainers">
|
||||||
|
|||||||
@@ -11,15 +11,14 @@ odoo.define("pms.AbstractWebClient", function (require) {
|
|||||||
var current_pms_property_id =
|
var current_pms_property_id =
|
||||||
session.user_pms_properties.current_pms_property[0];
|
session.user_pms_properties.current_pms_property[0];
|
||||||
if (!state.pms_pids) {
|
if (!state.pms_pids) {
|
||||||
state.pms_pids =
|
state.pms_pids = utils.get_cookie("pms_pids")
|
||||||
utils.get_cookie("pms_pids") !== null
|
? utils.get_cookie("pms_pids")
|
||||||
? utils.get_cookie("pms_pids")
|
: String(current_pms_property_id);
|
||||||
: String(current_pms_property_id);
|
|
||||||
}
|
}
|
||||||
var statePmsPropertyIDS = _.map(state.pms_pids.split(","), function (
|
var statePmsPropertyIDS = _.map(state.pms_pids.split(","), function (
|
||||||
pms_pid
|
pms_pid
|
||||||
) {
|
) {
|
||||||
return parseInt(pms_pid);
|
return parseInt(pms_pid, 10);
|
||||||
});
|
});
|
||||||
var userPmsPropertyIDS = _.map(
|
var userPmsPropertyIDS = _.map(
|
||||||
session.user_pms_properties.allowed_pms_properties,
|
session.user_pms_properties.allowed_pms_properties,
|
||||||
|
|||||||
41
pms/static/src/js/pms_list_controller.js
Normal file
41
pms/static/src/js/pms_list_controller.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
odoo.define("booking.engine.tree", function (require) {
|
||||||
|
"use strict";
|
||||||
|
var ListController = require("web.ListController");
|
||||||
|
var ListView = require("web.ListView");
|
||||||
|
var viewRegistry = require("web.view_registry");
|
||||||
|
|
||||||
|
function renderBookingEngineButton() {
|
||||||
|
if (this.$buttons) {
|
||||||
|
var self = this;
|
||||||
|
this.$buttons.on("click", ".o_button_booking_engine", function () {
|
||||||
|
self.do_action({
|
||||||
|
name: "Booking Engine",
|
||||||
|
type: "ir.actions.act_window",
|
||||||
|
res_model: "pms.folio.wizard",
|
||||||
|
target: "new",
|
||||||
|
views: [[false, "form"]],
|
||||||
|
context: {is_modal: true},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var BookingEngineRequestListController = ListController.extend({
|
||||||
|
start: function () {
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
buttons_template: "BookingEngineRequestListView.buttons",
|
||||||
|
renderButtons: function () {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
renderBookingEngineButton.apply(this, arguments);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var BookingEngineRequestListView = ListView.extend({
|
||||||
|
config: _.extend({}, ListView.prototype.config, {
|
||||||
|
Controller: BookingEngineRequestListController,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
viewRegistry.add("pms_booking_engine_request_tree", BookingEngineRequestListView);
|
||||||
|
});
|
||||||
@@ -3,9 +3,8 @@ odoo.define("pms.session", function (require) {
|
|||||||
|
|
||||||
var Session = require("web.Session");
|
var Session = require("web.Session");
|
||||||
var utils = require("web.utils");
|
var utils = require("web.utils");
|
||||||
var modules = odoo._modules;
|
|
||||||
|
|
||||||
var inherited_Session = Session.extend({
|
Session.include({
|
||||||
// TODO: require test and debug
|
// TODO: require test and debug
|
||||||
setPmsProperties: function (pms_main_property_id, pms_property_ids) {
|
setPmsProperties: function (pms_main_property_id, pms_property_ids) {
|
||||||
var hash = $.bbq.getState();
|
var hash = $.bbq.getState();
|
||||||
@@ -24,12 +23,4 @@ odoo.define("pms.session", function (require) {
|
|||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var pms_session = new inherited_Session(undefined, undefined, {
|
|
||||||
modules: modules,
|
|
||||||
use_cors: false,
|
|
||||||
});
|
|
||||||
pms_session.is_bound = pms_session.session_bind();
|
|
||||||
|
|
||||||
return pms_session;
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
odoo.define("pms.ListController", function (require) {
|
|
||||||
"use strict";
|
|
||||||
/*
|
|
||||||
* Pms
|
|
||||||
* GNU Public License
|
|
||||||
* Alexandre Díaz <dev@redneboa.es>
|
|
||||||
*/
|
|
||||||
|
|
||||||
var ListController = require("web.ListController");
|
|
||||||
var Core = require("web.core");
|
|
||||||
|
|
||||||
var _t = Core._t;
|
|
||||||
|
|
||||||
ListController.include({
|
|
||||||
renderButtons: function () {
|
|
||||||
this._super.apply(this, arguments); // Sets this.$buttons
|
|
||||||
var self = this;
|
|
||||||
if (this.modelName === "pms.reservation") {
|
|
||||||
this.$buttons.append(
|
|
||||||
"<button class='btn btn-sm oe_open_reservation_wizard oe_highlight' type='button'>" +
|
|
||||||
_t("Open Wizard") +
|
|
||||||
"</button>"
|
|
||||||
);
|
|
||||||
this.$buttons
|
|
||||||
.find(".oe_open_reservation_wizard")
|
|
||||||
.on("click", function () {
|
|
||||||
self.do_action("pms.open_wizard_reservations");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -8,13 +8,10 @@ odoo.define("web.SwitchPmsMenu", function (require) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var config = require("web.config");
|
var config = require("web.config");
|
||||||
// Var core = require("web.core");
|
var session = require("web.session");
|
||||||
var session = require("pms.session");
|
|
||||||
var SystrayMenu = require("web.SystrayMenu");
|
var SystrayMenu = require("web.SystrayMenu");
|
||||||
var Widget = require("web.Widget");
|
var Widget = require("web.Widget");
|
||||||
|
|
||||||
// Var _t = core._t;
|
|
||||||
|
|
||||||
var SwitchPmsMenu = Widget.extend({
|
var SwitchPmsMenu = Widget.extend({
|
||||||
template: "SwitchPmsMenu",
|
template: "SwitchPmsMenu",
|
||||||
events: {
|
events: {
|
||||||
@@ -50,7 +47,7 @@ odoo.define("web.SwitchPmsMenu", function (require) {
|
|||||||
)
|
)
|
||||||
.split(",")
|
.split(",")
|
||||||
.map(function (id) {
|
.map(function (id) {
|
||||||
return parseInt(id);
|
return parseInt(id, 10);
|
||||||
});
|
});
|
||||||
this.user_pms_properties =
|
this.user_pms_properties =
|
||||||
session.user_pms_properties.allowed_pms_properties;
|
session.user_pms_properties.allowed_pms_properties;
|
||||||
@@ -74,9 +71,9 @@ odoo.define("web.SwitchPmsMenu", function (require) {
|
|||||||
*/
|
*/
|
||||||
_onSwitchPmsPropertyClick: function (ev) {
|
_onSwitchPmsPropertyClick: function (ev) {
|
||||||
if (
|
if (
|
||||||
ev.type == "keydown" &&
|
ev.type === "keydown" &&
|
||||||
ev.which != $.ui.keyCode.ENTER &&
|
ev.which !== $.ui.keyCode.ENTER &&
|
||||||
ev.which != $.ui.keyCode.SPACE
|
ev.which !== $.ui.keyCode.SPACE
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -124,9 +121,9 @@ odoo.define("web.SwitchPmsMenu", function (require) {
|
|||||||
*/
|
*/
|
||||||
_onTogglePmsPropertyClick: function (ev) {
|
_onTogglePmsPropertyClick: function (ev) {
|
||||||
if (
|
if (
|
||||||
ev.type == "keydown" &&
|
ev.type === "keydown" &&
|
||||||
ev.which != $.ui.keyCode.ENTER &&
|
ev.which !== $.ui.keyCode.ENTER &&
|
||||||
ev.which != $.ui.keyCode.SPACE
|
ev.which !== $.ui.keyCode.SPACE
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
10
pms/static/src/xml/reservation_group_button_views.xml
Normal file
10
pms/static/src/xml/reservation_group_button_views.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates>
|
||||||
|
<t t-extend="ListView.buttons" t-name="BookingEngineRequestListView.buttons">
|
||||||
|
<t t-jquery="button.o_list_button_add" t-operation="after">
|
||||||
|
<button type="button" class="btn btn-primary o_button_booking_engine">
|
||||||
|
Booking Engine
|
||||||
|
</button>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
@@ -25,3 +25,18 @@ from . import test_pms_pricelist_priority
|
|||||||
from . import test_pms_checkin_partner
|
from . import test_pms_checkin_partner
|
||||||
from . import test_pms_sale_channel
|
from . import test_pms_sale_channel
|
||||||
from . import test_pms_folio
|
from . import test_pms_folio
|
||||||
|
from . import test_pms_availability_plan_rules
|
||||||
|
from . import test_pms_room_type
|
||||||
|
from . import test_pms_room_type_class
|
||||||
|
from . import test_pms_board_service
|
||||||
|
from . import test_pms_wizard_massive_changes
|
||||||
|
from . import test_pms_wizard_folio
|
||||||
|
from . import test_pms_res_users
|
||||||
|
from . import test_pms_amenity
|
||||||
|
from . import test_pms_room
|
||||||
|
from . import test_pms_board_service_line
|
||||||
|
from . import test_pms_board_service_room_type
|
||||||
|
from . import test_pms_board_service_room_type_line
|
||||||
|
from . import test_pms_folio_invoice
|
||||||
|
from . import test_pms_folio_sale_line
|
||||||
|
from . import test_pms_wizard_split_join_swap_reservation
|
||||||
|
|||||||
@@ -1,63 +1,29 @@
|
|||||||
##############################################################################
|
|
||||||
#
|
|
||||||
# OpenERP, Open Source Management Solution
|
|
||||||
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
|
|
||||||
# Alexandre Díaz <dev@redneboa.es>
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
#
|
|
||||||
##############################################################################
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from odoo.tests import common
|
from odoo.tests import common
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
class TestPms(common.SavepointCase):
|
||||||
class TestHotel(common.SavepointCase):
|
def setUp(self):
|
||||||
@classmethod
|
super().setUp()
|
||||||
def _init_mock_hotel(cls):
|
self.pricelist1 = self.env["product.pricelist"].create(
|
||||||
return True
|
{
|
||||||
|
"name": "Pricelist 1",
|
||||||
@classmethod
|
}
|
||||||
def setUpClass(cls):
|
)
|
||||||
super(TestHotel, cls).setUpClass()
|
self.company1 = self.env["res.company"].create(
|
||||||
|
{
|
||||||
cls._init_mock_hotel()
|
"name": "Company 1",
|
||||||
|
}
|
||||||
# Create Tests Records
|
)
|
||||||
cls.main_hotel_property = cls.env.ref("pms.main_pms_property")
|
self.pms_property1 = self.env["pms.property"].create(
|
||||||
cls.demo_hotel_property = cls.env.ref("pms.demo_pms_property")
|
{
|
||||||
|
"name": "Property 1",
|
||||||
cls.room_type_0 = cls.env.ref("pms.pms_room_type_0")
|
"company_id": self.company1.id,
|
||||||
cls.room_type_1 = cls.env.ref("pms.pms_room_type_1")
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
cls.room_type_2 = cls.env.ref("pms.pms_room_type_2")
|
}
|
||||||
cls.room_type_3 = cls.env.ref("pms.pms_room_type_3")
|
)
|
||||||
|
self.room_type_class1 = self.env["pms.room.type.class"].create(
|
||||||
cls.demo_room_type_0 = cls.env.ref("pms.demo_pms_room_type_0")
|
{
|
||||||
cls.demo_room_type_1 = cls.env.ref("pms.demo_pms_room_type_1")
|
"name": "Room Type Class 1",
|
||||||
|
"default_code": "RTC1",
|
||||||
cls.room_0 = cls.env.ref("pms.pms_room_0")
|
}
|
||||||
cls.room_1 = cls.env.ref("pms.pms_room_1")
|
|
||||||
cls.room_2 = cls.env.ref("pms.pms_room_2")
|
|
||||||
cls.room_3 = cls.env.ref("pms.pms_room_3")
|
|
||||||
cls.room_4 = cls.env.ref("pms.pms_room_4")
|
|
||||||
cls.room_5 = cls.env.ref("pms.pms_room_5")
|
|
||||||
cls.room_6 = cls.env.ref("pms.pms_room_6")
|
|
||||||
|
|
||||||
cls.list0 = cls.env.ref("product.list0")
|
|
||||||
cls.list1 = cls.env["product.pricelist"].create(
|
|
||||||
{"name": "Test Pricelist", "pricelist_type": ""}
|
|
||||||
)
|
)
|
||||||
|
|||||||
71
pms/tests/test_pms_amenity.py
Normal file
71
pms/tests/test_pms_amenity.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
from .common import TestPms
|
||||||
|
|
||||||
|
|
||||||
|
class TestPmsAmenity(TestPms):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
# Create two properties
|
||||||
|
# +-----------+-----------+
|
||||||
|
# | Properties |
|
||||||
|
# +-----------+-----------+
|
||||||
|
# | Property2 - Property3 |
|
||||||
|
# +-----------+-----------+
|
||||||
|
|
||||||
|
self.pms_property2 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_property_test2",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.pms_property3 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_property_test3",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_property_not_allowed(self):
|
||||||
|
# Creation of a Amenity with Properties incompatible with it Amenity Type
|
||||||
|
|
||||||
|
# +-----------------------------------+-----------------------------------+
|
||||||
|
# | 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),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# ACT & ASSERT
|
||||||
|
with self.assertRaises(UserError), self.cr.savepoint():
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
814
pms/tests/test_pms_availability_plan_rules.py
Normal file
814
pms/tests/test_pms_availability_plan_rules.py
Normal file
@@ -0,0 +1,814 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from odoo import fields
|
||||||
|
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):
|
||||||
|
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(
|
||||||
|
{
|
||||||
|
"name": "Availability plan for TEST",
|
||||||
|
"pms_pricelist_ids": [(6, 0, [self.test_pricelist1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# SEQUENCES
|
||||||
|
self.folio_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Folio",
|
||||||
|
"code": "pms.folio",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.env.ref("base.main_company").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.reservation_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Reservation",
|
||||||
|
"code": "pms.reservation",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.env.ref("base.main_company").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.checkin_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Checkin",
|
||||||
|
"code": "pms.checkin.partner",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.env.ref("base.main_company").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.property
|
||||||
|
self.test_property = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "MY PMS TEST",
|
||||||
|
"company_id": self.env.ref("base.main_company").id,
|
||||||
|
"default_pricelist_id": self.test_pricelist1.id,
|
||||||
|
"folio_sequence_id": self.folio_sequence.id,
|
||||||
|
"reservation_sequence_id": self.reservation_sequence.id,
|
||||||
|
"checkin_sequence_id": self.checkin_sequence.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room.type.class
|
||||||
|
self.test_room_type_class = self.env["pms.room.type.class"].create(
|
||||||
|
{"name": "Room", "default_code": "ROOM"}
|
||||||
|
)
|
||||||
|
|
||||||
|
# pms.room.type
|
||||||
|
self.test_room_type_single = self.env["pms.room.type"].create(
|
||||||
|
{
|
||||||
|
"pms_property_ids": [self.test_property.id],
|
||||||
|
"name": "Single Test",
|
||||||
|
"default_code": "SNG_Test",
|
||||||
|
"class_id": self.test_room_type_class.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room.type
|
||||||
|
self.test_room_type_double = self.env["pms.room.type"].create(
|
||||||
|
{
|
||||||
|
"pms_property_ids": [
|
||||||
|
(4, self.test_property.id),
|
||||||
|
],
|
||||||
|
"name": "Double Test",
|
||||||
|
"default_code": "DBL_Test",
|
||||||
|
"class_id": self.test_room_type_class.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room
|
||||||
|
self.test_room1_double = self.env["pms.room"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"name": "Double 201 test",
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"capacity": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room
|
||||||
|
self.test_room2_double = self.env["pms.room"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"name": "Double 202 test",
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"capacity": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room
|
||||||
|
# self.test_room3_double = self.env["pms.room"].create(
|
||||||
|
# {
|
||||||
|
# "pms_property_id": self.test_property.id,
|
||||||
|
# "name": "Double 203 test",
|
||||||
|
# "room_type_id": self.test_room_type_double.id,
|
||||||
|
# "capacity": 2,
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
# # pms.room
|
||||||
|
# self.test_room4_double = self.env["pms.room"].create(
|
||||||
|
# {
|
||||||
|
# "pms_property_id": self.test_property.id,
|
||||||
|
# "name": "Double 204 test",
|
||||||
|
# "room_type_id": self.test_room_type_double.id,
|
||||||
|
# "capacity": 2,
|
||||||
|
# }
|
||||||
|
# )
|
||||||
|
# pms.room
|
||||||
|
self.test_room1_single = self.env["pms.room"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"name": "Single 101 test",
|
||||||
|
"room_type_id": self.test_room_type_single.id,
|
||||||
|
"capacity": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room
|
||||||
|
self.test_room2_single = self.env["pms.room"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"name": "Single 102 test",
|
||||||
|
"room_type_id": self.test_room_type_single.id,
|
||||||
|
"capacity": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# partner
|
||||||
|
self.partner1 = self.env["res.partner"].create({"name": "Charles"})
|
||||||
|
|
||||||
|
def create_scenario_multiproperty(self):
|
||||||
|
self.create_common_scenario()
|
||||||
|
self.test_property3 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Property 3",
|
||||||
|
"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.availability_multiproperty = 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),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_availability_rooms_all(self):
|
||||||
|
# TEST CASE
|
||||||
|
# get availability withouth rules
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
|
||||||
|
checkin = fields.date.today()
|
||||||
|
checkout = (fields.datetime.today() + datetime.timedelta(days=4)).date()
|
||||||
|
test_rooms_double_rooms = self.env["pms.room"].search(
|
||||||
|
[("pms_property_id", "=", self.test_property.id)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
result = self.env["pms.availability.plan"].rooms_available(
|
||||||
|
checkin=checkin,
|
||||||
|
checkout=checkout,
|
||||||
|
)
|
||||||
|
# ASSERT
|
||||||
|
obtained = all(elem.id in result.ids for elem in test_rooms_double_rooms)
|
||||||
|
self.assertTrue(
|
||||||
|
obtained,
|
||||||
|
"Availability should contain the test rooms"
|
||||||
|
"because there's no availability rules for them.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_availability_rooms_all_lines(self):
|
||||||
|
# TEST CASE
|
||||||
|
# get availability withouth rules
|
||||||
|
# given reservation lines to not consider
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
checkin = fields.date.today()
|
||||||
|
checkout = (fields.datetime.today() + datetime.timedelta(days=4)).date()
|
||||||
|
test_rooms_double_rooms = self.env["pms.room"].search(
|
||||||
|
[("pms_property_id", "=", self.test_property.id)]
|
||||||
|
)
|
||||||
|
test_reservation = self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": checkin,
|
||||||
|
"checkout": checkout,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
result = self.env["pms.availability.plan"].rooms_available(
|
||||||
|
checkin=checkin,
|
||||||
|
checkout=checkout,
|
||||||
|
current_lines=test_reservation.reservation_line_ids.ids,
|
||||||
|
)
|
||||||
|
# ASSERT
|
||||||
|
obtained = all(elem.id in result.ids for elem in test_rooms_double_rooms)
|
||||||
|
self.assertTrue(
|
||||||
|
obtained,
|
||||||
|
"Availability should contain the test rooms"
|
||||||
|
"because there's no availability rules for them.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_availability_rooms_room_type(self):
|
||||||
|
# TEST CASE
|
||||||
|
# get availability withouth rules
|
||||||
|
# given a room type
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
test_rooms_double_rooms = self.env["pms.room"].search(
|
||||||
|
[
|
||||||
|
("pms_property_id", "=", self.test_property.id),
|
||||||
|
("room_type_id", "=", self.test_room_type_double.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
result = self.env["pms.availability.plan"].rooms_available(
|
||||||
|
checkin=fields.date.today(),
|
||||||
|
checkout=(fields.datetime.today() + datetime.timedelta(days=4)).date(),
|
||||||
|
room_type_id=self.test_room_type_double.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
obtained = all(elem.id in result.ids for elem in test_rooms_double_rooms)
|
||||||
|
self.assertTrue(
|
||||||
|
obtained,
|
||||||
|
"Availability should contain the test rooms"
|
||||||
|
"because there's no availability rules for them.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_availability_closed_no_room_type(self):
|
||||||
|
# TEST CASE:
|
||||||
|
# coverage for 2 points:
|
||||||
|
# 1. without room type, availability rules associated
|
||||||
|
# with the pricelist are applied
|
||||||
|
# 2. availability rule "closed" is taken into account
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
self.test_room_type_availability_rule1 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.test_room_type_availability1.id,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
|
||||||
|
"closed": True, # <- (1/2)
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# ACT
|
||||||
|
result = self.env["pms.availability.plan"].rooms_available(
|
||||||
|
checkin=fields.date.today(),
|
||||||
|
checkout=(fields.datetime.today() + datetime.timedelta(days=4)).date(),
|
||||||
|
# room_type_id=False, # <- (2/2)
|
||||||
|
pricelist_id=self.test_pricelist1.id,
|
||||||
|
)
|
||||||
|
# ASSERT
|
||||||
|
self.assertNotIn(
|
||||||
|
self.test_room_type_double,
|
||||||
|
result.mapped("room_type_id"),
|
||||||
|
"Availability should not contain rooms of a type "
|
||||||
|
"which its availability rules applies",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_availability_rules(self):
|
||||||
|
# TEST CASE
|
||||||
|
# the availability should take into acount availability rules:
|
||||||
|
# closed_arrival, closed_departure, min_stay, max_stay,
|
||||||
|
# min_stay_arrival, max_stay_arrival
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
|
||||||
|
self.test_room_type_availability_rule1 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.test_room_type_availability1.id,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"date": (fields.datetime.today() + datetime.timedelta(days=0)).date(),
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkin = fields.date.today()
|
||||||
|
checkout = (fields.datetime.today() + datetime.timedelta(days=4)).date()
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": True,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": True,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkout,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 5,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 2,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 5,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 3,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": 0,
|
||||||
|
"max_avail": -1,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"closed": False,
|
||||||
|
"closed_arrival": False,
|
||||||
|
"closed_departure": False,
|
||||||
|
"min_stay": 0,
|
||||||
|
"max_stay": 0,
|
||||||
|
"min_stay_arrival": 0,
|
||||||
|
"max_stay_arrival": 0,
|
||||||
|
"quota": -1,
|
||||||
|
"max_avail": 0,
|
||||||
|
"date": checkin,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
for test_case in test_cases:
|
||||||
|
with self.subTest(k=test_case):
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
self.test_room_type_availability_rule1.write(test_case)
|
||||||
|
|
||||||
|
result = self.env["pms.availability.plan"].rooms_available(
|
||||||
|
checkin=checkin,
|
||||||
|
checkout=checkout,
|
||||||
|
room_type_id=self.test_room_type_double.id,
|
||||||
|
pricelist_id=self.test_pricelist1.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertNotIn(
|
||||||
|
self.test_room_type_double,
|
||||||
|
result.mapped("room_type_id"),
|
||||||
|
"Availability should not contain rooms of a type "
|
||||||
|
"which its availability rules applies",
|
||||||
|
)
|
||||||
|
|
||||||
|
@freeze_time("1980-11-01")
|
||||||
|
def test_rule_on_create_reservation(self):
|
||||||
|
# TEST CASE
|
||||||
|
# an availability rule should be applied that would prevent the
|
||||||
|
# creation of reservations
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
self.test_room_type_availability_rule1 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.test_room_type_availability1.id,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
|
||||||
|
"closed": True,
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
checkin = datetime.datetime.now()
|
||||||
|
checkout = datetime.datetime.now() + datetime.timedelta(days=4)
|
||||||
|
|
||||||
|
# ACT & ASSERT
|
||||||
|
with self.assertRaises(
|
||||||
|
ValidationError,
|
||||||
|
msg="Availability rules should be applied that would"
|
||||||
|
" prevent the creation of the reservation.",
|
||||||
|
):
|
||||||
|
self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": checkin,
|
||||||
|
"checkout": checkout,
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"pricelist_id": self.test_pricelist1.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@freeze_time("1980-11-01")
|
||||||
|
def test_rules_on_create_splitted_reservation(self):
|
||||||
|
# TEST CASE
|
||||||
|
# an availability rule should be applied that would prevent the
|
||||||
|
# creation of reservations including splitted reservations.
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
self.test_room_type_availability_rule1 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.test_room_type_availability1.id,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
|
||||||
|
"closed": True,
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
checkin_test = datetime.datetime.now()
|
||||||
|
checkout_test = datetime.datetime.now() + datetime.timedelta(days=4)
|
||||||
|
|
||||||
|
self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": datetime.datetime.now(),
|
||||||
|
"checkout": datetime.datetime.now() + datetime.timedelta(days=2),
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"preferred_room_id": self.test_room1_double.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": datetime.datetime.now() + datetime.timedelta(days=2),
|
||||||
|
"checkout": datetime.datetime.now() + datetime.timedelta(days=4),
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"preferred_room_id": self.test_room2_double.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT & ASSERT
|
||||||
|
with self.assertRaises(
|
||||||
|
ValidationError,
|
||||||
|
msg="Availability rule should be applied that would"
|
||||||
|
" prevent the creation of splitted reservation.",
|
||||||
|
):
|
||||||
|
self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": checkin_test,
|
||||||
|
"checkout": checkout_test,
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"pricelist_id": self.test_pricelist1.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@freeze_time("1980-11-01")
|
||||||
|
def test_rule_update_quota_on_create_reservation(self):
|
||||||
|
# TEST CASE
|
||||||
|
# quota rule is changed after creating a reservation
|
||||||
|
# with pricelist linked to a availability plan that applies
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
|
||||||
|
self.test_room_type_availability_rule1 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.test_room_type_availability1.id,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"date": datetime.date.today(),
|
||||||
|
"quota": 1,
|
||||||
|
"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,
|
||||||
|
"checkin": datetime.date.today(),
|
||||||
|
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"pricelist_id": self.test_pricelist1.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
r1.flush()
|
||||||
|
with self.assertRaises(
|
||||||
|
ValidationError,
|
||||||
|
msg="The quota shouldnt be enough to create a new reservation",
|
||||||
|
):
|
||||||
|
self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": datetime.date.today(),
|
||||||
|
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"pricelist_id": self.test_pricelist1.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@freeze_time("1980-11-01")
|
||||||
|
def test_rule_update_quota_on_update_reservation(self):
|
||||||
|
# TEST CASE
|
||||||
|
# quota rule is restored after creating a reservation
|
||||||
|
# with pricelist linked to a availability rule that applies
|
||||||
|
# and then modify the pricelist of the reservation and
|
||||||
|
# no rules applies
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_common_scenario()
|
||||||
|
test_quota = 2
|
||||||
|
test_pricelist2 = self.env["product.pricelist"].create(
|
||||||
|
{
|
||||||
|
"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,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"date": datetime.date.today(),
|
||||||
|
"quota": test_quota,
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
reservation = self.env["pms.reservation"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property.id,
|
||||||
|
"checkin": datetime.date.today(),
|
||||||
|
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||||
|
"adults": 2,
|
||||||
|
"room_type_id": self.test_room_type_double.id,
|
||||||
|
"pricelist_id": self.test_pricelist1.id,
|
||||||
|
"partner_id": self.partner1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
reservation.pricelist_id = test_pricelist2.id
|
||||||
|
reservation.flush()
|
||||||
|
self.assertEqual(
|
||||||
|
test_quota,
|
||||||
|
rule.quota,
|
||||||
|
"The quota should be restored after changing the reservation's pricelist",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_availability_closed_no_room_type_check_property(self):
|
||||||
|
# TEST CASE:
|
||||||
|
# check that availability rules are applied to the correct properties
|
||||||
|
# There are two properties:
|
||||||
|
# test_property --> test_room_type_availability_rule1
|
||||||
|
# test_property2 --> test_room_type_availability_rule2
|
||||||
|
|
||||||
|
# ARRANGE
|
||||||
|
self.create_scenario_multiproperty()
|
||||||
|
self.test_room_type_special = self.env["pms.room.type"].create(
|
||||||
|
{
|
||||||
|
"pms_property_ids": [
|
||||||
|
(4, self.test_property1.id),
|
||||||
|
(4, self.test_property2.id),
|
||||||
|
],
|
||||||
|
"name": "Special Room Test",
|
||||||
|
"default_code": "SP_Test",
|
||||||
|
"class_id": self.test_room_type_class.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.test_room1 = self.env["pms.room"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property1.id,
|
||||||
|
"name": "Double 201 test",
|
||||||
|
"room_type_id": self.test_room_type_special.id,
|
||||||
|
"capacity": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# pms.room
|
||||||
|
self.test_room2 = self.env["pms.room"].create(
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property2.id,
|
||||||
|
"name": "Double 202 test",
|
||||||
|
"room_type_id": self.test_room_type_special.id,
|
||||||
|
"capacity": 2,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.test_room_type_availability_rule1 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.availability_multiproperty.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.test_room_type_availability_rule2 = self.env[
|
||||||
|
"pms.availability.plan.rule"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"availability_plan_id": self.availability_multiproperty.id,
|
||||||
|
"room_type_id": self.test_room_type_special.id,
|
||||||
|
"date": (fields.datetime.today() + datetime.timedelta(days=2)).date(),
|
||||||
|
"pms_property_id": self.test_property2.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# check that for that date test_property1 doesnt have rooms available
|
||||||
|
# (of that type:test_room_type_double),
|
||||||
|
# instead, property2 has test_room_type_double available
|
||||||
|
properties = [
|
||||||
|
{"property": self.test_property1.id, "value": False},
|
||||||
|
{"property": self.test_property2.id, "value": True},
|
||||||
|
]
|
||||||
|
|
||||||
|
for p in properties:
|
||||||
|
with self.subTest(k=p):
|
||||||
|
# ACT
|
||||||
|
rooms_avail = self.env["pms.availability.plan"].rooms_available(
|
||||||
|
checkin=fields.date.today(),
|
||||||
|
checkout=(
|
||||||
|
fields.datetime.today() + datetime.timedelta(days=2)
|
||||||
|
).date(),
|
||||||
|
room_type_id=self.test_room_type_special.id,
|
||||||
|
pricelist_id=self.test_pricelist1.id,
|
||||||
|
pms_property_id=p["property"],
|
||||||
|
)
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
len(rooms_avail) > 0, p["value"], "Availability is not correct"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_check_property_availability_room_type(self):
|
||||||
|
# TEST CASE:
|
||||||
|
# check integrity between availability properties and room_type properties
|
||||||
|
|
||||||
|
# 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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# Test cases when creating a availability_rule
|
||||||
|
# Allowed properties:
|
||||||
|
# Room Type(test_room_type_special) -->TEST_PROPERTY1 TEST_PROPERTY3
|
||||||
|
# Availability Plan(availability_example)-->TEST_PROPERTY1 TEST_PROPERTY2
|
||||||
|
|
||||||
|
# Both cases throw an exception:
|
||||||
|
# 1:Rule for property2,
|
||||||
|
# it is allowed in availability_plan but not in room_type
|
||||||
|
# 2:Rule for property3,
|
||||||
|
# it is allowed in room_type, but not in availability_plan
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property2.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pms_property_id": self.test_property3.id,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
# ASSERT
|
||||||
|
for test_case in test_cases:
|
||||||
|
with self.subTest(k=test_case):
|
||||||
|
with self.assertRaises(UserError):
|
||||||
|
self.availability_rule1.pms_property_id = test_case[
|
||||||
|
"pms_property_id"
|
||||||
|
]
|
||||||
423
pms/tests/test_pms_board_service.py
Normal file
423
pms/tests/test_pms_board_service.py
Normal file
@@ -0,0 +1,423 @@
|
|||||||
|
# Copyright 2021 Eric Antones <eantones@nuobit.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
from .common import TestPms
|
||||||
|
|
||||||
|
|
||||||
|
class TestBoardService(TestPms):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.company2 = self.env["res.company"].create(
|
||||||
|
{
|
||||||
|
"name": "Company 2",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.pms_property3 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Property 3",
|
||||||
|
"company_id": self.company2.id,
|
||||||
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# external integrity
|
||||||
|
def test_external_case_01(self):
|
||||||
|
"""
|
||||||
|
PRE: - board service bs1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has pms_property1
|
||||||
|
- pms_property1 has company company1
|
||||||
|
ACT: - create a new board_service2
|
||||||
|
- board_service2 has code c1
|
||||||
|
- board_service2 has pms_property1
|
||||||
|
- pms_property1 has company company1
|
||||||
|
POST: - Integrity error: the room type already exists
|
||||||
|
- board_service2 not created
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
# board_service1
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [(6, 0, [self.pms_property1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT & ASSERT
|
||||||
|
with self.assertRaises(
|
||||||
|
ValidationError, msg="The board service has been created and it shouldn't"
|
||||||
|
):
|
||||||
|
# board_service2
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs2",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [(6, 0, [self.pms_property1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_external_case_02(self):
|
||||||
|
"""
|
||||||
|
PRE: - board service bs1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has property pms_property1
|
||||||
|
- pms_property1 has company company1
|
||||||
|
ACT: - create a new board_service2
|
||||||
|
- board_service2 has code c1
|
||||||
|
- board_service2 has property pms_property1, pms_property2,
|
||||||
|
pms_property3
|
||||||
|
- pms_property1, pms_property2 has company company1
|
||||||
|
- pms_property3 has company company2
|
||||||
|
POST: - Integrity error: the board service already exists
|
||||||
|
- board_service2 not created
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
self.pms_property2 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Property 2",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# board_service1
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [(6, 0, [self.pms_property1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT & ASSERT
|
||||||
|
with self.assertRaises(
|
||||||
|
ValidationError, msg="The board service has been created and it shouldn't"
|
||||||
|
):
|
||||||
|
# board_service2
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs2",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [
|
||||||
|
(
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
[
|
||||||
|
self.pms_property1.id,
|
||||||
|
self.pms_property2.id,
|
||||||
|
self.pms_property3.id,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_single_case_01(self):
|
||||||
|
"""
|
||||||
|
PRE: - board service bs1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has 2 properties pms_property1 and pms_property2
|
||||||
|
- pms_property_1 and pms_property2 have the same company company1
|
||||||
|
ACT: - search board service with code c1 and pms_property1
|
||||||
|
- pms_property1 has company company1
|
||||||
|
POST: - only board_service1 board service found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
board_service1 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [
|
||||||
|
(6, 0, [self.pms_property1.id, self.pms_property3.id])
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property1.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
board_services.id,
|
||||||
|
board_service1.id,
|
||||||
|
"Expected board service not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_single_case_02(self):
|
||||||
|
"""
|
||||||
|
PRE: - board service bs1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has 2 properties pms_property1 and pms_property3
|
||||||
|
- pms_property1 and pms_property2 have different companies
|
||||||
|
- pms_property1 have company company1 and pms_property3 have company2
|
||||||
|
ACT: - search board service with code c1 and property pms_property1
|
||||||
|
- pms_property1 has company company1
|
||||||
|
POST: - only board_service1 room type found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
bs1 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [
|
||||||
|
(6, 0, [self.pms_property1.id, self.pms_property3.id])
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property1.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(board_services.id, bs1.id, "Expected board service not found")
|
||||||
|
|
||||||
|
def test_single_case_03(self):
|
||||||
|
"""
|
||||||
|
PRE: - board_service1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 with 2 properties pms_property1 and pms_property2
|
||||||
|
- pms_property1 and pms_property2 have same company company1
|
||||||
|
ACT: - search board service with code c1 and property pms_property3
|
||||||
|
- pms_property3 have company company2
|
||||||
|
POST: - no room type found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
self.pms_property2 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Property 2",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# board_service1
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [
|
||||||
|
(6, 0, [self.pms_property1.id, self.pms_property2.id])
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property3.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertFalse(
|
||||||
|
board_services, "Board service found but it should not have found any"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_single_case_04(self):
|
||||||
|
"""
|
||||||
|
PRE: - board_service1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 properties are null
|
||||||
|
ACT: - search board service with code c1 and property pms_property1
|
||||||
|
- pms_property1 have company company1
|
||||||
|
POST: - only board_service1 board service found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
board_service1 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property1.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
board_services.id,
|
||||||
|
board_service1.id,
|
||||||
|
"Expected board service not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
# tests with more than one board service
|
||||||
|
def test_multiple_case_01(self):
|
||||||
|
"""
|
||||||
|
PRE: - board_service1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has 2 properties pms_property1 and pms_property2
|
||||||
|
- pms_property1 and pms_property2 have the same company company1
|
||||||
|
- board service board_service2 exists
|
||||||
|
- board_service2 has code c1
|
||||||
|
- board_service2 has no properties
|
||||||
|
ACT: - search board service with code c1 and property pms_property1
|
||||||
|
- pms_property1 have company company1
|
||||||
|
POST: - only board_service1 board service found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
board_service1 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [
|
||||||
|
(6, 0, [self.pms_property1.id, self.pms_property3.id])
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# board_service2
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs2",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property1.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
board_services.id,
|
||||||
|
board_service1.id,
|
||||||
|
"Expected board service not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_multiple_case_02(self):
|
||||||
|
"""
|
||||||
|
PRE: - board_service1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has property pms_property1
|
||||||
|
- pms_property1 have the company company1
|
||||||
|
- board service board_service2 exists
|
||||||
|
- board_service2 has code c1
|
||||||
|
- board_service2 has no properties
|
||||||
|
ACT: - search board service with code c1 and pms_property2
|
||||||
|
- pms_property2 have company company1
|
||||||
|
POST: - only board_service1 board service found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
self.pms_property2 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Property 2",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.pricelist1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# board_service1
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [(6, 0, [self.pms_property1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
board_service2 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs2",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property2.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
board_services.id,
|
||||||
|
board_service2.id,
|
||||||
|
"Expected board service not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_multiple_case_03(self):
|
||||||
|
"""
|
||||||
|
PRE: - board_service1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has property pms_property1
|
||||||
|
- pms_property1 have the company company1
|
||||||
|
- board service board_service2 exists
|
||||||
|
- board_service2 has code c1
|
||||||
|
- board_service2 has no properties
|
||||||
|
ACT: - search board service with code c1 and property pms_property3
|
||||||
|
- pms_property3 have company company2
|
||||||
|
POST: - only board_service2 board service found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
# board_service1
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [(6, 0, [self.pms_property1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
board_service2 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs2",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property3.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
board_services.id,
|
||||||
|
board_service2.id,
|
||||||
|
"Expected board service not found",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_multiple_case_04(self):
|
||||||
|
"""
|
||||||
|
PRE: - board_service1 exists
|
||||||
|
- board_service1 has code c1
|
||||||
|
- board_service1 has property pms_property1
|
||||||
|
- pms_property1 have the company company1
|
||||||
|
- room type board_service2 exists
|
||||||
|
- board_service2 has code c1
|
||||||
|
- board_service2 has no properties
|
||||||
|
ACT: - search board service with code c1 and property pms_property3
|
||||||
|
- pms_property3 have company company2
|
||||||
|
POST: - r2 board service found
|
||||||
|
"""
|
||||||
|
# ARRANGE
|
||||||
|
# board_service1
|
||||||
|
self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service 1",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": [(6, 0, [self.pms_property1.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
board_service2 = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board service bs2",
|
||||||
|
"default_code": "c1",
|
||||||
|
"pms_property_ids": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ACT
|
||||||
|
board_services = self.env["pms.board.service"].get_unique_by_property_code(
|
||||||
|
self.pms_property3.id, "c1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ASSERT
|
||||||
|
self.assertEqual(
|
||||||
|
board_services.id, board_service2.id, "Expected room type not found"
|
||||||
|
)
|
||||||
75
pms/tests/test_pms_board_service_line.py
Normal file
75
pms/tests/test_pms_board_service_line.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from odoo.exceptions import UserError
|
||||||
|
from odoo.tests import common
|
||||||
|
|
||||||
|
|
||||||
|
class TestPmsBoardService(common.SavepointCase):
|
||||||
|
def test_property_integrity(self):
|
||||||
|
self.company1 = self.env["res.company"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_Company_Test",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.folio_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Folio",
|
||||||
|
"code": "pms.folio",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.reservation_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Reservation",
|
||||||
|
"code": "pms.reservation",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.checkin_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Checkin",
|
||||||
|
"code": "pms.checkin.partner",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.property1 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_property_test1",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.env.ref("product.list0").id,
|
||||||
|
"folio_sequence_id": self.folio_sequence.id,
|
||||||
|
"reservation_sequence_id": self.reservation_sequence.id,
|
||||||
|
"checkin_sequence_id": self.checkin_sequence.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.property2 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_property_test2",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.env.ref("product.list0").id,
|
||||||
|
"folio_sequence_id": self.folio_sequence.id,
|
||||||
|
"reservation_sequence_id": self.reservation_sequence.id,
|
||||||
|
"checkin_sequence_id": self.checkin_sequence.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.product = self.env["product.product"].create(
|
||||||
|
{"name": "Product", "pms_property_ids": self.property1}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.board_service = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board Service",
|
||||||
|
"default_code": "CB",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with self.assertRaises(UserError):
|
||||||
|
board_service_line = self.board_service_line = self.env[
|
||||||
|
"pms.board.service.line"
|
||||||
|
].create(
|
||||||
|
{
|
||||||
|
"product_id": self.product.id,
|
||||||
|
"pms_board_service_id": self.board_service.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
board_service_line.pms_property_ids = [self.property2.id]
|
||||||
69
pms/tests/test_pms_board_service_room_type.py
Normal file
69
pms/tests/test_pms_board_service_room_type.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
from odoo.tests import common
|
||||||
|
|
||||||
|
|
||||||
|
class TestPmsBoardServiceRoomType(common.SavepointCase):
|
||||||
|
def _create_common_scenario(self):
|
||||||
|
self.company1 = self.env["res.company"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_Company_Test",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.folio_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Folio",
|
||||||
|
"code": "pms.folio",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.reservation_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Reservation",
|
||||||
|
"code": "pms.reservation",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.checkin_sequence = self.env["ir.sequence"].create(
|
||||||
|
{
|
||||||
|
"name": "PMS Checkin",
|
||||||
|
"code": "pms.checkin.partner",
|
||||||
|
"padding": 4,
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.property1 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_property_test1",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.env.ref("product.list0").id,
|
||||||
|
"folio_sequence_id": self.folio_sequence.id,
|
||||||
|
"reservation_sequence_id": self.reservation_sequence.id,
|
||||||
|
"checkin_sequence_id": self.checkin_sequence.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.property2 = self.env["pms.property"].create(
|
||||||
|
{
|
||||||
|
"name": "Pms_property_test2",
|
||||||
|
"company_id": self.company1.id,
|
||||||
|
"default_pricelist_id": self.env.ref("product.list0").id,
|
||||||
|
"folio_sequence_id": self.folio_sequence.id,
|
||||||
|
"reservation_sequence_id": self.reservation_sequence.id,
|
||||||
|
"checkin_sequence_id": self.checkin_sequence.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.board_service = self.env["pms.board.service"].create(
|
||||||
|
{
|
||||||
|
"name": "Board Service",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.room_type_class = self.env["pms.room.type.class"].create(
|
||||||
|
{"name": "Room Type Class", "default_code": "SIN1"}
|
||||||
|
)
|
||||||
|
self.room_type = self.env["pms.room.type"].create(
|
||||||
|
{
|
||||||
|
"name": "Room Type",
|
||||||
|
"default_code": "Type1",
|
||||||
|
"class_id": self.room_type_class.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user