Merge branch 'commitsun/14.0' into 'OCA/14.0'

This commit is contained in:
Eric Antones
2021-05-26 16:23:50 +02:00
236 changed files with 31022 additions and 12051 deletions

View File

@@ -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"

View File

@@ -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)

View 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.

View 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

View 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,
}

View 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

View File

@@ -0,0 +1,5 @@
* `Commit [Sun] <https://www.commitsun.com>`:
* Dario Lodeiros
* Eric Antones
* Sara Lago

View File

@@ -0,0 +1 @@
Technical addon to support multiproperty in property management system (PMS).

View 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"``

View 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``

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View 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 = &quot;multi_pms_properties&quot;</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&nbsp; 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] &lt;https://www.commitsun.com&gt;</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>

View 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

View 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)

View 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)

View 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"})

View File

@@ -1 +1,2 @@
# See https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#oca_dependencies-txt partner-contact
reporting-engine

View File

@@ -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
~~~~~~~~~~~ ~~~~~~~~~~~

View File

@@ -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

View File

@@ -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",
} }

View File

@@ -0,0 +1 @@
from . import pms_portal

View 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)

View File

@@ -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">

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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, {

View File

@@ -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

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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"
) )

View File

@@ -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

View 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",
)

View 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

View 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,
)

View File

@@ -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)],
}

View File

@@ -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)]

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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 ¿?

View File

@@ -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")

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View 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")
)

View 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,
},
}

View 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")
)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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(
_("This Board Service in this Room can't repeat pricelist")
)
else:
board_pricelist = self.env["pms.board.service.room.type"].search(
[
("pricelist_id", "=", False),
("pms_room_type_id", "=", record.pms_room_type_id.id),
("pms_board_service_id", "=", record.pms_board_service_id.id),
("id", "!=", record.id),
]
)
if board_pricelist:
raise UserError(
_(
"This Board Service in this Room \
can't repeat without pricelist"
) )
) )
# TODO Check properties (with different propertys is allowed)
# Action methods if any(default_boards.filtered(lambda l: l.id != record.id)):
raise UserError(_("""Only can set one default board service"""))
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):
""" """

View File

@@ -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)

View File

@@ -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",
)

View File

@@ -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:
raise ValidationError(
_("Is mandatory indicate the reservation on the checkin")
)
draft_checkins = reservation.checkin_partner_ids.filtered( draft_checkins = reservation.checkin_partner_ids.filtered(
lambda c: c.state == "draft" lambda c: c.state == "draft"
) )
if len(draft_checkins) > 0 and vals.get("partner_id"): if len(reservation.checkin_partner_ids) < reservation.adults:
draft_checkins[0].sudo().unlink() 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"]
)
pms_property = self.env["pms.property"].browse(pms_property_id)
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):

View File

@@ -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

View File

@@ -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

View File

@@ -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"):

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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.",
)

View File

@@ -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"))

View File

@@ -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,
) )

View File

@@ -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

View File

@@ -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")

View File

@@ -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,
} }
) )

View 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"))

View File

@@ -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)),
)
)

View File

@@ -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,
) )

View 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
)

View File

@@ -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,
) )

View File

@@ -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.")

View File

@@ -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 []

View File

