[IMP]pms_api_rest: Imrpovement API logs, PUT folio service and action_confirm reservation

This commit is contained in:
Darío Lodeiros
2024-04-17 11:27:24 +02:00
parent 7bbc603b33
commit 9df63f790b
7 changed files with 486 additions and 321 deletions

View File

@@ -25,6 +25,7 @@
"data/sql_reports.xml", "data/sql_reports.xml",
"data/auth_jwt_validator.xml", "data/auth_jwt_validator.xml",
"data/pms_app_reset_password_template.xml", "data/pms_app_reset_password_template.xml",
"data/cron_jobs.xml",
"views/pms_property_views.xml", "views/pms_property_views.xml",
"views/res_users_views.xml", "views/res_users_views.xml",
"views/pms_room_type_class_views.xml", "views/pms_room_type_class_views.xml",

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record model="ir.cron" id="clean_log_pms_api_rest">
<field name="name">Clean Log PMS API REST</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_api_log" />
<field
name="nextcall"
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 06:00:00')"
/>
<field name="code">model.clean_log_data(offset=60)</field>
</record>
</odoo>

View File

@@ -1,4 +1,6 @@
from odoo import _, api, fields, models from datetime import timedelta
from odoo import _, fields, models
class PmsApiLog(models.Model): class PmsApiLog(models.Model):
@@ -72,10 +74,39 @@ class PmsApiLog(models.Model):
string="Response URL", string="Response URL",
help="Response URL", help="Response URL",
) )
model_id = fields.Many2one( request_type = fields.Selection(
string="Model", string="Request Type",
help="Model", help="Request Type",
comodel_name="ir.model", selection=[
("folios", "Folios"),
("availability", "Availability"),
("restrictions", "Restrictions rules"),
("prices", "Prices"),
],
)
target_date_from = fields.Date(
string="Target Date From",
help="Target Date From",
)
target_date_to = fields.Date(
string="Target Date To",
help="Target Date To",
)
folio_ids = fields.Many2many(
string="Folios",
help="Folios",
comodel_name="pms.folio",
relation="pms_folio_pms_api_log_rel",
column1="pms_api_log_ids",
column2="folio_ids",
)
room_type_ids = fields.Many2many(
string="Room Types",
help="Room Types",
comodel_name="pms.room.type",
relation="pms_room_type_pms_api_log_rel",
column1="pms_api_log_ids",
column2="room_type_ids",
) )
def related_action_open_record(self): def related_action_open_record(self):
@@ -90,10 +121,7 @@ class PmsApiLog(models.Model):
""" """
self.ensure_one() self.ensure_one()
if "pms_api_log_id" in self.env[self.model_id.model]._fields: records = self.folio_ids
records = self.env[self.model_id.model].search(
[("pms_api_log_id", "=", self.id)]
)
if not records: if not records:
return None return None
action = { action = {
@@ -114,21 +142,15 @@ class PmsApiLog(models.Model):
) )
return action return action
@api.model def clean_log_data(self, offset=60):
def create(self, vals): """Clean log data older than the offset.
:param int offset: The number of days to keep the log data.
""" """
set pms_api_log_id and origin_json in related records self.sudo().search(
if record_ids id present in context [
""" ("status", "=", "success"),
log_record = super().create(vals) ("create_date", "<", fields.Datetime.now() - timedelta(days=offset)),
if self.env.context.get("record_ids"): ]
records = self.env[self.env.context.get("model")].browse( ).unlink()
self.env.context.get("record_ids")
)
records.write(
{
"pms_api_log_id": log_record.id,
"origin_json": log_record.request,
}
)
return log_record

View File

@@ -4,12 +4,11 @@ from odoo import fields, models
class PmsFolio(models.Model): class PmsFolio(models.Model):
_inherit = "pms.folio" _inherit = "pms.folio"
pms_api_log_id = fields.Many2one( pms_api_log_ids = fields.Many2many(
string="PMS API Log", string="API Logs",
help="PMS API Log", help="API Logs",
comodel_name="pms.api.log", comodel_name="pms.api.log",
) relation="pms_folio_pms_api_log_rel",
origin_json = fields.Text( column1="folio_ids",
string="Origin JSON", column2="pms_api_log_ids",
help="Origin JSON",
) )

View File

@@ -573,6 +573,9 @@ class PmsProperty(models.Model):
clients = client clients = client
else: else:
clients = self.env["res.users"].search([("pms_api_client", "=", True)]) clients = self.env["res.users"].search([("pms_api_client", "=", True)])
room_type_ids = []
endpoint = ""
response = None
_logger.info("PMS API push batch") _logger.info("PMS API push batch")
if isinstance(date_from, str): if isinstance(date_from, str):
date_from = datetime.datetime.strptime(date_from, "%Y-%m-%d").date() date_from = datetime.datetime.strptime(date_from, "%Y-%m-%d").date()
@@ -593,73 +596,112 @@ class PmsProperty(models.Model):
] ]
) )
for pms_property in pms_properties: for pms_property in pms_properties:
property_client_conf = self.env["ota.property.settings"].search( try:
[ property_client_conf = (
("pms_property_id", "=", pms_property.id), self.env["ota.property.settings"]
("agency_id", "=", client.partner_id.id), .sudo()
] .search(
) [
pms_property_id = pms_property.id ("pms_property_id", "=", pms_property.id),
room_type_ids = ( ("agency_id", "=", client.partner_id.id),
[filter_room_type_id] ]
if filter_room_type_id )
else self.env["pms.room"]
.search([("pms_property_id", "=", pms_property_id)])
.mapped("room_type_id")
.filtered(
lambda r: r.id
not in property_client_conf.excluded_room_type_ids.ids
) )
.ids pms_property_id = pms_property.id
) room_type_ids = (
payload = { [filter_room_type_id]
"pmsPropertyId": pms_property_id, if filter_room_type_id
} else self.env["pms.room"]
data = [] .search([("pms_property_id", "=", pms_property_id)])
for room_type_id in room_type_ids: .mapped("room_type_id")
if call_type == "availability": .filtered(
endpoint = client.url_endpoint_availability lambda r: r.id
data.extend( not in property_client_conf.excluded_room_type_ids.ids
pms_property.generate_availability_json(
date_from=date_from,
date_to=date_to,
pms_property_id=pms_property_id,
room_type_id=room_type_id,
client=client,
)
) )
key_data = "avails" .ids
elif call_type == "restrictions": )
endpoint = client.url_endpoint_rules payload = {
data.extend( "pmsPropertyId": pms_property_id,
pms_property.generate_restrictions_json( }
date_from=date_from, data = []
date_to=date_to, for room_type_id in room_type_ids:
pms_property_id=pms_property_id, if call_type == "availability":
room_type_id=room_type_id, endpoint = client.url_endpoint_availability
client=client, data.extend(
) pms_property.generate_availability_json(
) date_from=date_from,
key_data = "rules" date_to=date_to,
elif call_type == "prices": pms_property_id=pms_property_id,
endpoint = client.url_endpoint_prices room_type_id=room_type_id,
data.extend( client=client,
pms_property.generate_prices_json( )
date_from=date_from, )
date_to=date_to, key_data = "avails"
pms_property_id=pms_property_id, elif call_type == "restrictions":
room_type_id=room_type_id, endpoint = client.url_endpoint_rules
client=client, data.extend(
) pms_property.generate_restrictions_json(
) date_from=date_from,
key_data = "prices" date_to=date_to,
else: pms_property_id=pms_property_id,
raise ValidationError(_("Invalid call type")) room_type_id=room_type_id,
if data: client=client,
payload[key_data] = data )
response = self.pms_api_push_payload(payload, endpoint, client) )
_logger.info( key_data = "rules"
f"""PMS API push batch response to elif call_type == "prices":
{endpoint}: {response.status_code} - {response.text}""" endpoint = client.url_endpoint_prices
data.extend(
pms_property.generate_prices_json(
date_from=date_from,
date_to=date_to,
pms_property_id=pms_property_id,
room_type_id=room_type_id,
client=client,
)
)
key_data = "prices"
else:
raise ValidationError(_("Invalid call type"))
if data:
payload[key_data] = data
response = self.pms_api_push_payload(payload, endpoint, client)
_logger.info(
f"""PMS API push batch response to
{endpoint}: {response.status_code} - {response.text}"""
)
self.invalidate_cache()
self.env["pms.api.log"].sudo().create(
{
"pms_property_id": pms_property_id,
"client_id": client.id,
"request": payload,
"response": str(response),
"status": "success" if response.ok else "error",
"request_date": fields.Datetime.now(),
"method": "PUSH",
"endpoint": endpoint,
"target_date_from": date_from,
"target_date_to": date_to,
"request_type": call_type,
"room_type_ids": room_type_ids,
}
)
except Exception as e:
_logger.error(f"""PMS API push batch error: {e}""")
self.env["pms.api.log"].sudo().create(
{
"pms_property_id": pms_property_id,
"client_id": client.id,
"request": payload,
"response": str(e),
"status": "error",
"request_date": fields.Datetime.now(),
"method": "PUSH",
"endpoint": endpoint,
"target_date_from": date_from,
"target_date_to": date_to,
"request_type": call_type,
"room_type_ids": room_type_ids,
}
) )
self.invalidate_cache()

