mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
845 lines
34 KiB
Python
845 lines
34 KiB
Python
import base64
|
|
import calendar
|
|
import datetime
|
|
import math
|
|
import xml.etree.cElementTree as ET
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
# TODO: Review code (code iso ?)
|
|
CODE_SPAIN = "ES"
|
|
|
|
|
|
class WizardIne(models.TransientModel):
|
|
_name = "pms.ine.wizard"
|
|
_description = "Wizard to generate statistical info."
|
|
|
|
pms_property_id = fields.Many2one(
|
|
string="Property",
|
|
comodel_name="pms.property",
|
|
default=lambda self: self.env["pms.property"].browse(
|
|
self.env.user.get_active_property_ids()[0]
|
|
),
|
|
check_pms_properties=True,
|
|
required=True,
|
|
)
|
|
|
|
txt_filename = fields.Text()
|
|
txt_binary = fields.Binary(string="File Download")
|
|
txt_message = fields.Char(string="File Preview")
|
|
|
|
start_date = fields.Date(
|
|
string="From",
|
|
required=True,
|
|
)
|
|
end_date = fields.Date(
|
|
string="To",
|
|
required=True,
|
|
)
|
|
|
|
adr = fields.Float(string="Range ADR")
|
|
revpar = fields.Float(string="Range RevPAR")
|
|
|
|
@api.model
|
|
def ine_rooms(self, start_date, end_date, pms_property_id):
|
|
"""
|
|
Returns a dictionary:
|
|
{
|
|
date_1: {
|
|
'double_rooms_single_use': number,
|
|
'double_rooms_double_use': number,
|
|
'other_rooms': number,
|
|
'extra_beds': number
|
|
},
|
|
# ... more dates
|
|
}
|
|
"""
|
|
# result object
|
|
rooms = dict()
|
|
|
|
# iterate days between start_date and end_date
|
|
for p_date in [
|
|
start_date + datetime.timedelta(days=x)
|
|
for x in range(0, (end_date - start_date).days + 1)
|
|
]:
|
|
|
|
# rooms with capacity 2 but only 1 adult using them
|
|
double_rooms_single_use = (
|
|
self.env["pms.reservation.line"]
|
|
.search(
|
|
[
|
|
("pms_property_id", "=", pms_property_id.id),
|
|
("occupies_availability", "=", True),
|
|
("reservation_id.reservation_type", "=", "normal"),
|
|
("room_id.in_ine", "=", True),
|
|
("date", "=", p_date),
|
|
("room_id.capacity", "=", 2),
|
|
(
|
|
"reservation_id.state",
|
|
"in",
|
|
["confirmed", "onboard", "done"],
|
|
),
|
|
]
|
|
)
|
|
.filtered(
|
|
lambda l: len(
|
|
l.reservation_id.checkin_partner_ids.filtered(
|
|
lambda c: c.state
|
|
not in ["dummy", "draft", "cancel", "precheckin"]
|
|
)
|
|
)
|
|
== 1
|
|
)
|
|
.mapped("room_id")
|
|
)
|
|
|
|
# rooms with capacity 2 with 2 adult using them
|
|
double_rooms_double_use = (
|
|
self.env["pms.reservation.line"]
|
|
.search(
|
|
[
|
|
("pms_property_id", "=", pms_property_id.id),
|
|
("occupies_availability", "=", True),
|
|
("reservation_id.reservation_type", "=", "normal"),
|
|
("room_id.in_ine", "=", True),
|
|
("date", "=", p_date),
|
|
("room_id.capacity", "=", 2),
|
|
(
|
|
"reservation_id.state",
|
|
"in",
|
|
["confirmed", "onboard", "done"],
|
|
),
|
|
]
|
|
)
|
|
.filtered(
|
|
lambda l: len(
|
|
l.reservation_id.checkin_partner_ids.filtered(
|
|
lambda c: c.state
|
|
not in ["dummy", "draft", "cancel", "precheckin"]
|
|
)
|
|
)
|
|
== 2
|
|
)
|
|
.mapped("room_id")
|
|
)
|
|
|
|
# service lines with extra beds
|
|
extra_bed_service_lines = self.env["pms.service.line"].search(
|
|
[
|
|
("pms_property_id", "=", pms_property_id.id),
|
|
("product_id.is_extra_bed", "=", True),
|
|
("reservation_id.reservation_type", "=", "normal"),
|
|
("reservation_id.state", "in", ["confirmed", "onboard", "done"]),
|
|
("date", "=", p_date),
|
|
]
|
|
)
|
|
|
|
extra_beds = 0
|
|
|
|
# get num. extra beds
|
|
for ebsl in extra_bed_service_lines:
|
|
reservation_lines = ebsl.reservation_id.reservation_line_ids.filtered(
|
|
lambda x: x.date == ebsl.date
|
|
and x.room_id.in_ine
|
|
and x.occupies_availability
|
|
)
|
|
if reservation_lines:
|
|
extra_beds += (
|
|
ebsl.day_qty
|
|
- reservation_lines.reservation_id.children_occupying
|
|
)
|
|
# children occuppying do not have checkin partner data
|
|
|
|
# search all rooms
|
|
all_rooms = (
|
|
self.env["pms.reservation.line"]
|
|
.search(
|
|
[
|
|
("date", "=", p_date),
|
|
("occupies_availability", "=", True),
|
|
("reservation_id.reservation_type", "=", "normal"),
|
|
("room_id.in_ine", "=", True),
|
|
("pms_property_id", "=", pms_property_id.id),
|
|
(
|
|
"reservation_id.state",
|
|
"in",
|
|
["confirmed", "onboard", "done"],
|
|
),
|
|
]
|
|
)
|
|
.filtered(
|
|
lambda l: len(
|
|
l.reservation_id.checkin_partner_ids.filtered(
|
|
lambda c: c.state
|
|
not in ["dummy", "draft", "cancel", "precheckin"]
|
|
)
|
|
)
|
|
> 0
|
|
)
|
|
.mapped("room_id")
|
|
)
|
|
|
|
# other rooms = all rooms - double rooms
|
|
other_rooms = (
|
|
all_rooms - double_rooms_double_use
|
|
) - double_rooms_single_use
|
|
|
|
# no room movements -> no dict entrys
|
|
if not (
|
|
extra_beds == 0
|
|
and len(other_rooms) == 0
|
|
and len(double_rooms_double_use) == 0
|
|
and len(double_rooms_single_use) == 0
|
|
):
|
|
# create result dict for each date
|
|
rooms[p_date] = dict()
|
|
rooms[p_date]["double_rooms_single_use"] = len(double_rooms_single_use)
|
|
rooms[p_date]["double_rooms_double_use"] = len(double_rooms_double_use)
|
|
rooms[p_date]["other_rooms"] = len(other_rooms)
|
|
rooms[p_date]["extra_beds"] = extra_beds
|
|
return rooms
|
|
|
|
@api.model
|
|
def ine_countries(self, start_date, end_date, pms_property_id):
|
|
"""
|
|
Returns a dictionary:
|
|
{
|
|
CODE_SPAIN: {
|
|
state.code_ine: {
|
|
date: {
|
|
'arrivals': number,
|
|
'departures': number,
|
|
'pernoctations': number,
|
|
},
|
|
# ... more dates
|
|
},
|
|
# ... more ine codes from spain
|
|
},
|
|
# ... more countries (except Spain)
|
|
country.code_alpha3: {
|
|
date: {
|
|
'arrivals': num. of arrivals
|
|
'departures': num. of departures
|
|
'pernoctations': num. of pernoctations
|
|
},
|
|
# ... more dates
|
|
},
|
|
# ... more countries (except Spain)
|
|
}
|
|
"""
|
|
|
|
def ine_add_arrivals_departures_pernoctations(
|
|
date, type_of_entry, read_group_result
|
|
):
|
|
"""
|
|
date = date to add the entry to dic
|
|
type_of_entry = 'arrivals' | 'departures' | 'pernoctations'
|
|
read_group_result = result of read_group by type_of_entry
|
|
"""
|
|
|
|
for entry in read_group_result:
|
|
if not entry["residence_country_id"]:
|
|
guests_with_no_residence_country = self.env[
|
|
"pms.checkin.partner"
|
|
].search(entry["__domain"])
|
|
guests_with_no_residence_country = (
|
|
str(guests_with_no_residence_country.mapped("name"))
|
|
.replace("[", "")
|
|
.replace("]", "")
|
|
)
|
|
raise ValidationError(
|
|
_(
|
|
"The following guests have no residence country set :%s.",
|
|
guests_with_no_residence_country,
|
|
)
|
|
)
|
|
# get residence_country_id from group set read_group results
|
|
residence_country_id_code = (
|
|
self.env["res.country"]
|
|
.search([("id", "=", entry["residence_country_id"][0])])
|
|
.code
|
|
)
|
|
# all countries except Spain
|
|
if residence_country_id_code != CODE_SPAIN:
|
|
|
|
# get count of each result
|
|
num = entry["__count"]
|
|
|
|
# update/create dicts for countries & dates and set num. arrivals
|
|
if not countries.get(residence_country_id_code):
|
|
countries[residence_country_id_code] = dict()
|
|
if not countries[residence_country_id_code].get(date):
|
|
countries[residence_country_id_code][date] = dict()
|
|
countries[residence_country_id_code][date][type_of_entry] = num
|
|
else:
|
|
# arrivals grouped by state_id (Spain "provincias")
|
|
read_by_arrivals_spain = self.env["pms.checkin.partner"].read_group(
|
|
entry["__domain"],
|
|
["residence_state_id"],
|
|
["residence_state_id"],
|
|
lazy=False,
|
|
)
|
|
# iterate read_group results from Spain
|
|
for entry_from_spain in read_by_arrivals_spain:
|
|
if not entry_from_spain["residence_state_id"]:
|
|
spanish_guests_with_no_state = self.env[
|
|
"pms.checkin.partner"
|
|
].search(entry_from_spain["__domain"])
|
|
spanish_guests_with_no_state = (
|
|
str(spanish_guests_with_no_state.mapped("name"))
|
|
.replace("[", "")
|
|
.replace("]", "")
|
|
)
|
|
raise ValidationError(
|
|
_(
|
|
"The following spanish guests have no state set :%s.",
|
|
spanish_guests_with_no_state,
|
|
)
|
|
)
|
|
residence_state_id = self.env["res.country.state"].browse(
|
|
entry_from_spain["residence_state_id"][0]
|
|
) # .ine_code
|
|
ine_code = residence_state_id.ine_code
|
|
|
|
if not ine_code:
|
|
raise ValidationError(
|
|
_(
|
|
"%s does not have the INE Code configured"
|
|
% residence_state_id.name
|
|
)
|
|
)
|
|
# get count of each result
|
|
num_spain = entry_from_spain["__count"]
|
|
|
|
# update/create dicts for states & dates and set num. arrivals
|
|
if not countries.get(CODE_SPAIN):
|
|
countries[CODE_SPAIN] = dict()
|
|
|
|
if not countries[CODE_SPAIN].get(ine_code):
|
|
countries[CODE_SPAIN][ine_code] = dict()
|
|
|
|
if not countries[CODE_SPAIN][ine_code].get(date):
|
|
countries[CODE_SPAIN][ine_code][date] = dict()
|
|
countries[CODE_SPAIN][ine_code][date][type_of_entry] = num_spain
|
|
|
|
# result object
|
|
countries = dict()
|
|
|
|
# iterate days between start_date and end_date
|
|
for p_date in [
|
|
start_date + datetime.timedelta(days=x)
|
|
for x in range(0, (end_date - start_date).days + 1)
|
|
]:
|
|
# search for checkin partners
|
|
hosts = self.env["pms.checkin.partner"].search(
|
|
[
|
|
("reservation_id.pms_property_id", "=", pms_property_id),
|
|
("reservation_id.checkin", "<=", p_date),
|
|
("reservation_id.checkout", ">=", p_date),
|
|
("reservation_id.reservation_type", "=", "normal"),
|
|
("state", "not in", ["dummy", "draft", "cancel", "precheckin"]),
|
|
]
|
|
)
|
|
hosts = hosts.filtered(
|
|
lambda x: all(
|
|
x.reservation_id.reservation_line_ids.mapped("room_id.in_ine")
|
|
)
|
|
)
|
|
|
|
# arrivals
|
|
arrivals = hosts.filtered(lambda x: x.reservation_id.checkin == p_date)
|
|
|
|
# arrivals grouped by residence_country_id
|
|
read_by_arrivals = self.env["pms.checkin.partner"].read_group(
|
|
[("id", "in", arrivals.ids)],
|
|
["residence_country_id"],
|
|
["residence_country_id"],
|
|
orderby="residence_country_id",
|
|
lazy=False,
|
|
)
|
|
|
|
# departures
|
|
departures = hosts.filtered(lambda x: x.reservation_id.checkout == p_date)
|
|
|
|
# departures grouped by residence_country_id
|
|
read_by_departures = self.env["pms.checkin.partner"].read_group(
|
|
[("id", "in", departures.ids)],
|
|
["residence_country_id"],
|
|
["residence_country_id"],
|
|
orderby="residence_country_id",
|
|
lazy=False,
|
|
)
|
|
|
|
# pernoctations
|
|
pernoctations = hosts - departures
|
|
|
|
# pernoctations grouped by residence_country_id
|
|
read_by_pernoctations = self.env["pms.checkin.partner"].read_group(
|
|
[("id", "in", pernoctations.ids)],
|
|
["residence_country_id"],
|
|
["residence_country_id"],
|
|
orderby="residence_country_id",
|
|
lazy=False,
|
|
)
|
|
ine_add_arrivals_departures_pernoctations(
|
|
p_date, "arrivals", read_by_arrivals
|
|
)
|
|
ine_add_arrivals_departures_pernoctations(
|
|
p_date, "departures", read_by_departures
|
|
)
|
|
ine_add_arrivals_departures_pernoctations(
|
|
p_date, "pernoctations", read_by_pernoctations
|
|
)
|
|
|
|
return countries
|
|
|
|
def ine_calculate_adr(self, start_date, end_date, domain=False):
|
|
"""
|
|
Calculate date range ADR for a property only in INE rooms
|
|
:param start_date: start date
|
|
:param pms_property_id: pms property id
|
|
:param domain: domain to filter reservations (channel, agencies, etc...)
|
|
"""
|
|
self.ensure_one()
|
|
domain = [] if not domain else domain
|
|
domain.append(("room_id.in_ine", "=", True))
|
|
adr = self.pms_property_id._get_adr(start_date, end_date, domain)
|
|
self.adr = adr
|
|
return adr
|
|
|
|
def ine_calculate_revpar(self, start_date, end_date, domain=False):
|
|
"""
|
|
Calculate date range revpar for a property only in INE rooms
|
|
:param start_date: start date
|
|
:param pms_property_id: pms property id
|
|
:param domain: domain to filter reservations (channel, agencies, etc...)
|
|
"""
|
|
self.ensure_one()
|
|
domain = [] if not domain else domain
|
|
domain.append(("room_id.in_ine", "=", True))
|
|
revpar = self.pms_property_id._get_revpar(start_date, end_date, domain)
|
|
self.revpar = revpar
|
|
return revpar
|
|
|
|
def ine_calculate_occupancy(self, start_date, end_date, domain=False):
|
|
"""
|
|
Calculate date range occupancy for a property only in INE rooms
|
|
:param start_date: start date
|
|
:param pms_property_id: pms property id
|
|
:param domain: domain to filter reservations (channel, agencies, etc...)
|
|
"""
|
|
self.ensure_one()
|
|
domain = [] if not domain else domain
|
|
total_domain = [
|
|
("room_id.in_ine", "=", True),
|
|
("date", ">=", start_date),
|
|
("date", "<=", end_date),
|
|
]
|
|
total_reservations = self.env["pms.reservation.line"].search(total_domain)
|
|
domain.extend(total_domain)
|
|
filter_reservations = self.env["pms.reservation.line"].search(domain)
|
|
if len(filter_reservations) > 0:
|
|
filter_percent = len(filter_reservations) * 100 / len(total_reservations)
|
|
# round to 2 decimals, but if the result is > 0 and < 0.01, return 0.01
|
|
filter_percent = (
|
|
math.ceil(filter_percent * 100) / 100
|
|
if filter_percent < 0.01 and filter_percent > 0
|
|
else round(filter_percent, 2)
|
|
)
|
|
else:
|
|
filter_percent = 0
|
|
return filter_percent
|
|
|
|
@api.model
|
|
def ine_get_nif_cif(self, cif_nif):
|
|
country_codes = self.env["res.country"].search([]).mapped("code")
|
|
if cif_nif[:2] in country_codes:
|
|
return cif_nif[2:].strip()
|
|
return cif_nif.strip()
|
|
|
|
@api.model
|
|
def check_ine_mandatory_fields(self, pms_property_id):
|
|
if not pms_property_id.name:
|
|
raise ValidationError(_("The property name is not established."))
|
|
|
|
if not pms_property_id.company_id.vat:
|
|
raise ValidationError(_("The company VAT is not established."))
|
|
|
|
if not pms_property_id.company_id.name:
|
|
raise ValidationError(_("The company name is not established."))
|
|
|
|
if not pms_property_id.name:
|
|
raise ValidationError(_("The property name is not established."))
|
|
|
|
if not pms_property_id.ine_tourism_number:
|
|
raise ValidationError(_("The property tourism number is not established."))
|
|
|
|
if not pms_property_id.ine_tourism_number:
|
|
raise ValidationError(_("The property tourism number is not established."))
|
|
|
|
if not pms_property_id.street:
|
|
raise ValidationError(_("The property street is not established."))
|
|
|
|
if not pms_property_id.zip:
|
|
raise ValidationError(_("The property zip is not established."))
|
|
|
|
if not pms_property_id.city:
|
|
raise ValidationError(_("The property city is not established."))
|
|
|
|
if not pms_property_id.partner_id.state_id:
|
|
raise ValidationError(_("The property state is not established."))
|
|
|
|
if not pms_property_id.phone:
|
|
raise ValidationError(_("The property phone is not established."))
|
|
|
|
if not pms_property_id.ine_category_id:
|
|
raise ValidationError(_("The property category is not established."))
|
|
|
|
def ine_generate_xml(self):
|
|
|
|
self.check_ine_mandatory_fields(self.pms_property_id)
|
|
|
|
number_of_rooms = sum(
|
|
self.env["pms.room"]
|
|
.search(
|
|
[
|
|
("in_ine", "=", True),
|
|
("pms_property_id", "=", self.pms_property_id.id),
|
|
]
|
|
)
|
|
.mapped("capacity")
|
|
)
|
|
|
|
if number_of_rooms > self.pms_property_id.ine_seats:
|
|
raise ValidationError(
|
|
_(
|
|
"The number of seats, excluding extra beds (%s)"
|
|
% str(number_of_rooms)
|
|
+ " exceeds the number of seats established in the property (%s)"
|
|
% str(self.pms_property_id.ine_seats)
|
|
)
|
|
)
|
|
|
|
# INE XML
|
|
survey_tag = ET.Element("ENCUESTA")
|
|
|
|
# INE XML -> PROPERTY
|
|
header_tag = ET.SubElement(survey_tag, "CABECERA")
|
|
date = ET.SubElement(header_tag, "FECHA_REFERENCIA")
|
|
ET.SubElement(date, "MES").text = f"{self.start_date.month:02}"
|
|
ET.SubElement(date, "ANYO").text = str(self.start_date.year)
|
|
ET.SubElement(header_tag, "DIAS_ABIERTO_MES_REFERENCIA").text = str(
|
|
calendar.monthrange(self.start_date.year, self.start_date.month)[1]
|
|
)
|
|
ET.SubElement(
|
|
header_tag, "RAZON_SOCIAL"
|
|
).text = self.pms_property_id.company_id.name
|
|
ET.SubElement(
|
|
header_tag, "NOMBRE_ESTABLECIMIENTO"
|
|
).text = self.pms_property_id.name
|
|
|
|
ET.SubElement(header_tag, "CIF_NIF").text = self.ine_get_nif_cif(
|
|
self.pms_property_id.company_id.vat
|
|
)
|
|
ET.SubElement(
|
|
header_tag, "NUMERO_REGISTRO"
|
|
).text = self.pms_property_id.ine_tourism_number
|
|
ET.SubElement(header_tag, "DIRECCION").text = self.pms_property_id.street
|
|
ET.SubElement(header_tag, "CODIGO_POSTAL").text = self.pms_property_id.zip
|
|
ET.SubElement(header_tag, "LOCALIDAD").text = self.pms_property_id.city
|
|
ET.SubElement(header_tag, "MUNICIPIO").text = self.pms_property_id.city
|
|
ET.SubElement(
|
|
header_tag, "PROVINCIA"
|
|
).text = self.pms_property_id.partner_id.state_id.name
|
|
ET.SubElement(
|
|
header_tag, "TELEFONO_1"
|
|
).text = self.pms_property_id.phone.replace(" ", "")[0:12]
|
|
ET.SubElement(
|
|
header_tag, "TIPO"
|
|
).text = self.pms_property_id.ine_category_id.type
|
|
ET.SubElement(
|
|
header_tag, "CATEGORIA"
|
|
).text = self.pms_property_id.ine_category_id.category
|
|
ET.SubElement(header_tag, "HABITACIONES").text = str(
|
|
self.env["pms.room"].search_count(
|
|
[
|
|
("in_ine", "=", True),
|
|
("pms_property_id", "=", self.pms_property_id.id),
|
|
]
|
|
)
|
|
)
|
|
|
|
ET.SubElement(header_tag, "PLAZAS_DISPONIBLES_SIN_SUPLETORIAS").text = str(
|
|
self.pms_property_id.ine_seats
|
|
)
|
|
ET.SubElement(header_tag, "URL").text = self.pms_property_id.website
|
|
|
|
# INE XML -> GUESTS
|
|
accommodation_tag = ET.SubElement(survey_tag, "ALOJAMIENTO")
|
|
|
|
countries = self.ine_countries(
|
|
self.start_date, self.end_date, self.pms_property_id.id
|
|
)
|
|
for key_country, value_country in countries.items():
|
|
|
|
country = self.env["res.country"].search([("code", "=", key_country)])
|
|
|
|
if key_country != CODE_SPAIN:
|
|
residency_tag = ET.SubElement(accommodation_tag, "RESIDENCIA")
|
|
ET.SubElement(residency_tag, "ID_PAIS").text = country.code_alpha3
|
|
|
|
for key_date, value_dates in value_country.items():
|
|
movement = ET.SubElement(residency_tag, "MOVIMIENTO")
|
|
ET.SubElement(movement, "N_DIA").text = f"{key_date.day:02}"
|
|
num_arrivals = (
|
|
value_dates["arrivals"] if value_dates.get("arrivals") else 0
|
|
)
|
|
num_departures = (
|
|
value_dates["departures"]
|
|
if value_dates.get("departures")
|
|
else 0
|
|
)
|
|
num_pernoctations = (
|
|
value_dates["pernoctations"]
|
|
if value_dates.get("pernoctations")
|
|
else 0
|
|
)
|
|
|
|
ET.SubElement(movement, "ENTRADAS").text = str(num_arrivals)
|
|
ET.SubElement(movement, "SALIDAS").text = str(num_departures)
|
|
ET.SubElement(movement, "PERNOCTACIONES").text = str(
|
|
num_pernoctations
|
|
)
|
|
else:
|
|
for code_ine, value_state in value_country.items():
|
|
residency_tag = ET.SubElement(accommodation_tag, "RESIDENCIA")
|
|
ET.SubElement(residency_tag, "ID_PROVINCIA_ISLA").text = code_ine
|
|
for key_date, value_dates in value_state.items():
|
|
movement = ET.SubElement(residency_tag, "MOVIMIENTO")
|
|
ET.SubElement(movement, "N_DIA").text = f"{key_date.day:02}"
|
|
num_arrivals = (
|
|
value_dates["arrivals"]
|
|
if value_dates.get("arrivals")
|
|
else 0
|
|
)
|
|
num_departures = (
|
|
value_dates["departures"]
|
|
if value_dates.get("departures")
|
|
else 0
|
|
)
|
|
num_pernoctations = (
|
|
value_dates["pernoctations"]
|
|
if value_dates.get("pernoctations")
|
|
else 0
|
|
)
|
|
ET.SubElement(movement, "ENTRADAS").text = str(num_arrivals)
|
|
ET.SubElement(movement, "SALIDAS").text = str(num_departures)
|
|
ET.SubElement(movement, "PERNOCTACIONES").text = str(
|
|
num_pernoctations
|
|
)
|
|
|
|
rooms_tag = ET.SubElement(survey_tag, "HABITACIONES")
|
|
rooms = self.ine_rooms(self.start_date, self.end_date, self.pms_property_id)
|
|
# INE XML -> ROOMS
|
|
for key_date, value_rooms in rooms.items():
|
|
rooms_move = ET.SubElement(rooms_tag, "HABITACIONES_MOVIMIENTO")
|
|
ET.SubElement(rooms_move, "HABITACIONES_N_DIA").text = f"{key_date.day:02}"
|
|
ET.SubElement(rooms_move, "PLAZAS_SUPLETORIAS").text = str(
|
|
value_rooms["extra_beds"]
|
|
)
|
|
ET.SubElement(rooms_move, "HABITACIONES_DOBLES_USO_DOBLE").text = str(
|
|
value_rooms["double_rooms_double_use"]
|
|
)
|
|
ET.SubElement(rooms_move, "HABITACIONES_DOBLES_USO_INDIVIDUAL").text = str(
|
|
value_rooms["double_rooms_single_use"]
|
|
)
|
|
ET.SubElement(rooms_move, "HABITACIONES_OTRAS").text = str(
|
|
value_rooms["other_rooms"]
|
|
)
|
|
prices_tag = ET.SubElement(survey_tag, "PRECIOS")
|
|
|
|
ET.SubElement(prices_tag, "REVPAR_MENSUAL").text = str(
|
|
self.ine_calculate_revpar(
|
|
self.start_date,
|
|
self.end_date,
|
|
)
|
|
)
|
|
|
|
ET.SubElement(prices_tag, "ADR_MENSUAL").text = str(
|
|
self.ine_calculate_adr(
|
|
self.start_date,
|
|
self.end_date,
|
|
)
|
|
)
|
|
basic_domain = [
|
|
("pms_property_id", "=", self.pms_property_id.id),
|
|
("occupies_availability", "=", True),
|
|
("reservation_id.reservation_type", "=", "normal"),
|
|
]
|
|
total_groups_domains = {
|
|
"tour_operator_offline": basic_domain
|
|
+ [
|
|
("reservation_id.agency_id.sale_channel_id.name", "ilike", "Operator"),
|
|
("reservation_id.agency_id.sale_channel_id.is_on_line", "=", True),
|
|
],
|
|
"tour_operator_online": basic_domain
|
|
+ [
|
|
("reservation_id.agency_id.sale_channel_id.name", "ilike", "Operator"),
|
|
("reservation_id.agency_id.sale_channel_id.is_on_line", "=", False),
|
|
],
|
|
"companies": basic_domain
|
|
+ [
|
|
("reservation_id.partner_id", "!=", False),
|
|
("reservation_id.partner_id.is_company", "=", True),
|
|
("reservation_id.partner_id.is_agency", "=", False),
|
|
],
|
|
"agencies": basic_domain
|
|
+ [
|
|
("reservation_id.agency_id", "!=", False),
|
|
("reservation_id.agency_id.sale_channel_id.is_on_line", "=", False),
|
|
],
|
|
"otas": basic_domain
|
|
+ [
|
|
("reservation_id.agency_id", "!=", False),
|
|
("reservation_id.agency_id.sale_channel_id.is_on_line", "=", True),
|
|
],
|
|
"persons": basic_domain
|
|
+ [
|
|
"|",
|
|
("reservation_id.partner_id", "=", False),
|
|
("reservation_id.partner_id.is_company", "=", False),
|
|
],
|
|
"groups": basic_domain
|
|
+ [("reservation_id.folio_id.number_of_rooms", ">=", 4)],
|
|
"internet": basic_domain
|
|
+ [("reservation_id.sale_channel_origin_id.is_on_line", "=", True)],
|
|
"others": basic_domain
|
|
+ [
|
|
"|",
|
|
("reservation_id.sale_channel_origin_id.is_on_line", "!=", True),
|
|
("reservation_id.sale_channel_origin_id", "=", False),
|
|
],
|
|
}
|
|
percents = {}
|
|
adrs = {}
|
|
for group, domain in total_groups_domains.items():
|
|
percents[group] = self.ine_calculate_occupancy(
|
|
self.start_date,
|
|
self.end_date,
|
|
domain,
|
|
)
|
|
adrs[group] = self.ine_calculate_adr(
|
|
self.start_date,
|
|
self.end_date,
|
|
domain,
|
|
)
|
|
|
|
# In this point, the groups adrs and percents are well calculated.... but,
|
|
# our statist friends want the total of the percentage groupings to add up = 100%,
|
|
# without conceiving that the groupings overlap, so they cannot receive real data
|
|
# and force us to pervert the original data so that it fits in their grid notebook.
|
|
# The purpose of the following lines of code is only to show the inefficiency
|
|
# of the state statistics,
|
|
# so at least I will feel that the effort made some sense :)
|
|
|
|
total_percent = sum([val for val in percents.values()])
|
|
sum_percentages = 0
|
|
for group in total_groups_domains.keys():
|
|
percents[group] = round(percents[group] * 100 / (total_percent or 1), 2)
|
|
sum_percentages += percents[group]
|
|
|
|
if sum_percentages < 100:
|
|
for group in total_groups_domains.keys():
|
|
if percents[group] > 0:
|
|
percents[group] = round(
|
|
percents[group] + ((100 - sum_percentages) * 100) / 100, 2
|
|
)
|
|
break
|
|
elif sum_percentages > 100:
|
|
for group in total_groups_domains.keys():
|
|
if percents[group] > 0:
|
|
percents[group] = round(
|
|
percents[group] - ((100 - sum_percentages) * 100) / 100, 2
|
|
)
|
|
break
|
|
|
|
ET.SubElement(prices_tag, "ADR_TOUROPERADOR_TRADICIONAL").text = str(
|
|
adrs["tour_operator_offline"]
|
|
)
|
|
ET.SubElement(
|
|
prices_tag, "PCTN_HABITACIONES_OCUPADAS_TOUROPERADOR_TRADICIONAL"
|
|
).text = str(percents["tour_operator_offline"])
|
|
ET.SubElement(prices_tag, "ADR_TOUROPERADOR_ONLINE").text = str(
|
|
adrs["tour_operator_online"]
|
|
)
|
|
ET.SubElement(
|
|
prices_tag, "PCTN_HABITACIONES_OCUPADAS_TOUROPERADOR_ONLINE"
|
|
).text = str(percents["tour_operator_online"])
|
|
ET.SubElement(prices_tag, "ADR_EMPRESAS").text = str(adrs["companies"])
|
|
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_EMPRESAS").text = str(
|
|
percents["companies"]
|
|
)
|
|
ET.SubElement(prices_tag, "ADR_AGENCIA_DE_VIAJE_TRADICIONAL").text = str(
|
|
adrs["agencies"]
|
|
)
|
|
ET.SubElement(
|
|
prices_tag, "PCTN_HABITACIONES_OCUPADAS_AGENCIA_TRADICIONAL"
|
|
).text = str(percents["agencies"])
|
|
ET.SubElement(prices_tag, "ADR_AGENCIA_DE_VIAJE_ONLINE").text = str(
|
|
adrs["otas"]
|
|
)
|
|
ET.SubElement(
|
|
prices_tag, "PCTN_HABITACIONES_OCUPADAS_AGENCIA_ONLINE"
|
|
).text = str(percents["otas"])
|
|
ET.SubElement(prices_tag, "ADR_PARTICULARES").text = str(adrs["persons"])
|
|
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_PARTICULARES").text = str(
|
|
percents["persons"]
|
|
)
|
|
ET.SubElement(prices_tag, "ADR_GRUPOS").text = str(adrs["groups"])
|
|
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_GRUPOS").text = str(
|
|
percents["groups"]
|
|
)
|
|
ET.SubElement(prices_tag, "ADR_INTERNET").text = str(adrs["internet"])
|
|
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_INTERNET").text = str(
|
|
percents["internet"]
|
|
)
|
|
ET.SubElement(prices_tag, "ADR_OTROS").text = str(adrs["others"])
|
|
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_OTROS").text = str(
|
|
percents["others"]
|
|
)
|
|
|
|
staff_tag = ET.SubElement(survey_tag, "PERSONAL_OCUPADO")
|
|
ET.SubElement(staff_tag, "PERSONAL_NO_REMUNERADO").text = str(
|
|
self.pms_property_id.ine_unpaid_staff
|
|
)
|
|
ET.SubElement(staff_tag, "PERSONAL_REMUNERADO_FIJO").text = str(
|
|
self.pms_property_id.ine_permanent_staff
|
|
)
|
|
ET.SubElement(staff_tag, "PERSONAL_REMUNERADO_EVENTUAL").text = str(
|
|
self.pms_property_id.ine_eventual_staff
|
|
)
|
|
|
|
xmlstr = '<?xml version="1.0" encoding="ISO-8859-1"?>'
|
|
xmlstr += ET.tostring(survey_tag).decode("utf-8")
|
|
|
|
self.txt_binary = base64.b64encode(str.encode(xmlstr))
|
|
self.txt_filename = (
|
|
"INE_"
|
|
+ str(self.start_date.month)
|
|
+ "_"
|
|
+ str(self.start_date.year)
|
|
+ ".xml"
|
|
)
|
|
|
|
return {
|
|
"context": self.env.context,
|
|
"view_type": "form",
|
|
"view_mode": "form",
|
|
"res_model": "pms.ine.wizard",
|
|
"res_id": self.id,
|
|
"view_id": False,
|
|
"type": "ir.actions.act_window",
|
|
"target": "new",
|
|
}
|