mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
Merge branch '14.0' into 14.0-pms_pricelist_rules_priority
This commit is contained in:
@@ -60,6 +60,7 @@
|
||||
"views/res_partner_views.xml",
|
||||
"views/product_pricelist_views.xml",
|
||||
"views/product_pricelist_item_views.xml",
|
||||
"views/pms_sale_channel.xml",
|
||||
"views/product_template_views.xml",
|
||||
"views/webclient_templates.xml",
|
||||
"views/ir_sequence_views.xml",
|
||||
|
||||
@@ -1,6 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<!-- Set reservation like No Show if the client does not show up -->
|
||||
<record model="ir.cron" id="noshow_reservations">
|
||||
<field name="name">Automatic No Show Reservation</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_pms_reservation" />
|
||||
<field
|
||||
name="nextcall"
|
||||
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 09:00:00')"
|
||||
/>
|
||||
<field name="code">model.auto_no_show()</field>
|
||||
</record>
|
||||
<!-- Set reservation like No Checout if checkout is not confirmed-->
|
||||
<record model="ir.cron" id="nocheckout_reservations">
|
||||
<field name="name">Automatic No Checkout Reservations</field>
|
||||
<field name="interval_number">5</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_pms_reservation" />
|
||||
<field name="nextcall" eval="DateTime.now()" />
|
||||
<field name="code">model.auto_no_checkout()</field>
|
||||
</record>
|
||||
<!-- Scheduler For To Inform Guest About Reservation Before 24 Hours -->
|
||||
<record model="ir.cron" id="autocheckout_reservations">
|
||||
<field name="name">Automatic Checkout on past reservations</field>
|
||||
|
||||
@@ -34,5 +34,22 @@
|
||||
<field name="pms_property_ids" eval="[(4, ref('main_pms_property'))]" />
|
||||
<field name="groups_id" eval="[(4,ref('pms.group_pms_manager'))]" />
|
||||
</record>
|
||||
<!-- pms.sale.channel-->
|
||||
<record id="main_pms_sale_channel_0" model="pms.sale.channel">
|
||||
<field name="name">Door</field>
|
||||
<field name="channel_type">direct</field>
|
||||
</record>
|
||||
<record id="main_pms_sale_channel_1" model="pms.sale.channel">
|
||||
<field name="name">Phone</field>
|
||||
<field name="channel_type">direct</field>
|
||||
</record>
|
||||
<record id="main_pms_sale_channel_2" model="pms.sale.channel">
|
||||
<field name="name">Mail</field>
|
||||
<field name="channel_type">direct</field>
|
||||
</record>
|
||||
<record id="main_pms_sale_channel_3" model="pms.sale.channel">
|
||||
<field name="name">Agency</field>
|
||||
<field name="channel_type">indirect</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
/>
|
||||
</record>
|
||||
<!-- reservation of 3 single rooms for 3 people with 1 cancelled -->
|
||||
<!-- TODO: The third reservation is marked from State: Cancelled to Pending Entry 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">
|
||||
<field name="partner_id" ref="base.res_partner_address_10" />
|
||||
<field
|
||||
@@ -344,7 +344,7 @@
|
||||
/>
|
||||
</record>
|
||||
<!--Reservation of the conference room whit cancelled-->
|
||||
<!-- TODO: The reservation is marked from State: Cancelled to Pending Entry 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">
|
||||
<field name="partner_id" ref="base.res_partner_18" />
|
||||
<field name="reservation_type">normal</field>
|
||||
@@ -381,7 +381,7 @@
|
||||
/>
|
||||
</record>
|
||||
<!--Reservation of triple room whit draft state-->
|
||||
<!-- TODO: The reservation is marked from State: Pre-reservation to Pending Entry 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">
|
||||
<field name="partner_id" ref="base.res_partner_address_32" />
|
||||
<field name="reservation_type">normal</field>
|
||||
|
||||
@@ -24,6 +24,20 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_0" />
|
||||
<field name="adults">2</field>
|
||||
<field name="state">onboard</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {
|
||||
'partner_id': ref('base.res_partner_address_28'),
|
||||
'state': 'onboard'
|
||||
}),
|
||||
(0, 0, {
|
||||
'partner_id': ref('base.res_partner_12'),
|
||||
'state': 'onboard'
|
||||
}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today()" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(1)" />
|
||||
<field name="board_service_room_id" ref="pms_board_service_room_1" />
|
||||
@@ -58,6 +72,15 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_1" />
|
||||
<field name="adults">1</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {
|
||||
'partner_id': ref('base.res_partner_address_27'),
|
||||
'state': 'onboard'
|
||||
}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today()" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(3)" />
|
||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||
@@ -67,6 +90,14 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_1" />
|
||||
<field name="adults">1</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {
|
||||
'partner_id': ref('base.res_partner_address_10'),
|
||||
}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today()" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(3)" />
|
||||
<field name="board_service_room_id" ref="pms_board_service_room_0" />
|
||||
@@ -129,6 +160,13 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_2" />
|
||||
<field name="adults">2</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_12')}),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_18')}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||
</record>
|
||||
@@ -164,6 +202,13 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_2" />
|
||||
<field name="adults">2</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_18')}),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_12')}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today() + timedelta(11)" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(13)" />
|
||||
</record>
|
||||
@@ -217,6 +262,14 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_3" />
|
||||
<field name="adults">3</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_10')}),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_address_10')}),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_address_18')}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today() + timedelta(6)" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(8)" />
|
||||
</record>
|
||||
@@ -225,6 +278,12 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_3" />
|
||||
<field name="adults">1</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_3')}),
|
||||
]"
|
||||
/>
|
||||
<field name="children">2</field>
|
||||
<field name="checkin" eval="DateTime.today() + timedelta(10)" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(11)" />
|
||||
@@ -235,6 +294,14 @@
|
||||
<field name="pricelist_id" ref="product.list0" />
|
||||
<field name="room_type_id" ref="pms_room_type_3" />
|
||||
<field name="adults">3</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_3')}),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_address_14')}),
|
||||
(0, 0, {'partner_id': ref('base.res_partner_address_33')}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today() + timedelta(4)" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(6)" />
|
||||
</record>
|
||||
@@ -245,6 +312,19 @@
|
||||
<field name="adults">2</field>
|
||||
<field name="children">1</field>
|
||||
<field name="state">onboard</field>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
eval="[(5, 0),
|
||||
(0, 0, {
|
||||
'partner_id': ref('base.res_partner_12'),
|
||||
'state': 'onboard'
|
||||
}),
|
||||
(0, 0, {
|
||||
'partner_id': ref('base.res_partner_2'),
|
||||
'state': 'onboard'
|
||||
}),
|
||||
]"
|
||||
/>
|
||||
<field name="checkin" eval="DateTime.today()" />
|
||||
<field name="checkout" eval="DateTime.today() + timedelta(4)" />
|
||||
<field name="board_service_room_id" ref="pms_board_service_room_2" />
|
||||
|
||||
@@ -5700,12 +5700,12 @@ msgid "End Date"
|
||||
msgstr "Fecha de finalización"
|
||||
|
||||
#. module: hotel
|
||||
#: model:ir.model.fields,field_description:hotel.field_hotel_checkin_partner_enter_date
|
||||
#: model:ir.model.fields,field_description:hotel.field_hotel_checkin_partner_arrival
|
||||
msgid "Enter Date"
|
||||
msgstr "Fecha de entrada"
|
||||
|
||||
#. module: hotel
|
||||
#: model:ir.model.fields,field_description:hotel.field_hotel_checkin_partner_exit_date
|
||||
#: model:ir.model.fields,field_description:hotel.field_hotel_checkin_partner_departure
|
||||
msgid "Exit Date"
|
||||
msgstr "Fecha salida"
|
||||
|
||||
@@ -5877,11 +5877,6 @@ msgstr "Generica"
|
||||
msgid "Get in"
|
||||
msgstr "Entrar"
|
||||
|
||||
#. module: hotel
|
||||
#: model:ir.model.fields,field_description:hotel.field_hotel_checkin_partner_auto_booking
|
||||
msgid "Get in Now"
|
||||
msgstr "Entra ahora!"
|
||||
|
||||
#. module: hotel
|
||||
#: model:ir.model.fields,help:hotel.field_hotel_room_type_packaging_ids
|
||||
msgid "Gives the different ways to package the same product."
|
||||
@@ -6671,7 +6666,7 @@ msgstr "Mail"
|
||||
#: model:ir.model.fields,help:hotel.field_hotel_room_type_property_valuation
|
||||
msgid ""
|
||||
"Manual: The accounting entries to value the inventory are not posted automatically.\n"
|
||||
" Automated: An accounting entry is automatically created to value the inventory when a product enters or leaves the company."
|
||||
" Automated: An accounting arrival is automatically created to value the inventory when a product enters or leaves the company."
|
||||
msgstr ""
|
||||
"Manual: Los registros contables de valoración del inventario no se publican automáticamente.\n"
|
||||
" Automatizado: Se crea automáticamente un registro contable para evaluar el inventario cuando un producto entra o sale de la empresa."
|
||||
@@ -7215,7 +7210,7 @@ msgstr "Pagos"
|
||||
|
||||
#. module: hotel
|
||||
#: selection:hotel.checkin.partner,state:0 selection:hotel.reservation,state:0
|
||||
msgid "Pending Entry"
|
||||
msgid "Pending arrival"
|
||||
msgstr "Por entrar"
|
||||
|
||||
#. module: hotel
|
||||
|
||||
@@ -31,6 +31,7 @@ from . import pms_checkin_partner
|
||||
from . import product_pricelist
|
||||
from . import product_pricelist_item
|
||||
from . import res_partner
|
||||
from . import pms_sale_channel
|
||||
|
||||
# from . import mail_compose_message
|
||||
from . import pms_room_type_class
|
||||
|
||||
@@ -1,229 +1,171 @@
|
||||
# Copyright 2017 Dario Lodeiros
|
||||
# Copyright 2018 Alexandre Diaz
|
||||
# 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, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
|
||||
class PmsCheckinPartner(models.Model):
|
||||
_name = "pms.checkin.partner"
|
||||
_description = "Partner Checkins"
|
||||
|
||||
# Default Methods ang Gets
|
||||
def _default_reservation_id(self):
|
||||
if "reservation_id" in self.env.context:
|
||||
reservation = self.env["pms.reservation"].browse(
|
||||
[self.env.context["reservation_id"]]
|
||||
)
|
||||
return reservation
|
||||
return False
|
||||
|
||||
def _default_partner_id(self):
|
||||
if "reservation_id" in self.env.context:
|
||||
reservation = self.env["pms.reservation"].browse(
|
||||
[self.env.context["reservation_id"]]
|
||||
)
|
||||
partner_ids = []
|
||||
if reservation.folio_id:
|
||||
for room in reservation.folio_id.reservation_ids:
|
||||
partner_ids.append(room.mapped("checkin_partner_ids.partner_id.id"))
|
||||
if "checkin_partner_ids" in self.env.context:
|
||||
for checkin in self.env.context["checkin_partner_ids"]:
|
||||
if checkin[0] == 0:
|
||||
partner_ids.append(checkin[2].get("partner_id"))
|
||||
if (
|
||||
self._context.get("include_customer")
|
||||
and reservation.partner_id.id not in partner_ids
|
||||
and not reservation.partner_id.is_company
|
||||
):
|
||||
return reservation.partner_id
|
||||
return False
|
||||
|
||||
def _default_folio_id(self):
|
||||
if "folio_id" in self.env.context:
|
||||
folio = self.env["pms.folio"].browse([self.env.context["folio_id"]])
|
||||
return folio
|
||||
if "reservation_id" in self.env.context:
|
||||
folio = (
|
||||
self.env["pms.reservation"]
|
||||
.browse([self.env.context["reservation_id"]])
|
||||
.folio_id
|
||||
)
|
||||
return folio
|
||||
return False
|
||||
|
||||
def _default_enter_date(self):
|
||||
if "reservation_id" in self.env.context:
|
||||
reservation = self.env["pms.reservation"].browse(
|
||||
[self.env.context["reservation_id"]]
|
||||
)
|
||||
return reservation.checkin
|
||||
return False
|
||||
|
||||
def _default_exit_date(self):
|
||||
if "reservation_id" in self.env.context:
|
||||
reservation = self.env["pms.reservation"].browse(
|
||||
[self.env.context["reservation_id"]]
|
||||
)
|
||||
return reservation.checkout
|
||||
return False
|
||||
|
||||
@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
|
||||
partner_id = fields.Many2one(
|
||||
"res.partner", default=_default_partner_id, required=True
|
||||
identifier = fields.Char(
|
||||
"Identifier",
|
||||
compute="_compute_identifier",
|
||||
readonly=False,
|
||||
store=True,
|
||||
)
|
||||
reservation_id = fields.Many2one("pms.reservation", default=_default_reservation_id)
|
||||
partner_id = fields.Many2one(
|
||||
"res.partner",
|
||||
domain="[('is_company', '=', False)]",
|
||||
)
|
||||
reservation_id = fields.Many2one("pms.reservation")
|
||||
folio_id = fields.Many2one(
|
||||
"pms.folio", default=_default_folio_id, readonly=True, required=True
|
||||
"pms.folio",
|
||||
compute="_compute_folio_id",
|
||||
store=True,
|
||||
)
|
||||
pms_property_id = fields.Many2one(
|
||||
"pms.property", default=_get_default_pms_property, required=True
|
||||
)
|
||||
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")
|
||||
enter_date = fields.Date(default=_default_enter_date, required=True)
|
||||
exit_date = fields.Date(default=_default_exit_date, required=True)
|
||||
arrival_hour = fields.Char("Arrival Hour", help="Default Arrival Hour (HH:MM)")
|
||||
departure_hour = fields.Char(
|
||||
"Departure Hour", help="Default Departure Hour (HH:MM)"
|
||||
image_128 = fields.Image(related="partner_id.image_128")
|
||||
segmentation_ids = fields.Many2many(
|
||||
related="reservation_id.segmentation_ids",
|
||||
readonly=True,
|
||||
)
|
||||
auto_booking = fields.Boolean("Get in Now", default=False)
|
||||
arrival = fields.Datetime("Enter")
|
||||
departure = fields.Datetime("Exit")
|
||||
state = fields.Selection(
|
||||
selection=[
|
||||
("draft", "Pending Entry"),
|
||||
("draft", "Unkown Guest"),
|
||||
("precheckin", "Pending arrival"),
|
||||
("onboard", "On Board"),
|
||||
("done", "Out"),
|
||||
("cancelled", "Cancelled"),
|
||||
],
|
||||
string="State",
|
||||
compute="_compute_state",
|
||||
store=True,
|
||||
readonly=True,
|
||||
default=lambda *a: "draft",
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
# Compute
|
||||
@api.depends("reservation_id", "folio_id", "reservation_id.preferred_room_id")
|
||||
def _compute_identifier(self):
|
||||
for record in self:
|
||||
# TODO: Identifier
|
||||
checkins = []
|
||||
if record.reservation_id.filtered("preferred_room_id"):
|
||||
checkins = record.reservation_id.checkin_partner_ids
|
||||
record.identifier = (
|
||||
record.reservation_id.preferred_room_id.name
|
||||
+ "-"
|
||||
+ str(len(checkins) - 1)
|
||||
)
|
||||
elif record.folio_id:
|
||||
record.identifier = record.folio_id.name + "-" + str(len(checkins) - 1)
|
||||
else:
|
||||
record.identifier = False
|
||||
|
||||
@api.depends("reservation_id", "reservation_id.folio_id")
|
||||
def _compute_folio_id(self):
|
||||
for record in self.filtered("reservation_id"):
|
||||
record.folio_id = record.reservation_id.folio_id
|
||||
|
||||
@api.depends(lambda self: self._checkin_mandatory_fields(depends=True))
|
||||
def _compute_state(self):
|
||||
for record in self:
|
||||
if not record.state:
|
||||
record.state = "draft"
|
||||
if record.reservation_id.state == "cancelled":
|
||||
record.state = "cancelled"
|
||||
elif record.state in ("draft", "cancelled"):
|
||||
if any(
|
||||
not getattr(record, field)
|
||||
for field in record._checkin_mandatory_fields()
|
||||
):
|
||||
record.state = "draft"
|
||||
else:
|
||||
record.state = "precheckin"
|
||||
|
||||
@api.model
|
||||
def _checkin_mandatory_fields(self, depends=False):
|
||||
# api.depends need "reservation_id.state" in de lambda function
|
||||
if depends:
|
||||
return ["reservation_id.state", "name"]
|
||||
return ["name"]
|
||||
|
||||
# Constraints and onchanges
|
||||
|
||||
@api.constrains("exit_date", "enter_date")
|
||||
def _check_exit_date(self):
|
||||
@api.constrains("departure", "arrival")
|
||||
def _check_departure(self):
|
||||
for record in self:
|
||||
date_in = fields.Date.from_string(record.enter_date)
|
||||
date_out = fields.Date.from_string(record.exit_date)
|
||||
if date_out < date_in:
|
||||
raise models.ValidationError(
|
||||
if record.departure and record.arrival > record.departure:
|
||||
raise ValidationError(
|
||||
_("Departure date (%s) is prior to arrival on %s")
|
||||
% (date_out, date_in)
|
||||
% (record.departure, record.arrival)
|
||||
)
|
||||
|
||||
@api.onchange("enter_date", "exit_date")
|
||||
def _onchange_enter_date(self):
|
||||
date_in = fields.Date.from_string(self.enter_date)
|
||||
date_out = fields.Date.from_string(self.exit_date)
|
||||
if date_out <= date_in:
|
||||
date_out = date_in + datetime.timedelta(days=1)
|
||||
self.update({"exit_date": date_out})
|
||||
raise ValidationError(
|
||||
_("Departure date, is prior to arrival. Check it now. %s") % date_out
|
||||
)
|
||||
|
||||
@api.onchange("partner_id")
|
||||
@api.constrains("partner_id")
|
||||
def _check_partner_id(self):
|
||||
for record in self:
|
||||
if record.partner_id:
|
||||
if record.partner_id.is_company:
|
||||
raise models.ValidationError(
|
||||
_(
|
||||
"A Checkin Guest is configured like a company, \
|
||||
modify it in contact form if its a mistake"
|
||||
)
|
||||
)
|
||||
indoor_partner_ids = record.reservation_id.checkin_partner_ids.filtered(
|
||||
lambda r: r.id != record.id
|
||||
).mapped("partner_id.id")
|
||||
if indoor_partner_ids.count(record.partner_id.id) > 1:
|
||||
record.partner_id = None
|
||||
raise models.ValidationError(
|
||||
raise ValidationError(
|
||||
_("This guest is already registered in the room")
|
||||
)
|
||||
|
||||
# CRUD
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# The checkin records are created automatically from adult depends
|
||||
# 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")
|
||||
if reservation_id:
|
||||
reservation = self.env["pms.reservation"].browse(reservation_id)
|
||||
draft_checkins = reservation.checkin_partner_ids.filtered(
|
||||
lambda c: c.state == "draft"
|
||||
)
|
||||
if len(draft_checkins) > 0 and vals.get("partner_id"):
|
||||
draft_checkins[0].sudo().unlink()
|
||||
return super(PmsCheckinPartner, self).create(vals)
|
||||
|
||||
# Action methods
|
||||
|
||||
def action_on_board(self):
|
||||
for record in self:
|
||||
if record.reservation_id.checkin > fields.Date.today():
|
||||
raise models.ValidationError(_("It is not yet checkin day!"))
|
||||
hour = record._get_arrival_hour()
|
||||
raise ValidationError(_("It is not yet checkin day!"))
|
||||
if record.reservation_id.checkout <= fields.Date.today():
|
||||
raise ValidationError(_("Its too late to checkin"))
|
||||
vals = {
|
||||
"state": "onboard",
|
||||
"arrival_hour": hour,
|
||||
"arrival": fields.Datetime.now(),
|
||||
}
|
||||
record.update(vals)
|
||||
if record.reservation_id.state == "confirm":
|
||||
if record.reservation_id.left_for_checkin:
|
||||
record.reservation_id.state = "onboard"
|
||||
return {
|
||||
"type": "ir.actions.do_nothing",
|
||||
}
|
||||
|
||||
def action_done(self):
|
||||
for record in self:
|
||||
if record.state == "onboard":
|
||||
hour = record._get_departure_hour()
|
||||
vals = {
|
||||
"state": "done",
|
||||
"departure_hour": hour,
|
||||
}
|
||||
record.update(vals)
|
||||
for record in self.filtered(lambda c: c.state == "onboard"):
|
||||
vals = {
|
||||
"state": "done",
|
||||
"departure": fields.Datetime.now(),
|
||||
}
|
||||
record.update(vals)
|
||||
return True
|
||||
|
||||
# ORM Overrides
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
record = super(PmsCheckinPartner, self).create(vals)
|
||||
if vals.get("auto_booking", False):
|
||||
record.action_on_board()
|
||||
return record
|
||||
|
||||
# Business methods
|
||||
def _get_arrival_hour(self):
|
||||
self.ensure_one()
|
||||
tz_property = self.env.user.pms_property_id.tz
|
||||
today = fields.Datetime.context_timestamp(
|
||||
self.with_context(tz=tz_property),
|
||||
datetime.datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT),
|
||||
)
|
||||
default_arrival_hour = self.env.user.pms_property_id.default_arrival_hour
|
||||
if self.reservation_id.checkin < today.strftime(DEFAULT_SERVER_DATE_FORMAT):
|
||||
return default_arrival_hour
|
||||
now = fields.Datetime.context_timestamp(
|
||||
self.with_context(tz=tz_property),
|
||||
datetime.datetime.strptime(
|
||||
fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT
|
||||
),
|
||||
)
|
||||
arrival_hour = now.strftime("%H:%M")
|
||||
return arrival_hour
|
||||
|
||||
def _get_departure_hour(self):
|
||||
self.ensure_one()
|
||||
tz_property = self.env.user.pms_property_id.tz
|
||||
today = fields.Datetime.context_timestamp(
|
||||
self.with_context(tz=tz_property),
|
||||
datetime.datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT),
|
||||
)
|
||||
default_departure_hour = self.env.user.pms_property_id.default_departure_hour
|
||||
if self.reservation_id.checkout < today.strftime(DEFAULT_SERVER_DATE_FORMAT):
|
||||
return default_departure_hour
|
||||
now = fields.Datetime.context_timestamp(
|
||||
self.with_context(tz=tz_property),
|
||||
datetime.datetime.strptime(
|
||||
fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT
|
||||
),
|
||||
)
|
||||
departure_hour = now.strftime("%H:%M")
|
||||
return departure_hour
|
||||
|
||||
@@ -50,7 +50,9 @@ class PmsFolio(models.Model):
|
||||
pms_property_id = fields.Many2one(
|
||||
"pms.property", default=_get_default_pms_property, required=True
|
||||
)
|
||||
partner_id = fields.Many2one("res.partner", tracking=True, ondelete="restrict")
|
||||
partner_id = fields.Many2one(
|
||||
"res.partner", compute="_compute_partner_id", tracking=True, ondelete="restrict"
|
||||
)
|
||||
reservation_ids = fields.One2many(
|
||||
"pms.reservation",
|
||||
"folio_id",
|
||||
@@ -102,6 +104,13 @@ class PmsFolio(models.Model):
|
||||
readonly=False,
|
||||
help="Pricelist for current folio.",
|
||||
)
|
||||
commission = fields.Float(
|
||||
string="Commission",
|
||||
compute="_compute_commission",
|
||||
store=True,
|
||||
readonly=True,
|
||||
default=0,
|
||||
)
|
||||
user_id = fields.Many2one(
|
||||
"res.users",
|
||||
string="Salesperson",
|
||||
@@ -114,10 +123,16 @@ class PmsFolio(models.Model):
|
||||
)
|
||||
agency_id = fields.Many2one(
|
||||
"res.partner",
|
||||
"Agency",
|
||||
string="Agency",
|
||||
ondelete="restrict",
|
||||
domain=[("is_agency", "=", True)],
|
||||
)
|
||||
channel_type_id = fields.Many2one(
|
||||
"pms.sale.channel",
|
||||
string="Direct Sale Channel",
|
||||
ondelete="restrict",
|
||||
domain=[("channel_type", "=", "direct")],
|
||||
)
|
||||
payment_ids = fields.One2many("account.payment", "folio_id", readonly=True)
|
||||
# return_ids = fields.One2many("payment.return", "folio_id", readonly=True)
|
||||
payment_term_id = fields.Many2one(
|
||||
@@ -163,15 +178,6 @@ class PmsFolio(models.Model):
|
||||
string="Type",
|
||||
default=lambda *a: "normal",
|
||||
)
|
||||
channel_type = fields.Selection(
|
||||
[
|
||||
("direct", "Direct"),
|
||||
("agency", "Agency"),
|
||||
],
|
||||
string="Sales Channel",
|
||||
compute="_compute_channel_type",
|
||||
store=True,
|
||||
)
|
||||
date_order = fields.Datetime(
|
||||
string="Order Date",
|
||||
required=True,
|
||||
@@ -244,14 +250,13 @@ class PmsFolio(models.Model):
|
||||
tracking=True,
|
||||
)
|
||||
# Checkin Fields-----------------------------------------------------
|
||||
booking_pending = fields.Integer(
|
||||
"Booking pending", compute="_compute_checkin_partner_count"
|
||||
reservation_pending_arrival_ids = fields.One2many(
|
||||
comodel_name="pms.checkin.partner",
|
||||
string="Pending Arrival Rooms",
|
||||
compute="_compute_reservations_pending_arrival",
|
||||
)
|
||||
checkin_partner_count = fields.Integer(
|
||||
"Checkin counter", compute="_compute_checkin_partner_count"
|
||||
)
|
||||
checkin_partner_pending_count = fields.Integer(
|
||||
"Checkin Pending", compute="_compute_checkin_partner_count"
|
||||
reservations_pending_count = fields.Integer(
|
||||
compute="_compute_reservations_pending_arrival"
|
||||
)
|
||||
# Invoice Fields-----------------------------------------------------
|
||||
invoice_status = fields.Selection(
|
||||
@@ -285,17 +290,26 @@ class PmsFolio(models.Model):
|
||||
folio.reservation_ids.filtered(lambda a: a.state != "cancelled")
|
||||
)
|
||||
|
||||
@api.depends("partner_id")
|
||||
@api.depends("partner_id", "agency_id")
|
||||
def _compute_pricelist_id(self):
|
||||
for folio in self:
|
||||
pricelist_id = (
|
||||
folio.partner_id.property_product_pricelist
|
||||
and folio.partner_id.property_product_pricelist.id
|
||||
or self.env.user.pms_property_id.default_pricelist_id.id
|
||||
)
|
||||
if folio.partner_id and folio.partner_id.property_product_pricelist:
|
||||
pricelist_id = folio.partner_id.property_product_pricelist.id
|
||||
else:
|
||||
pricelist_id = self.env.user.pms_property_id.default_pricelist_id.id
|
||||
if folio.pricelist_id.id != pricelist_id:
|
||||
# TODO: Warning change de pricelist?
|
||||
folio.pricelist_id = pricelist_id
|
||||
if folio.agency_id and folio.agency_id.apply_pricelist:
|
||||
pricelist_id = folio.agency_id.property_product_pricelist.id
|
||||
|
||||
@api.depends("agency_id")
|
||||
def _compute_partner_id(self):
|
||||
for folio in self:
|
||||
if folio.agency_id and folio.agency_id.invoice_agency:
|
||||
folio.partner_id = folio.agency_id.id
|
||||
elif not folio.partner_id:
|
||||
folio.partner_id = False
|
||||
|
||||
@api.depends("partner_id")
|
||||
def _compute_user_id(self):
|
||||
@@ -309,24 +323,25 @@ class PmsFolio(models.Model):
|
||||
addr = folio.partner_id.address_get(["invoice"])
|
||||
folio.partner_invoice_id = addr["invoice"]
|
||||
|
||||
@api.depends("agency_id")
|
||||
def _compute_channel_type(self):
|
||||
for folio in self:
|
||||
if folio.agency_id:
|
||||
folio.channel_type = "agency"
|
||||
else:
|
||||
folio.channel_type = "direct"
|
||||
|
||||
@api.depends("partner_id")
|
||||
def _compute_payment_term_id(self):
|
||||
self.payment_term_id = False
|
||||
for folio in self:
|
||||
folio.payment_term_id = (
|
||||
self.partner_id.property_payment_term_id
|
||||
and self.partner_id.property_payment_term_id.id
|
||||
folio.partner_id.property_payment_term_id
|
||||
and folio.partner_id.property_payment_term_id.id
|
||||
or False
|
||||
)
|
||||
|
||||
@api.depends("reservation_ids")
|
||||
def _compute_commission(self):
|
||||
for folio in self:
|
||||
for reservation in folio.reservation_ids:
|
||||
if reservation.commission_amount != 0:
|
||||
folio.commission += reservation.commission_amount
|
||||
else:
|
||||
folio.commission = 0
|
||||
|
||||
@api.depends(
|
||||
"state", "reservation_ids.invoice_status", "service_ids.invoice_status"
|
||||
)
|
||||
@@ -444,6 +459,16 @@ class PmsFolio(models.Model):
|
||||
}
|
||||
)
|
||||
|
||||
@api.depends("reservation_ids", "reservation_ids.state")
|
||||
def _compute_reservations_pending_arrival(self):
|
||||
for record in self:
|
||||
record.reservation_pending_arrival_ids = record.reservation_ids.filtered(
|
||||
lambda r: r.state in ("draft", "precheckin")
|
||||
)
|
||||
record.reservations_pending_count = len(
|
||||
record.reservations_pending_arrival_ids
|
||||
)
|
||||
|
||||
# TODO: Add return_ids to depends
|
||||
@api.depends("amount_total", "payment_ids", "reservation_type", "state")
|
||||
def _compute_amount(self):
|
||||
@@ -658,3 +683,10 @@ class PmsFolio(models.Model):
|
||||
(line[0].name, line[1]["amount"], line[1]["base"], len(res)) for line in res
|
||||
]
|
||||
return res
|
||||
|
||||
# Check that only one sale channel is selected
|
||||
@api.constrains("agency_id", "channel_type_id")
|
||||
def _check_only_one_channel(self):
|
||||
for record in self:
|
||||
if record.agency_id and record.channel_type_id:
|
||||
raise models.ValidationError(_("There must be only one sale channel"))
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
# Copyright 2019 Dario Lodeiros
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
import pytz
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from odoo.addons.base.models.res_partner import _tz_get
|
||||
|
||||
|
||||
class PmsProperty(models.Model):
|
||||
_name = "pms.property"
|
||||
@@ -54,12 +58,50 @@ class PmsProperty(models.Model):
|
||||
folio_sequence_id = fields.Many2one(
|
||||
"ir.sequence", "Folio Sequence", check_company=True, copy=False
|
||||
)
|
||||
tz = fields.Selection(
|
||||
_tz_get,
|
||||
string="Timezone",
|
||||
required=True,
|
||||
default=lambda self: self.env.user.tz or "UTC",
|
||||
help="This field is used in order to define \
|
||||
in which timezone the arrival/departure will work.",
|
||||
)
|
||||
|
||||
# Constraints and onchanges
|
||||
@api.constrains("default_arrival_hour", "default_departure_hour")
|
||||
def _check_hours(self):
|
||||
r = re.compile("[0-2][0-9]:[0-5][0-9]")
|
||||
if not r.match(self.default_arrival_hour):
|
||||
raise ValidationError(_("Invalid arrival hour (Format: HH:mm)"))
|
||||
if not r.match(self.default_departure_hour):
|
||||
raise ValidationError(_("Invalid departure hour (Format: HH:mm)"))
|
||||
@api.constrains("default_arrival_hour")
|
||||
def _check_arrival_hour(self):
|
||||
for record in self:
|
||||
try:
|
||||
time.strptime(record.default_arrival_hour, "%H:%M")
|
||||
return True
|
||||
except ValueError:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Format Arrival Hour (HH:MM) Error: %s",
|
||||
record.default_arrival_hour,
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("default_departure_hour")
|
||||
def _check_departure_hour(self):
|
||||
for record in self:
|
||||
try:
|
||||
time.strptime(record.default_departure_hour, "%H:%M")
|
||||
return True
|
||||
except ValueError:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Format Departure Hour (HH:MM) Error: %s",
|
||||
record.default_departure_hour,
|
||||
)
|
||||
)
|
||||
|
||||
def date_property_timezone(self, date):
|
||||
self.ensure_one()
|
||||
tz_property = self.tz
|
||||
date = pytz.timezone(tz_property).localize(date)
|
||||
date = date.replace(tzinfo=None)
|
||||
date = pytz.timezone(self.env.user.tz).localize(date)
|
||||
date = date.astimezone(pytz.utc)
|
||||
date = date.replace(tzinfo=None)
|
||||
return date
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# Copyright 2017-2018 Alexandre Díaz
|
||||
# Copyright 2017 Dario Lodeiros
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import datetime
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
import time
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, float_compare, float_is_zero
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -39,7 +40,7 @@ class PmsReservation(models.Model):
|
||||
if folio and folio.reservation_ids:
|
||||
return folio.reservation_ids[0].checkout
|
||||
else:
|
||||
return fields.Date.today() + timedelta(1)
|
||||
return fields.Date.today() + datetime.timedelta(1)
|
||||
|
||||
def _get_default_arrival_hour(self):
|
||||
folio = False
|
||||
@@ -146,7 +147,14 @@ class PmsReservation(models.Model):
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
agency_id = fields.Many2one(related="folio_id.agency_id")
|
||||
agency_id = fields.Many2one(
|
||||
related="folio_id.agency_id",
|
||||
readonly=True,
|
||||
)
|
||||
channel_type_id = fields.Many2one(
|
||||
related="folio_id.channel_type_id",
|
||||
readonly=True,
|
||||
)
|
||||
partner_invoice_id = fields.Many2one(
|
||||
"res.partner",
|
||||
string="Invoice Address",
|
||||
@@ -185,8 +193,53 @@ class PmsReservation(models.Model):
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
commission_percent = fields.Float(
|
||||
string="Commission percent (%)",
|
||||
compute="_compute_commission_percent",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
commission_amount = fields.Float(
|
||||
string="Commission amount",
|
||||
compute="_compute_commission_amount",
|
||||
store=True,
|
||||
)
|
||||
# TODO: Warning Mens to update pricelist
|
||||
checkin_partner_ids = fields.One2many("pms.checkin.partner", "reservation_id")
|
||||
checkin_partner_ids = fields.One2many(
|
||||
"pms.checkin.partner",
|
||||
"reservation_id",
|
||||
compute="_compute_checkin_partner_ids",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
count_pending_arrival = fields.Integer(
|
||||
"Pending Arrival",
|
||||
compute="_compute_count_pending_arrival",
|
||||
store=True,
|
||||
)
|
||||
checkins_ratio = fields.Integer(
|
||||
string="Pending Arrival Ratio",
|
||||
compute="_compute_checkins_ratio",
|
||||
)
|
||||
pending_checkin_data = fields.Integer(
|
||||
"Checkin Data",
|
||||
compute="_compute_pending_checkin_data",
|
||||
store=True,
|
||||
)
|
||||
ratio_checkin_data = fields.Integer(
|
||||
string="Pending Checkin Data",
|
||||
compute="_compute_ratio_checkin_data",
|
||||
)
|
||||
ready_for_checkin = fields.Boolean(compute="_compute_ready_for_checkin")
|
||||
left_for_checkin = fields.Boolean(
|
||||
compute="_compute_left_for_checkin", search="_search_left_for_checkin"
|
||||
)
|
||||
checkin_today = fields.Boolean(
|
||||
compute="_compute_checkin_today", search="_search_checkin_today"
|
||||
)
|
||||
departure_today = fields.Boolean(
|
||||
compute="_compute_departure_today", search="_search_departure_today"
|
||||
)
|
||||
segmentation_ids = fields.Many2many(
|
||||
"res.partner.category",
|
||||
string="Segmentation",
|
||||
@@ -239,14 +292,17 @@ class PmsReservation(models.Model):
|
||||
state = fields.Selection(
|
||||
[
|
||||
("draft", "Pre-reservation"),
|
||||
("confirm", "Pending Entry"),
|
||||
("confirm", "Pending arrival"),
|
||||
("onboard", "On Board"),
|
||||
("done", "Out"),
|
||||
("cancelled", "Cancelled"),
|
||||
("no_show", "No Show"),
|
||||
("no_checkout", "No Checkout"),
|
||||
],
|
||||
string="Status",
|
||||
default=lambda *a: "draft",
|
||||
copy=False,
|
||||
index=True,
|
||||
tracking=True,
|
||||
readonly=True,
|
||||
)
|
||||
@@ -280,6 +336,14 @@ class PmsReservation(models.Model):
|
||||
default=_get_default_departure_hour,
|
||||
help="Default Departure Hour (HH:MM)",
|
||||
)
|
||||
checkin_datetime = fields.Datetime(
|
||||
"Exact Arrival",
|
||||
compute="_compute_checkin_datetime",
|
||||
)
|
||||
checkout_datetime = fields.Datetime(
|
||||
"Exact Departure",
|
||||
compute="_compute_checkout_datetime",
|
||||
)
|
||||
# TODO: As checkin_partner_count is a computed field, it can't not
|
||||
# be used in a domain filer Non-stored field
|
||||
# pms.reservation.checkin_partner_count cannot be searched
|
||||
@@ -296,22 +360,6 @@ class PmsReservation(models.Model):
|
||||
overbooking = fields.Boolean("Is Overbooking", default=False)
|
||||
reselling = fields.Boolean("Is Reselling", default=False)
|
||||
nights = fields.Integer("Nights", compute="_compute_nights", store=True)
|
||||
channel_type = fields.Selection(
|
||||
selection=[
|
||||
("direct", "Direct"),
|
||||
("agency", "Agency"),
|
||||
],
|
||||
string="Sales Channel",
|
||||
default="direct",
|
||||
)
|
||||
subchannel_direct = fields.Selection(
|
||||
selection=[
|
||||
("door", "Door"),
|
||||
("mail", "Mail"),
|
||||
("phone", "Phone"),
|
||||
],
|
||||
string="Direct Channel",
|
||||
)
|
||||
origin = fields.Char("Origin", compute="_compute_origin", store=True)
|
||||
detail_origin = fields.Char(
|
||||
"Detail Origin", compute="_compute_detail_origin", store=True
|
||||
@@ -426,6 +474,32 @@ class PmsReservation(models.Model):
|
||||
elif not reservation.room_type_id:
|
||||
reservation.room_type_id = False
|
||||
|
||||
@api.depends("checkin", "arrival_hour")
|
||||
def _compute_checkin_datetime(self):
|
||||
for reservation in self:
|
||||
checkin_hour = int(reservation.arrival_hour[0:2])
|
||||
checkin_minut = int(reservation.arrival_hour[3:5])
|
||||
checkin_time = datetime.time(checkin_hour, checkin_minut)
|
||||
checkin_datetime = datetime.datetime.combine(
|
||||
reservation.checkin, checkin_time
|
||||
)
|
||||
reservation.checkin_datetime = (
|
||||
reservation.pms_property_id.date_property_timezone(checkin_datetime)
|
||||
)
|
||||
|
||||
@api.depends("checkout", "departure_hour")
|
||||
def _compute_checkout_datetime(self):
|
||||
for reservation in self:
|
||||
checkout_hour = int(reservation.departure_hour[0:2])
|
||||
checkout_minut = int(reservation.departure_hour[3:5])
|
||||
checkout_time = datetime.time(checkout_hour, checkout_minut)
|
||||
checkout_datetime = datetime.datetime.combine(
|
||||
reservation.checkout, checkout_time
|
||||
)
|
||||
reservation.checkout_datetime = (
|
||||
reservation.pms_property_id.date_property_timezone(checkout_datetime)
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"reservation_line_ids.date", "overbooking", "state", "preferred_room_id"
|
||||
)
|
||||
@@ -447,7 +521,7 @@ class PmsReservation(models.Model):
|
||||
)
|
||||
reservation.allowed_room_ids = rooms_available
|
||||
|
||||
@api.depends("reservation_type")
|
||||
@api.depends("reservation_type", "agency_id")
|
||||
def _compute_partner_id(self):
|
||||
for reservation in self:
|
||||
if reservation.reservation_type == "out":
|
||||
@@ -456,6 +530,8 @@ class PmsReservation(models.Model):
|
||||
reservation.partner_id = reservation.folio_id.partner_id
|
||||
else:
|
||||
reservation.partner_id = False
|
||||
if not reservation.partner_id and reservation.agency_id:
|
||||
reservation.partner_id = reservation.agency_id
|
||||
|
||||
@api.depends("partner_id")
|
||||
def _compute_partner_invoice_id(self):
|
||||
@@ -472,7 +548,7 @@ class PmsReservation(models.Model):
|
||||
cmds = []
|
||||
days_diff = (reservation.checkout - reservation.checkin).days
|
||||
for i in range(0, days_diff):
|
||||
idate = reservation.checkin + timedelta(days=i)
|
||||
idate = reservation.checkin + datetime.timedelta(days=i)
|
||||
old_line = reservation.reservation_line_ids.filtered(
|
||||
lambda r: r.date == idate
|
||||
)
|
||||
@@ -534,6 +610,180 @@ class PmsReservation(models.Model):
|
||||
# TODO: Warning change de pricelist?
|
||||
reservation.pricelist_id = pricelist_id
|
||||
|
||||
@api.depends("adults")
|
||||
def _compute_checkin_partner_ids(self):
|
||||
for reservation in self:
|
||||
assigned_checkins = reservation.checkin_partner_ids.filtered(
|
||||
lambda c: c.state in ("precheckin", "onboard", "done")
|
||||
)
|
||||
unassigned_checkins = reservation.checkin_partner_ids.filtered(
|
||||
lambda c: c.state == "draft"
|
||||
)
|
||||
leftover_unassigneds_count = (
|
||||
len(assigned_checkins) + len(unassigned_checkins) - reservation.adults
|
||||
)
|
||||
if len(assigned_checkins) > reservation.adults:
|
||||
raise UserError(
|
||||
_("Remove some of the leftover assigned checkins first")
|
||||
)
|
||||
elif leftover_unassigneds_count > 0:
|
||||
for i in range(0, leftover_unassigneds_count):
|
||||
unassigned_checkins[i].sudo().unlink()
|
||||
elif reservation.adults > len(reservation.checkin_partner_ids):
|
||||
checkins_lst = []
|
||||
count_new_checkins = reservation.adults - len(
|
||||
reservation.checkin_partner_ids
|
||||
)
|
||||
for _i in range(0, count_new_checkins):
|
||||
checkins_lst.append(
|
||||
(
|
||||
0,
|
||||
False,
|
||||
{
|
||||
"reservation_id": reservation.id,
|
||||
},
|
||||
)
|
||||
)
|
||||
reservation.with_context(
|
||||
{"auto_create_checkin": True}
|
||||
).checkin_partner_ids = checkins_lst
|
||||
|
||||
@api.depends("checkin_partner_ids", "checkin_partner_ids.state")
|
||||
def _compute_count_pending_arrival(self):
|
||||
for reservation in self:
|
||||
reservation.count_pending_arrival = len(
|
||||
reservation.checkin_partner_ids.filtered(
|
||||
lambda c: c.state in ("draft", "precheckin")
|
||||
)
|
||||
)
|
||||
|
||||
@api.depends("count_pending_arrival")
|
||||
def _compute_checkins_ratio(self):
|
||||
self.checkins_ratio = 0
|
||||
for reservation in self.filtered(lambda r: r.adults > 0):
|
||||
reservation.checkins_ratio = (
|
||||
(reservation.adults - reservation.count_pending_arrival)
|
||||
* 100
|
||||
/ reservation.adults
|
||||
)
|
||||
|
||||
@api.depends("checkin_partner_ids", "checkin_partner_ids.state")
|
||||
def _compute_pending_checkin_data(self):
|
||||
for reservation in self:
|
||||
reservation.pending_checkin_data = len(
|
||||
reservation.checkin_partner_ids.filtered(lambda c: c.state == "draft")
|
||||
)
|
||||
|
||||
@api.depends("pending_checkin_data")
|
||||
def _compute_ratio_checkin_data(self):
|
||||
self.ratio_checkin_data = 0
|
||||
for reservation in self.filtered(lambda r: r.adults > 0):
|
||||
reservation.ratio_checkin_data = (
|
||||
(reservation.adults - reservation.pending_checkin_data)
|
||||
* 100
|
||||
/ reservation.adults
|
||||
)
|
||||
|
||||
def _compute_left_for_checkin(self):
|
||||
# Reservations still pending entry today
|
||||
for record in self:
|
||||
record.left_for_checkin = (
|
||||
True
|
||||
if (
|
||||
record.state in ["draft", "confirm", "no_show"]
|
||||
and record.checkin <= fields.Date.today()
|
||||
)
|
||||
else False
|
||||
)
|
||||
|
||||
def _search_left_for_checkin(self, operator, value):
|
||||
if operator not in ("=",):
|
||||
raise UserError(
|
||||
_("Invalid domain operator %s for left of checkin", operator)
|
||||
)
|
||||
|
||||
if value not in (True,):
|
||||
raise UserError(
|
||||
_("Invalid domain right operand %s for left of checkin", value)
|
||||
)
|
||||
|
||||
today = fields.Date.context_today(self)
|
||||
return [
|
||||
("state", "in", ("draft", "confirm", "no_show")),
|
||||
("checkin", "<=", today),
|
||||
]
|
||||
|
||||
def _compute_ready_for_checkin(self):
|
||||
# Reservations with hosts data enought to checkin
|
||||
for record in self:
|
||||
record.ready_for_checkin = (
|
||||
record.left_for_checkin
|
||||
and len(
|
||||
record.checkin_partner_ids.filtered(
|
||||
lambda c: c.state == "precheckin"
|
||||
)
|
||||
)
|
||||
>= 1
|
||||
)
|
||||
|
||||
def _compute_checkin_today(self):
|
||||
for record in self:
|
||||
record.checkin_today = (
|
||||
True if record.checkin == fields.Date.today() else False
|
||||
)
|
||||
# REVIEW: Late checkin?? (next day)
|
||||
|
||||
def _search_checkin_today(self, operator, value):
|
||||
if operator not in ("=", "!="):
|
||||
raise UserError(_("Invalid domain operator %s", operator))
|
||||
|
||||
if value not in (False, True):
|
||||
raise UserError(_("Invalid domain right operand %s", value))
|
||||
|
||||
today = fields.Date.context_today(self)
|
||||
|
||||
return [("checkin", operator, today)]
|
||||
|
||||
def _compute_departure_today(self):
|
||||
for record in self:
|
||||
record.departure_today = (
|
||||
True if record.checkout == fields.Date.today() else False
|
||||
)
|
||||
|
||||
def _search_departure_today(self, operator, value):
|
||||
if operator not in ("=", "!="):
|
||||
raise UserError(_("Invalid domain operator %s", operator))
|
||||
|
||||
if value not in (False, True):
|
||||
raise UserError(_("Invalid domain right operand %s", value))
|
||||
|
||||
searching_for_true = (operator == "=" and value) or (
|
||||
operator == "!=" and not value
|
||||
)
|
||||
today = fields.Date.context_today(self)
|
||||
|
||||
return [("checkout", searching_for_true, today)]
|
||||
|
||||
@api.depends("agency_id")
|
||||
def _compute_commission_percent(self):
|
||||
for reservation in self:
|
||||
if reservation.agency_id:
|
||||
reservation.commission_percent = (
|
||||
reservation.agency_id.default_commission
|
||||
)
|
||||
else:
|
||||
reservation.commission_percent = 0
|
||||
|
||||
@api.depends("commission_percent", "price_total")
|
||||
def _compute_commission_amount(self):
|
||||
for reservation in self:
|
||||
if reservation.commission_percent > 0:
|
||||
reservation.commission_amount = (
|
||||
reservation.price_total * reservation.commission_percent
|
||||
)
|
||||
else:
|
||||
reservation.commission_amount = 0
|
||||
|
||||
# REVIEW: Dont run with set room_type_id -> room_id(compute)-> No set adults¿?
|
||||
@api.depends("preferred_room_id")
|
||||
def _compute_adults(self):
|
||||
@@ -572,7 +822,7 @@ class PmsReservation(models.Model):
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
for line in self:
|
||||
if line.state in ("draft"):
|
||||
if line.state == "draft":
|
||||
line.invoice_status = "no"
|
||||
elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
|
||||
line.invoice_status = "to invoice"
|
||||
@@ -707,11 +957,63 @@ class PmsReservation(models.Model):
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("checkin_partner_ids")
|
||||
@api.constrains("checkin_partner_ids", "adults")
|
||||
def _max_checkin_partner_ids(self):
|
||||
for record in self:
|
||||
if len(record.checkin_partner_ids) > record.adults + record.children:
|
||||
raise models.ValidationError(_("The room already is completed"))
|
||||
if len(record.checkin_partner_ids) > record.adults:
|
||||
raise models.ValidationError(
|
||||
_("The room already is completed (%s)", record.name)
|
||||
)
|
||||
|
||||
@api.constrains("adults")
|
||||
def _check_adults(self):
|
||||
for record in self:
|
||||
extra_bed = record.service_ids.filtered(
|
||||
lambda r: r.product_id.is_extra_bed is True
|
||||
)
|
||||
for room in record.reservation_line_ids.room_id:
|
||||
if record.adults + record.children_occupying > room.get_capacity(
|
||||
len(extra_bed)
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Persons can't be higher than room capacity (%s)",
|
||||
record.name,
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("state")
|
||||
def _check_onboard_reservation(self):
|
||||
for record in self:
|
||||
if (
|
||||
not record.checkin_partner_ids.filtered(lambda c: c.state == "onboard")
|
||||
and record.state == "onboard"
|
||||
):
|
||||
raise ValidationError(
|
||||
_("No person from reserve %s has arrived", record.name)
|
||||
)
|
||||
|
||||
@api.constrains("arrival_hour")
|
||||
def _check_arrival_hour(self):
|
||||
for record in self:
|
||||
try:
|
||||
time.strptime(record.arrival_hour, "%H:%M")
|
||||
return True
|
||||
except ValueError:
|
||||
raise ValidationError(
|
||||
_("Format Arrival Hour (HH:MM) Error: %s", record.arrival_hour)
|
||||
)
|
||||
|
||||
@api.constrains("departure_hour")
|
||||
def _check_departure_hour(self):
|
||||
for record in self:
|
||||
try:
|
||||
time.strptime(record.departure_hour, "%H:%M")
|
||||
return True
|
||||
except ValueError:
|
||||
raise ValidationError(
|
||||
_("Format Departure Hour (HH:MM) Error: %s", record.departure_hour)
|
||||
)
|
||||
|
||||
# @api.constrains("reservation_type", "partner_id")
|
||||
# def _check_partner_reservation(self):
|
||||
@@ -734,12 +1036,6 @@ class PmsReservation(models.Model):
|
||||
# _("Only the out reservations can has a clousure reason")
|
||||
# )
|
||||
|
||||
# @api.onchange("checkin_partner_ids")
|
||||
# def onchange_checkin_partner_ids(self):
|
||||
# for record in self:
|
||||
# if len(record.checkin_partner_ids) > record.adults + record.children:
|
||||
# raise models.ValidationError(_("The room already is completed"))
|
||||
|
||||
# self._compute_tax_ids() TODO: refact
|
||||
|
||||
# Action methods
|
||||
@@ -833,19 +1129,11 @@ class PmsReservation(models.Model):
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if "folio_id" in vals and "channel_type" not in vals:
|
||||
if "folio_id" in vals:
|
||||
folio = self.env["pms.folio"].browse(vals["folio_id"])
|
||||
channel_type = (
|
||||
vals["channel_type"] if "channel_type" in vals else folio.channel_type
|
||||
)
|
||||
partner_id = (
|
||||
vals["partner_id"] if "partner_id" in vals else folio.partner_id.id
|
||||
)
|
||||
vals.update({"channel_type": channel_type, "partner_id": partner_id})
|
||||
elif "partner_id" in vals:
|
||||
folio_vals = {
|
||||
"partner_id": int(vals.get("partner_id")),
|
||||
"channel_type": vals.get("channel_type"),
|
||||
}
|
||||
# Create the folio in case of need
|
||||
# (To allow to create reservations direct)
|
||||
@@ -854,7 +1142,6 @@ class PmsReservation(models.Model):
|
||||
{
|
||||
"folio_id": folio.id,
|
||||
"reservation_type": vals.get("reservation_type"),
|
||||
"channel_type": vals.get("channel_type"),
|
||||
}
|
||||
)
|
||||
record = super(PmsReservation, self).create(vals)
|
||||
@@ -896,7 +1183,7 @@ class PmsReservation(models.Model):
|
||||
def autocheckout(self):
|
||||
reservations = self.env["pms.reservation"].search(
|
||||
[
|
||||
("state", "not in", ("done", "cancelled")),
|
||||
("state", "not in", ["done", "cancelled"]),
|
||||
("checkout", "<", fields.Date.today()),
|
||||
]
|
||||
)
|
||||
@@ -935,7 +1222,7 @@ class PmsReservation(models.Model):
|
||||
def confirm(self):
|
||||
for record in self:
|
||||
vals = {}
|
||||
if record.checkin_partner_ids:
|
||||
if record.checkin_partner_ids.filtered(lambda c: c.state == "onboard"):
|
||||
vals.update({"state": "onboard"})
|
||||
else:
|
||||
vals.update({"state": "confirm"})
|
||||
@@ -945,14 +1232,6 @@ class PmsReservation(models.Model):
|
||||
record.folio_id.action_confirm()
|
||||
return True
|
||||
|
||||
def button_done(self):
|
||||
"""
|
||||
@param self: object pointer
|
||||
"""
|
||||
for record in self:
|
||||
record.action_reservation_checkout()
|
||||
return True
|
||||
|
||||
def action_cancel(self):
|
||||
for record in self:
|
||||
cancel_reason = (
|
||||
@@ -1024,20 +1303,6 @@ class PmsReservation(models.Model):
|
||||
record.checkin_partner_count = 0
|
||||
record.checkin_partner_pending_count = 0
|
||||
|
||||
@api.depends("channel_type", "subchannel_direct")
|
||||
def _compute_origin(self):
|
||||
for reservation in self:
|
||||
if reservation.channel_type == "direct":
|
||||
reservation.origin = reservation.subchannel_direct
|
||||
elif reservation.channel_type == "agency":
|
||||
reservation.origin = reservation.agency_id.name
|
||||
|
||||
@api.depends("origin")
|
||||
def _compute_detail_origin(self):
|
||||
for reservation in self:
|
||||
if reservation.channel_type in ["direct", "agency"]:
|
||||
reservation.detail_origin = reservation.sudo().create_uid.name
|
||||
|
||||
def _search_checkin_partner_pending(self, operator, value):
|
||||
self.ensure_one()
|
||||
recs = self.search([]).filtered(lambda x: x.checkin_partner_pending_count > 0)
|
||||
@@ -1054,13 +1319,61 @@ class PmsReservation(models.Model):
|
||||
|
||||
def action_checks(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0]
|
||||
action["views"] = [
|
||||
(self.env.ref("pms.pms_reservation_checkin_view_form").id, "form")
|
||||
]
|
||||
action["res_id"] = self.id
|
||||
action["target"] = "new"
|
||||
return action
|
||||
tree_id = self.env.ref("pms.pms_checkin_partner_reservation_view_tree").id
|
||||
return {
|
||||
"name": _("Register Partners"),
|
||||
"views": [[tree_id, "tree"]],
|
||||
"res_model": "pms.checkin.partner",
|
||||
"type": "ir.actions.act_window",
|
||||
"context": {
|
||||
"create": False,
|
||||
"edit": True,
|
||||
"popup": True,
|
||||
},
|
||||
"domain": [("reservation_id", "=", self.id), ("state", "=", "draft")],
|
||||
"target": "new",
|
||||
}
|
||||
|
||||
def action_onboard(self):
|
||||
self.ensure_one()
|
||||
kanban_id = self.env.ref("pms.pms_checkin_partner_kanban_view").id
|
||||
return {
|
||||
"name": _("Register Checkins"),
|
||||
"views": [[kanban_id, "kanban"]],
|
||||
"res_model": "pms.checkin.partner",
|
||||
"type": "ir.actions.act_window",
|
||||
"context": {
|
||||
"create": False,
|
||||
"edit": True,
|
||||
"popup": True,
|
||||
},
|
||||
"domain": [("reservation_id", "=", self.id)],
|
||||
"target": "new",
|
||||
}
|
||||
|
||||
@api.model
|
||||
def auto_no_show(self):
|
||||
# No show when pass 1 day from checkin day
|
||||
no_show_reservations = self.env["pms.reservation"].search(
|
||||
[
|
||||
("state", "in", ("draft", "confirm")),
|
||||
("checkin", "<", fields.Date.today()),
|
||||
]
|
||||
)
|
||||
no_show_reservations.state = "no_show"
|
||||
|
||||
@api.model
|
||||
def auto_no_checkout(self):
|
||||
# No checkout when pass checkout hour
|
||||
reservations = self.env["pms.reservation"].search(
|
||||
[
|
||||
("state", "in", ("onboard",)),
|
||||
("checkout", "=", fields.Datetime.today()),
|
||||
]
|
||||
)
|
||||
for reservation in reservations:
|
||||
if reservation.checkout_datetime <= fields.Datetime.now():
|
||||
reservations.state = "no_checkout"
|
||||
|
||||
def unify(self):
|
||||
# TODO
|
||||
|
||||
@@ -250,7 +250,6 @@ class PmsReservationLine(models.Model):
|
||||
line.reservation_id.tax_ids,
|
||||
line.reservation_id.company_id,
|
||||
)
|
||||
# _logger.info(line.price)
|
||||
# TODO: Out of service 0 amount
|
||||
else:
|
||||
line.price = line._origin.price
|
||||
@@ -395,7 +394,7 @@ class PmsReservationLine(models.Model):
|
||||
# negative discounts (= surcharge) are included in the display price
|
||||
return max(base_price, final_price)
|
||||
|
||||
@api.constrains("reservation_id.adults", "room_id")
|
||||
@api.constrains("room_id")
|
||||
def _check_adults(self):
|
||||
for record in self.filtered("room_id"):
|
||||
extra_bed = record.reservation_id.service_ids.filtered(
|
||||
|
||||
12
pms/models/pms_sale_channel.py
Normal file
12
pms/models/pms_sale_channel.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PmsSaleChannel(models.Model):
|
||||
_name = "pms.sale.channel"
|
||||
_description = "Sales Channel"
|
||||
|
||||
# Fields declaration
|
||||
name = fields.Text(string="Sale Channel Name")
|
||||
channel_type = fields.Selection(
|
||||
[("direct", "Direct"), ("indirect", "Indirect")], string="Sale Channel Type"
|
||||
)
|
||||
@@ -459,7 +459,7 @@ class PmsService(models.Model):
|
||||
)
|
||||
for line in self:
|
||||
state = line.folio_id.state or "draft"
|
||||
if state in ("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"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,20 +21,41 @@ class ResPartner(models.Model):
|
||||
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(
|
||||
"pms.sale.channel",
|
||||
string="Sale Channel",
|
||||
ondelete="restrict",
|
||||
domain=[("channel_type", "=", "indirect")],
|
||||
)
|
||||
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):
|
||||
pms_reservation_obj = self.env["pms.reservation"]
|
||||
for record in self:
|
||||
record.reservations_count = pms_reservation_obj.search_count(
|
||||
[("partner_id.id", "=", record.id)]
|
||||
[
|
||||
(
|
||||
"partner_id.id",
|
||||
"=",
|
||||
record.id if isinstance(record.id, int) else False,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
def _compute_folios_count(self):
|
||||
pms_folio_obj = self.env["pms.folio"]
|
||||
for record in self:
|
||||
record.folios_count = pms_folio_obj.search_count(
|
||||
[("partner_id.id", "=", record.id)]
|
||||
[
|
||||
(
|
||||
"partner_id.id",
|
||||
"=",
|
||||
record.id if isinstance(record.id, int) else False,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# ORM Overrides
|
||||
@@ -64,3 +85,11 @@ class ResPartner(models.Model):
|
||||
name, args=args, operator=operator, limit=limit_rest
|
||||
)
|
||||
return res
|
||||
|
||||
@api.constrains("is_agency", "sale_channel_id")
|
||||
def _check_is_agency(self):
|
||||
for record in self:
|
||||
if record.is_agency and not record.sale_channel_id:
|
||||
raise models.ValidationError(_("Sale Channel must be entered"))
|
||||
if not record.is_agency and record.sale_channel_id:
|
||||
record.sale_channel_id = None
|
||||
|
||||
@@ -8,7 +8,7 @@ user_access_pms_reservation_line,user_access_pms_reservation_line,model_pms_rese
|
||||
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_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,1
|
||||
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_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_shared_pms_room,user_access_pms_shared_room,model_pms_shared_room,pms.group_pms_user,1,0,0,0
|
||||
@@ -24,6 +24,7 @@ user_access_pms_cancelation_rule,user_access_pms_cancelation_rule,model_pms_canc
|
||||
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_availability,user_access_availability,model_pms_room_type_availability,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_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
|
||||
@@ -33,7 +34,7 @@ manager_access_pms_reservation_line,manager_access_pms_reservation_line,model_pm
|
||||
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_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,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_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_shared_room,manager_access_pms_shared_room,model_pms_shared_room,pms.group_pms_manager,1,1,1,1
|
||||
@@ -47,4 +48,5 @@ manager_access_pms_board_service_line,manager_access_pms_board_service_line,mode
|
||||
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_availability,manager_access_availability,model_pms_room_type_availability,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
|
||||
|
||||
|
BIN
pms/static/description/avatar.png
Normal file
BIN
pms/static/description/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -22,3 +22,6 @@
|
||||
from . import test_pms_reservation
|
||||
from . import test_pms_pricelist
|
||||
from . import test_pms_pricelist_priority
|
||||
from . import test_pms_checkin_partner
|
||||
from . import test_pms_sale_channel
|
||||
from . import test_pms_folio
|
||||
384
pms/tests/test_pms_checkin_partner.py
Normal file
384
pms/tests/test_pms_checkin_partner.py
Normal file
@@ -0,0 +1,384 @@
|
||||
import logging
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import fields
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from .common import TestHotel
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@freeze_time("2012-01-14")
|
||||
class TestPmsCheckinPartner(TestHotel):
|
||||
@classmethod
|
||||
def arrange_single_checkin(cls):
|
||||
# Arrange for one checkin on one reservation
|
||||
cls.host1 = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Miguel",
|
||||
"phone": "654667733",
|
||||
"email": "miguel@example.com",
|
||||
}
|
||||
)
|
||||
reservation_vals = {
|
||||
"checkin": "2012-01-14",
|
||||
"checkout": "2012-01-17",
|
||||
"room_type_id": cls.env.ref("pms.pms_room_type_3").id,
|
||||
"partner_id": cls.host1.id,
|
||||
"adults": 3,
|
||||
"pms_property_id": cls.env.ref("pms.main_pms_property").id,
|
||||
}
|
||||
demo_user = cls.env.ref("base.user_demo")
|
||||
cls.reservation_1 = (
|
||||
cls.env["pms.reservation"].with_user(demo_user).create(reservation_vals)
|
||||
)
|
||||
cls.checkin1 = cls.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": cls.host1.id,
|
||||
"reservation_id": cls.reservation_1.id,
|
||||
}
|
||||
)
|
||||
|
||||
def test_auto_create_checkins(self):
|
||||
|
||||
# ACTION
|
||||
self.arrange_single_checkin()
|
||||
checkins_count = len(self.reservation_1.checkin_partner_ids)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
checkins_count,
|
||||
3,
|
||||
"the automatic partner checkin was not created successful",
|
||||
)
|
||||
|
||||
def test_auto_unlink_checkins(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
|
||||
# ACTION
|
||||
host2 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Carlos",
|
||||
"phone": "654667733",
|
||||
"email": "carlos@example.com",
|
||||
}
|
||||
)
|
||||
self.reservation_1.checkin_partner_ids = [
|
||||
(
|
||||
0,
|
||||
False,
|
||||
{
|
||||
"partner_id": host2.id,
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
checkins_count = len(self.reservation_1.checkin_partner_ids)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
checkins_count,
|
||||
3,
|
||||
"the automatic partner checkin was not updated successful",
|
||||
)
|
||||
|
||||
def test_onboard_checkin(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
self.reservation_1.state = "onboard"
|
||||
|
||||
def test_onboard_reservation(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
|
||||
# ACT
|
||||
self.checkin1.action_on_board()
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
self.reservation_1.state,
|
||||
"onboard",
|
||||
"the reservation checkin was not successful",
|
||||
)
|
||||
|
||||
def test_premature_checkin(self):
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
self.reservation_1.write(
|
||||
{
|
||||
"checkin": "2012-01-15",
|
||||
}
|
||||
)
|
||||
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
self.checkin1.action_on_board()
|
||||
|
||||
def test_late_checkin(self):
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
self.reservation_1.write(
|
||||
{
|
||||
"checkin": "2012-01-13",
|
||||
}
|
||||
)
|
||||
|
||||
# ACT
|
||||
self.checkin1.action_on_board()
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
self.checkin1.arrival,
|
||||
fields.datetime.now(),
|
||||
"the late checkin has problems",
|
||||
)
|
||||
|
||||
def test_too_many_people_checkin(self):
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
host2 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Carlos",
|
||||
"phone": "654667733",
|
||||
"email": "carlos@example.com",
|
||||
}
|
||||
)
|
||||
host3 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Enmanuel",
|
||||
"phone": "654667733",
|
||||
"email": "enmanuel@example.com",
|
||||
}
|
||||
)
|
||||
host4 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "Enrique",
|
||||
"phone": "654667733",
|
||||
"email": "enrique@example.com",
|
||||
}
|
||||
)
|
||||
self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": host2.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": host3.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
self.reservation_1.write(
|
||||
{
|
||||
"checkin_partner_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"partner_id": host4.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
},
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def arrange_folio_reservations(cls):
|
||||
# Arrange on one folio with 3 reservations
|
||||
demo_user = cls.env.ref("base.user_demo")
|
||||
cls.host1 = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Miguel",
|
||||
"phone": "654667733",
|
||||
"email": "miguel@example.com",
|
||||
}
|
||||
)
|
||||
cls.host2 = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Carlos",
|
||||
"phone": "654667733",
|
||||
"email": "carlos@example.com",
|
||||
}
|
||||
)
|
||||
cls.host3 = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Enmanuel",
|
||||
"phone": "654667733",
|
||||
"email": "enmanuel@example.com",
|
||||
}
|
||||
)
|
||||
cls.host4 = cls.env["res.partner"].create(
|
||||
{
|
||||
"name": "Enrique",
|
||||
"phone": "654667733",
|
||||
"email": "enrique@example.com",
|
||||
}
|
||||
)
|
||||
folio_vals = {
|
||||
"partner_id": cls.host1.id,
|
||||
}
|
||||
cls.folio_1 = cls.env["pms.folio"].with_user(demo_user).create(folio_vals)
|
||||
reservation1_vals = {
|
||||
"checkin": "2012-01-14",
|
||||
"checkout": "2012-01-17",
|
||||
"room_type_id": cls.env.ref("pms.pms_room_type_3").id,
|
||||
"partner_id": cls.host1.id,
|
||||
"adults": 3,
|
||||
"pms_property_id": cls.env.ref("pms.main_pms_property").id,
|
||||
"folio_id": cls.folio_1.id,
|
||||
}
|
||||
reservation2_vals = {
|
||||
"checkin": "2012-01-14",
|
||||
"checkout": "2012-01-17",
|
||||
"room_type_id": cls.env.ref("pms.pms_room_type_2").id,
|
||||
"partner_id": cls.host1.id,
|
||||
"adults": 2,
|
||||
"pms_property_id": cls.env.ref("pms.main_pms_property").id,
|
||||
"folio_id": cls.folio_1.id,
|
||||
}
|
||||
reservation3_vals = {
|
||||
"checkin": "2012-01-14",
|
||||
"checkout": "2012-01-17",
|
||||
"room_type_id": cls.env.ref("pms.pms_room_type_2").id,
|
||||
"partner_id": cls.host1.id,
|
||||
"adults": 2,
|
||||
"pms_property_id": cls.env.ref("pms.main_pms_property").id,
|
||||
"folio_id": cls.folio_1.id,
|
||||
}
|
||||
cls.reservation_1 = (
|
||||
cls.env["pms.reservation"].with_user(demo_user).create(reservation1_vals)
|
||||
)
|
||||
cls.reservation_2 = (
|
||||
cls.env["pms.reservation"].with_user(demo_user).create(reservation2_vals)
|
||||
)
|
||||
cls.reservation_3 = (
|
||||
cls.env["pms.reservation"].with_user(demo_user).create(reservation3_vals)
|
||||
)
|
||||
|
||||
def test_count_pending_arrival_persons(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_folio_reservations()
|
||||
self.checkin1 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.host1.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
self.checkin2 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.host2.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
self.checkin3 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.host3.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
|
||||
# ACT
|
||||
self.checkin1.action_on_board()
|
||||
self.checkin2.action_on_board()
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
self.reservation_1.count_pending_arrival,
|
||||
1,
|
||||
"Fail the count pending arrival on reservation",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.reservation_1.checkins_ratio,
|
||||
int(2 * 100 / 3),
|
||||
"Fail the checkins ratio on reservation",
|
||||
)
|
||||
|
||||
def test_complete_checkin_data(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_folio_reservations()
|
||||
|
||||
# ACT
|
||||
self.checkin1 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.host1.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
self.checkin2 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.host2.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
pending_checkin_data = self.reservation_1.pending_checkin_data
|
||||
ratio_checkin_data = self.reservation_1.ratio_checkin_data
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
pending_checkin_data,
|
||||
1,
|
||||
"Fail the count pending checkin data on reservation",
|
||||
)
|
||||
self.assertEqual(
|
||||
ratio_checkin_data,
|
||||
int(2 * 100 / 3),
|
||||
"Fail the checkins data ratio on reservation",
|
||||
)
|
||||
|
||||
def test_auto_no_show(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_folio_reservations()
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
|
||||
# ACTION
|
||||
freezer = freeze_time("2012-01-15 10:00:00")
|
||||
freezer.start()
|
||||
PmsReservation.auto_no_show()
|
||||
|
||||
no_show_reservations = PmsReservation.search([("state", "=", "no_show")])
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
len(no_show_reservations),
|
||||
3,
|
||||
"Reservations not set like No Show",
|
||||
)
|
||||
freezer.stop()
|
||||
|
||||
def test_auto_no_checkout(self):
|
||||
|
||||
# ARRANGE
|
||||
self.arrange_single_checkin()
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
self.checkin1.action_on_board()
|
||||
|
||||
# ACTION
|
||||
freezer = freeze_time("2012-01-17 12:00:00")
|
||||
freezer.start()
|
||||
PmsReservation.auto_no_checkout()
|
||||
|
||||
no_checkout_reservations = PmsReservation.search(
|
||||
[("state", "=", "no_checkout")]
|
||||
)
|
||||
freezer.stop()
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
len(no_checkout_reservations),
|
||||
1,
|
||||
"Reservations not set like No checkout",
|
||||
)
|
||||
58
pms/tests/test_pms_folio.py
Normal file
58
pms/tests/test_pms_folio.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import datetime
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from .common import TestHotel
|
||||
|
||||
freeze_time("2000-02-02")
|
||||
|
||||
|
||||
class TestPmsFolio(TestHotel):
|
||||
def test_commission_and_partner_correct(self):
|
||||
# ARRANGE
|
||||
PmsFolio = self.env["pms.folio"]
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
PmsPartner = self.env["res.partner"]
|
||||
PmsSaleChannel = self.env["pms.sale.channel"]
|
||||
# ACT
|
||||
saleChannel = PmsSaleChannel.create(
|
||||
{"name": "saleChannel1", "channel_type": "indirect"}
|
||||
)
|
||||
agency = PmsPartner.create(
|
||||
{
|
||||
"name": "partner1",
|
||||
"is_agency": True,
|
||||
"invoice_agency": True,
|
||||
"default_commission": 15,
|
||||
"sale_channel_id": saleChannel.id,
|
||||
}
|
||||
)
|
||||
|
||||
reservation = PmsReservation.create(
|
||||
{
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
|
||||
"agency_id": agency.id,
|
||||
}
|
||||
)
|
||||
folio = PmsFolio.create(
|
||||
{
|
||||
"agency_id": agency.id,
|
||||
"reservation_ids": [reservation.id],
|
||||
}
|
||||
)
|
||||
|
||||
commission = 0
|
||||
for reservation in folio:
|
||||
commission += reservation.commission_amount
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
folio.commission,
|
||||
commission,
|
||||
"Folio commission don't math with his reservation commission",
|
||||
)
|
||||
if folio.agency_id:
|
||||
self.assertEqual(
|
||||
folio.agency_id, folio.partner_id, "Agency has to be the partner"
|
||||
)
|
||||
103
pms/tests/test_pms_sale_channel.py
Normal file
103
pms/tests/test_pms_sale_channel.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import datetime
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from .common import TestHotel
|
||||
|
||||
|
||||
@freeze_time("2010-01-01")
|
||||
class TestPmsSaleChannel(TestHotel):
|
||||
def test_not_agency_as_agency(self):
|
||||
# ARRANGE
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
not_agency = self.env["res.partner"].create(
|
||||
{"name": "partner1", "is_agency": False}
|
||||
)
|
||||
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
PmsReservation.create(
|
||||
{
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
|
||||
"agency_id": not_agency.id,
|
||||
}
|
||||
)
|
||||
|
||||
def test_partner_as_direct_channel(self):
|
||||
# ARRANGE
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
partner = self.env.ref("base.res_partner_12")
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
PmsReservation.create(
|
||||
{
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
|
||||
"channel_type_id": partner.id,
|
||||
}
|
||||
)
|
||||
|
||||
def test_channel_type_id_only_directs(self):
|
||||
# ARRANGE
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
PmsSaleChannel = self.env["pms.sale.channel"]
|
||||
# ACT
|
||||
saleChannel = PmsSaleChannel.create({"channel_type": "direct"})
|
||||
reservation = PmsReservation.create(
|
||||
{
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.datetimedelta(days=3),
|
||||
"channel_type_id": saleChannel.id,
|
||||
}
|
||||
)
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
self.browse_ref(reservation.channel_type_id).channel_type,
|
||||
"direct",
|
||||
"Sale channel is not direct",
|
||||
)
|
||||
|
||||
def test_agency_id_is_agency(self):
|
||||
# ARRANGE
|
||||
PmsReservation = self.env["pms.reservation"]
|
||||
|
||||
# ACT
|
||||
agency = self.env["res.partner"].create({"name": "partner1", "is_agency": True})
|
||||
reservation = PmsReservation.create(
|
||||
{
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.datetimedelta(days=3),
|
||||
"agency_id": agency.id,
|
||||
}
|
||||
)
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
self.browse_ref(reservation.agency_id).is_agency,
|
||||
True,
|
||||
"Agency_id doesn't correspond to an agency",
|
||||
)
|
||||
|
||||
def test_sale_channel_id_only_indirect(self):
|
||||
# ARRANGE
|
||||
PmsSaleChannel = self.env["pms.sale.channel"]
|
||||
# ACT
|
||||
saleChannel = PmsSaleChannel.create({"channel_type": "indirect"})
|
||||
agency = self.env["res.partner"].create(
|
||||
{"name": "example", "is_agency": True, "sale_channel_id": saleChannel.id}
|
||||
)
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
self.browse_ref(agency.sale_channel_id).channel_type,
|
||||
"indirect",
|
||||
"An agency should be a indirect channel",
|
||||
)
|
||||
|
||||
def test_agency_without_sale_channel_id(self):
|
||||
# ARRANGE & ACT & ASSERT
|
||||
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||||
self.env["res.partner"].create(
|
||||
{"name": "example", "is_agency": True, "sale_channel_id": None}
|
||||
)
|
||||
@@ -4,7 +4,8 @@
|
||||
id="action_checkin_partner"
|
||||
name="Action checkin"
|
||||
res_model="pms.checkin.partner"
|
||||
view_mode="tree,form"
|
||||
view_mode="kanban,tree,form"
|
||||
domain="[('state', '!=', 'draft')]"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_pms_checkin_partner"
|
||||
@@ -18,6 +19,9 @@
|
||||
<field name="model">pms.checkin.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form create="false">
|
||||
<header>
|
||||
<field name="state" widget="statusbar" />
|
||||
</header>
|
||||
<sheet>
|
||||
<group name="group_top">
|
||||
<group name="group_left">
|
||||
@@ -27,13 +31,13 @@
|
||||
domain="[('is_company','=', False)]"
|
||||
/>
|
||||
<field name="pms_property_id" invisible="1" />
|
||||
<field name="enter_date" />
|
||||
<field name="exit_date" />
|
||||
<field name="arrival_hour" />
|
||||
<field name="departure_hour" />
|
||||
<field name="arrival" />
|
||||
<field name="departure" />
|
||||
</group>
|
||||
<group name="group_left">
|
||||
<field name="reservation_id" />
|
||||
<field name="folio_id" />
|
||||
<field name="identifier" />
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
@@ -47,7 +51,6 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree
|
||||
editable="bottom"
|
||||
create="1"
|
||||
decoration-danger="state == 'draft'"
|
||||
decoration-info="state == 'done'"
|
||||
decoration-muted="state == 'cancelled'"
|
||||
@@ -56,20 +59,20 @@
|
||||
<button
|
||||
type="object"
|
||||
class="oe_read_only oe_stat_button"
|
||||
icon="fa fa-2x fa-check-circle"
|
||||
icon="fa-2x fa-check-circle"
|
||||
name="action_on_board"
|
||||
help="Get in"
|
||||
attrs="{'invisible': [('state','!=','draft')]}"
|
||||
attrs="{'invisible': [('state','!=','precheckin')]}"
|
||||
/>
|
||||
<field name="auto_booking" invisible="1" />
|
||||
<field name="identifier" />
|
||||
<field name="partner_id" required="True" />
|
||||
<field name="mobile" />
|
||||
<field name="email" />
|
||||
<field name="enter_date" />
|
||||
<field name="exit_date" />
|
||||
<field name="arrival" />
|
||||
<field name="departure" />
|
||||
<field name="reservation_id" invisible="1" />
|
||||
<field name="folio_id" force_save="1" invisible="1" />
|
||||
<field name="state" invisible="1" />
|
||||
<field name="state" invisible="0" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -89,9 +92,10 @@
|
||||
class="oe_stat_button"
|
||||
icon="fa fa-2x fa-check-circle"
|
||||
name="action_on_board"
|
||||
attrs="{'invisible':[('state','not in', ['draft'])]}"
|
||||
attrs="{'invisible':[('state','not in', ['preconfirm'])]}"
|
||||
help="Get in"
|
||||
/>
|
||||
<field name="identifier" />
|
||||
<field
|
||||
name="partner_id"
|
||||
required="True"
|
||||
@@ -99,14 +103,140 @@
|
||||
/>
|
||||
<field name="mobile" />
|
||||
<field name="email" />
|
||||
<field name="enter_date" />
|
||||
<field name="exit_date" />
|
||||
<field name="arrival" />
|
||||
<field name="departure" />
|
||||
<field name="reservation_id" />
|
||||
<field name="folio_id" force_save="1" invisible="1" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="pms_checkin_partner_kanban_view">
|
||||
<field name="name">pms.checkin.partner.kanban</field>
|
||||
<field name="model">pms.checkin.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban
|
||||
default_group_by="state"
|
||||
class="o_res_partner_kanban"
|
||||
sample="1"
|
||||
create="false"
|
||||
>
|
||||
<field name="id" />
|
||||
<field name="identifier" />
|
||||
<field name="partner_id" />
|
||||
<field name="reservation_id" />
|
||||
<field name="folio_id" />
|
||||
<field name="pms_property_id" />
|
||||
<field name="name" />
|
||||
<field name="email" />
|
||||
<field name="mobile" />
|
||||
<field name="arrival" />
|
||||
<field name="departure" />
|
||||
<field name="state" />
|
||||
<field name="image_128" />
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div
|
||||
class="oe_kanban_global_click o_kanban_record_has_image_fill o_res_partner_kanban"
|
||||
>
|
||||
<div class="o_kanban_image">
|
||||
<img
|
||||
alt="Contact image"
|
||||
t-if="record.image_128.raw_value"
|
||||
t-att-src="kanban_image('pms.checkin.partner', 'image_128', record.id.raw_value)"
|
||||
/>
|
||||
<t t-if="!record.image_128.raw_value">
|
||||
<!--TODO: Use npm avatar generation? https://github.com/Ashwinvalento/cartoon-avatar-->
|
||||
<img
|
||||
alt="Draft"
|
||||
t-if="record.state.raw_value === 'draft'"
|
||||
t-att-src=""pms/static/description/avatar.png""
|
||||
/>
|
||||
<img
|
||||
alt="Cancelled"
|
||||
t-if="record.state.raw_value === 'cancelled'"
|
||||
t-att-src=""pms/static/description/avatar.png""
|
||||
/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="oe_kanban_details">
|
||||
<div class="o_kanban_record_top">
|
||||
<div class="o_kanban_record_headings">
|
||||
<strong class="o_kanban_record_title">
|
||||
<div class="float-right">
|
||||
<field
|
||||
name="state"
|
||||
widget="label_selection"
|
||||
options="{'classes': {'draft': 'default', 'precheckin': 'info', 'onboard': 'warning', 'onboard': 'success', 'done': 'secondary'}}"
|
||||
/>
|
||||
</div>
|
||||
<field
|
||||
name="name"
|
||||
placeholder="Hosted's Name"
|
||||
/>
|
||||
<field
|
||||
name="reservation_id"
|
||||
placeholder="Room Reservation"
|
||||
/>
|
||||
</strong>
|
||||
<span
|
||||
t-if="record.arrival.raw_value"
|
||||
class="o_kanban_record_subtitle"
|
||||
>
|
||||
<field name="arrival" />
|
||||
</span>
|
||||
<span
|
||||
t-if="record.departure.raw_value"
|
||||
class="o_kanban_record_subtitle"
|
||||
>
|
||||
<field name="departure" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<field
|
||||
name="segmentation_ids"
|
||||
widget="many2many_tags"
|
||||
options="{'color_field': 'color'}"
|
||||
/>
|
||||
<ul>
|
||||
<li
|
||||
t-if="record.email.raw_value"
|
||||
class="o_kanban_record_subtitle"
|
||||
>
|
||||
<field name="email" />
|
||||
</li>
|
||||
<li
|
||||
t-if="record.mobile.raw_value"
|
||||
class="o_kanban_record_subtitle"
|
||||
>
|
||||
<field name="mobile" />
|
||||
</li>
|
||||
</ul>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<a
|
||||
name="action_on_board"
|
||||
tabindex="-1"
|
||||
type="object"
|
||||
attrs="{'invisible': [('state', '!=', 'precheckin')]}"
|
||||
class="o_project_kanban_box"
|
||||
>
|
||||
<div>
|
||||
<span class="o_label">
|
||||
<i
|
||||
class="fa fa-lg fa-play-circle text-success"
|
||||
/>
|
||||
Check-in
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="pms_checkin_partner_view_search">
|
||||
<field name="name">pms.checkin.partner.search</field>
|
||||
<field name="model">pms.checkin.partner</field>
|
||||
@@ -129,21 +259,21 @@
|
||||
<filter
|
||||
string="Checkins Tomorrow"
|
||||
name="enter_tomorrow"
|
||||
domain="[('enter_date', '=', (context_today()+datetime.timedelta(days=1)).strftime('%Y-%m-%d')),
|
||||
domain="[('arrival', '=', (context_today()+datetime.timedelta(days=1)).strftime('%Y-%m-%d')),
|
||||
('state', '=', 'confirm')]"
|
||||
help="Show all checkins for enter tomorrow"
|
||||
/>
|
||||
<filter
|
||||
string="Checkins to 7 days"
|
||||
name="next_res_week"
|
||||
domain="[('enter_date', '<', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d')),
|
||||
domain="[('arrival', '<', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d')),
|
||||
('state', '=', 'confirm')]"
|
||||
help="Show all reservations for which date enter is before than 7 days"
|
||||
/>
|
||||
<filter
|
||||
string="On Board Tomorrow"
|
||||
name="next_res_2week"
|
||||
domain="[('enter_date', '<', (context_today()+datetime.timedelta(days=14)).strftime('%Y-%m-%d')),
|
||||
domain="[('arrival', '<', (context_today()+datetime.timedelta(days=14)).strftime('%Y-%m-%d')),
|
||||
('state', 'in', ['confirm','onboard'])]"
|
||||
help="Show all checkins for Tomorrow"
|
||||
/>
|
||||
@@ -167,36 +297,44 @@
|
||||
<filter
|
||||
string="Checkin by Month"
|
||||
name="checkin_by_month"
|
||||
context="{'group_by':'enter_date', 'default_order': 'enter_date asc'}"
|
||||
context="{'group_by':'arrival', 'default_order': 'arrival asc'}"
|
||||
/>
|
||||
<filter
|
||||
string="Checkin by Week"
|
||||
name="checkin_by_week"
|
||||
context="{'group_by':'enter_date:week', 'default_order': 'enter_date'}"
|
||||
context="{'group_by':'arrival:week', 'default_order': 'arrival'}"
|
||||
/>
|
||||
<filter
|
||||
string="Checkin by Day"
|
||||
name="checkin_by_week"
|
||||
context="{'group_by':'enter_date:day', 'default_order': 'enter_date'}"
|
||||
context="{'group_by':'arrival:day', 'default_order': 'arrival'}"
|
||||
/>
|
||||
<separator />
|
||||
<filter
|
||||
string="Checkout by Month"
|
||||
name="checkout_by_month"
|
||||
context="{'group_by':'exit_date', 'default_order': 'exit_date asc'}"
|
||||
context="{'group_by':'departure', 'default_order': 'departure asc'}"
|
||||
/>
|
||||
<filter
|
||||
string="Checkout by Week"
|
||||
name="checkout_by_week"
|
||||
context="{'group_by':'exit_date:week', 'default_order': 'exit_date'}"
|
||||
context="{'group_by':'departure:week', 'default_order': 'departure'}"
|
||||
/>
|
||||
<filter
|
||||
string="Checkout by Day"
|
||||
name="checkout_by_week"
|
||||
context="{'group_by':'exit_date:day', 'default_order': 'exit_date'}"
|
||||
context="{'group_by':'departure:day', 'default_order': 'departure'}"
|
||||
/>
|
||||
<separator />
|
||||
</group>
|
||||
<searchpanel>
|
||||
<field
|
||||
name="state"
|
||||
string="State"
|
||||
enable_counters="1"
|
||||
select="multi"
|
||||
/>
|
||||
</searchpanel>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -98,14 +98,16 @@
|
||||
name="reservation_type"
|
||||
attrs="{'readonly':[('state','not in',('draft'))]}"
|
||||
/>
|
||||
<field
|
||||
<!--<field
|
||||
name="channel_type"
|
||||
attrs="{'required':[('reservation_type','=','normal')]}"
|
||||
/>
|
||||
<field
|
||||
name="agency_id"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
/>-->
|
||||
<field name="agency_id" />
|
||||
<field name="channel_type_id" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="partner_internal_comment" />
|
||||
@@ -278,9 +280,6 @@
|
||||
<field name="pending_amount" />
|
||||
<!-- <field name="refund_amount" /> -->
|
||||
<field name="invoices_paid" />
|
||||
<field name="booking_pending" />
|
||||
<field name="checkin_partner_count" />
|
||||
<field name="checkin_partner_pending_count" />
|
||||
<field name="partner_internal_comment" />
|
||||
<field name="cancelled_reason" />
|
||||
<field name="prepaid_warning_days" />
|
||||
@@ -295,27 +294,6 @@
|
||||
<strong class="oe_partner_heading">
|
||||
<field name="partner_id" />
|
||||
</strong>
|
||||
<ul>
|
||||
<li t-if="record.name.raw_value">
|
||||
<field name="name" />
|
||||
</li>
|
||||
<span
|
||||
t-if="record.checkin_partner_count.value>0"
|
||||
class="badge"
|
||||
>
|
||||
<i class="fa fa-fw fa-bed" />
|
||||
<t t-esc="record.checkin_partner_count.value" />
|
||||
</span>
|
||||
<span
|
||||
t-if="record.checkin_partner_pending_count.value>0"
|
||||
class="badge"
|
||||
>
|
||||
<i class="fa fa-fw fa-user-plus" />
|
||||
<t
|
||||
t-esc="record.checkin_partner_pending_count.value"
|
||||
/>
|
||||
</span>
|
||||
</ul>
|
||||
<div class="oe_kanban_partner_links" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<header>
|
||||
<field name="splitted" invisible="True" />
|
||||
<field name="tax_ids" invisible="1" />
|
||||
<field name="checkin_today" invisible="1" />
|
||||
<field name="checkin_partner_count" invisible="1" />
|
||||
<button
|
||||
name="confirm"
|
||||
@@ -30,7 +31,7 @@
|
||||
/>
|
||||
<button
|
||||
name="action_reservation_checkout"
|
||||
string="Done"
|
||||
string="Check Out"
|
||||
states="onboard"
|
||||
type="object"
|
||||
/>
|
||||
@@ -79,11 +80,24 @@
|
||||
</div>
|
||||
<sheet>
|
||||
<field name="shared_folio" invisible="1" />
|
||||
<field name="left_for_checkin" invisible="1" />
|
||||
<field name="ready_for_checkin" invisible="1" />
|
||||
<field name="departure_today" invisible="1" />
|
||||
<div
|
||||
class="oe_button_box"
|
||||
name="button_box"
|
||||
attrs="{'invisible': [('folio_id','=',False)]}"
|
||||
>
|
||||
<button
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-sign-out"
|
||||
name="action_reservation_checkout"
|
||||
>
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">Checkout</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
@@ -96,18 +110,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="action"
|
||||
class="oe_stat_button"
|
||||
icon="fa-list-ul"
|
||||
attrs="{'invisible': ['|', ('partner_id','=',False), ('reservation_type','in',('out'))]}"
|
||||
name="%(open_pms_reservation_form_tree_all)d"
|
||||
context="{'search_default_partner_id': partner_id}"
|
||||
>
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_text">Books</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
@@ -131,20 +133,30 @@
|
||||
<button
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-users"
|
||||
name="action_checks"
|
||||
attrs="{'invisible': [('checkin_partner_pending_count','<=',0)]}"
|
||||
name="action_onboard"
|
||||
attrs="{'invisible': [
|
||||
('ready_for_checkin', '!=', True),
|
||||
]}"
|
||||
>
|
||||
<div class="o_form_field o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field
|
||||
name="checkin_partner_pending_count"
|
||||
widget="statinfo"
|
||||
nolabel="1"
|
||||
/>
|
||||
</span>
|
||||
<span class="o_stat_text">Pending Checks</span>
|
||||
</div>
|
||||
<field
|
||||
name="checkins_ratio"
|
||||
string="On Board"
|
||||
widget="percentpie"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
name="action_checks"
|
||||
attrs="{'invisible': [
|
||||
('left_for_checkin', '!=', True),
|
||||
]}"
|
||||
>
|
||||
<field
|
||||
name="ratio_checkin_data"
|
||||
string="Checkin Data"
|
||||
widget="percentpie"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<span
|
||||
@@ -243,19 +255,6 @@
|
||||
'readonly': [('partner_id', '!=', False),
|
||||
('mobile','!=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="phone"
|
||||
colspan="2"
|
||||
nolabel="1"
|
||||
placeholder="phone"
|
||||
widget="phone"
|
||||
force_save="1"
|
||||
attrs="{'invisible': [('reservation_type','in',('out'))],
|
||||
'required': [('channel_type','in',('door','mail','phone')),
|
||||
('mobile','=','')],
|
||||
'readonly': [('partner_id', '!=', False),
|
||||
('phone','!=', False)]}"
|
||||
/>
|
||||
<field
|
||||
placeholder="Partner Notes"
|
||||
colspan="2"
|
||||
@@ -285,10 +284,10 @@
|
||||
name="pricelist_id"
|
||||
attrs="{'invisible': [('reservation_type','in',('out'))]}"
|
||||
/>
|
||||
<field
|
||||
<!--<field
|
||||
name="agency_id"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
/>-->
|
||||
<field
|
||||
name="cancelled_reason"
|
||||
attrs="{'invisible': [('state', 'not in', ('cancelled'))]}"
|
||||
@@ -329,6 +328,8 @@
|
||||
nolabel="1"
|
||||
placeholder="Reservation Notes"
|
||||
/>
|
||||
<field name="agency_id" />
|
||||
<field name="channel_type_id" />
|
||||
</group>
|
||||
<group
|
||||
colspan="2"
|
||||
@@ -368,6 +369,17 @@
|
||||
string="Only Room"
|
||||
widget="monetary"
|
||||
/>
|
||||
<field
|
||||
name="commission_percent"
|
||||
string="Commission percent (%)"
|
||||
>
|
||||
<field
|
||||
name="commission_amount"
|
||||
string="Commission Amount"
|
||||
>
|
||||
|
||||
</field>
|
||||
</field>
|
||||
<field name="invoice_status" invisible="1" />
|
||||
<field name="currency_id" invisible="1" />
|
||||
<field
|
||||
@@ -490,11 +502,10 @@
|
||||
</group>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
mode="kanban"
|
||||
options="{'no_create': True, 'no_delete': True}"
|
||||
context="{
|
||||
'checkin_partner_ids': checkin_partner_ids,
|
||||
'default_reservation_id': id,
|
||||
'reservation_id': id,
|
||||
'tree_view_ref':'pms.pms_checkin_partner_reservation_view_tree',
|
||||
'kanban_view_ref':'pms.pms_checkin_partner_kanban_view',
|
||||
}"
|
||||
/>
|
||||
</page>
|
||||
@@ -516,10 +527,10 @@
|
||||
<field name="overbooking" />
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
<!--<field
|
||||
name="channel_type"
|
||||
attrs="{'required':[('reservation_type','not in',('staff','out'))]}"
|
||||
/>
|
||||
/>-->
|
||||
<field name="reservation_type" />
|
||||
</group>
|
||||
</page>
|
||||
@@ -539,12 +550,12 @@
|
||||
<field name="model">pms.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<calendar
|
||||
date_start="checkin"
|
||||
date_stop="checkout"
|
||||
date_start="checkin_datetime"
|
||||
date_stop="checkout_datetime"
|
||||
string="Reservations"
|
||||
quick_add="False"
|
||||
mode="month"
|
||||
scales="month,year"
|
||||
scales="week,month,year"
|
||||
>
|
||||
<field name="partner_id" avatar_field="image_128" />
|
||||
<field name="room_type_id" />
|
||||
@@ -552,108 +563,6 @@
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Form view Checkin Partners from reservation -->
|
||||
<record model="ir.ui.view" id="pms_reservation_checkin_view_form">
|
||||
<field name="name">pms.reservation.checkin.form</field>
|
||||
<field name="model">pms.reservation</field>
|
||||
<field name="priority">100</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Reservation">
|
||||
<field name="reservation_type" invisible="1" />
|
||||
<field name="splitted" invisible="1" />
|
||||
<field name="state" invisible="1" />
|
||||
<field name="overbooking" invisible="1" />
|
||||
<field name="folio_id" invisible="1" />
|
||||
<field name="adults" invisible="1" />
|
||||
<field name="children" invisible="1" />
|
||||
<div
|
||||
class="alert alert-info"
|
||||
role="alert"
|
||||
style="margin-bottom:0px;"
|
||||
attrs="{'invisible': ['|',('shared_folio','=',False),('splitted', '=', True)]}"
|
||||
>
|
||||
This reservation has other reservantions and/or services in the
|
||||
folio, you can check it in the
|
||||
<bold>
|
||||
<button
|
||||
class="alert-link"
|
||||
type="object"
|
||||
name="open_folio"
|
||||
string="Folio Form"
|
||||
/>
|
||||
</bold>
|
||||
</div>
|
||||
<field name="shared_folio" invisible="1" />
|
||||
<field name="allowed_room_ids" invisible="1" />
|
||||
<sheet>
|
||||
<span
|
||||
class="label label-danger"
|
||||
attrs="{'invisible': [('state', 'not in', ('cancelled'))]}"
|
||||
>
|
||||
Cancelled Reservation!
|
||||
</span>
|
||||
<span
|
||||
class="label label-warning"
|
||||
attrs="{'invisible': [('overbooking', '=', False)]}"
|
||||
>
|
||||
OverBooking!
|
||||
</span>
|
||||
<h2>
|
||||
<field
|
||||
name="preferred_room_id"
|
||||
readonly="1"
|
||||
nolabel="1"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
style="margin-right: 30px;"
|
||||
/>
|
||||
<!-- <field
|
||||
name="partner_id"
|
||||
readonly="1"
|
||||
options="{'no_open': True}"
|
||||
/>
|
||||
<span
|
||||
class="fa-user"
|
||||
style="margin-left:20px;"
|
||||
attrs="{'invisible': [('reservation_type','not in',('normal'))]}"
|
||||
/>
|
||||
<span
|
||||
class="fa-black-tie"
|
||||
style="margin-left:20px; color: #C67;"
|
||||
attrs="{'invisible': [('reservation_type','not in',('staff'))]}"
|
||||
/> -->
|
||||
<h3>
|
||||
From
|
||||
<span class="fa-sign-in" style="margin: 5px;" />
|
||||
<field
|
||||
name="checkin"
|
||||
style="margin-right: 10px;"
|
||||
readonly="1"
|
||||
/>
|
||||
to
|
||||
<span class="fa-sign-out" style="margin-right: 5px;" />
|
||||
<field name="checkout" readonly="1" />
|
||||
</h3>
|
||||
</h2>
|
||||
<group>
|
||||
<field
|
||||
name="segmentation_ids"
|
||||
widget="many2many_tags"
|
||||
placeholder="Segmentation..."
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
</group>
|
||||
<field
|
||||
name="checkin_partner_ids"
|
||||
context="{
|
||||
'default_reservation_id': id,
|
||||
'reservation_id': id,
|
||||
'tree_view_ref':'pms.pms_checkin_partner_reservation_view_tree',
|
||||
}"
|
||||
/>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="pms_reservation_view_tree">
|
||||
<field name="name">pms.reservation.tree</field>
|
||||
<field name="model">pms.reservation</field>
|
||||
@@ -669,12 +578,7 @@
|
||||
<field name="splitted" invisible="1" />
|
||||
<field name="pricelist_id" invisible="1" />
|
||||
<field name="rooms" />
|
||||
<field
|
||||
name="checkin"
|
||||
widget="daterange"
|
||||
class="oe_inline"
|
||||
options="{'related_end_date': 'checkout'}"
|
||||
/>
|
||||
<field name="checkin" />
|
||||
<field name="checkout" widget="date" />
|
||||
<field name="nights" />
|
||||
<button
|
||||
@@ -687,7 +591,8 @@
|
||||
<field name="allowed_room_ids" invisible="1" />
|
||||
<field name="partner_id" />
|
||||
<field name="room_type_id" invisible="1" />
|
||||
<field name="adults" string="Persons" />
|
||||
<field name="adults" />
|
||||
<field name="ratio_checkin_data" widget="progressbar" optional="show" />
|
||||
<field name="overbooking" invisible="1" />
|
||||
<field name="activity_ids" widget="list_activity" optional="show" />
|
||||
<field
|
||||
@@ -711,8 +616,12 @@
|
||||
/>
|
||||
<field
|
||||
name="state"
|
||||
decoration-success="state == 'done'"
|
||||
decoration-success="state == 'onboard'"
|
||||
decoration-info="state == 'confirm'"
|
||||
decoration-primary="state == 'no_checkout'"
|
||||
decoration-danger="state == 'cancelled'"
|
||||
decoration-bf="state == 'draft'"
|
||||
decoration-warning="state == 'no_show'"
|
||||
widget="badge"
|
||||
optional="show"
|
||||
/>
|
||||
@@ -793,7 +702,7 @@
|
||||
<filter
|
||||
string="To enter"
|
||||
name="to_enter"
|
||||
domain="[('state', '=', 'confirm')]"
|
||||
domain="[('left_for_checkin', '=', True)]"
|
||||
/>
|
||||
<filter
|
||||
string="Overbookings"
|
||||
@@ -827,7 +736,7 @@
|
||||
domain="[('to_assign','=',True)]"
|
||||
/>
|
||||
<separator />
|
||||
<filter
|
||||
<!--<filter
|
||||
string="Web"
|
||||
name="web"
|
||||
domain="[('channel_type', '=', 'web')]"
|
||||
@@ -846,7 +755,7 @@
|
||||
string="Phone"
|
||||
name="phone"
|
||||
domain="[('channel_type', '=', 'phone')]"
|
||||
/>
|
||||
/>-->
|
||||
<separator />
|
||||
<filter
|
||||
string="Still to be paid"
|
||||
|
||||
39
pms/views/pms_sale_channel.xml
Normal file
39
pms/views/pms_sale_channel.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="pms_sale_channel_view_form">
|
||||
<field name="name">pms.sale.channel.form</field>
|
||||
<field name="model">pms.sale.channel</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Sale Channel">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name" colspan="1" />
|
||||
<field name="channel_type" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="pms_sale_channel_view_tree">
|
||||
<field name="name">pms.sale.channel.tree</field>
|
||||
<field name="model">pms.sale.channel</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string=" Sale Channel">
|
||||
<field name="name" />
|
||||
<field name="channel_type" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="open_pms_sale_channel_form_tree">
|
||||
<field name="name">Sale Channel</field>
|
||||
<field name="res_model">pms.sale.channel</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem
|
||||
name="Sale Channel"
|
||||
id="menu_open_pms_sale_channel_form_tree"
|
||||
action="open_pms_sale_channel_form_tree"
|
||||
sequence="21"
|
||||
parent="pms.configuration_others"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -39,12 +39,32 @@
|
||||
<field string="Folios" name="folios_count" widget="statinfo" />
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='user_id']" position="after">
|
||||
<field name="company_type" invisible="1" />
|
||||
<field
|
||||
name="is_agency"
|
||||
attrs="{'invisible': [('company_type','!=','company')]}"
|
||||
/>
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<group>
|
||||
<field name="is_agency" />
|
||||
</group>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='internal_notes']" position="after">
|
||||
<page
|
||||
name="agency"
|
||||
string="Agency"
|
||||
attrs="{'invisible':[('is_agency','!=',True)]}"
|
||||
>
|
||||
<group>
|
||||
<field
|
||||
name="sale_channel_id"
|
||||
options="{'no_create': True,'no_open': True}"
|
||||
/>
|
||||
<field name="default_commission" />
|
||||
<!-- <label for="price_discount"/>
|
||||
<div class="o_row">
|
||||
<field name="price_discount"/>
|
||||
<span>%%</span>
|
||||
</div>-->
|
||||
<field name="apply_pricelist" />
|
||||
<field name="invoice_agency" />
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
Reference in New Issue
Block a user