View File

@@ -1,4 +1,3 @@
import ast
import base64 import base64
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -584,6 +583,12 @@ class PmsFolioService(Component):
def create_folio(self, pms_folio_info): def create_folio(self, pms_folio_info):
external_app = self.env.user.pms_api_client external_app = self.env.user.pms_api_client
log_payload = pms_folio_info log_payload = pms_folio_info
min_checkin_payload = min(
pms_folio_info.reservations, key=lambda x: x.checkin
).checkin
max_checkout_payload = max(
pms_folio_info.reservations, key=lambda x: x.checkout
).checkout
try: try:
if pms_folio_info.reservationType == "out": if pms_folio_info.reservationType == "out":
vals = { vals = {
@@ -760,7 +765,7 @@ class PmsFolioService(Component):
force_write_blocked=True if external_app else False, force_write_blocked=True if external_app else False,
)._compute_board_service_room_id() )._compute_board_service_room_id()
pms_folio_info.transactions = self.normalize_payments_structure( pms_folio_info.transactions = self.normalize_payments_structure(
pms_folio_info pms_folio_info, folio
) )
if pms_folio_info.transactions: if pms_folio_info.transactions:
self.compute_transactions(folio, pms_folio_info.transactions) self.compute_transactions(folio, pms_folio_info.transactions)
@@ -793,9 +798,7 @@ class PmsFolioService(Component):
date_to=date_to, date_to=date_to,
) )
if external_app: if external_app:
self.env["pms.api.log"].with_context( self.env["pms.api.log"].sudo().create(
record_ids=folio.ids
).sudo().create(
{ {
"pms_property_id": pms_folio_info.pmsPropertyId, "pms_property_id": pms_folio_info.pmsPropertyId,
"client_id": self.env.user.id, "client_id": self.env.user.id,
@@ -805,9 +808,10 @@ class PmsFolioService(Component):
"request_date": fields.Datetime.now(), "request_date": fields.Datetime.now(),
"method": "POST", "method": "POST",
"endpoint": "/folios", "endpoint": "/folios",
"model_id": self.env["ir.model"] "folio_ids": folio.ids,
.search([("model", "=", "pms.folio")]) "target_date_from": min_checkin_payload,
.id, "target_date_to": max_checkout_payload,
"request_type": "folios",
} }
) )
return folio.id return folio.id
@@ -827,9 +831,10 @@ class PmsFolioService(Component):
"request_date": fields.Datetime.now(), "request_date": fields.Datetime.now(),
"method": "POST", "method": "POST",
"endpoint": "/folios", "endpoint": "/folios",
"model_id": self.env["ir.model"] "folio_ids": [],
.search([("model", "=", "pms.folio")]) "target_date_from": min_checkin_payload,
.id, "target_date_to": max_checkout_payload,
"request_type": "folios",
} }
) )
if not external_app: if not external_app:
@@ -844,52 +849,62 @@ class PmsFolioService(Component):
reference += transaction.reference reference += transaction.reference
else: else:
raise ValidationError(_("The transaction reference is required")) raise ValidationError(_("The transaction reference is required"))
if not self.env["account.payment"].search( proposed_transaction = self.env["account.payment"].search(
[ [
("pms_property_id", "=", folio.pms_property_id.id), ("pms_property_id", "=", folio.pms_property_id.id),
("payment_type", "=", transaction.transactionType), ("payment_type", "=", transaction.transactionType),
("folio_ids", "in", folio.id), ("folio_ids", "in", folio.id),
("ref", "ilike", transaction.reference), ("ref", "ilike", reference),
("state", "=", "posted"),
] ]
)
if (
not proposed_transaction
or proposed_transaction.amount != transaction.amount
): ):
journal = self.env["account.journal"].search( if proposed_transaction:
[("id", "=", transaction.journalId)] proposed_transaction.action_draft()
) proposed_transaction.amount = transaction.amount
if not journal: proposed_transaction.action_post()
ota_conf = self.env["ota.property.settings"].search( else:
[ journal = self.env["account.journal"].search(
("pms_property_id", "=", folio.pms_property_id.id), [("id", "=", transaction.journalId)]
("agency_id", "=", self.env.user.partner_id.id),
]
)
if ota_conf:
journal = ota_conf.pms_api_payment_journal_id
if transaction.transactionType == "inbound":
folio.do_payment(
journal,
journal.suspense_account_id,
self.env.user,
transaction.amount,
folio,
reservations=False,
services=False,
partner=False,
date=datetime.strptime(transaction.date, "%Y-%m-%d"),
ref=reference,
)
elif transaction.transactionType == "outbound":
folio.do_refund(
journal,
journal.suspense_account_id,
self.env.user,
transaction.amount,
folio,
reservations=False,
services=False,
partner=False,
date=datetime.strptime(transaction.date, "%Y-%m-%d"),
ref=reference,
) )
if not journal:
ota_conf = self.env["ota.property.settings"].search(
[
("pms_property_id", "=", folio.pms_property_id.id),
("agency_id", "=", self.env.user.partner_id.id),
]
)
if ota_conf:
journal = ota_conf.pms_api_payment_journal_id
if transaction.transactionType == "inbound":
folio.do_payment(
journal,
journal.suspense_account_id,
self.env.user,
transaction.amount,
folio,
reservations=False,
services=False,
partner=False,
date=datetime.strptime(transaction.date, "%Y-%m-%d"),
ref=reference,
)
elif transaction.transactionType == "outbound":
folio.do_refund(
journal,
journal.suspense_account_id,
self.env.user,
transaction.amount,
folio,
reservations=False,
services=False,
partner=False,
date=datetime.strptime(transaction.date, "%Y-%m-%d"),
ref=reference,
)
@restapi.method( @restapi.method(
[ [
@@ -913,7 +928,7 @@ class PmsFolioService(Component):
folio.action_cancel() folio.action_cancel()
if pms_folio_info.confirmReservations: if pms_folio_info.confirmReservations:
for reservation in folio.reservation_ids: for reservation in folio.reservation_ids:
reservation.confirm() reservation.action_confirm()
if pms_folio_info.internalComment is not None: if pms_folio_info.internalComment is not None:
folio_vals.update({"internal_comment": pms_folio_info.internalComment}) folio_vals.update({"internal_comment": pms_folio_info.internalComment})
if pms_folio_info.partnerId: if pms_folio_info.partnerId:
@@ -1593,6 +1608,12 @@ class PmsFolioService(Component):
def update_put_external_folio(self, external_reference, pms_folio_info): def update_put_external_folio(self, external_reference, pms_folio_info):
external_app = self.env.user.pms_api_client external_app = self.env.user.pms_api_client
log_payload = pms_folio_info log_payload = pms_folio_info
min_checkin_payload = min(
pms_folio_info.reservations, key=lambda x: x.checkin
).checkin
max_checkout_payload = max(
pms_folio_info.reservations, key=lambda x: x.checkout
).checkout
try: try:
folio = self.env["pms.folio"].search( folio = self.env["pms.folio"].search(
[ [
@@ -1603,7 +1624,7 @@ class PmsFolioService(Component):
if not folio or len(folio) > 1: if not folio or len(folio) > 1:
raise MissingError(_("Folio not found")) raise MissingError(_("Folio not found"))
self.update_folio_values(folio, pms_folio_info) self.update_folio_values(folio, pms_folio_info)
self.env["pms.api.log"].with_context(record_ids=folio.ids).sudo().create( self.env["pms.api.log"].sudo().create(
{ {
"pms_property_id": pms_folio_info.pmsPropertyId, "pms_property_id": pms_folio_info.pmsPropertyId,
"client_id": self.env.user.id, "client_id": self.env.user.id,
@@ -1613,9 +1634,10 @@ class PmsFolioService(Component):
"request_date": fields.Datetime.now(), "request_date": fields.Datetime.now(),
"method": "PUT", "method": "PUT",
"endpoint": "/folios", "endpoint": "/folios",
"model_id": self.env["ir.model"] "folio_ids": folio.ids,
.search([("model", "=", "pms.folio")]) "target_date_from": min_checkin_payload,
.id, "target_date_to": max_checkout_payload,
"request_type": "folios",
} }
) )
return folio.id return folio.id
@@ -1635,9 +1657,10 @@ class PmsFolioService(Component):
"request_date": fields.Datetime.now(), "request_date": fields.Datetime.now(),
"method": "PUT", "method": "PUT",
"endpoint": "/folios", "endpoint": "/folios",
"model_id": self.env["ir.model"] "folio_ids": [],
.search([("model", "=", "pms.folio")]) "target_date_from": min_checkin_payload,
.id, "target_date_to": max_checkout_payload,
"request_type": "folios",
} }
) )
if not external_app: if not external_app:
@@ -1660,12 +1683,18 @@ class PmsFolioService(Component):
def update_put_folio(self, folio_id, pms_folio_info): def update_put_folio(self, folio_id, pms_folio_info):
external_app = self.env.user.pms_api_client external_app = self.env.user.pms_api_client
log_payload = pms_folio_info log_payload = pms_folio_info
min_checkin_payload = min(
pms_folio_info.reservations, key=lambda x: x.checkin
).checkin
max_checkout_payload = max(
pms_folio_info.reservations, key=lambda x: x.checkout
).checkout
try: try:
folio = self.env["pms.folio"].browse(folio_id) folio = self.env["pms.folio"].browse(folio_id)
if not folio: if not folio:
raise MissingError(_("Folio not found")) raise MissingError(_("Folio not found"))
self.update_folio_values(folio, pms_folio_info) self.update_folio_values(folio, pms_folio_info)
self.env["pms.api.log"].with_context(record_ids=folio.ids).sudo().create( self.env["pms.api.log"].sudo().create(
{ {
"pms_property_id": pms_folio_info.pmsPropertyId, "pms_property_id": pms_folio_info.pmsPropertyId,
"client_id": self.env.user.id, "client_id": self.env.user.id,
@@ -1675,9 +1704,10 @@ class PmsFolioService(Component):
"request_date": fields.Datetime.now(), "request_date": fields.Datetime.now(),
"method": "PUT", "method": "PUT",
"endpoint": "/folios", "endpoint": "/folios",
"model_id": self.env["ir.model"] "folio_ids": folio.ids,
.search([("model", "=", "pms.folio")]) "target_date_from": min_checkin_payload,
.id, "target_date_to": max_checkout_payload,
"request_type": "folios",
} }
) )
@@ -1698,9 +1728,10 @@ class PmsFolioService(Component):
"request_date": fields.Datetime.now(), "request_date": fields.Datetime.now(),
"method": "PUT", "method": "PUT",
"endpoint": "/folios", "endpoint": "/folios",
"model_id": self.env["ir.model"] "folio_ids": [],
.search([("model", "=", "pms.folio")]) "target_date_from": min_checkin_payload,
.id, "target_date_to": max_checkout_payload,
"request_type": "folios",
} }
) )
if not external_app: if not external_app:
@@ -1710,17 +1741,11 @@ class PmsFolioService(Component):
def update_folio_values(self, folio, pms_folio_info): def update_folio_values(self, folio, pms_folio_info):
external_app = self.env.user.pms_api_client external_app = self.env.user.pms_api_client
origin_values_dict = False
if external_app:
origin_values_dict = ast.literal_eval(folio.origin_json)
if origin_values_dict:
# Compare the values of the origin folio with the new values
# and set the new value to None if it is the same as the origin value
for key, value in origin_values_dict:
if value == pms_folio_info[key]:
pms_folio_info[key] = None
folio_vals = {} folio_vals = {}
if pms_folio_info.state == "cancel": if pms_folio_info.state == "cancel" and folio.state != "cancel":
draft_invoices = folio.invoice_ids.filtered(lambda i: i.state == "draft")
if draft_invoices:
draft_invoices.action_cancel()
folio.action_cancel() folio.action_cancel()
return folio.id return folio.id
# if ( # if (
@@ -1731,7 +1756,7 @@ class PmsFolioService(Component):
# ) # )
# ): # ):
# for reservation in folio.reservation_ids: # for reservation in folio.reservation_ids:
# reservation.confirm() # reservation.action_confirm()
if ( if (
pms_folio_info.internalComment is not None pms_folio_info.internalComment is not None
and pms_folio_info.internalComment not in folio.internal_comment and pms_folio_info.internalComment not in folio.internal_comment
@@ -1765,32 +1790,49 @@ class PmsFolioService(Component):
folio_vals.update({"mobile": pms_folio_info.partnerPhone}) folio_vals.update({"mobile": pms_folio_info.partnerPhone})
if ( if (
self.get_language(pms_folio_info.language) self.get_language(pms_folio_info.language)
and self.get_language(pms_folio_info.language) != pms_folio_info.language and self.get_language(pms_folio_info.language) != folio.lang
): ):
folio_vals.update({"lang": self.get_language(pms_folio_info.language)}) folio_vals.update({"lang": self.get_language(pms_folio_info.language)})
reservations_vals = []
if pms_folio_info.reservations: if pms_folio_info.reservations:
reservations_vals = self.wrapper_reservations( reservations_vals = self.wrapper_reservations(
folio, pms_folio_info.reservations folio, pms_folio_info.reservations
) )
if reservations_vals: if reservations_vals:
folio_vals.update({"reservation_ids": reservations_vals})
if folio_vals:
if reservations_vals:
# Cancel the old reservations that have not been included in the update
update_reservation_ids = [] update_reservation_ids = []
for val in reservations_vals: for val in reservations_vals:
# Cancel the old reservations that have not been included in the update
if val[0] == 1: if val[0] == 1:
if val[2].get("state") == "cancel":
self.env["pms.reservation"].with_context(
force_write_blocked=True
).browse(val[1]).action_cancel()
# delete from reservations_vals the reservation that has been canceled
reservations_vals.pop(reservations_vals.index(val))
if val[2].get("state") == "confirm":
self.env["pms.reservation"].with_context(
force_write_blocked=True
).browse(val[1]).action_confirm()
# delete from reservations_vals the field state
val[2].pop("state")
update_reservation_ids.append(val[1]) update_reservation_ids.append(val[1])
folio.reservation_ids.filtered( old_reservations_to_cancel = folio.reservation_ids.filtered(
lambda r: r.state != "cancel" and r.id not in update_reservation_ids lambda r: r.state != "cancel" and r.id not in update_reservation_ids
).with_context(modified=True, force_write_blocked=True).action_cancel() )
old_reservations_to_cancel.with_context(
modified=True, force_write_blocked=True
).action_cancel()
folio_vals.update({"reservation_ids": reservations_vals})
if folio_vals:
folio.with_context( folio.with_context(
skip_compute_service_ids=False if external_app else True, skip_compute_service_ids=False if external_app else True,
force_overbooking=True if external_app else False, force_overbooking=True if external_app else False,
force_write_blocked=True if external_app else False, force_write_blocked=True if external_app else False,
).write(folio_vals) ).write(folio_vals)
# Compute OTA transactions # Compute OTA transactions
pms_folio_info.transactions = self.normalize_payments_structure(pms_folio_info) pms_folio_info.transactions = self.normalize_payments_structure(
pms_folio_info, folio
)
if pms_folio_info.transactions: if pms_folio_info.transactions:
self.compute_transactions(folio, pms_folio_info.transactions) self.compute_transactions(folio, pms_folio_info.transactions)
# Force update availability # Force update availability
@@ -1804,7 +1846,7 @@ class PmsFolioService(Component):
date_to=date_to, date_to=date_to,
) )
def normalize_payments_structure(self, pms_folio_info): def normalize_payments_structure(self, pms_folio_info, folio):
""" """
This method use the OTA payment structure to normalize the structure This method use the OTA payment structure to normalize the structure
and incorporate them in the transactions datamodel param and incorporate them in the transactions datamodel param
@@ -1821,6 +1863,17 @@ class PmsFolioService(Component):
("agency_id", "=", self.env.user.partner_id.id), ("agency_id", "=", self.env.user.partner_id.id),
] ]
) )
if not ota_conf:
raise ValidationError(
_("No OTA configuration found for this property")
)
if not ota_conf.pms_api_payment_journal_id:
raise ValidationError(
_(
"No payment journal configured for this property for %s"
% ota_conf.name
)
)
transaction.journalId = ota_conf.pms_api_payment_journal_id.id transaction.journalId = ota_conf.pms_api_payment_journal_id.id
elif pms_folio_info.agencyId: elif pms_folio_info.agencyId:
ota_conf = self.env["ota.property.settings"].search( ota_conf = self.env["ota.property.settings"].search(
@@ -1849,7 +1902,7 @@ class PmsFolioService(Component):
pmsTransactionInfo( pmsTransactionInfo(
journalId=journal.id, journalId=journal.id,
transactionType="inbound", transactionType="inbound",
amount=pms_folio_info.totalPrice, amount=round(folio.amount_total, 2),
date=fields.Date.today().strftime("%Y-%m-%d"), date=fields.Date.today().strftime("%Y-%m-%d"),
reference=pms_folio_info.externalReference, reference=pms_folio_info.externalReference,
) )
@@ -1871,22 +1924,17 @@ class PmsFolioService(Component):
# Search a reservation in saved_reservations whose sum of night amounts is equal # Search a reservation in saved_reservations whose sum of night amounts is equal
# to the sum of night amounts of info_reservation, and dates equal, # to the sum of night amounts of info_reservation, and dates equal,
# if we find it we update it # if we find it we update it
payload_nights = round(
sum(info_reservation.reservationLines.mapped("price")), 2
)
proposed_reservation = saved_reservations.filtered( proposed_reservation = saved_reservations.filtered(
lambda r: r.checkin == info_reservation.checkin lambda r: r.checkin
and r.checkout == info_reservation.checkout == datetime.strptime(info_reservation.checkin, "%Y-%m-%d").date()
and r.room_type_id == info_reservation.roomTypeId and r.checkout
).filtered( == datetime.strptime(info_reservation.checkout, "%Y-%m-%d").date()
lambda r: round( and r.room_type_id.id == info_reservation.roomTypeId
sum(r.reservation_line_ids.mapped("price")) and r.adults == info_reservation.adults
+ r.service_ids.filtered(lambda s: s.is_board_service).mapped(""), and r.children == info_reservation.children
2,
)
== payload_nights
) )
if proposed_reservation: if proposed_reservation:
proposed_reservation = proposed_reservation[0]
saved_reservations -= proposed_reservation saved_reservations -= proposed_reservation
vals = {} vals = {}
new_res = not proposed_reservation new_res = not proposed_reservation
@@ -1900,12 +1948,17 @@ class PmsFolioService(Component):
): ):
vals.update({"room_type_id": info_reservation.roomTypeId}) vals.update({"room_type_id": info_reservation.roomTypeId})
if info_reservation.checkin: if info_reservation.checkin:
if new_res or proposed_reservation.checkin != info_reservation.checkin: if (
new_res
or proposed_reservation.checkin
!= datetime.strptime(info_reservation.checkin, "%Y-%m-%d").date()
):
vals.update({"checkin": info_reservation.checkin}) vals.update({"checkin": info_reservation.checkin})
if info_reservation.checkout: if info_reservation.checkout:
if ( if (
new_res new_res
or proposed_reservation.checkout != info_reservation.checkout or proposed_reservation.checkout
!= datetime.strptime(info_reservation.checkout, "%Y-%m-%d").date()
): ):
vals.update({"checkout": info_reservation.checkout}) vals.update({"checkout": info_reservation.checkout})
if info_reservation.pricelistId: if info_reservation.pricelistId:
@@ -1929,7 +1982,7 @@ class PmsFolioService(Component):
if info_reservation.preferredRoomId: if info_reservation.preferredRoomId:
if ( if (
new_res new_res
or proposed_reservation.preferredRoomId or proposed_reservation.preferred_room_id.id
!= info_reservation.preferredRoomId != info_reservation.preferredRoomId
): ):
vals.update({"preferred_room_id": info_reservation.preferredRoomId}) vals.update({"preferred_room_id": info_reservation.preferredRoomId})
@@ -1942,7 +1995,9 @@ class PmsFolioService(Component):
or proposed_reservation.children != info_reservation.children or proposed_reservation.children != info_reservation.children
): ):
vals.update({"children": info_reservation.children}) vals.update({"children": info_reservation.children})
if info_reservation.reservationLines and new_res: if new_res or info_reservation.stateCode != proposed_reservation.state:
vals.update({"state": info_reservation.stateCode})
if info_reservation.reservationLines:
# The service price is included in day price when it is a board service (external api) # The service price is included in day price when it is a board service (external api)
board_day_price = 0 board_day_price = 0
if external_app and vals.get("board_service_room_id"): if external_app and vals.get("board_service_room_id"):
@@ -1974,6 +2029,7 @@ class PmsFolioService(Component):
reservation_lines_cmds = self.wrapper_reservation_lines( reservation_lines_cmds = self.wrapper_reservation_lines(
reservation=info_reservation, reservation=info_reservation,
board_day_price=board_day_price, board_day_price=board_day_price,
proposed_reservation=proposed_reservation,
) )
if reservation_lines_cmds: if reservation_lines_cmds:
vals.update({"reservation_line_ids": reservation_lines_cmds}) vals.update({"reservation_line_ids": reservation_lines_cmds})
@@ -1994,20 +2050,38 @@ class PmsFolioService(Component):
cmds.append((1, proposed_reservation.id, vals)) cmds.append((1, proposed_reservation.id, vals))
return cmds return cmds
def wrapper_reservation_lines(self, reservation, board_day_price=0): def wrapper_reservation_lines(
self, reservation, board_day_price=0, proposed_reservation=False
):
cmds = [] cmds = []
for line in reservation.reservationLines: for line in reservation.reservationLines:
cmds.append( if proposed_reservation:
( # Not is necesay check new dates, becouse a if the dates change, the reservation is new
0, proposed_line = proposed_reservation.reservation_line_ids.filtered(
False, lambda l: l.date == datetime.strptime(line.date, "%Y-%m-%d").date()
{
"date": line.date,
"price": line.price - board_day_price,
"discount": line.discount or 0,
},
) )
) if proposed_line:
vals = {}
if round(proposed_line.price, 2) != round(
line.price - board_day_price, 2
):
vals.update({"price": line.price - board_day_price})
if round(proposed_line.discount, 2) != round(line.discount, 2):
vals.update({"discount": line.discount})
if vals:
cmds.append((1, proposed_line.id, vals))
else:
cmds.append(
(
0,
False,
{
"date": line.date,
"price": line.price - board_day_price,
"discount": line.discount or 0,
},
)
)
return cmds return cmds
def wrapper_reservation_services(self, info_services, services=False): def wrapper_reservation_services(self, info_services, services=False):
@@ -2045,17 +2119,21 @@ class PmsFolioService(Component):
It is used to override potential availability changes on the channel made unilaterally, It is used to override potential availability changes on the channel made unilaterally,
for example, upon entering or canceling a reservation. for example, upon entering or canceling a reservation.
""" """
api_clients = self.env["res.users"].search( api_clients = (
[ self.env["res.users"]
("pms_api_client", "=", True), .sudo()
("pms_property_ids", "in", pms_property_id), .search(
] [
("pms_api_client", "=", True),
("pms_property_ids", "in", pms_property_id),
]
)
) )
if not room_type_ids or not api_clients: if not room_type_ids or not api_clients:
return False return False
for room_type_id in room_type_ids: for room_type_id in room_type_ids:
pms_property = self.env["pms.property"].browse(pms_property_id) pms_property = self.env["pms.property"].browse(pms_property_id)
self.env["pms.property"].pms_api_push_batch( self.env["pms.property"].sudo().pms_api_push_batch(
call_type="availability", # 'availability', 'prices', 'restrictions' call_type="availability", # 'availability', 'prices', 'restrictions'
date_from=date_from.strftime("%Y-%m-%d"), # 'YYYY-MM-DD' date_from=date_from.strftime("%Y-%m-%d"), # 'YYYY-MM-DD'
date_to=date_to.strftime("%Y-%m-%d"), # 'YYYY-MM-DD' date_to=date_to.strftime("%Y-%m-%d"), # 'YYYY-MM-DD'

View File

@@ -3,12 +3,12 @@ from datetime import datetime, timedelta
from odoo import _, fields from odoo import _, fields
from odoo.exceptions import MissingError from odoo.exceptions import MissingError
from odoo.osv import expression
from odoo.tools.safe_eval import safe_eval
from odoo.addons.base_rest import restapi from odoo.addons.base_rest import restapi
from odoo.addons.base_rest_datamodel.restapi import Datamodel from odoo.addons.base_rest_datamodel.restapi import Datamodel
from odoo.addons.component.core import Component from odoo.addons.component.core import Component
from odoo.osv import expression
from odoo.tools.safe_eval import safe_eval
class PmsReservationService(Component): class PmsReservationService(Component):
@@ -284,13 +284,12 @@ class PmsReservationService(Component):
if reservation_data.stateCode == "cancel": if reservation_data.stateCode == "cancel":
reservation.action_cancel() reservation.action_cancel()
if reservation_data.stateCode == "confirm": if reservation_data.stateCode == "confirm":
reservation.confirm() reservation.action_confirm()
if reservation_data.toCheckout is not None and reservation_data.toCheckout: if reservation_data.toCheckout is not None and reservation_data.toCheckout:
reservation.action_reservation_checkout() reservation.action_reservation_checkout()
if reservation_data.undoOnboard: if reservation_data.undoOnboard:
reservation.action_undo_onboard() reservation.action_undo_onboard()
def _get_reservation_lines_mapped(self, origin_data, reservation_line=False): def _get_reservation_lines_mapped(self, origin_data, reservation_line=False):
# Return dict witch reservation.lines values (only modified if line exist, # Return dict witch reservation.lines values (only modified if line exist,
# or all pass values if line not exist) # or all pass values if line not exist)
@@ -587,7 +586,8 @@ class PmsReservationService(Component):
if checkin_partner.document_number if checkin_partner.document_number
else None, else None,
documentExpeditionDate=datetime.combine( documentExpeditionDate=datetime.combine(
checkin_partner.document_expedition_date, datetime.min.time() checkin_partner.document_expedition_date,
datetime.min.time(),
).isoformat() ).isoformat()
if checkin_partner.document_expedition_date if checkin_partner.document_expedition_date
else None, else None,
@@ -658,7 +658,7 @@ class PmsReservationService(Component):
checkin_partner.write( checkin_partner.write(
self.mapping_checkin_partner_values( self.mapping_checkin_partner_values(
pms_checkin_partner_info, pms_checkin_partner_info,
checkin_partner.partner_id.id if checkin_partner.partner_id else False checkin_partner.partner_id.id if checkin_partner.partner_id else False,
) )
) )
# if not partner_id we need to force compute to create partner # if not partner_id we need to force compute to create partner
@@ -860,7 +860,9 @@ class PmsReservationService(Component):
checkin_partner.write( checkin_partner.write(
self.mapping_checkin_partner_values( self.mapping_checkin_partner_values(
pms_checkin_partner_info, pms_checkin_partner_info,
checkin_partner.partner_id.id if checkin_partner.partner_id else False checkin_partner.partner_id.id
if checkin_partner.partner_id
else False,
) )
) )
# if not partner_id we need to force compute to create partner # if not partner_id we need to force compute to create partner
@@ -884,7 +886,9 @@ class PmsReservationService(Component):
if checkin_partner: if checkin_partner:
checkin_partner.unlink() checkin_partner.unlink()
def mapping_checkin_partner_values(self, pms_checkin_partner_info, partner_id=False): def mapping_checkin_partner_values(
self, pms_checkin_partner_info, partner_id=False
):
vals = { vals = {
"firstname": pms_checkin_partner_info.firstname, "firstname": pms_checkin_partner_info.firstname,
"lastname": pms_checkin_partner_info.lastname, "lastname": pms_checkin_partner_info.lastname,
@@ -1099,17 +1103,17 @@ class PmsReservationService(Component):
) )
def wizard_states(self, reservation_id): def wizard_states(self, reservation_id):
reservation = self.env["pms.reservation"].search([("id", "=", reservation_id)]) reservation = self.env["pms.reservation"].search([("id", "=", reservation_id)])
today = datetime.now().strftime('%Y-%m-%d') today = datetime.now().strftime("%Y-%m-%d")
wizard_states = [ wizard_states = [
{ {
"code": "overbooking_with_availability", "code": "overbooking_with_availability",
"title": "Overbooking", "title": "Overbooking",
"domain": "[" "domain": "["
"('state', 'in', ['draft', 'confirm', 'arrival_delayed']), " "('state', 'in', ['draft', 'confirm', 'arrival_delayed']), "
"('overbooking', '=', True), " "('overbooking', '=', True), "
f"('checkin', '>=', '{today}')," f"('checkin', '>=', '{today}'),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"filtered": "lambda r: r.count_alternative_free_rooms", "filtered": "lambda r: r.count_alternative_free_rooms",
"text": f"Parece que ha entrado una reserva sin haber disponibilidad para {reservation.room_type_id.name}.", "text": f"Parece que ha entrado una reserva sin haber disponibilidad para {reservation.room_type_id.name}.",
"priority": 100, "priority": 100,
@@ -1118,64 +1122,64 @@ class PmsReservationService(Component):
"code": "overbooking_without_availability", "code": "overbooking_without_availability",
"title": "Overbooking", "title": "Overbooking",
"domain": "[" "domain": "["
"('state', 'in', ['draft', 'confirm', 'arrival_delayed']), " "('state', 'in', ['draft', 'confirm', 'arrival_delayed']), "
"('overbooking', '=', True), " "('overbooking', '=', True), "
f"('checkin', '>=', '{today}')," f"('checkin', '>=', '{today}'),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"filtered": "lambda r: r.count_alternative_free_rooms <= 0", "filtered": "lambda r: r.count_alternative_free_rooms <= 0",
"text": f"Parece que ha entrado una reserva sin haber disponibilidad para {reservation.room_type_id.name}." "text": f"Parece que ha entrado una reserva sin haber disponibilidad para {reservation.room_type_id.name}."
f"Por desgracia no parece que hay ninguna " f"Por desgracia no parece que hay ninguna "
f"habitación disponible con la capacidad suficiente para esta reserva", f"habitación disponible con la capacidad suficiente para esta reserva",
"priority": 150, "priority": 150,
}, },
{ {
"code": "splitted_without_availability", "code": "splitted_without_availability",
"title": "Divididas", "title": "Divididas",
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed'])," "domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
"('splitted', '=', True)," "('splitted', '=', True),"
f"('checkin', '>=', '{today}')," f"('checkin', '>=', '{today}'),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"filtered": "lambda r: r.count_alternative_free_rooms <= 0", "filtered": "lambda r: r.count_alternative_free_rooms <= 0",
"text": f"Parece que a {reservation.partner_name} le ha tocado dormir en habitaciones diferentes " "text": f"Parece que a {reservation.partner_name} le ha tocado dormir en habitaciones diferentes "
f" pero no hay ninguna habitación disponible para asignarle, puedes probar a mover otras reservas " f" pero no hay ninguna habitación disponible para asignarle, puedes probar a mover otras reservas "
f" para poder establecerle una única habitación. ", f" para poder establecerle una única habitación. ",
"priority": 200, "priority": 200,
}, },
{ {
"code": "splitted_with_availability", "code": "splitted_with_availability",
"title": "Divididas", "title": "Divididas",
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed'])," "domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
"('splitted', '=', True)," "('splitted', '=', True),"
f"('checkin', '>=', '{today}')," f"('checkin', '>=', '{today}'),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"filtered": "lambda r: r.count_alternative_free_rooms", "filtered": "lambda r: r.count_alternative_free_rooms",
"text": f"Parece que a {reservation.partner_name} le ha tocado dormir en habitaciones diferentes" "text": f"Parece que a {reservation.partner_name} le ha tocado dormir en habitaciones diferentes"
f" pero tienes la posibilidad de moverlo a {reservation.count_alternative_free_rooms} " f" pero tienes la posibilidad de moverlo a {reservation.count_alternative_free_rooms} "
f" {' habitación' if reservation.count_alternative_free_rooms == 1 else ' habitaciones'}.", f" {' habitación' if reservation.count_alternative_free_rooms == 1 else ' habitaciones'}.",
"priority": 220, "priority": 220,
}, },
{ {
"code": "to_assign", "code": "to_assign",
"title": "Por asignar", "title": "Por asignar",
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed'])," "domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
"('to_assign', '=', True)," "('to_assign', '=', True),"
"('reservation_type', 'in', ['normal', 'staff'])," "('reservation_type', 'in', ['normal', 'staff']),"
f"('checkin', '>=', '{today}')," f"('checkin', '>=', '{today}'),"
"]", "]",
"text": f"La reserva de {reservation.partner_name} ha sido asignada a la habitación {reservation.preferred_room_id.name}," "text": f"La reserva de {reservation.partner_name} ha sido asignada a la habitación {reservation.preferred_room_id.name},"
" puedes confirmar la habitación o cambiar a otra desde aquí.", " puedes confirmar la habitación o cambiar a otra desde aquí.",
"priority": 300, "priority": 300,
}, },
{ {
"code": "to_confirm", "code": "to_confirm",
"title": "Por confirmar", "title": "Por confirmar",
"domain": "[('state', '=', 'draft')," "domain": "[('state', '=', 'draft'),"
f"('checkin', '>=', '{today}')," f"('checkin', '>=', '{today}'),"
"('reservation_type', 'in', ['normal', 'staff'])," "('reservation_type', 'in', ['normal', 'staff']),"
"]", "]",
"text": f"La reserva de {reservation.partner_name} está pendiente de confirmar, puedes confirmarla desde aquí.", "text": f"La reserva de {reservation.partner_name} está pendiente de confirmar, puedes confirmarla desde aquí.",
"priority": 400, "priority": 400,
}, },
@@ -1183,36 +1187,36 @@ class PmsReservationService(Component):
"code": "checkin_done_precheckin", "code": "checkin_done_precheckin",
"title": "Entrada Hoy", "title": "Entrada Hoy",
"domain": "[('state', '=', 'confirm')," "domain": "[('state', '=', 'confirm'),"
f"('checkin', '=', '{today}')," f"('checkin', '=', '{today}'),"
"('pending_checkin_data', '=', 0)," "('pending_checkin_data', '=', 0),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"text": "Todos los huéspedes de esta reserva tienen los datos registrados, " "text": "Todos los huéspedes de esta reserva tienen los datos registrados, "
" puedes marcar la entrada directamente desde aquí", " puedes marcar la entrada directamente desde aquí",
"priority": 500, "priority": 500,
}, },
{ {
"code": "checkin_partial_precheckin", "code": "checkin_partial_precheckin",
"title": "Entrada Hoy", "title": "Entrada Hoy",
"domain": "[('state', '=', 'confirm')," "domain": "[('state', '=', 'confirm'),"
f"('checkin', '=', '{today}')," f"('checkin', '=', '{today}'),"
"('pending_checkin_data', '>', 0)," "('pending_checkin_data', '>', 0),"
"('checkin_partner_ids.state','=', 'precheckin')," "('checkin_partner_ids.state','=', 'precheckin'),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"text": f"Faltan {reservation.pending_checkin_data} {' huésped ' if reservation.pending_checkin_data == 1 else ' huéspedes '} " "text": f"Faltan {reservation.pending_checkin_data} {' huésped ' if reservation.pending_checkin_data == 1 else ' huéspedes '} "
f"por registrar sus datos.Puedes abrir el asistente de checkin " f"por registrar sus datos.Puedes abrir el asistente de checkin "
f" para completar los datos.", f" para completar los datos.",
"priority": 530, "priority": 530,
}, },
{ {
"code": "checkin_no_precheckin", "code": "checkin_no_precheckin",
"title": "Entrada Hoy", "title": "Entrada Hoy",
"domain": "[('state', '=', 'confirm')," "domain": "[('state', '=', 'confirm'),"
f"('checkin', '=', '{today}')," f"('checkin', '=', '{today}'),"
"('pending_checkin_data', '>', 0)," "('pending_checkin_data', '>', 0),"
"('reservation_type', 'in', ['normal', 'staff'])" "('reservation_type', 'in', ['normal', 'staff'])"
"]", "]",
"filtered": "lambda r: all([c.state in ('draft','dummy') for c in r.checkin_partner_ids]) ", "filtered": "lambda r: all([c.state in ('draft','dummy') for c in r.checkin_partner_ids]) ",
"text": "Registra los datos de los huéspedes desde el asistente del checkin.", "text": "Registra los datos de los huéspedes desde el asistente del checkin.",
"priority": 580, "priority": 580,
@@ -1221,24 +1225,24 @@ class PmsReservationService(Component):
"code": "confirmed_without_payment_and_precheckin", "code": "confirmed_without_payment_and_precheckin",
"title": "Confirmadas a futuro sin pagar y sin precheckin realizado", "title": "Confirmadas a futuro sin pagar y sin precheckin realizado",
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed'])," "domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
"('reservation_type', 'in', ['normal', 'staff'])," "('reservation_type', 'in', ['normal', 'staff']),"
f"('checkin', '>', '{today}')," f"('checkin', '>', '{today}'),"
"('pending_checkin_data', '>', 0)," "('pending_checkin_data', '>', 0),"
"('folio_payment_state', 'in', ['not_paid', 'partial'])" "('folio_payment_state', 'in', ['not_paid', 'partial'])"
"]", "]",
"text": "Esta reserva está pendiente de cobro y de que los huéspedes " "text": "Esta reserva está pendiente de cobro y de que los huéspedes "
" registren sus datos: puedes enviarles un recordatorio desde aquí", " registren sus datos: puedes enviarles un recordatorio desde aquí",
"priority": 600, "priority": 600,
}, },
{ {
"code": "confirmed_without_payment", "code": "confirmed_without_payment",
"title": "Confirmadas a futuro sin pagar", "title": "Confirmadas a futuro sin pagar",
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed'])," "domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
"('reservation_type', 'in', ['normal', 'staff'])," "('reservation_type', 'in', ['normal', 'staff']),"
f"('checkin', '>', '{today}')," f"('checkin', '>', '{today}'),"
"('pending_checkin_data', '=', 0)," "('pending_checkin_data', '=', 0),"
"('folio_payment_state', 'in', ['not_paid', 'partial'])" "('folio_payment_state', 'in', ['not_paid', 'partial'])"
"]", "]",
"text": "Esta reserva está pendiente de cobro, puedes enviarle sun recordatorio desde aquí", "text": "Esta reserva está pendiente de cobro, puedes enviarle sun recordatorio desde aquí",
"priority": 630, "priority": 630,
}, },
@@ -1246,11 +1250,11 @@ class PmsReservationService(Component):
"code": "confirmed_without_precheckin", "code": "confirmed_without_precheckin",
"title": "Confirmadas a futuro sin pagar", "title": "Confirmadas a futuro sin pagar",
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed'])," "domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
"('reservation_type', 'in', ['normal', 'staff'])," "('reservation_type', 'in', ['normal', 'staff']),"
f"('checkin', '>', '{today}')," f"('checkin', '>', '{today}'),"
"('pending_checkin_data', '>', 0)," "('pending_checkin_data', '>', 0),"
"('folio_payment_state', 'in', ['paid', 'overpayment','nothing_to_pay'])" "('folio_payment_state', 'in', ['paid', 'overpayment','nothing_to_pay'])"
"]", "]",
"text": "Esta reserva no tiene los datos de los huéspedes registrados, puedes enviarles un recordatorio desde aquí", "text": "Esta reserva no tiene los datos de los huéspedes registrados, puedes enviarles un recordatorio desde aquí",
"priority": 660, "priority": 660,
}, },
@@ -1258,20 +1262,20 @@ class PmsReservationService(Component):
"code": "cancelled", "code": "cancelled",
"title": "Cancelada con cargos y sin cobrar", "title": "Cancelada con cargos y sin cobrar",
"domain": "[('state', '=', 'cancel')," "domain": "[('state', '=', 'cancel'),"
"('cancelled_reason', 'in',['late','noshow'])," "('cancelled_reason', 'in',['late','noshow']),"
"('folio_payment_state', 'in', ['not_paid', 'partial'])," "('folio_payment_state', 'in', ['not_paid', 'partial']),"
"]", "]",
"filtered": "lambda r: r.service_ids.filtered(lambda s: s.is_cancel_penalty and s.price_total > 0)", "filtered": "lambda r: r.service_ids.filtered(lambda s: s.is_cancel_penalty and s.price_total > 0)",
"text": f"La reserva de {reservation.partner_name} ha sido cancelada con una penalización de {reservation.service_ids.filtered(lambda s: s.is_cancel_penalty).price_total}€," "text": f"La reserva de {reservation.partner_name} ha sido cancelada con una penalización de {reservation.service_ids.filtered(lambda s: s.is_cancel_penalty).price_total}€,"
" puedes eliminar la penalización en caso de que no se vaya a cobrar.", " puedes eliminar la penalización en caso de que no se vaya a cobrar.",
"priority": 700, "priority": 700,
}, },
{ {
"code": "onboard_without_payment", "code": "onboard_without_payment",
"title": "Por cobrar dentro", "title": "Por cobrar dentro",
"domain": "[('state', 'in', ['onboard', 'departure_delayed'])," "domain": "[('state', 'in', ['onboard', 'departure_delayed']),"
"('folio_payment_state', 'in', ['not_paid', 'partial'])" "('folio_payment_state', 'in', ['not_paid', 'partial'])"
"]", "]",
"text": f"En esta reserva tenemos un pago pendiente de {reservation.folio_pending_amount}. Puedes registrar el pago desde aquí.", "text": f"En esta reserva tenemos un pago pendiente de {reservation.folio_pending_amount}. Puedes registrar el pago desde aquí.",
"priority": 800, "priority": 800,
}, },
@@ -1279,37 +1283,37 @@ class PmsReservationService(Component):
"code": "done_without_payment", "code": "done_without_payment",
"title": "Por cobrar pasadas", "title": "Por cobrar pasadas",
"domain": "[('state', '=', 'done')," "domain": "[('state', '=', 'done'),"
"('folio_payment_state', 'in', ['not_paid', 'partial'])" "('folio_payment_state', 'in', ['not_paid', 'partial'])"
"]", "]",
"text": f"Esta reserva ha quedado con un cargo pendiente de {reservation.folio_pending_amount}€." "text": f"Esta reserva ha quedado con un cargo pendiente de {reservation.folio_pending_amount}€."
" Cuando gestiones el cobro puedes registrarlo desde aquí.", " Cuando gestiones el cobro puedes registrarlo desde aquí.",
"priority": 900, "priority": 900,
}, },
{ {
"code":"checkout", "code": "checkout",
"title": "Checkout", "title": "Checkout",
"domain": "[('state', 'in', ['onboard', 'departure_delayed'])," "domain": "[('state', 'in', ['onboard', 'departure_delayed']),"
f"('checkout', '=', '{today}')," f"('checkout', '=', '{today}'),"
"]", "]",
"text": "Reserva lista para el checkout, marca la salida directamente desde aquí.", "text": "Reserva lista para el checkout, marca la salida directamente desde aquí.",
"priority": 1000, "priority": 1000,
}, },
] ]
# We order the states by priority and return the first state whose domain meets the reservation; # We order the states by priority and return the first
# if the state also has the key 'filtered,' it must also meet that filter. # state whose domain meets the reservation;
# if the state also has the key 'filtered,'
# it must also meet that filter.
sorted_wizard_states = sorted(wizard_states, key=lambda x: x['priority']) sorted_wizard_states = sorted(wizard_states, key=lambda x: x["priority"])
PmsWizardStateInfo = self.env.datamodels["pms.wizard.state.info"] PmsWizardStateInfo = self.env.datamodels["pms.wizard.state.info"]
for state in sorted_wizard_states: for state in sorted_wizard_states:
domain = expression.AND([ domain = expression.AND(
[("id", "=", reservation_id)], [[("id", "=", reservation_id)], safe_eval(state["domain"])]
safe_eval(state["domain"]) )
])
if self.env["pms.reservation"].search_count(domain): if self.env["pms.reservation"].search_count(domain):
if ( if state.get("filtered") and not self.env["pms.reservation"].browse(
state.get("filtered") reservation_id
and not self.env["pms.reservation"].browse(reservation_id).filtered(safe_eval(state["filtered"])) ).filtered(safe_eval(state["filtered"])):
):
continue continue
return PmsWizardStateInfo( return PmsWizardStateInfo(
@@ -1319,8 +1323,7 @@ class PmsReservationService(Component):
) )
return PmsWizardStateInfo( return PmsWizardStateInfo(
code='', code="",
title='', title="",
text='', text="",
) )