mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP]pms_api_rest: Imrpovement API logs, PUT folio service and action_confirm reservation
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"data/sql_reports.xml",
|
||||
"data/auth_jwt_validator.xml",
|
||||
"data/pms_app_reset_password_template.xml",
|
||||
"data/cron_jobs.xml",
|
||||
"views/pms_property_views.xml",
|
||||
"views/res_users_views.xml",
|
||||
"views/pms_room_type_class_views.xml",
|
||||
|
||||
20
pms_api_rest/data/cron_jobs.xml
Normal file
20
pms_api_rest/data/cron_jobs.xml
Normal 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>
|
||||
@@ -1,4 +1,6 @@
|
||||
from odoo import _, api, fields, models
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
||||
|
||||
class PmsApiLog(models.Model):
|
||||
@@ -72,10 +74,39 @@ class PmsApiLog(models.Model):
|
||||
string="Response URL",
|
||||
help="Response URL",
|
||||
)
|
||||
model_id = fields.Many2one(
|
||||
string="Model",
|
||||
help="Model",
|
||||
comodel_name="ir.model",
|
||||
request_type = fields.Selection(
|
||||
string="Request Type",
|
||||
help="Request Type",
|
||||
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):
|
||||
@@ -90,10 +121,7 @@ class PmsApiLog(models.Model):
|
||||
|
||||
"""
|
||||
self.ensure_one()
|
||||
if "pms_api_log_id" in self.env[self.model_id.model]._fields:
|
||||
records = self.env[self.model_id.model].search(
|
||||
[("pms_api_log_id", "=", self.id)]
|
||||
)
|
||||
records = self.folio_ids
|
||||
if not records:
|
||||
return None
|
||||
action = {
|
||||
@@ -114,21 +142,15 @@ class PmsApiLog(models.Model):
|
||||
)
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
def clean_log_data(self, offset=60):
|
||||
"""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
|
||||
if record_ids id present in context
|
||||
"""
|
||||
log_record = super().create(vals)
|
||||
if self.env.context.get("record_ids"):
|
||||
records = self.env[self.env.context.get("model")].browse(
|
||||
self.env.context.get("record_ids")
|
||||
)
|
||||
records.write(
|
||||
{
|
||||
"pms_api_log_id": log_record.id,
|
||||
"origin_json": log_record.request,
|
||||
}
|
||||
)
|
||||
return log_record
|
||||
self.sudo().search(
|
||||
[
|
||||
("status", "=", "success"),
|
||||
("create_date", "<", fields.Datetime.now() - timedelta(days=offset)),
|
||||
]
|
||||
).unlink()
|
||||
|
||||
@@ -4,12 +4,11 @@ from odoo import fields, models
|
||||
class PmsFolio(models.Model):
|
||||
_inherit = "pms.folio"
|
||||
|
||||
pms_api_log_id = fields.Many2one(
|
||||
string="PMS API Log",
|
||||
help="PMS API Log",
|
||||
pms_api_log_ids = fields.Many2many(
|
||||
string="API Logs",
|
||||
help="API Logs",
|
||||
comodel_name="pms.api.log",
|
||||
)
|
||||
origin_json = fields.Text(
|
||||
string="Origin JSON",
|
||||
help="Origin JSON",
|
||||
relation="pms_folio_pms_api_log_rel",
|
||||
column1="folio_ids",
|
||||
column2="pms_api_log_ids",
|
||||
)
|
||||
|
||||
@@ -573,6 +573,9 @@ class PmsProperty(models.Model):
|
||||
clients = client
|
||||
else:
|
||||
clients = self.env["res.users"].search([("pms_api_client", "=", True)])
|
||||
room_type_ids = []
|
||||
endpoint = ""
|
||||
response = None
|
||||
_logger.info("PMS API push batch")
|
||||
if isinstance(date_from, str):
|
||||
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:
|
||||
property_client_conf = self.env["ota.property.settings"].search(
|
||||
[
|
||||
("pms_property_id", "=", pms_property.id),
|
||||
("agency_id", "=", client.partner_id.id),
|
||||
]
|
||||
)
|
||||
pms_property_id = pms_property.id
|
||||
room_type_ids = (
|
||||
[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
|
||||
try:
|
||||
property_client_conf = (
|
||||
self.env["ota.property.settings"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
("pms_property_id", "=", pms_property.id),
|
||||
("agency_id", "=", client.partner_id.id),
|
||||
]
|
||||
)
|
||||
)
|
||||
.ids
|
||||
)
|
||||
payload = {
|
||||
"pmsPropertyId": pms_property_id,
|
||||
}
|
||||
data = []
|
||||
for room_type_id in room_type_ids:
|
||||
if call_type == "availability":
|
||||
endpoint = client.url_endpoint_availability
|
||||
data.extend(
|
||||
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,
|
||||
)
|
||||
pms_property_id = pms_property.id
|
||||
room_type_ids = (
|
||||
[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
|
||||
)
|
||||
key_data = "avails"
|
||||
elif call_type == "restrictions":
|
||||
endpoint = client.url_endpoint_rules
|
||||
data.extend(
|
||||
pms_property.generate_restrictions_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 = "rules"
|
||||
elif call_type == "prices":
|
||||
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}"""
|
||||
.ids
|
||||
)
|
||||
payload = {
|
||||
"pmsPropertyId": pms_property_id,
|
||||
}
|
||||
data = []
|
||||
for room_type_id in room_type_ids:
|
||||
if call_type == "availability":
|
||||
endpoint = client.url_endpoint_availability
|
||||
data.extend(
|
||||
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"
|
||||
elif call_type == "restrictions":
|
||||
endpoint = client.url_endpoint_rules
|
||||
data.extend(
|
||||
pms_property.generate_restrictions_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 = "rules"
|
||||
elif call_type == "prices":
|
||||
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()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import ast
|
||||
import base64
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
@@ -584,6 +583,12 @@ class PmsFolioService(Component):
|
||||
def create_folio(self, pms_folio_info):
|
||||
external_app = self.env.user.pms_api_client
|
||||
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:
|
||||
if pms_folio_info.reservationType == "out":
|
||||
vals = {
|
||||
@@ -760,7 +765,7 @@ class PmsFolioService(Component):
|
||||
force_write_blocked=True if external_app else False,
|
||||
)._compute_board_service_room_id()
|
||||
pms_folio_info.transactions = self.normalize_payments_structure(
|
||||
pms_folio_info
|
||||
pms_folio_info, folio
|
||||
)
|
||||
if pms_folio_info.transactions:
|
||||
self.compute_transactions(folio, pms_folio_info.transactions)
|
||||
@@ -793,9 +798,7 @@ class PmsFolioService(Component):
|
||||
date_to=date_to,
|
||||
)
|
||||
if external_app:
|
||||
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,
|
||||
"client_id": self.env.user.id,
|
||||
@@ -805,9 +808,10 @@ class PmsFolioService(Component):
|
||||
"request_date": fields.Datetime.now(),
|
||||
"method": "POST",
|
||||
"endpoint": "/folios",
|
||||
"model_id": self.env["ir.model"]
|
||||
.search([("model", "=", "pms.folio")])
|
||||
.id,
|
||||
"folio_ids": folio.ids,
|
||||
"target_date_from": min_checkin_payload,
|
||||
"target_date_to": max_checkout_payload,
|
||||
"request_type": "folios",
|
||||
}
|
||||
)
|
||||
return folio.id
|
||||
@@ -827,9 +831,10 @@ class PmsFolioService(Component):
|
||||
"request_date": fields.Datetime.now(),
|
||||
"method": "POST",
|
||||
"endpoint": "/folios",
|
||||
"model_id": self.env["ir.model"]
|
||||
.search([("model", "=", "pms.folio")])
|
||||
.id,
|
||||
"folio_ids": [],
|
||||
"target_date_from": min_checkin_payload,
|
||||
"target_date_to": max_checkout_payload,
|
||||
"request_type": "folios",
|
||||
}
|
||||
)
|
||||
if not external_app:
|
||||
@@ -844,52 +849,62 @@ class PmsFolioService(Component):
|
||||
reference += transaction.reference
|
||||
else:
|
||||
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),
|
||||
("payment_type", "=", transaction.transactionType),
|
||||
("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(
|
||||
[("id", "=", transaction.journalId)]
|
||||
)
|
||||
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,
|
||||
if proposed_transaction:
|
||||
proposed_transaction.action_draft()
|
||||
proposed_transaction.amount = transaction.amount
|
||||
proposed_transaction.action_post()
|
||||
else:
|
||||
journal = self.env["account.journal"].search(
|
||||
[("id", "=", transaction.journalId)]
|
||||
)
|
||||
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(
|
||||
[
|
||||
@@ -913,7 +928,7 @@ class PmsFolioService(Component):
|
||||
folio.action_cancel()
|
||||
if pms_folio_info.confirmReservations:
|
||||
for reservation in folio.reservation_ids:
|
||||
reservation.confirm()
|
||||
reservation.action_confirm()
|
||||
if pms_folio_info.internalComment is not None:
|
||||
folio_vals.update({"internal_comment": pms_folio_info.internalComment})
|
||||
if pms_folio_info.partnerId:
|
||||
@@ -1593,6 +1608,12 @@ class PmsFolioService(Component):
|
||||
def update_put_external_folio(self, external_reference, pms_folio_info):
|
||||
external_app = self.env.user.pms_api_client
|
||||
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:
|
||||
folio = self.env["pms.folio"].search(
|
||||
[
|
||||
@@ -1603,7 +1624,7 @@ class PmsFolioService(Component):
|
||||
if not folio or len(folio) > 1:
|
||||
raise MissingError(_("Folio not found"))
|
||||
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,
|
||||
"client_id": self.env.user.id,
|
||||
@@ -1613,9 +1634,10 @@ class PmsFolioService(Component):
|
||||
"request_date": fields.Datetime.now(),
|
||||
"method": "PUT",
|
||||
"endpoint": "/folios",
|
||||
"model_id": self.env["ir.model"]
|
||||
.search([("model", "=", "pms.folio")])
|
||||
.id,
|
||||
"folio_ids": folio.ids,
|
||||
"target_date_from": min_checkin_payload,
|
||||
"target_date_to": max_checkout_payload,
|
||||
"request_type": "folios",
|
||||
}
|
||||
)
|
||||
return folio.id
|
||||
@@ -1635,9 +1657,10 @@ class PmsFolioService(Component):
|
||||
"request_date": fields.Datetime.now(),
|
||||
"method": "PUT",
|
||||
"endpoint": "/folios",
|
||||
"model_id": self.env["ir.model"]
|
||||
.search([("model", "=", "pms.folio")])
|
||||
.id,
|
||||
"folio_ids": [],
|
||||
"target_date_from": min_checkin_payload,
|
||||
"target_date_to": max_checkout_payload,
|
||||
"request_type": "folios",
|
||||
}
|
||||
)
|
||||
if not external_app:
|
||||
@@ -1660,12 +1683,18 @@ class PmsFolioService(Component):
|
||||
def update_put_folio(self, folio_id, pms_folio_info):
|
||||
external_app = self.env.user.pms_api_client
|
||||
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:
|
||||
folio = self.env["pms.folio"].browse(folio_id)
|
||||
if not folio:
|
||||
raise MissingError(_("Folio not found"))
|
||||
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,
|
||||
"client_id": self.env.user.id,
|
||||
@@ -1675,9 +1704,10 @@ class PmsFolioService(Component):
|
||||
"request_date": fields.Datetime.now(),
|
||||
"method": "PUT",
|
||||
"endpoint": "/folios",
|
||||
"model_id": self.env["ir.model"]
|
||||
.search([("model", "=", "pms.folio")])
|
||||
.id,
|
||||
"folio_ids": folio.ids,
|
||||
"target_date_from": min_checkin_payload,
|
||||
"target_date_to": max_checkout_payload,
|
||||
"request_type": "folios",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1698,9 +1728,10 @@ class PmsFolioService(Component):
|
||||
"request_date": fields.Datetime.now(),
|
||||
"method": "PUT",
|
||||
"endpoint": "/folios",
|
||||
"model_id": self.env["ir.model"]
|
||||
.search([("model", "=", "pms.folio")])
|
||||
.id,
|
||||
"folio_ids": [],
|
||||
"target_date_from": min_checkin_payload,
|
||||
"target_date_to": max_checkout_payload,
|
||||
"request_type": "folios",
|
||||
}
|
||||
)
|
||||
if not external_app:
|
||||
@@ -1710,17 +1741,11 @@ class PmsFolioService(Component):
|
||||
|
||||
def update_folio_values(self, folio, pms_folio_info):
|
||||
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 = {}
|
||||
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()
|
||||
return folio.id
|
||||
# if (
|
||||
@@ -1731,7 +1756,7 @@ class PmsFolioService(Component):
|
||||
# )
|
||||
# ):
|
||||
# for reservation in folio.reservation_ids:
|
||||
# reservation.confirm()
|
||||
# reservation.action_confirm()
|
||||
if (
|
||||
pms_folio_info.internalComment is not None
|
||||
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})
|
||||
if (
|
||||
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)})
|
||||
reservations_vals = []
|
||||
if pms_folio_info.reservations:
|
||||
reservations_vals = self.wrapper_reservations(
|
||||
folio, pms_folio_info.reservations
|
||||
)
|
||||
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 = []
|
||||
for val in reservations_vals:
|
||||
# Cancel the old reservations that have not been included in the update
|
||||
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])
|
||||
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
|
||||
).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(
|
||||
skip_compute_service_ids=False if external_app else True,
|
||||
force_overbooking=True if external_app else False,
|
||||
force_write_blocked=True if external_app else False,
|
||||
).write(folio_vals)
|
||||
# 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:
|
||||
self.compute_transactions(folio, pms_folio_info.transactions)
|
||||
# Force update availability
|
||||
@@ -1804,7 +1846,7 @@ class PmsFolioService(Component):
|
||||
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
|
||||
and incorporate them in the transactions datamodel param
|
||||
@@ -1821,6 +1863,17 @@ class PmsFolioService(Component):
|
||||
("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
|
||||
elif pms_folio_info.agencyId:
|
||||
ota_conf = self.env["ota.property.settings"].search(
|
||||
@@ -1849,7 +1902,7 @@ class PmsFolioService(Component):
|
||||
pmsTransactionInfo(
|
||||
journalId=journal.id,
|
||||
transactionType="inbound",
|
||||
amount=pms_folio_info.totalPrice,
|
||||
amount=round(folio.amount_total, 2),
|
||||
date=fields.Date.today().strftime("%Y-%m-%d"),
|
||||
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
|
||||
# to the sum of night amounts of info_reservation, and dates equal,
|
||||
# if we find it we update it
|
||||
payload_nights = round(
|
||||
sum(info_reservation.reservationLines.mapped("price")), 2
|
||||
)
|
||||
proposed_reservation = saved_reservations.filtered(
|
||||
lambda r: r.checkin == info_reservation.checkin
|
||||
and r.checkout == info_reservation.checkout
|
||||
and r.room_type_id == info_reservation.roomTypeId
|
||||
).filtered(
|
||||
lambda r: round(
|
||||
sum(r.reservation_line_ids.mapped("price"))
|
||||
+ r.service_ids.filtered(lambda s: s.is_board_service).mapped(""),
|
||||
2,
|
||||
)
|
||||
== payload_nights
|
||||
lambda r: r.checkin
|
||||
== datetime.strptime(info_reservation.checkin, "%Y-%m-%d").date()
|
||||
and r.checkout
|
||||
== datetime.strptime(info_reservation.checkout, "%Y-%m-%d").date()
|
||||
and r.room_type_id.id == info_reservation.roomTypeId
|
||||
and r.adults == info_reservation.adults
|
||||
and r.children == info_reservation.children
|
||||
)
|
||||
if proposed_reservation:
|
||||
proposed_reservation = proposed_reservation[0]
|
||||
saved_reservations -= proposed_reservation
|
||||
vals = {}
|
||||
new_res = not proposed_reservation
|
||||
@@ -1900,12 +1948,17 @@ class PmsFolioService(Component):
|
||||
):
|
||||
vals.update({"room_type_id": info_reservation.roomTypeId})
|
||||
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})
|
||||
if info_reservation.checkout:
|
||||
if (
|
||||
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})
|
||||
if info_reservation.pricelistId:
|
||||
@@ -1929,7 +1982,7 @@ class PmsFolioService(Component):
|
||||
if info_reservation.preferredRoomId:
|
||||
if (
|
||||
new_res
|
||||
or proposed_reservation.preferredRoomId
|
||||
or proposed_reservation.preferred_room_id.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
|
||||
):
|
||||
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)
|
||||
board_day_price = 0
|
||||
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=info_reservation,
|
||||
board_day_price=board_day_price,
|
||||
proposed_reservation=proposed_reservation,
|
||||
)
|
||||
if 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))
|
||||
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 = []
|
||||
for line in reservation.reservationLines:
|
||||
cmds.append(
|
||||
(
|
||||
0,
|
||||
False,
|
||||
{
|
||||
"date": line.date,
|
||||
"price": line.price - board_day_price,
|
||||
"discount": line.discount or 0,
|
||||
},
|
||||
if proposed_reservation:
|
||||
# Not is necesay check new dates, becouse a if the dates change, the reservation is new
|
||||
proposed_line = proposed_reservation.reservation_line_ids.filtered(
|
||||
lambda l: l.date == datetime.strptime(line.date, "%Y-%m-%d").date()
|
||||
)
|
||||
)
|
||||
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
|
||||
|
||||
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,
|
||||
for example, upon entering or canceling a reservation.
|
||||
"""
|
||||
api_clients = self.env["res.users"].search(
|
||||
[
|
||||
("pms_api_client", "=", True),
|
||||
("pms_property_ids", "in", pms_property_id),
|
||||
]
|
||||
api_clients = (
|
||||
self.env["res.users"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
("pms_api_client", "=", True),
|
||||
("pms_property_ids", "in", pms_property_id),
|
||||
]
|
||||
)
|
||||
)
|
||||
if not room_type_ids or not api_clients:
|
||||
return False
|
||||
for room_type_id in room_type_ids:
|
||||
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'
|
||||
date_from=date_from.strftime("%Y-%m-%d"), # 'YYYY-MM-DD'
|
||||
date_to=date_to.strftime("%Y-%m-%d"), # 'YYYY-MM-DD'
|
||||
|
||||
@@ -3,12 +3,12 @@ from datetime import datetime, timedelta
|
||||
|
||||
from odoo import _, fields
|
||||
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_datamodel.restapi import Datamodel
|
||||
from odoo.addons.component.core import Component
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
class PmsReservationService(Component):
|
||||
@@ -284,13 +284,12 @@ class PmsReservationService(Component):
|
||||
if reservation_data.stateCode == "cancel":
|
||||
reservation.action_cancel()
|
||||
if reservation_data.stateCode == "confirm":
|
||||
reservation.confirm()
|
||||
reservation.action_confirm()
|
||||
if reservation_data.toCheckout is not None and reservation_data.toCheckout:
|
||||
reservation.action_reservation_checkout()
|
||||
if reservation_data.undoOnboard:
|
||||
reservation.action_undo_onboard()
|
||||
|
||||
|
||||
def _get_reservation_lines_mapped(self, origin_data, reservation_line=False):
|
||||
# Return dict witch reservation.lines values (only modified if line exist,
|
||||
# or all pass values if line not exist)
|
||||
@@ -587,7 +586,8 @@ class PmsReservationService(Component):
|
||||
if checkin_partner.document_number
|
||||
else None,
|
||||
documentExpeditionDate=datetime.combine(
|
||||
checkin_partner.document_expedition_date, datetime.min.time()
|
||||
checkin_partner.document_expedition_date,
|
||||
datetime.min.time(),
|
||||
).isoformat()
|
||||
if checkin_partner.document_expedition_date
|
||||
else None,
|
||||
@@ -658,7 +658,7 @@ class PmsReservationService(Component):
|
||||
checkin_partner.write(
|
||||
self.mapping_checkin_partner_values(
|
||||
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
|
||||
@@ -860,7 +860,9 @@ class PmsReservationService(Component):
|
||||
checkin_partner.write(
|
||||
self.mapping_checkin_partner_values(
|
||||
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
|
||||
@@ -884,7 +886,9 @@ class PmsReservationService(Component):
|
||||
if checkin_partner:
|
||||
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 = {
|
||||
"firstname": pms_checkin_partner_info.firstname,
|
||||
"lastname": pms_checkin_partner_info.lastname,
|
||||
@@ -1099,17 +1103,17 @@ class PmsReservationService(Component):
|
||||
)
|
||||
def wizard_states(self, 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 = [
|
||||
{
|
||||
"code": "overbooking_with_availability",
|
||||
"title": "Overbooking",
|
||||
"domain": "["
|
||||
"('state', 'in', ['draft', 'confirm', 'arrival_delayed']), "
|
||||
"('overbooking', '=', True), "
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"('state', 'in', ['draft', 'confirm', 'arrival_delayed']), "
|
||||
"('overbooking', '=', True), "
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"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}.",
|
||||
"priority": 100,
|
||||
@@ -1118,64 +1122,64 @@ class PmsReservationService(Component):
|
||||
"code": "overbooking_without_availability",
|
||||
"title": "Overbooking",
|
||||
"domain": "["
|
||||
"('state', 'in', ['draft', 'confirm', 'arrival_delayed']), "
|
||||
"('overbooking', '=', True), "
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"('state', 'in', ['draft', 'confirm', 'arrival_delayed']), "
|
||||
"('overbooking', '=', True), "
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"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}."
|
||||
f"Por desgracia no parece que hay ninguna "
|
||||
f"habitación disponible con la capacidad suficiente para esta reserva",
|
||||
f"Por desgracia no parece que hay ninguna "
|
||||
f"habitación disponible con la capacidad suficiente para esta reserva",
|
||||
"priority": 150,
|
||||
},
|
||||
{
|
||||
"code": "splitted_without_availability",
|
||||
"title": "Divididas",
|
||||
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
|
||||
"('splitted', '=', True),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"('splitted', '=', True),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"filtered": "lambda r: r.count_alternative_free_rooms <= 0",
|
||||
"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" para poder establecerle una única habitación. ",
|
||||
f" pero no hay ninguna habitación disponible para asignarle, puedes probar a mover otras reservas "
|
||||
f" para poder establecerle una única habitación. ",
|
||||
"priority": 200,
|
||||
},
|
||||
{
|
||||
"code": "splitted_with_availability",
|
||||
"title": "Divididas",
|
||||
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
|
||||
"('splitted', '=', True),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"('splitted', '=', True),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"filtered": "lambda r: r.count_alternative_free_rooms",
|
||||
"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" {' habitación' if reservation.count_alternative_free_rooms == 1 else ' habitaciones'}.",
|
||||
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'}.",
|
||||
"priority": 220,
|
||||
},
|
||||
{
|
||||
"code": "to_assign",
|
||||
"title": "Por asignar",
|
||||
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
|
||||
"('to_assign', '=', True),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"]",
|
||||
"('to_assign', '=', True),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"]",
|
||||
"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,
|
||||
},
|
||||
{
|
||||
"code": "to_confirm",
|
||||
"title": "Por confirmar",
|
||||
"domain": "[('state', '=', 'draft'),"
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
"]",
|
||||
f"('checkin', '>=', '{today}'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
"]",
|
||||
"text": f"La reserva de {reservation.partner_name} está pendiente de confirmar, puedes confirmarla desde aquí.",
|
||||
"priority": 400,
|
||||
},
|
||||
@@ -1183,36 +1187,36 @@ class PmsReservationService(Component):
|
||||
"code": "checkin_done_precheckin",
|
||||
"title": "Entrada Hoy",
|
||||
"domain": "[('state', '=', 'confirm'),"
|
||||
f"('checkin', '=', '{today}'),"
|
||||
"('pending_checkin_data', '=', 0),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
f"('checkin', '=', '{today}'),"
|
||||
"('pending_checkin_data', '=', 0),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"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,
|
||||
},
|
||||
{
|
||||
"code": "checkin_partial_precheckin",
|
||||
"title": "Entrada Hoy",
|
||||
"domain": "[('state', '=', 'confirm'),"
|
||||
f"('checkin', '=', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('checkin_partner_ids.state','=', 'precheckin'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
f"('checkin', '=', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('checkin_partner_ids.state','=', 'precheckin'),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"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" para completar los datos.",
|
||||
f"por registrar sus datos.Puedes abrir el asistente de checkin "
|
||||
f" para completar los datos.",
|
||||
"priority": 530,
|
||||
},
|
||||
{
|
||||
"code": "checkin_no_precheckin",
|
||||
"title": "Entrada Hoy",
|
||||
"domain": "[('state', '=', 'confirm'),"
|
||||
f"('checkin', '=', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
f"('checkin', '=', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('reservation_type', 'in', ['normal', 'staff'])"
|
||||
"]",
|
||||
"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.",
|
||||
"priority": 580,
|
||||
@@ -1221,24 +1225,24 @@ class PmsReservationService(Component):
|
||||
"code": "confirmed_without_payment_and_precheckin",
|
||||
"title": "Confirmadas a futuro sin pagar y sin precheckin realizado",
|
||||
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('folio_payment_state', 'in', ['not_paid', 'partial'])"
|
||||
"]",
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('folio_payment_state', 'in', ['not_paid', 'partial'])"
|
||||
"]",
|
||||
"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,
|
||||
},
|
||||
{
|
||||
"code": "confirmed_without_payment",
|
||||
"title": "Confirmadas a futuro sin pagar",
|
||||
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>', '{today}'),"
|
||||
"('pending_checkin_data', '=', 0),"
|
||||
"('folio_payment_state', 'in', ['not_paid', 'partial'])"
|
||||
"]",
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>', '{today}'),"
|
||||
"('pending_checkin_data', '=', 0),"
|
||||
"('folio_payment_state', 'in', ['not_paid', 'partial'])"
|
||||
"]",
|
||||
"text": "Esta reserva está pendiente de cobro, puedes enviarle sun recordatorio desde aquí",
|
||||
"priority": 630,
|
||||
},
|
||||
@@ -1246,11 +1250,11 @@ class PmsReservationService(Component):
|
||||
"code": "confirmed_without_precheckin",
|
||||
"title": "Confirmadas a futuro sin pagar",
|
||||
"domain": "[('state', 'in', ['draft', 'confirm', 'arrival_delayed']),"
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('folio_payment_state', 'in', ['paid', 'overpayment','nothing_to_pay'])"
|
||||
"]",
|
||||
"('reservation_type', 'in', ['normal', 'staff']),"
|
||||
f"('checkin', '>', '{today}'),"
|
||||
"('pending_checkin_data', '>', 0),"
|
||||
"('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í",
|
||||
"priority": 660,
|
||||
},
|
||||
@@ -1258,20 +1262,20 @@ class PmsReservationService(Component):
|
||||
"code": "cancelled",
|
||||
"title": "Cancelada con cargos y sin cobrar",
|
||||
"domain": "[('state', '=', 'cancel'),"
|
||||
"('cancelled_reason', 'in',['late','noshow']),"
|
||||
"('folio_payment_state', 'in', ['not_paid', 'partial']),"
|
||||
"]",
|
||||
"('cancelled_reason', 'in',['late','noshow']),"
|
||||
"('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)",
|
||||
"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,
|
||||
},
|
||||
{
|
||||
"code": "onboard_without_payment",
|
||||
"title": "Por cobrar dentro",
|
||||
"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í.",
|
||||
"priority": 800,
|
||||
},
|
||||
@@ -1279,37 +1283,37 @@ class PmsReservationService(Component):
|
||||
"code": "done_without_payment",
|
||||
"title": "Por cobrar pasadas",
|
||||
"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}€."
|
||||
" Cuando gestiones el cobro puedes registrarlo desde aquí.",
|
||||
" Cuando gestiones el cobro puedes registrarlo desde aquí.",
|
||||
"priority": 900,
|
||||
},
|
||||
{
|
||||
"code":"checkout",
|
||||
"code": "checkout",
|
||||
"title": "Checkout",
|
||||
"domain": "[('state', 'in', ['onboard', 'departure_delayed']),"
|
||||
f"('checkout', '=', '{today}'),"
|
||||
"]",
|
||||
f"('checkout', '=', '{today}'),"
|
||||
"]",
|
||||
"text": "Reserva lista para el checkout, marca la salida directamente desde aquí.",
|
||||
"priority": 1000,
|
||||
},
|
||||
]
|
||||
# We order the states by priority and return the first state whose domain meets the reservation;
|
||||
# if the state also has the key 'filtered,' it must also meet that filter.
|
||||
# We order the states by priority and return the first
|
||||
# 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"]
|
||||
for state in sorted_wizard_states:
|
||||
domain = expression.AND([
|
||||
[("id", "=", reservation_id)],
|
||||
safe_eval(state["domain"])
|
||||
])
|
||||
domain = expression.AND(
|
||||
[[("id", "=", reservation_id)], safe_eval(state["domain"])]
|
||||
)
|
||||
if self.env["pms.reservation"].search_count(domain):
|
||||
if (
|
||||
state.get("filtered")
|
||||
and not self.env["pms.reservation"].browse(reservation_id).filtered(safe_eval(state["filtered"]))
|
||||
):
|
||||
if state.get("filtered") and not self.env["pms.reservation"].browse(
|
||||
reservation_id
|
||||
).filtered(safe_eval(state["filtered"])):
|
||||
continue
|
||||
|
||||
return PmsWizardStateInfo(
|
||||
@@ -1319,8 +1323,7 @@ class PmsReservationService(Component):
|
||||
)
|
||||
|
||||
return PmsWizardStateInfo(
|
||||
code='',
|
||||
title='',
|
||||
text='',
|
||||
)
|
||||
|
||||
code="",
|
||||
title="",
|
||||
text="",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user