mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
@@ -1,3 +1,4 @@
|
||||
partner-contact
|
||||
reporting-engine
|
||||
queue
|
||||
community-data-files
|
||||
|
||||
@@ -342,7 +342,6 @@ class PmsReservation(models.Model):
|
||||
reservation_type = fields.Selection(
|
||||
string="Reservation Type",
|
||||
help="Type of reservations. It can be 'normal', 'staff' or 'out of service",
|
||||
default=lambda *a: "normal",
|
||||
related="folio_id.reservation_type",
|
||||
store=True,
|
||||
readonly=False,
|
||||
@@ -605,6 +604,29 @@ class PmsReservation(models.Model):
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
check_adults = fields.Boolean(
|
||||
help="Internal field to force room capacity validations",
|
||||
compute="_compute_check_adults",
|
||||
readonly=False,
|
||||
store=True,
|
||||
)
|
||||
|
||||
def _compute_date_order(self):
|
||||
for record in self:
|
||||
record.date_order = datetime.datetime.today()
|
||||
|
||||
@api.depends(
|
||||
"service_ids",
|
||||
"service_ids.service_line_ids",
|
||||
"service_ids.service_line_ids.product_id",
|
||||
"service_ids.service_line_ids.day_qty",
|
||||
"reservation_line_ids",
|
||||
"reservation_line_ids.room_id",
|
||||
)
|
||||
def _compute_check_adults(self):
|
||||
for record in self:
|
||||
record.check_adults = True
|
||||
|
||||
@api.depends(
|
||||
"checkin",
|
||||
"checkout",
|
||||
@@ -1423,23 +1445,6 @@ class PmsReservation(models.Model):
|
||||
# _("The room already is completed (%s)", record.name)
|
||||
# )
|
||||
|
||||
@api.constrains("adults")
|
||||
def _check_adults(self):
|
||||
for record in self:
|
||||
extra_bed = record.service_ids.filtered(
|
||||
lambda r: r.product_id.is_extra_bed is True
|
||||
)
|
||||
for room in record.reservation_line_ids.room_id:
|
||||
if record.adults + record.children_occupying > room.get_capacity(
|
||||
sum(extra_bed.mapped("product_qty"))
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Persons can't be higher than room capacity (%s)",
|
||||
record.name,
|
||||
)
|
||||
)
|
||||
|
||||
@api.constrains("state")
|
||||
def _check_onboard_reservation(self):
|
||||
for record in self:
|
||||
@@ -1484,6 +1489,13 @@ class PmsReservation(models.Model):
|
||||
if record.agency_id and not record.agency_id.is_agency:
|
||||
raise ValidationError(_("booking agency with wrong configuration: "))
|
||||
|
||||
@api.constrains("check_adults")
|
||||
def _check_capacity(self):
|
||||
for record in self:
|
||||
self.env["pms.room"]._check_adults(
|
||||
record, record.service_ids.service_line_ids
|
||||
)
|
||||
|
||||
# Action methods
|
||||
def open_partner(self):
|
||||
""" Utility method used to add an "View Customer" button in reservation views """
|
||||
@@ -1623,6 +1635,12 @@ class PmsReservation(models.Model):
|
||||
)
|
||||
pms_property = self.env["pms.property"].browse(pms_property_id)
|
||||
vals["name"] = pms_property.reservation_sequence_id._next_do()
|
||||
|
||||
if not vals.get("reservation_type"):
|
||||
vals["reservation_type"] = (
|
||||
folio.reservation_type if folio.reservation_type else "normal"
|
||||
)
|
||||
|
||||
record = super(PmsReservation, self).create(vals)
|
||||
if record.preconfirm:
|
||||
record.confirm()
|
||||
|
||||
@@ -470,17 +470,3 @@ class PmsReservationLine(models.Model):
|
||||
)
|
||||
if duplicated:
|
||||
raise ValidationError(_("Duplicated reservation line date"))
|
||||
|
||||
@api.constrains("room_id")
|
||||
def _check_adults(self):
|
||||
for record in self.filtered("room_id"):
|
||||
extra_bed = record.reservation_id.service_ids.filtered(
|
||||
lambda r: r.product_id.is_extra_bed is True
|
||||
)
|
||||
if (
|
||||
record.reservation_id.adults + record.reservation_id.children_occupying
|
||||
> record.room_id.get_capacity(len(extra_bed))
|
||||
):
|
||||
raise ValidationError(_("Persons can't be higher than room capacity"))
|
||||
# if record.reservation_id.adults == 0:
|
||||
# raise ValidationError(_("Reservation has no adults"))
|
||||
|
||||
@@ -118,6 +118,26 @@ class PmsRoom(models.Model):
|
||||
)
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _check_adults(self, reservation, service_line_ids=False):
|
||||
for line in reservation.reservation_line_ids:
|
||||
num_extra_beds = 0
|
||||
if service_line_ids:
|
||||
extra_beds = service_line_ids.filtered(
|
||||
lambda x: x.date == line.date and x.product_id.is_extra_bed is True
|
||||
)
|
||||
num_extra_beds = sum(extra_beds.mapped("day_qty")) if extra_beds else 0
|
||||
if line.room_id:
|
||||
if (
|
||||
reservation.adults + reservation.children_occupying
|
||||
) > line.room_id.get_capacity(num_extra_beds):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Persons can't be higher than room capacity (%s)",
|
||||
reservation.name,
|
||||
)
|
||||
)
|
||||
|
||||
# Business methods
|
||||
|
||||
def get_capacity(self, extra_bed=0):
|
||||
|
||||
@@ -340,23 +340,24 @@ class PmsService(models.Model):
|
||||
move_day = 0
|
||||
if consumed_on == "after":
|
||||
move_day = 1
|
||||
service.service_line_ids -= (
|
||||
service.service_line_ids.filtered_domain(
|
||||
[
|
||||
"|",
|
||||
(
|
||||
"date",
|
||||
"<",
|
||||
reservation.checkin + timedelta(move_day),
|
||||
),
|
||||
(
|
||||
"date",
|
||||
">=",
|
||||
reservation.checkout + timedelta(move_day),
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
for del_service_id in service.service_line_ids.filtered_domain(
|
||||
[
|
||||
"|",
|
||||
(
|
||||
"date",
|
||||
"<",
|
||||
reservation.checkin + timedelta(move_day),
|
||||
),
|
||||
(
|
||||
"date",
|
||||
">=",
|
||||
reservation.checkout + timedelta(move_day),
|
||||
),
|
||||
]
|
||||
).ids:
|
||||
lines.append((2, del_service_id))
|
||||
# TODO: check intermediate states in check_adults restriction
|
||||
# when lines are removed
|
||||
service.service_line_ids = lines
|
||||
else:
|
||||
if not service.service_line_ids:
|
||||
@@ -535,10 +536,11 @@ class PmsService(models.Model):
|
||||
# Businness Methods
|
||||
def _service_day_qty(self):
|
||||
self.ensure_one()
|
||||
qty = self.product_qty if len(self.service_line_ids) == 1 else 0
|
||||
qty = self.product_qty if len(self.service_line_ids) == 1 else 1
|
||||
if not self.reservation_id:
|
||||
return qty
|
||||
# TODO: Pass per_person to service line from product default_per_person
|
||||
# When the user modifies the quantity avoid overwriting
|
||||
if self.product_id.per_person:
|
||||
qty = self.reservation_id.adults
|
||||
return qty
|
||||
|
||||
@@ -36,6 +36,7 @@ class TestPmsReservations(TestPms):
|
||||
"name": "Double 101",
|
||||
"room_type_id": self.room_type_double.id,
|
||||
"capacity": 2,
|
||||
"extra_beds_allowed": 1,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -626,19 +627,20 @@ class TestPmsReservations(TestPms):
|
||||
# ACT & ASSERT
|
||||
with self.assertRaises(
|
||||
ValidationError,
|
||||
msg="The number of people is lower than the capacity of the room",
|
||||
msg="The number of people is greater than the capacity of the room",
|
||||
):
|
||||
self.env["pms.reservation"].create(
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"adults": 2,
|
||||
"children_occupying": 1,
|
||||
"checkin": datetime.datetime.now(),
|
||||
"checkout": datetime.datetime.now() + datetime.timedelta(days=1),
|
||||
"room_type_id": self.room_type_double.id,
|
||||
"preferred_room_id": self.room1.id,
|
||||
"partner_id": self.partner1.id,
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
}
|
||||
)
|
||||
reservation.flush()
|
||||
|
||||
def test_to_assign_priority_reservation(self):
|
||||
"""
|
||||
@@ -1930,16 +1932,17 @@ class TestPmsReservations(TestPms):
|
||||
}
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
self.env["pms.reservation"].create(
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"checkin": fields.date.today(),
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"partner_id": self.host1.id,
|
||||
"room_type_id": self.room_type_double.id,
|
||||
"preferred_room_id": self.room1.id,
|
||||
"adults": 4,
|
||||
}
|
||||
)
|
||||
reservation.flush()
|
||||
|
||||
def test_check_format_arrival_hour(self):
|
||||
"""
|
||||
@@ -2616,7 +2619,7 @@ class TestPmsReservations(TestPms):
|
||||
{
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=-3),
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
||||
"room_type_id": self.room_type_double.id,
|
||||
"preferred_room_id": self.room1.id,
|
||||
"partner_id": self.partner1.id,
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"pricelist_id": self.pricelist1.id,
|
||||
|
||||
@@ -14,7 +14,46 @@
|
||||
<notebook>
|
||||
<page string="General Information" name="property_general">
|
||||
<group>
|
||||
|
||||
<div class="o_address_format">
|
||||
<field
|
||||
name="street"
|
||||
placeholder="Street..."
|
||||
class="o_address_street"
|
||||
/>
|
||||
<field
|
||||
name="street2"
|
||||
placeholder="Street 2..."
|
||||
class="o_address_street"
|
||||
/>
|
||||
<field
|
||||
name="city"
|
||||
placeholder="City"
|
||||
class="o_address_city"
|
||||
/>
|
||||
<field
|
||||
name="state_id"
|
||||
class="o_address_state"
|
||||
placeholder="State"
|
||||
/>
|
||||
<field
|
||||
name="zip"
|
||||
placeholder="ZIP"
|
||||
class="o_address_zip"
|
||||
/>
|
||||
<field
|
||||
name="country_id"
|
||||
placeholder="Country"
|
||||
class="o_address_country"
|
||||
/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<label for="phone" />
|
||||
<field name="phone" />
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Settings" name="property_settings">
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<field name="tax_ids" invisible="1" />
|
||||
<field name="checkin_partner_count" invisible="1" />
|
||||
<field name="to_assign" invisible="1" />
|
||||
<field name="check_adults" invisible="1" />
|
||||
<button
|
||||
name="confirm"
|
||||
string="Confirm"
|
||||
|
||||
@@ -16,22 +16,29 @@
|
||||
"partner_contact_gender",
|
||||
"partner_contact_birthdate",
|
||||
"partner_contact_nationality",
|
||||
"base_iso3166",
|
||||
"queue_job",
|
||||
],
|
||||
"external_dependencies": {
|
||||
"python": [
|
||||
"bs4",
|
||||
"pycountry",
|
||||
],
|
||||
},
|
||||
"data": [
|
||||
"data/res.country.state.csv",
|
||||
"data/pms.ine.tourism.type.category.csv",
|
||||
"data/cron_jobs.xml",
|
||||
"data/pms_data.xml",
|
||||
"data/queue_data.xml",
|
||||
"data/queue_job_function_data.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"views/pms_property_views.xml",
|
||||
"views/pms_room_views.xml",
|
||||
"views/pms_log_institution_traveller_report_views.xml",
|
||||
"views/pms_ine_tourism_type_category.xml",
|
||||
"wizards/traveller_report.xml",
|
||||
"wizards/wizard_ine.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
|
||||
53
pms_l10n_es/data/pms.ine.tourism.type.category.csv
Normal file
53
pms_l10n_es/data/pms.ine.tourism.type.category.csv
Normal file
@@ -0,0 +1,53 @@
|
||||
"type","category"
|
||||
Hoteles,H1
|
||||
Hoteles,H2
|
||||
Hoteles,H3
|
||||
Hoteles,H4
|
||||
Hoteles,H5
|
||||
Hoteles-apartamentos,AP
|
||||
Hoteles-apartamentos,HA
|
||||
Hoteles-apartamentos,HA1
|
||||
Hoteles-apartamentos,HA2
|
||||
Hoteles-apartamentos,HA3
|
||||
Hoteles-apartamentos,HA4
|
||||
Hoteles-apartamentos,HA5
|
||||
Hoteles-residencias,HR
|
||||
Hoteles-residencias,HR1
|
||||
Hoteles-residencias,HR2
|
||||
Hoteles-residencias,HR3
|
||||
Hoteles-residencias,HR4
|
||||
Hoteles-residencias,HR5
|
||||
Moteles,M1
|
||||
Moteles,M2
|
||||
Moteles,M3
|
||||
Paradores Nacionales,PN3
|
||||
Paradores Nacionales,PN4
|
||||
Paradores Nacionales,PN5
|
||||
Ciudades de vacaciones,CV1
|
||||
Ciudades de vacaciones,CV2
|
||||
Ciudades de vacaciones,CV3
|
||||
Residencias-apartamentos,RA1
|
||||
Residencias-apartamentos,RA2
|
||||
Residencias-apartamentos,RA3
|
||||
Residencias-apartamentos,RA4
|
||||
Hostales,HS
|
||||
Hostales,HS1
|
||||
Hostales,HS2
|
||||
Hostales,HS3
|
||||
Hostales,HSR
|
||||
Hostales,HSR1
|
||||
Hostales,HSR2
|
||||
Hostales,HSR3
|
||||
Hostales generales, HSE
|
||||
Casas de Huéspedes,CH
|
||||
Casas de Huéspedes,CH1
|
||||
Fondas,F1
|
||||
Fondas,F2
|
||||
Fondas,F3
|
||||
Pensiones,P
|
||||
Pensiones,P1
|
||||
Pensiones,P2
|
||||
Pensiones,P3
|
||||
Pensiones,PA
|
||||
Pensiones,PT
|
||||
Otros,Otras
|
||||
|
53
pms_l10n_es/data/res.country.state.csv
Normal file
53
pms_l10n_es/data/res.country.state.csv
Normal file
@@ -0,0 +1,53 @@
|
||||
"id","ine_code"
|
||||
base.state_es_c,"ES111"
|
||||
base.state_es_vi,"ES211"
|
||||
base.state_es_ab,"ES421"
|
||||
base.state_es_a,"ES521"
|
||||
base.state_es_al,"ES611"
|
||||
base.state_es_o,"ES120"
|
||||
base.state_es_av,"ES411"
|
||||
base.state_es_ba,"ES431"
|
||||
base.state_es_pm,"ES530"
|
||||
base.state_es_b,"ES511"
|
||||
base.state_es_bu,"ES412"
|
||||
base.state_es_cc,"ES432"
|
||||
base.state_es_ca,"ES612"
|
||||
base.state_es_s,"ES130"
|
||||
base.state_es_cs,"ES522"
|
||||
base.state_es_ce,"ES630"
|
||||
base.state_es_cr,"ES422"
|
||||
base.state_es_co,"ES613"
|
||||
base.state_es_cu,"ES423"
|
||||
base.state_es_gi,"ES512"
|
||||
base.state_es_gr,"ES614"
|
||||
base.state_es_gu,"ES424"
|
||||
base.state_es_ss,"ES212"
|
||||
base.state_es_h,"ES615"
|
||||
base.state_es_hu,"ES241"
|
||||
base.state_es_j,"ES616"
|
||||
base.state_es_lo,"ES230"
|
||||
base.state_es_gc,"ES701"
|
||||
base.state_es_le,"ES413"
|
||||
base.state_es_l,"ES513"
|
||||
base.state_es_lu,"ES112"
|
||||
base.state_es_m,"ES300"
|
||||
base.state_es_ma,"ES617"
|
||||
base.state_es_ml,"ES640"
|
||||
base.state_es_mu,"ES620"
|
||||
base.state_es_na,"ES220"
|
||||
base.state_es_or,"ES113"
|
||||
base.state_es_p,"ES414"
|
||||
base.state_es_po,"ES114"
|
||||
base.state_es_sa,"ES415"
|
||||
base.state_es_tf,"ES702"
|
||||
base.state_es_sg,"ES416"
|
||||
base.state_es_se,"ES618"
|
||||
base.state_es_so,"ES417"
|
||||
base.state_es_t,"ES514"
|
||||
base.state_es_te,"ES242"
|
||||
base.state_es_to,"ES425"
|
||||
base.state_es_v,"ES523"
|
||||
base.state_es_va,"ES418"
|
||||
base.state_es_bi,"ES213"
|
||||
base.state_es_za,"ES419"
|
||||
base.state_es_z,"ES243"
|
||||
|
@@ -2,3 +2,6 @@
|
||||
# from . import pms_checkin_partner
|
||||
from . import pms_property
|
||||
from . import pms_log_institution_traveller_report
|
||||
from . import res_country_state
|
||||
from . import pms_ine_tourism_type_category
|
||||
from . import pms_room
|
||||
|
||||
16
pms_l10n_es/models/pms_ine_tourism_type_category.py
Normal file
16
pms_l10n_es/models/pms_ine_tourism_type_category.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PmsIneTourismCategory(models.Model):
|
||||
_name = "pms.ine.tourism.type.category"
|
||||
_description = "Hotel category in the Ministry of Tourism. Used for INE statistics."
|
||||
|
||||
type = fields.Char("Type", required=True)
|
||||
category = fields.Char("Category", required=True)
|
||||
|
||||
def name_get(self):
|
||||
data = []
|
||||
for record in self:
|
||||
display_value = record.category + " (" + record.type + ") "
|
||||
data.append((record.id, display_value))
|
||||
return data
|
||||
@@ -33,6 +33,28 @@ class PmsProperty(models.Model):
|
||||
string="Institution password",
|
||||
help="Password provided by institution to send the data.",
|
||||
)
|
||||
ine_tourism_number = fields.Char(
|
||||
"Tourism number",
|
||||
help="Registration number in the Ministry of Tourism. Used for INE statistics.",
|
||||
)
|
||||
ine_seats = fields.Integer(
|
||||
"Beds available excluding extra beds",
|
||||
default=0,
|
||||
help="Used for INE statistics.",
|
||||
)
|
||||
ine_permanent_staff = fields.Integer(
|
||||
"Permanent Staff", default=0, help="Used for INE statistics."
|
||||
)
|
||||
ine_eventual_staff = fields.Integer(
|
||||
"Eventual Staff", default=0, help="Used for INE statistics."
|
||||
)
|
||||
ine_unpaid_staff = fields.Integer(
|
||||
"Unpaid Staff", default=0, help="Used for INE statistics."
|
||||
)
|
||||
ine_category_id = fields.Many2one(
|
||||
"pms.ine.tourism.type.category",
|
||||
help="Hotel category in the Ministry of Tourism. Used for INE statistics.",
|
||||
)
|
||||
|
||||
def test_connection(self):
|
||||
headers = {
|
||||
|
||||
10
pms_l10n_es/models/pms_room.py
Normal file
10
pms_l10n_es/models/pms_room.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PmsRoom(models.Model):
|
||||
_inherit = "pms.room"
|
||||
in_ine = fields.Boolean(
|
||||
string="In INE",
|
||||
help="Take it into account to generate INE statistics",
|
||||
default=True,
|
||||
)
|
||||
6
pms_l10n_es/models/res_country_state.py
Normal file
6
pms_l10n_es/models/res_country_state.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCountryState(models.Model):
|
||||
_inherit = "res.country.state"
|
||||
ine_code = fields.Char(string="INE State Code")
|
||||
@@ -1,3 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
user_access_traveller_report_wizard,user_access_traveller_report_wizard,model_traveller_report_wizard,pms.group_pms_user,1,1,1,1
|
||||
user_access_traveller_report_logs,user_access_traveller_report_logs,model_pms_log_institution_traveller_report,pms.group_pms_user,1,1,1,1
|
||||
user_access_pms_ine_tourism_type_category,user_access_pms_ine_tourism_type_category,model_pms_ine_tourism_type_category,pms.group_pms_user,1,1,1,1
|
||||
user_access_pms_ine_wizard,user_access_pms_ine_wizard,model_pms_ine_wizard,pms.group_pms_user,1,1,1,1
|
||||
|
||||
|
1
pms_l10n_es/tests/__init__.py
Normal file
1
pms_l10n_es/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import test_wizard_ine
|
||||
29
pms_l10n_es/tests/common.py
Normal file
29
pms_l10n_es/tests/common.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestPms(common.SavepointCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.pricelist1 = self.env["product.pricelist"].create(
|
||||
{
|
||||
"name": "Pricelist 1",
|
||||
}
|
||||
)
|
||||
self.company1 = self.env["res.company"].create(
|
||||
{
|
||||
"name": "Company 1",
|
||||
}
|
||||
)
|
||||
self.pms_property1 = self.env["pms.property"].create(
|
||||
{
|
||||
"name": "Property 1",
|
||||
"company_id": self.company1.id,
|
||||
"default_pricelist_id": self.pricelist1.id,
|
||||
}
|
||||
)
|
||||
self.room_type_class1 = self.env["pms.room.type.class"].create(
|
||||
{
|
||||
"name": "Room Type Class 1",
|
||||
"default_code": "RTC1",
|
||||
}
|
||||
)
|
||||
632
pms_l10n_es/tests/test_wizard_ine.py
Normal file
632
pms_l10n_es/tests/test_wizard_ine.py
Normal file
@@ -0,0 +1,632 @@
|
||||
import datetime
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from .common import TestPms
|
||||
|
||||
|
||||
@freeze_time("2021-02-01")
|
||||
class TestWizardINE(TestPms):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# number of seats established in the property
|
||||
self.pms_property1.ine_seats = 50
|
||||
|
||||
# create room types
|
||||
self.room_type = self.env["pms.room.type"].create(
|
||||
{
|
||||
"name": "Room type test",
|
||||
"default_code": "DBL_Test",
|
||||
"class_id": self.room_type_class1.id,
|
||||
}
|
||||
)
|
||||
# create rooms
|
||||
self.room_double_1 = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"name": "Room test 1",
|
||||
"room_type_id": self.room_type.id,
|
||||
"capacity": 2,
|
||||
}
|
||||
)
|
||||
self.room_double_2 = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"name": "Room test 2",
|
||||
"room_type_id": self.room_type.id,
|
||||
"capacity": 2,
|
||||
}
|
||||
)
|
||||
self.room_single_1 = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"name": "Room test 3",
|
||||
"room_type_id": self.room_type.id,
|
||||
"capacity": 1,
|
||||
"extra_beds_allowed": 1,
|
||||
}
|
||||
)
|
||||
self.room_triple1 = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"name": "Room test 4",
|
||||
"room_type_id": self.room_type.id,
|
||||
"capacity": 3,
|
||||
}
|
||||
)
|
||||
|
||||
# create document category
|
||||
self.id_category_passport = self.env["res.partner.id_category"].create(
|
||||
{
|
||||
"name": "Passport",
|
||||
"code": "P",
|
||||
"active": True,
|
||||
}
|
||||
)
|
||||
# get records of russia, italy and afghanistan
|
||||
self.country_russia = self.env["res.country"].search([("code", "=", "RU")])
|
||||
self.country_russia.ensure_one()
|
||||
self.country_italy = self.env["res.country"].search([("code", "=", "IT")])
|
||||
self.country_italy.ensure_one()
|
||||
self.country_afghanistan = self.env["res.country"].search([("code", "=", "AF")])
|
||||
self.country_afghanistan.ensure_one()
|
||||
# Create partner 1 (italy)
|
||||
self.partner_1 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner1",
|
||||
"country_id": self.country_italy.id,
|
||||
"nationality_id": self.country_italy.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "55103354T",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_1.id,
|
||||
}
|
||||
)
|
||||
|
||||
# Create partner 2 (russia)
|
||||
self.partner_2 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner2",
|
||||
"country_id": self.country_russia.id,
|
||||
"nationality_id": self.country_russia.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "45437298Q",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_2.id,
|
||||
}
|
||||
)
|
||||
# Create partner 3 (italy)
|
||||
self.partner_3 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner3",
|
||||
"country_id": self.country_italy.id,
|
||||
"nationality_id": self.country_italy.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "81534086Y",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_3.id,
|
||||
}
|
||||
)
|
||||
# Create partner 4 (italy)
|
||||
self.partner_4 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner4",
|
||||
"country_id": self.country_italy.id,
|
||||
"nationality_id": self.country_italy.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "00807643K",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_4.id,
|
||||
}
|
||||
)
|
||||
# Create partner 5 (afghanistan)
|
||||
self.partner_5 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner5",
|
||||
"country_id": self.country_afghanistan.id,
|
||||
"nationality_id": self.country_afghanistan.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "54564399G",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_5.id,
|
||||
}
|
||||
)
|
||||
# Create partner 6 (afghanistan)
|
||||
self.partner_6 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner6",
|
||||
"country_id": self.country_afghanistan.id,
|
||||
"nationality_id": self.country_afghanistan.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "39854152M",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_6.id,
|
||||
}
|
||||
)
|
||||
# Create partner 7 (afghanistan)
|
||||
self.partner_7 = self.env["res.partner"].create(
|
||||
{
|
||||
"name": "partner7",
|
||||
"country_id": self.country_afghanistan.id,
|
||||
"nationality_id": self.country_afghanistan.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
}
|
||||
)
|
||||
self.env["res.partner.id_number"].create(
|
||||
{
|
||||
"category_id": self.id_category_passport.id,
|
||||
"name": "39854152O",
|
||||
"valid_from": datetime.date.today(),
|
||||
"partner_id": self.partner_7.id,
|
||||
}
|
||||
)
|
||||
|
||||
# Create reservation 1
|
||||
self.reservation_1 = self.env["pms.reservation"].create(
|
||||
{
|
||||
"checkin": datetime.date.today(),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"preferred_room_id": self.room_double_1.id,
|
||||
"partner_id": self.partner_1.id,
|
||||
"adults": 2,
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
}
|
||||
)
|
||||
self.checkin1 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_1.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
|
||||
self.checkin2 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_2.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
}
|
||||
)
|
||||
# Create reservation 2
|
||||
self.reservation_2 = self.env["pms.reservation"].create(
|
||||
{
|
||||
"checkin": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=2),
|
||||
"preferred_room_id": self.room_triple1.id,
|
||||
"partner_id": self.partner_3.id,
|
||||
"adults": 2,
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
}
|
||||
)
|
||||
self.checkin3 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_3.id,
|
||||
"reservation_id": self.reservation_2.id,
|
||||
}
|
||||
)
|
||||
self.checkin4 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_4.id,
|
||||
"reservation_id": self.reservation_2.id,
|
||||
}
|
||||
)
|
||||
# Create reservation 3
|
||||
self.reservation_3 = self.env["pms.reservation"].create(
|
||||
{
|
||||
"checkin": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=3),
|
||||
"preferred_room_id": self.room_double_2.id,
|
||||
"partner_id": self.partner_5.id,
|
||||
"adults": 1,
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
}
|
||||
)
|
||||
self.checkin5 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_5.id,
|
||||
"reservation_id": self.reservation_3.id,
|
||||
}
|
||||
)
|
||||
# Create extra bed service
|
||||
product_extra_bed = self.env["product.product"].create(
|
||||
{
|
||||
"name": "Product test",
|
||||
"is_extra_bed": True,
|
||||
"consumed_on": "before",
|
||||
"per_day": True,
|
||||
}
|
||||
)
|
||||
vals_service_extra_bed = {
|
||||
"is_board_service": False,
|
||||
"product_id": product_extra_bed.id,
|
||||
}
|
||||
# Create reservation 4
|
||||
self.reservation_4 = self.env["pms.reservation"].create(
|
||||
{
|
||||
"checkin": datetime.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": datetime.date.today() + datetime.timedelta(days=3),
|
||||
"preferred_room_id": self.room_single_1.id,
|
||||
"partner_id": self.partner_6.id,
|
||||
"adults": 2,
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"service_ids": [(0, 0, vals_service_extra_bed)],
|
||||
}
|
||||
)
|
||||
self.checkin6 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_6.id,
|
||||
"reservation_id": self.reservation_4.id,
|
||||
}
|
||||
)
|
||||
|
||||
self.checkin7 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_7.id,
|
||||
"reservation_id": self.reservation_4.id,
|
||||
}
|
||||
)
|
||||
# checkin partners on board
|
||||
self.checkin1.action_on_board()
|
||||
self.checkin2.action_on_board()
|
||||
with freeze_time("2021-02-02"):
|
||||
self.checkin3.action_on_board()
|
||||
self.checkin4.action_on_board()
|
||||
self.checkin5.action_on_board()
|
||||
self.checkin6.action_on_board()
|
||||
self.checkin7.action_on_board()
|
||||
|
||||
# set prices for nights
|
||||
self.reservation_1.reservation_line_ids[0].price = 25.0
|
||||
self.reservation_2.reservation_line_ids[0].price = 21.0
|
||||
self.reservation_3.reservation_line_ids[0].price = 25.0
|
||||
self.reservation_3.reservation_line_ids[1].price = 25.0
|
||||
self.reservation_4.reservation_line_ids[0].price = 21.50
|
||||
self.reservation_4.reservation_line_ids[1].price = 21.50
|
||||
|
||||
def test_room_type_num_by_date(self):
|
||||
"""
|
||||
+============================+==============+==============+=============+
|
||||
| | 01 | 02 | 03 |
|
||||
+============================+==============+==============+=============+
|
||||
| r1 2 adults | DOUBLE ROOM | | |
|
||||
| r2 2 adults | | TRIPLE ROOM | |
|
||||
| r3 1 adult | | DOUBLE ROOM | DOUBLE ROOM |
|
||||
| r4 2 adults (1 extra bed) | | SINGLE ROOM | SINGLE ROOM |
|
||||
+============================+==============+==============+=============+
|
||||
| double rooms (use double) | 1 | 0 | 0 |
|
||||
+============================+==============+==============+=============+
|
||||
| double rooms (use single) | 0 | 1 | 1 |
|
||||
+============================+==============+==============+=============+
|
||||
| other rooms | 0 | 2 | 1 |
|
||||
+============================+==============+==============+=============+
|
||||
| extra beds | 0 | 1 | 1 |
|
||||
+============================+==============+==============+=============+
|
||||
"""
|
||||
# ARRANGE
|
||||
start_date = datetime.date(2021, 2, 1)
|
||||
second_date = datetime.date(2021, 2, 2)
|
||||
end_date = datetime.date(2021, 2, 3)
|
||||
|
||||
expected_result = {
|
||||
start_date: {
|
||||
"double_rooms_double_use": 1,
|
||||
"double_rooms_single_use": 0,
|
||||
"other_rooms": 0,
|
||||
"extra_beds": 0,
|
||||
},
|
||||
second_date: {
|
||||
"double_rooms_double_use": 0,
|
||||
"double_rooms_single_use": 1,
|
||||
"other_rooms": 2,
|
||||
"extra_beds": 1,
|
||||
},
|
||||
end_date: {
|
||||
"double_rooms_double_use": 0,
|
||||
"double_rooms_single_use": 1,
|
||||
"other_rooms": 1,
|
||||
"extra_beds": 1,
|
||||
},
|
||||
}
|
||||
|
||||
# ACT
|
||||
rooms = self.env["pms.ine.wizard"].ine_rooms(
|
||||
start_date, end_date, self.pms_property1
|
||||
)
|
||||
# ASSERT
|
||||
self.assertDictEqual(rooms, expected_result)
|
||||
|
||||
def test_arrivals_departures_pernoctations_by_date(self):
|
||||
"""
|
||||
+===========================+==============+==============+=============+=============+
|
||||
| | 01 | 02 | 03 | 04 |
|
||||
+===========================+==============+==============+=============+=============+
|
||||
| r1 2 adults | italy,russia | italy,russia | | |
|
||||
+---------------------------+--------------+--------------+-------------+-------------+
|
||||
| r2 2 adults | | italy,italy | italy,italy | |
|
||||
+---------------------------+--------------+--------------+-------------+-------------+
|
||||
| r3 1 adult | | afghanistan | afghanistan | afghanistan |
|
||||
+---------------------------+--------------+--------------+-------------+-------------+
|
||||
| r4 2 adults | | afghanistan | afghanistan | afghanistan |
|
||||
| | | afghanistan | afghanistan | afghanistan |
|
||||
+===========================+==============+==============+=============+=============+
|
||||
| arrivals Afghanistan | | 3 | | |
|
||||
| arrivals Italy | 1 | 2 | | |
|
||||
| arrivals Russia | 1 | | | |
|
||||
+===========================+==============+==============+=============+=============+
|
||||
| pernoctations Afghanistan | | 3 | 3 | |
|
||||
| pernoctations Italy | 1 | 2 | | |
|
||||
| pernoctations Russia | 1 | | | |
|
||||
+===========================+==============+==============+=============+=============+
|
||||
| departures Afghanistan | | | | 3 |
|
||||
| departures Italy | | 1 | 2 | |
|
||||
| departures Russia | | 1 | | |
|
||||
+===========================+==============+==============+=============+=============+
|
||||
"""
|
||||
# ARRANGE
|
||||
start_date = datetime.date(2021, 2, 1)
|
||||
second_date = datetime.date(2021, 2, 2)
|
||||
third_date = datetime.date(2021, 2, 3)
|
||||
end_date = datetime.date(2021, 2, 4)
|
||||
|
||||
expected_result = {
|
||||
self.country_afghanistan.code: {
|
||||
second_date: {
|
||||
"arrivals": 3,
|
||||
"pernoctations": 3,
|
||||
},
|
||||
third_date: {
|
||||
"pernoctations": 3,
|
||||
},
|
||||
end_date: {
|
||||
"departures": 3,
|
||||
},
|
||||
},
|
||||
self.country_italy.code: {
|
||||
start_date: {
|
||||
"arrivals": 1,
|
||||
"pernoctations": 1,
|
||||
},
|
||||
second_date: {
|
||||
"arrivals": 2,
|
||||
"pernoctations": 2,
|
||||
"departures": 1,
|
||||
},
|
||||
third_date: {
|
||||
"departures": 2,
|
||||
},
|
||||
},
|
||||
self.country_russia.code: {
|
||||
start_date: {
|
||||
"arrivals": 1,
|
||||
"pernoctations": 1,
|
||||
},
|
||||
second_date: {
|
||||
"departures": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
# ACT
|
||||
nationalities = self.env["pms.ine.wizard"].ine_nationalities(
|
||||
start_date, end_date, self.pms_property1.id
|
||||
)
|
||||
# ASSERT
|
||||
self.assertDictEqual(nationalities, expected_result)
|
||||
|
||||
def test_spain_arrivals_departures_pernoctations_by_date(self):
|
||||
"""
|
||||
+==========================+============+============+=========+========+
|
||||
| | 01 | 02 | 03 | 04 |
|
||||
+==========================+============+============+=========+========+
|
||||
| r1 2 adults | Ourense | Ourense | | |
|
||||
| | Pontevedra | Pontevedra | | |
|
||||
+--------------------------+------------+------------+---------+--------+
|
||||
| r2 2 adults | | Ourense | Ourense | |
|
||||
| | | Ourense | Ourense | |
|
||||
+--------------------------+------------+------------+---------+--------+
|
||||
| r3 1 adult | | Madrid | Madrid | Madrid |
|
||||
+--------------------------+------------+------------+---------+--------+
|
||||
| r4 2 adults | | Madrid | Madrid | Madrid |
|
||||
| | | Madrid | Madrid | Madrid |
|
||||
+==========================+============+============+=========+========+
|
||||
| arrivals Madrid | | 3 | | |
|
||||
| arrivals Ourense | 1 | 2 | | |
|
||||
| arrivals Pontevedra | 1 | | | |
|
||||
+==========================+============+============+=========+========+
|
||||
| pernoctations Madrid | | 3 | 3 | |
|
||||
| pernoctations Ourense | 1 | 2 | | |
|
||||
| pernoctations Pontevedra | 1 | | | |
|
||||
0+=========================+============+============+=========+========+
|
||||
| departures Madrid | | | | 3 |
|
||||
| departures Ourense | | 1 | 2 | |
|
||||
| departures Pontevedra | | 1 | | |
|
||||
+==========================+============+============+=========+========+
|
||||
"""
|
||||
# ARRANGE
|
||||
start_date = datetime.date(2021, 2, 1)
|
||||
second_date = datetime.date(2021, 2, 2)
|
||||
third_date = datetime.date(2021, 2, 3)
|
||||
end_date = datetime.date(2021, 2, 4)
|
||||
|
||||
country_spain = self.env["res.country"].search([("code", "=", "ES")])
|
||||
state_madrid = self.env["res.country.state"].search([("name", "=", "Madrid")])
|
||||
state_ourense = self.env["res.country.state"].search(
|
||||
[("name", "=", "Ourense (Orense)")]
|
||||
)
|
||||
state_pontevedra = self.env["res.country.state"].search(
|
||||
[("name", "=", "Pontevedra")]
|
||||
)
|
||||
|
||||
self.checkin1.nationality_id = country_spain
|
||||
self.partner_1.nationality_id = country_spain
|
||||
self.checkin1.state_id = state_ourense
|
||||
self.partner_1.state_id = state_ourense
|
||||
|
||||
self.checkin2.nationality_id = country_spain
|
||||
self.partner_2.nationality_id = country_spain
|
||||
self.checkin2.state_id = state_pontevedra
|
||||
self.partner_2.state_id = state_pontevedra
|
||||
|
||||
self.checkin3.nationality_id = country_spain
|
||||
self.partner_3.nationality_id = country_spain
|
||||
self.checkin3.state_id = state_ourense
|
||||
self.partner_3.state_id = state_ourense
|
||||
|
||||
self.checkin4.nationality_id = country_spain
|
||||
self.partner_4.nationality_id = country_spain
|
||||
self.checkin4.state_id = state_ourense
|
||||
self.partner_4.state_id = state_ourense
|
||||
|
||||
self.checkin5.nationality_id = country_spain
|
||||
self.partner_5.nationality_id = country_spain
|
||||
self.checkin5.state_id = state_madrid
|
||||
self.partner_5.state_id = state_madrid
|
||||
|
||||
self.checkin6.nationality_id = country_spain
|
||||
self.partner_6.nationality_id = country_spain
|
||||
self.checkin6.state_id = state_madrid
|
||||
self.partner_6.state_id = state_madrid
|
||||
|
||||
self.checkin7.nationality_id = country_spain
|
||||
self.partner_7.nationality_id = country_spain
|
||||
self.checkin7.state_id = state_madrid
|
||||
self.partner_7.state_id = state_madrid
|
||||
|
||||
expected_result = {
|
||||
country_spain.code: {
|
||||
state_madrid.ine_code: {
|
||||
second_date: {
|
||||
"arrivals": 3,
|
||||
"pernoctations": 3,
|
||||
},
|
||||
third_date: {
|
||||
"pernoctations": 3,
|
||||
},
|
||||
end_date: {
|
||||
"departures": 3,
|
||||
},
|
||||
},
|
||||
state_ourense.ine_code: {
|
||||
start_date: {
|
||||
"arrivals": 1,
|
||||
"pernoctations": 1,
|
||||
},
|
||||
second_date: {
|
||||
"arrivals": 2,
|
||||
"pernoctations": 2,
|
||||
"departures": 1,
|
||||
},
|
||||
third_date: {
|
||||
"departures": 2,
|
||||
},
|
||||
},
|
||||
state_pontevedra.ine_code: {
|
||||
start_date: {
|
||||
"arrivals": 1,
|
||||
"pernoctations": 1,
|
||||
},
|
||||
second_date: {
|
||||
"departures": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
# ACT
|
||||
nationalities = self.env["pms.ine.wizard"].ine_nationalities(
|
||||
start_date, end_date, self.pms_property1.id
|
||||
)
|
||||
# ASSERT
|
||||
self.assertDictEqual(nationalities, expected_result)
|
||||
|
||||
def test_calculate_monthly_adr(self):
|
||||
"""
|
||||
+-------------+-------+-------+-------+
|
||||
| | 01 | 02 | 03 |
|
||||
+-------------+-------+-------+-------+
|
||||
| r1 | 25.00 | | |
|
||||
| r2 | | 21.00 | |
|
||||
| r3 | | 25.00 | 25.00 |
|
||||
| r4 | | 21.50 | 21.50 |
|
||||
+-------------+-------+-------+-------+
|
||||
| adr | 25.00 | 22.50 | 23.25 |
|
||||
+-------------+-------+-------+-------+
|
||||
| monthly adr | 23.58 |
|
||||
+-------------+-------+-------+-------+
|
||||
"""
|
||||
# ARRANGE
|
||||
start_date = datetime.date(2021, 2, 1)
|
||||
expected_monthly_adr = 23.58
|
||||
|
||||
# ACT
|
||||
monthly_adr = self.env["pms.ine.wizard"].ine_calculate_monthly_adr(
|
||||
start_date, self.pms_property1.id
|
||||
)
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
expected_monthly_adr,
|
||||
monthly_adr,
|
||||
)
|
||||
|
||||
def test_calculate_monthly_revpar(self):
|
||||
"""
|
||||
+----------------+-------+-------+-------+
|
||||
| | 01 | 02 | 03 |
|
||||
+----------------+-------+-------+-------+
|
||||
| r1 | 25.00 | | |
|
||||
| r2 | | 21.00 | |
|
||||
| r3 | | 25.00 | 25.00 |
|
||||
| r4 | | 21.50 | 21.50 |
|
||||
+----------------+-------+-------+-------+
|
||||
| monthly revpar | 23.58 |
|
||||
+----------------+-------+-------+-------+
|
||||
num rooms avail. = 4
|
||||
income = 25.00 + 21.00 + 25.00 + 25.00 + 21.50 + 21.50 = 139
|
||||
monthly revpar = 139 / (4 * 28)
|
||||
"""
|
||||
# ARRANGE
|
||||
start_date = datetime.date(2021, 2, 1)
|
||||
expected_monthly_revpar = 1.24
|
||||
|
||||
# ACT
|
||||
monthly_revpar = self.env["pms.ine.wizard"].ine_calculate_monthly_revpar(
|
||||
start_date, self.pms_property1.id
|
||||
)
|
||||
# ASSERT
|
||||
self.assertEqual(
|
||||
expected_monthly_revpar,
|
||||
monthly_revpar,
|
||||
)
|
||||
39
pms_l10n_es/views/pms_ine_tourism_type_category.xml
Normal file
39
pms_l10n_es/views/pms_ine_tourism_type_category.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="pms_ine_tourism_category_view_form">
|
||||
<field name="name">pms.ine.tourism.category.form</field>
|
||||
<field name="model">pms.ine.tourism.type.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="type" />
|
||||
<field name="category" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="pms_ine_tourism_category_view_tree">
|
||||
<field name="name">pms.ine.tourism.category.tree</field>
|
||||
<field name="model">pms.ine.tourism.type.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="type" />
|
||||
<field name="category" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="open_pms_ine_tourism_category">
|
||||
<field name="name">INE Categories</field>
|
||||
<field name="res_model">pms.ine.tourism.type.category</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<!-- <menuitem-->
|
||||
<!-- name="INE Tourism Categories"-->
|
||||
<!-- id="menu_open_pms_ine_tourism_category_form_tree"-->
|
||||
<!-- action="open_pms_ine_tourism_category"-->
|
||||
<!-- parent="pms.menu_reservations"-->
|
||||
<!-- sequence="30"-->
|
||||
<!-- />-->
|
||||
</odoo>
|
||||
@@ -34,6 +34,27 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="o_horizontal_separator">
|
||||
INE Settings
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<group>
|
||||
<field name="ine_tourism_number" />
|
||||
<field name="ine_category_id" />
|
||||
</group>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<group>
|
||||
<field name="ine_seats" />
|
||||
<field name="ine_permanent_staff" />
|
||||
<field name="ine_eventual_staff" />
|
||||
<field name="ine_unpaid_staff" />
|
||||
</group>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
13
pms_l10n_es/views/pms_room_views.xml
Normal file
13
pms_l10n_es/views/pms_room_views.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="view_room_form_pms_l10n_es" model="ir.ui.view">
|
||||
<field name="name">Property Form l10n_es</field>
|
||||
<field name="model">pms.room</field>
|
||||
<field name="inherit_id" ref="pms.pms_room_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='sequence']" position="after">
|
||||
<field name="in_ine" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -1 +1,2 @@
|
||||
from . import traveller_report
|
||||
from . import wizard_ine
|
||||
|
||||
680
pms_l10n_es/wizards/wizard_ine.py
Normal file
680
pms_l10n_es/wizards/wizard_ine.py
Normal file
@@ -0,0 +1,680 @@
|
||||
import base64
|
||||
import calendar
|
||||
import datetime
|
||||
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="Monthly ADR")
|
||||
revpar = fields.Float(string="Monthly 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.adults", "=", 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.adults", "=", 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"),
|
||||
("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),
|
||||
]
|
||||
)
|
||||
.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_nationalities(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:
|
||||
# get nationality_id from group set read_group results
|
||||
nationality_id_code = (
|
||||
self.env["res.country"]
|
||||
.search([("id", "=", entry["nationality_id"][0])])
|
||||
.code
|
||||
)
|
||||
# all countries except Spain
|
||||
if nationality_id_code != CODE_SPAIN:
|
||||
|
||||
# get count of each result
|
||||
num = entry["__count"]
|
||||
|
||||
# update/create dicts for countries & dates and set num. arrivals
|
||||
if not nationalities.get(nationality_id_code):
|
||||
nationalities[nationality_id_code] = dict()
|
||||
if not nationalities[nationality_id_code].get(date):
|
||||
nationalities[nationality_id_code][date] = dict()
|
||||
nationalities[nationality_id_code][date][type_of_entry] = num
|
||||
else:
|
||||
# arrivals grouped by state_id (Spain "provincias")
|
||||
read_by_arrivals_spain = self.env["res.partner"].read_group(
|
||||
entry["__domain"],
|
||||
["state_id"],
|
||||
["state_id"],
|
||||
lazy=False,
|
||||
)
|
||||
# iterate read_group results from Spain
|
||||
for entry_from_spain in read_by_arrivals_spain:
|
||||
state_id = self.env["res.country.state"].browse(
|
||||
entry_from_spain["state_id"][0]
|
||||
) # .ine_code
|
||||
ine_code = state_id.ine_code
|
||||
|
||||
# get count of each result
|
||||
num_spain = entry_from_spain["__count"]
|
||||
|
||||
# update/create dicts for states & dates and set num. arrivals
|
||||
if not nationalities.get(CODE_SPAIN):
|
||||
nationalities[CODE_SPAIN] = dict()
|
||||
|
||||
if not nationalities[CODE_SPAIN].get(ine_code):
|
||||
nationalities[CODE_SPAIN][ine_code] = dict()
|
||||
|
||||
if not nationalities[CODE_SPAIN][ine_code].get(date):
|
||||
nationalities[CODE_SPAIN][ine_code][date] = dict()
|
||||
|
||||
nationalities[CODE_SPAIN][ine_code][date][
|
||||
type_of_entry
|
||||
] = num_spain
|
||||
|
||||
# result object
|
||||
nationalities = 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(
|
||||
[
|
||||
("partner_id", "!=", False),
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("checkin", "<=", p_date),
|
||||
("checkout", ">=", p_date),
|
||||
]
|
||||
)
|
||||
|
||||
# only checkin partners housed in "in_ine" rooms
|
||||
hosts = hosts.filtered(
|
||||
lambda x: x.reservation_id.reservation_line_ids.mapped("room_id").in_ine
|
||||
)
|
||||
|
||||
# arrivals
|
||||
arrivals = hosts.filtered(lambda x: x.checkin == p_date)
|
||||
|
||||
# arrivals grouped by nationality_id
|
||||
read_by_arrivals = self.env["res.partner"].read_group(
|
||||
[("id", "in", arrivals.mapped("partner_id").ids)],
|
||||
["nationality_id"],
|
||||
["nationality_id"],
|
||||
lazy=False,
|
||||
)
|
||||
|
||||
# departures
|
||||
departures = hosts.filtered(lambda x: x.checkout == p_date)
|
||||
|
||||
# departures grouped by nationality_id
|
||||
read_by_departures = self.env["res.partner"].read_group(
|
||||
[("id", "in", departures.mapped("partner_id").ids)],
|
||||
["nationality_id"],
|
||||
["nationality_id"],
|
||||
lazy=False,
|
||||
)
|
||||
|
||||
# pernoctations
|
||||
pernoctations = hosts - departures
|
||||
|
||||
# pernoctations grouped by nationality_id
|
||||
read_by_pernoctations = self.env["res.partner"].read_group(
|
||||
[("id", "in", pernoctations.mapped("partner_id").ids)],
|
||||
["nationality_id"],
|
||||
["nationality_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 nationalities
|
||||
|
||||
@api.model
|
||||
def ine_calculate_monthly_adr(self, start_date, pms_property_id):
|
||||
month = start_date.month
|
||||
year = start_date.year
|
||||
month_range = calendar.monthrange(start_date.year, start_date.month)
|
||||
first_day = datetime.date(year, month, 1)
|
||||
last_day = datetime.date(year, month, month_range[1])
|
||||
group_adr = self.env["pms.reservation.line"].read_group(
|
||||
[
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("occupies_availability", "=", True),
|
||||
("reservation_id.reservation_type", "=", "normal"),
|
||||
("room_id.in_ine", "=", True),
|
||||
("date", ">=", first_day),
|
||||
("date", "<=", last_day),
|
||||
],
|
||||
["price:avg"],
|
||||
["date:day"],
|
||||
)
|
||||
if not len(group_adr):
|
||||
return 0
|
||||
adr = 0
|
||||
for day_adr in group_adr:
|
||||
adr += day_adr["price"]
|
||||
|
||||
adr = round(adr / len(group_adr), 2)
|
||||
self.adr = adr
|
||||
return adr
|
||||
|
||||
@api.model
|
||||
def ine_calculate_monthly_revpar(self, start_date, pms_property_id):
|
||||
month = start_date.month
|
||||
year = start_date.year
|
||||
month_range = calendar.monthrange(start_date.year, start_date.month)
|
||||
first_day = datetime.date(year, month, 1)
|
||||
last_day = datetime.date(year, month, month_range[1])
|
||||
sum_group_price = self.env["pms.reservation.line"].read_group(
|
||||
[
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("occupies_availability", "=", True),
|
||||
("reservation_id.reservation_type", "=", "normal"),
|
||||
("room_id.in_ine", "=", True),
|
||||
("date", ">=", first_day),
|
||||
("date", "<=", last_day),
|
||||
],
|
||||
["price"],
|
||||
[],
|
||||
)
|
||||
rooms_not_allowed = (
|
||||
self.env["pms.reservation.line"]
|
||||
.search(
|
||||
[
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("occupies_availability", "=", True),
|
||||
("reservation_id.reservation_type", "!=", "normal"),
|
||||
]
|
||||
)
|
||||
.mapped("room_id")
|
||||
.ids
|
||||
)
|
||||
available_rooms = self.env["pms.room"].search_count(
|
||||
[
|
||||
("in_ine", "=", True),
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("id", "not in", rooms_not_allowed),
|
||||
]
|
||||
)
|
||||
if not sum_group_price[0]["price"]:
|
||||
return 0
|
||||
revpar = round(
|
||||
sum_group_price[0]["price"] / (available_rooms * last_day.day), 2
|
||||
)
|
||||
self.revpar = revpar
|
||||
return revpar
|
||||
|
||||
@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)
|
||||
|
||||
if self.start_date.month != self.end_date.month:
|
||||
raise ValidationError(_("The date range must belong to the same month."))
|
||||
|
||||
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)])
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
nationalities = self.ine_nationalities(
|
||||
self.start_date, self.end_date, self.pms_property_id.id
|
||||
)
|
||||
for key_country, value_country in nationalities.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_monthly_revpar(
|
||||
self.start_date,
|
||||
self.pms_property_id.id,
|
||||
)
|
||||
)
|
||||
|
||||
ET.SubElement(prices_tag, "ADR_MENSUAL").text = str(
|
||||
self.ine_calculate_monthly_adr(
|
||||
self.start_date,
|
||||
self.pms_property_id.id,
|
||||
)
|
||||
)
|
||||
|
||||
# TODO:
|
||||
# Evaluate how to get occupation & ADR for:
|
||||
# -traditional/online tour-operator
|
||||
# -traditional/online agency
|
||||
# -companys
|
||||
|
||||
ET.SubElement(prices_tag, "ADR_TOUROPERADOR_TRADICIONAL").text = "0"
|
||||
ET.SubElement(
|
||||
prices_tag, "PCTN_HABITACIONES_OCUPADAS_TOUROPERADOR_TRADICIONAL"
|
||||
).text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_TOUROPERADOR_ONLINE").text = "0"
|
||||
ET.SubElement(
|
||||
prices_tag, "PCTN_HABITACIONES_OCUPADAS_TOUROPERADOR_ONLINE"
|
||||
).text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_EMPRESAS").text = "0"
|
||||
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_EMPRESAS").text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_AGENCIA_DE_VIAJE_TRADICIONAL").text = "0"
|
||||
ET.SubElement(
|
||||
prices_tag, "PCTN_HABITACIONES_OCUPADAS_AGENCIA_TRADICIONAL"
|
||||
).text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_AGENCIA_DE_VIAJE_ONLINE").text = "0"
|
||||
ET.SubElement(
|
||||
prices_tag, "PCTN_HABITACIONES_OCUPADAS_AGENCIA_ONLINE"
|
||||
).text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_PARTICULARES").text = "0"
|
||||
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_PARTICULARES").text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_GRUPOS").text = "0"
|
||||
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_GRUPOS").text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_INTERNET").text = "0"
|
||||
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_INTERNET").text = "0"
|
||||
ET.SubElement(prices_tag, "ADR_OTROS").text = "0"
|
||||
ET.SubElement(prices_tag, "PCTN_HABITACIONES_OCUPADAS_OTROS").text = "0"
|
||||
|
||||
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",
|
||||
}
|
||||
102
pms_l10n_es/wizards/wizard_ine.xml
Normal file
102
pms_l10n_es/wizards/wizard_ine.xml
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<record id="pms_ine_wizard" model="ir.ui.view">
|
||||
<field name="name">INE</field>
|
||||
<field name="model">pms.ine.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<group>
|
||||
<field name="pms_property_id" />
|
||||
</group>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<group>
|
||||
<field
|
||||
name="start_date"
|
||||
widget="daterange"
|
||||
options="{'related_end_date': 'end_date'}"
|
||||
/>
|
||||
<field
|
||||
name="end_date"
|
||||
widget="daterange"
|
||||
options="{'related_start_date': 'start_date'}"
|
||||
/>
|
||||
</group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<group>
|
||||
<field name="txt_filename" invisible="1" />
|
||||
<field
|
||||
name="txt_binary"
|
||||
filename="txt_filename"
|
||||
readonly="1"
|
||||
attrs="{'invisible': [('txt_filename','=', False)]}"
|
||||
/>
|
||||
</group>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<group>
|
||||
<field
|
||||
name="adr"
|
||||
attrs="{'invisible': [('txt_filename','=', False)]}"
|
||||
readonly="1"
|
||||
/>
|
||||
<field
|
||||
name="revpar"
|
||||
attrs="{'invisible': [('txt_filename','=', False)]}"
|
||||
readonly="1"
|
||||
/>
|
||||
</group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button
|
||||
name="ine_generate_xml"
|
||||
class="btn btn-primary btn-sm"
|
||||
type="object"
|
||||
string="Generate INE XML"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="col-6"
|
||||
attrs="{'invisible': [('txt_filename','=', False)]}"
|
||||
>
|
||||
<b>
|
||||
<a
|
||||
href="https://arce.ine.es/ARCE/jsp/encuestaXml.jsp"
|
||||
target="_blank"
|
||||
>
|
||||
Send survey to Spanish National Institute of Statistics (INE)
|
||||
<i class="fa fa-signal" />
|
||||
</a>
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
<footer />
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ine_report" model="ir.actions.act_window">
|
||||
<field name="name">Generate INE file</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">pms.ine.wizard</field>
|
||||
<field name="view_id" ref="pms_ine_wizard" />
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_ine"
|
||||
name="Generate INE file"
|
||||
sequence="31"
|
||||
parent="pms.menu_reservations"
|
||||
action="action_ine_report"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -1,3 +1,4 @@
|
||||
# generated from manifests external_dependencies
|
||||
bs4
|
||||
pycountry
|
||||
xlrd
|
||||
|
||||
Reference in New Issue
Block a user