@@ -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")
)

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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>`_.

View File

@@ -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>
@@ -26,155 +27,186 @@
</p> </p>
</div> </div>
</div> </div>
<h2> </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 not in ['draft','sent']">Order #</span>
<span t-if="doc.state in ['draft','sent']">Quotation #</span> <span t-if="doc.state in ['draft','sent']">Quotation #</span>
<span t-field="doc.name" /> <span t-field="doc.name" />
</h2> </h2>
<div class="row mt32 mb32" id="informations"> <div class="row mt32 mb32" id="informations">
<div t-if="doc.client_order_ref" class="col-xs-3"> <div t-if="doc.client_order_ref" class="col-auto col-3 mw-100 mb-2">
<strong>Your Reference:</strong> <strong>Your Reference:</strong>
<p t-field="doc.client_order_ref" /> <p class="m-0" t-field="doc.client_order_ref" />
</div> </div>
<div <div
t-if="doc.confirmation_date and doc.state not in ['draft','sent']" t-if="doc.date_order and doc.state not in ['draft','sent']"
class="col-xs-3" class="col-auto col-3 mw-100 mb-2"
> >
<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
class="m-0"
t-field="doc.date_order"
t-options='{"widget": "date"}'
/>
</div> </div>
<div t-if="doc.user_id.name" class="col-xs-3"> <div t-if="doc.user_id.name" class="col-auto col-3 mw-100 mb-2">
<strong>Salesperson:</strong> <strong>Salesperson:</strong>
<p t-field="doc.user_id" /> <p class="m-0" 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> </div>
<!-- Is there a discount on at least one line? --> <!-- Is there a discount on at least one line? -->
<t <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">
<!-- In case we want to repeat the header, remove "display: table-row-group" -->
<thead style="display: table-row-group">
<tr> <tr>
<th>Description</th> <th name="th_description" class="text-left">Description</th>
<th class="text-right">Quantity</th> <th name="th_quantity" class="text-right">Quantity</th>
<th name="th_priceunit" class="text-right">Unit Price</th>
<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>
Amount <span
</th> groups="account.group_show_line_subtotals_tax_included"
<th >Total Price</span>
class="text-right price_tax_included"
groups="sale.group_show_price_total"
>
Total Price
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody class="sale_tbody"> <tbody class="sale_tbody">
<!-- Lines associated -->
<t t-foreach="doc.reservation_ids" t-as="l"> <t t-set="current_subtotal" t-value="0" />
<t t-if="l.price_total > 0">
<tr> <t t-foreach="doc.sale_line_ids" t-as="line">
<td>
<span t-field="l.name" /> <t
</td> t-set="current_subtotal"
<td class="text-right"> t-value="current_subtotal + line.price_subtotal"
<span t-field="l.nights" /> groups="account.group_show_line_subtotals_tax_excluded"
</td> />
<td <t
t-if="display_discount" t-set="current_subtotal"
class="text-right" t-value="current_subtotal + line.price_total"
groups="sale.group_discount_per_so_line" 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 ''"
> >
<span t-field="l.discount" /> <t t-if="not line.display_type">
</td> <t t-set="price" t-value="line.price_unit" />
<td class="text-right">
<t t-if="line.reservation_id">
<t
t-set="print_board_service"
t-value="line.reservation_id.board_service_room_id.pms_board_service_id.show_detail_report"
/>
<t t-if="not print_board_service">
<t
t-foreach="line.reservation_id.service_ids"
t-as="service"
>
<t t-if="service.is_board_service">
<t
t-set="price"
t-value="service.product_qty/line.price_total*(1-(service.reservation_id.discount or 0.0)*0.01) + price"
/>
</t>
</t>
</t>
</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 <span
t-esc="', '.join(map(lambda x: (x.description or x.name), l.tax_ids))" t-field="line.product_uom"
groups="uom.group_uom"
/> />
</td> </td>
<td
class="text-right" <td name="td_priceunit" class="text-right">
groups="sale.group_show_price_subtotal"
>
<span <span
t-field="l.price_subtotal" t-esc="price"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}' t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/> />
</td> </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 <td
class="text-right" name="td_subtotal"
groups="sale.group_show_price_total" class="text-right o_price_total"
> >
<span <span
t-field="l.price_total" 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}' t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/> />
</td> </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> </tr>
</t>
</t> <t
<t t-foreach="doc.service_ids" t-as="l"> t-if="current_section and (line_last or doc.sale_line_ids[line_index+1].display_type == 'line_section')"
<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" /> <tr class="is-subtotal text-right">
</td> <td name="td_section_subtotal" colspan="99">
<td class="text-right"> <strong class="mr16">Subtotal</strong>
<span <span
t-esc="', '.join(map(lambda x: (x.description or x.name), l.tax_ids))" t-esc="current_subtotal"
/>
</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}' t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/> />
</td> </td>
@@ -183,46 +215,39 @@
</t> </t>
</tbody> </tbody>
</table> </table>
<div class="clearfix">
<div class="row" name="total"> <div class="clearfix" name="so_total_summary">
<div class="col-xs-4 pull-right"> <div id="total" class="row" name="total">
<table <div
class="table table-condensed" t-attf-class="#{'col-4' if report_type != 'html' else 'col-sm-7 col-md-5'} ml-auto"
style="min-width: 200px;max-width: 350px;"
> >
<tr <table class="table table-sm">
class="border-black" <tr class="border-black o_subtotal" style="">
style="border-bottom:1px solid #dddddd;" <td name="td_amount_untaxed_label"><strong
> >Subtotal</strong></td>
<td> <td name="td_amount_untaxed" class="text-right">
<strong>Subtotal</strong> <span t-field="doc.amount_untaxed" />
</td>
<td class="text-right">
<span
t-field="doc.amount_untaxed"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/>
</td> </td>
</tr> </tr>
-
<t <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>&amp;nbsp;<span> <span>&amp;nbsp;<span>on</span>&amp;nbsp;<t
on
</span>&amp;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
name="td_amount_by_group_3"
class="text-right o_price_total"
>
<span <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}'
@@ -230,10 +255,13 @@
</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
name="td_amount_by_group"
class="text-right o_price_total"
>
<span <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}'
@@ -242,91 +270,42 @@
</t> </t>
</tr> </tr>
</t> </t>
<tr class="border-black"> <tr class="border-black o_total">
<td> <td name="td_amount_total_label"><strong
<strong>Total</strong> >Total</strong></td>
</td> <td name="td_amount_total" class="text-right">
<td class="text-right"> <span t-field="doc.amount_total" />
<span
t-field="doc.amount_total"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/>
</td>
</tr>
<tr class="border-black">
<td>
<strong>Pending Payment</strong>
</td>
<td class="text-right">
<span
t-field="doc.pending_amount"
t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'
/>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<div>
<span t-if="doc.payment_ids"> <div class="oe_structure" />
<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-field="doc.note" />
<p t-if="doc.payment_term_id.note"> <p t-if="doc.payment_term_id.note">
<span t-field="doc.payment_term_id.note" /> <span t-field="doc.payment_term_id.note" />
</p> </p>
<div class="oe_structure" /> <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> </div>
</t> </t>
</template> </template>
<template id="report_folio">
<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>

View File

@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 user_access_pms_floor user_access_pms_ubication user_access_pms_floor user_access_pms_ubication model_pms_floor model_pms_ubication pms.group_pms_user 1 0 0 0
3 user_access_pms_amenity user_access_pms_amenity model_pms_amenity pms.group_pms_user 1 0 0 0
4 user_access_pms_amenity_type user_access_pms_amenity_type model_pms_amenity_type pms.group_pms_user 1 0 0 0
5 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
6 user_access_pms_reservation_line user_access_pms_reservation_line model_pms_reservation_line pms.group_pms_user 1 1 1 1
7 user_access_room_closure_reason user_access_room_closure_reason model_room_closure_reason pms.group_pms_user 1 0 0 0
8 user_access_pms_service_line user_access_pms_service_line model_pms_service_line pms.group_pms_user 1 1 1 1
9 user_access_pms_board_service user_access_pms_board_service model_pms_board_service pms.group_pms_user 1 0 0 0
10 user_access_pms_checkin_partner user_access_pms_checkin_partner model_pms_checkin_partner pms.group_pms_user 1 1 1 0 1
11 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
12 user_access_pms_room user_access_pms_room model_pms_room pms.group_pms_user 1 0 0 0
13 user_access_shared_pms_room user_access_pms_shared_room model_pms_shared_room pms.group_pms_user 1 0 0 0
14 user_access_pms_room_type_restriction_item user_access_pms_availability_plan_rule user_access_pms_room_type_restriction_item user_access_pms_availability_plan_rule model_pms_room_type_restriction_item model_pms_availability_plan_rule pms.group_pms_user 1 0 0 0
15 user_access_pms_availability user_access_pms_availability model_pms_availability pms.group_pms_user 1 1 1 0
16 user_access_pms_reservation user_access_pms_reservation model_pms_reservation pms.group_pms_user 1 1 1 1
17 user_access_pms_folio user_access_pms_folio model_pms_folio pms.group_pms_user 1 1 1 1
18 user_access_pms_room_type user_access_pms_room_type model_pms_room_type pms.group_pms_user 1 0 0 0
23 user_access_pms_cancelation_rule user_access_pms_cancelation_rule model_pms_cancelation_rule pms.group_pms_user 1 0 0 0
24 user_access_account_full_reconcile user_access_account_full_reconcile account.model_account_full_reconcile pms.group_pms_user 1 1 1 1
25 user_access_property user_access_property model_pms_property pms.group_pms_user 1 0 0 0
26 user_access_availability user_access_availability model_pms_room_type_availability model_pms_availability_plan pms.group_pms_user 1 0 0 0
27 user_access_pms_sale_channel user_access_pms_sale_channel model_pms_sale_channel pms.group_pms_user 1 0 0 0
28 manager_access_pms_floor manager_access_pms_ubication manager_access_pms_floor manager_access_pms_ubication model_pms_floor model_pms_ubication pms.group_pms_manager 1 1 1 1
29 manager_access_pms_amenity manager_access_pms_amenity model_pms_amenity pms.group_pms_manager 1 1 1 1
30 manager_access_pms_amenity_type manager_access_pms_amenity_type model_pms_amenity_type pms.group_pms_manager 1 1 1 1
31 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
32 manager_access_pms_reservation_line manager_access_pms_reservation_line model_pms_reservation_line pms.group_pms_manager 1 1 1 1
33 manager_access_room_closure_reason manager_access_room_closure_reason model_room_closure_reason pms.group_pms_manager 1 1 1 1
34 manager_access_pms_service_line manager_access_pms_service_line model_pms_service_line pms.group_pms_manager 1 1 1 1
35 manager_access_pms_board_service manager_access_pms_board_service model_pms_board_service pms.group_pms_manager 1 1 1 1
36 manager_access_pms_checkin_partner manager_access_pms_checkin_partner model_pms_checkin_partner pms.group_pms_manager 1 1 1 0 1
37 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
38 manager_access_pms_room manager_access_pms_room model_pms_room pms.group_pms_manager 1 1 1 1
39 manager_access_pms_shared_room manager_access_pms_shared_room model_pms_shared_room pms.group_pms_manager 1 1 1 1
40 manager_access_pms_room_type_restriction_item manager_access_pms_availability_plan_rule manager_access_pms_room_type_restriction_item manager_access_pms_availability_plan_rule model_pms_room_type_restriction_item model_pms_availability_plan_rule pms.group_pms_manager 1 1 1 1
41 manager_access_pms_reservation manager_access_pms_reservation model_pms_reservation pms.group_pms_manager 1 1 1 1
42 manager_access_pms_availability manager_access_pms_availability model_pms_availability pms.group_pms_manager 1 1 1 0
43 manager_access_pms_folio manager_access_pms_folio model_pms_folio pms.group_pms_manager 1 1 1 1
44 manager_access_pms_room_type manager_access_pms_room_type model_pms_room_type pms.group_pms_manager 1 1 1 1
45 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 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
48 manager_access_property manager_access_property model_pms_property pms.group_pms_manager 1 1 1 1
49 manager_access_pms_cancelation_rule manager_access_pms_cancelation_rule model_pms_cancelation_rule pms.group_pms_manager 1 1 1 1
50 manager_access_availability manager_access_availability model_pms_room_type_availability model_pms_availability_plan pms.group_pms_manager 1 1 1 1
51 manager_access_pms_sale_channel manager_access_pms_sale_channel model_pms_sale_channel pms.group_pms_manager 1 1 1 1
52 user_access_pms_reservation_wizard user_access_pms_reservation_split_join_swap_wizard user_access_pms_reservation_wizard user_access_pms_reservation_split_join_swap_wizard model_pms_reservation_wizard model_pms_reservation_split_join_swap_wizard pms.group_pms_user 1 1 1 1
53 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
54 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
55 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
56 user_access_pms_folio_wizard user_access_pms_folio_wizard model_pms_folio_wizard pms.group_pms_user 1 1 1 1
57 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
58 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
59 user_access_pms_folio_sale_line user_access_pms_folio_sale_line model_folio_sale_line pms.group_pms_user 1 0 0 0
60 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
61 user_access_wizard_payment_folio user_access_wizard_payment_folio model_wizard_payment_folio pms.group_pms_user 1 1 1 1
62 user_access_wizard_folio_changes user_access_wizard_folio_changes model_wizard_folio_changes pms.group_pms_user 1 1 1 1
63 user_access_pms_folio_portal user_access_pms_folio_portal model_pms_folio base.group_portal 1 0 0 0
64 user_access_pms_reservation_portal user_access_pms_reservation_portal model_pms_reservation base.group_portal 1 0 0 0

View File

@@ -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

View File

@@ -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 propertys daily operations.</p> for managing every aspect of your propertys 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 &gt; Users &amp; Companies &gt; Hotels &gt; Your Hotel.</p> <p>You will find the hotel settings in PMS Management &gt; Configuration &gt; Properties &gt; 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 &lt;<a class="reference external" href="mailto:dario&#64;commitsun.com">dario&#64;commitsun.com</a>&gt;</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] &lt;https://www.commitsun.com&gt;</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">

View File

@@ -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,

View 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);
});

View File

@@ -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;
}); });

View File

@@ -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");
});
}
},
});
});

View File

@@ -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;
} }

View 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>

View File

@@ -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

View File

@@ -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": ""}
) )

View 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,
],
)
],
}
)

View 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"
]

View 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"
)

View 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]

View 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