Merge branch '14.0' into 14.0-pms_checkin_flow

This commit is contained in:
Darío Lodeiros
2021-06-19 08:19:19 +02:00
committed by GitHub
30 changed files with 2202 additions and 1301 deletions

View File

@@ -20,9 +20,9 @@ Available addons
addon | version | summary
--- | --- | ---
[multi_pms_properties](multi_pms_properties/) | 14.0.1.0.0 | Multi Properties Manager
[pms](pms/) | 14.0.1.0.8 | A property management system
[pms](pms/) | 14.0.1.0.10 | A property management system
[pms_housekeeping](pms_housekeeping/) | 14.0.1.0.1 | Housekeeping
[pms_l10n_es](pms_l10n_es/) | 14.0.1.0.1 | PMS Spanish Adaptation
[pms_l10n_es](pms_l10n_es/) | 14.0.1.0.2 | PMS Spanish Adaptation
[pms_rooming_xls](pms_rooming_xls/) | 14.0.1.0.0 | Rooming xlsx Management
[//]: # (end addons)

View File

@@ -4,7 +4,7 @@
{
"name": "PMS (Property Management System)",
"summary": "A property management system",
"version": "14.0.1.0.8",
"version": "14.0.1.0.10",
"development_status": "Alpha",
"category": "Generic Modules/Property Management System",
"website": "https://github.com/OCA/pms",

View File

@@ -72,8 +72,8 @@ msgstr "%s no encontrado en los checkins (%s)"
#. module: pms
#: code:addons/pms/models/pms_reservation_line.py:0
#, python-format
msgid "%s: No room available."
msgstr "%s: Habitación no disponible."
msgid "%s: No room available in %s <-> %s."
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_reservation_line.py:0
@@ -6457,6 +6457,12 @@ msgstr "Línea de la Reserva"
msgid "Reservation availability plan"
msgstr "Plan de Disponibilidad de la Reserva"
#. module: pms
#: code:addons/pms/models/pms_reservation.py:0
#, python-format
msgid "Reservation dates should be consecutives"
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_pms_service__reservation_id
msgid "Reservation in which the service is included"
@@ -7800,6 +7806,12 @@ msgstr "La capacidad de la habitación debe ser mayor que 0."
msgid "The checkin partners on a folio"
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_reservation.py:0
#, python-format
msgid "The checkout date must be greater than the checkin date"
msgstr ""
#. module: pms
#: code:addons/pms/models/res_users.py:0
#, python-format
@@ -9042,6 +9054,9 @@ msgid ""
"you cannot have more than one room with the same name in the same property"
msgstr ""
#~ msgid "%s: No room available."
#~ msgstr "%s: Habitación no disponible."
#, fuzzy
#~ msgid "Technical field"
#~ msgstr "Computación técnica"

View File

@@ -60,7 +60,7 @@ msgstr ""
#. module: pms
#: code:addons/pms/models/pms_reservation_line.py:0
#, python-format
msgid "%s: No room available."
msgid "%s: No room available in %s <-> %s."
msgstr ""
#. module: pms
@@ -6129,6 +6129,12 @@ msgstr ""
msgid "Reservation availability plan"
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_reservation.py:0
#, python-format
msgid "Reservation dates should be consecutives"
msgstr ""
#. module: pms
#: model:ir.model.fields,help:pms.field_pms_service__reservation_id
msgid "Reservation in which the service is included"
@@ -7419,6 +7425,12 @@ msgstr ""
msgid "The checkin partners on a folio"
msgstr ""
#. module: pms
#: code:addons/pms/models/pms_reservation.py:0
#, python-format
msgid "The checkout date must be greater than the checkin date"
msgstr ""
#. module: pms
#: code:addons/pms/models/res_users.py:0
#, python-format

View File

@@ -140,7 +140,6 @@ class PmsFolio(models.Model):
commission = fields.Float(
string="Commission",
readonly=True,
default=0,
store=True,
compute="_compute_commission",
)
@@ -642,7 +641,7 @@ class PmsFolio(models.Model):
or False
)
@api.depends("reservation_ids")
@api.depends("reservation_ids", "reservation_ids.commission_amount")
def _compute_commission(self):
for folio in self:
for reservation in folio.reservation_ids:

View File

@@ -109,6 +109,7 @@ class PmsReservation(models.Model):
readonly=False,
store=True,
related="folio_id.agency_id",
depends=["folio_id.agency_id"],
tracking=True,
)
channel_type_id = fields.Many2one(
@@ -376,16 +377,18 @@ class PmsReservation(models.Model):
checkin = fields.Date(
string="Check In",
help="It is the checkin date of the reservation, ",
required=True,
default=lambda self: self._get_default_checkin(),
compute="_compute_checkin",
readonly=False,
store=True,
copy=False,
tracking=True,
)
checkout = fields.Date(
string="Check Out",
help="It is the checkout date of the reservation, ",
required=True,
default=lambda self: self._get_default_checkout(),
compute="_compute_checkout",
readonly=False,
store=True,
copy=False,
tracking=True,
)
@@ -924,6 +927,53 @@ class PmsReservation(models.Model):
for reservation in self:
reservation.access_url = "/my/reservations/%s" % (reservation.id)
@api.depends("reservation_line_ids")
def _compute_checkin(self):
"""
Allows to calculate the checkin by default or when the create
specifically indicates the lines of the reservation
"""
for record in self:
if record.reservation_line_ids:
checkin_line_date = min(record.reservation_line_ids.mapped("date"))
# check if the checkin was created directly as reservation_line_id:
if checkin_line_date != record.checkin:
record.checkin = checkin_line_date
elif not record.checkin:
# default checkout other folio reservations or today
if len(record.folio_id.reservation_ids) > 1:
record.checkin = record.folio_id.reservation_ids[0].checkin
else:
record.checkin = fields.date.today()
@api.depends("reservation_line_ids", "checkin")
def _compute_checkout(self):
"""
Allows to calculate the checkout by default or when the create
specifically indicates the lines of the reservation
"""
for record in self:
if record.reservation_line_ids:
checkout_line_date = max(
record.reservation_line_ids.mapped("date")
) + datetime.timedelta(days=1)
# check if the checkout was created directly as reservation_line_id:
if checkout_line_date != record.checkout:
record.checkout = checkout_line_date
# default checkout if checkin is set
elif record.checkin and not record.checkout:
if len(record.folio_id.reservation_ids) > 1:
record.checkin = record.folio_id.reservation_ids[0].checkout
else:
record.checkout = record.checkin + datetime.timedelta(days=1)
elif not record.checkout:
record.checkout = False
# date checking
if record.checkin and record.checkout and record.checkin >= record.checkout:
raise UserError(
_("The checkout date must be greater than the checkin date")
)
@api.depends("pms_property_id", "folio_id")
def _compute_arrival_hour(self):
for record in self:
@@ -975,7 +1025,7 @@ class PmsReservation(models.Model):
for reservation in self:
if reservation.commission_percent > 0:
reservation.commission_amount = (
reservation.price_total * reservation.commission_percent
reservation.price_total * reservation.commission_percent / 100
)
else:
reservation.commission_amount = 0
@@ -1184,28 +1234,6 @@ class PmsReservation(models.Model):
recs = self.search([]).filtered(lambda x: x.checkin_partner_pending_count > 0)
return [("id", "in", [x.id for x in recs])] if recs else []
def _get_default_checkin(self):
folio = False
if "folio_id" in self._context:
folio = self.env["pms.folio"].search(
[("id", "=", self._context["folio_id"])]
)
if folio and folio.reservation_ids:
return folio.reservation_ids[0].checkin
else:
return fields.Date.today()
def _get_default_checkout(self):
folio = False
if "folio_id" in self._context:
folio = self.env["pms.folio"].search(
[("id", "=", self._context["folio_id"])]
)
if folio and folio.reservation_ids:
return folio.reservation_ids[0].checkout
else:
return fields.Date.today() + datetime.timedelta(1)
def _get_default_segmentation(self):
folio = False
segmentation_ids = False
@@ -1234,6 +1262,20 @@ class PmsReservation(models.Model):
)
)
@api.constrains("reservation_line_ids")
def check_consecutive_dates(self):
"""
simply convert date objects to integers using the .toordinal() method
of datetime objects. The difference between the maximum and minimum value
of the set of ordinal dates is one more than the length of the set
"""
for record in self:
if record.reservation_line_ids and len(record.reservation_line_ids) > 1:
dates = record.reservation_line_ids.mapped("date")
date_ints = {d.toordinal() for d in dates}
if not (max(date_ints) - min(date_ints) == len(date_ints) - 1):
raise ValidationError(_("Reservation dates should be consecutives"))
# @api.constrains("checkin_partner_ids", "adults")
# def _max_checkin_partner_ids(self):
# for record in self:

View File

@@ -210,8 +210,12 @@ class PmsReservationLine(models.Model):
# if the preferred room is NOT available
else:
raise ValidationError(
_("%s: No room available.")
% (reservation.preferred_room_id.name)
_("%s: No room available in %s <-> %s.")
% (
reservation.preferred_room_id.name,
line.reservation_id.checkin,
line.reservation_id.checkout,
)
)
# otherwise we assign the first of those

View File

@@ -21,7 +21,6 @@
##############################################################################
from . import test_pms_reservation
from . import test_pms_pricelist
from . import test_pms_pricelist_priority
from . import test_pms_checkin_partner
from . import test_pms_sale_channel
from . import test_pms_folio

View File

@@ -4,73 +4,34 @@ from freezegun import freeze_time
from odoo import fields
from odoo.exceptions import UserError
from odoo.tests import common
freeze_time("2000-02-02")
from .common import TestPms
class TestPmsFolio(common.SavepointCase):
def create_common_scenario(self):
# create a room type availability
self.room_type_availability = self.env["pms.availability.plan"].create(
{"name": "Availability plan for TEST"}
)
# sequences
self.folio_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Folio",
"code": "pms.folio",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.reservation_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Reservation",
"code": "pms.reservation",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.checkin_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Checkin",
"code": "pms.checkin.partner",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
# create a property
self.property = self.env["pms.property"].create(
{
"name": "MY PMS TEST",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
class TestPmsFolio(TestPms):
# create room type class
self.room_type_class = self.env["pms.room.type.class"].create(
{"name": "Room", "default_code": "ROOM"}
)
# SetUp and Common Scenarios methods
def setUp(self):
"""
- common + room_type_double with 2 rooms (double1 and double2) in pms_property1
"""
super().setUp()
# create room type
self.room_type_double = self.env["pms.room.type"].create(
{
"pms_property_ids": [self.property.id],
"pms_property_ids": [self.pms_property1.id],
"name": "Double Test",
"default_code": "DBL_Test",
"class_id": self.room_type_class.id,
"class_id": self.room_type_class1.id,
"price": 25,
}
)
# create room
self.room1 = self.env["pms.room"].create(
self.double1 = self.env["pms.room"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"name": "Double 101",
"room_type_id": self.room_type_double.id,
"capacity": 2,
@@ -78,9 +39,9 @@ class TestPmsFolio(common.SavepointCase):
)
# create room
self.room2 = self.env["pms.room"].create(
self.double2 = self.env["pms.room"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"name": "Double 102",
"room_type_id": self.room_type_double.id,
"capacity": 2,
@@ -88,233 +49,344 @@ class TestPmsFolio(common.SavepointCase):
)
def create_multiproperty_scenario(self):
self.create_common_scenario()
self.property1 = self.env["pms.property"].create(
{
"name": "Property_1",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
self.property2 = self.env["pms.property"].create(
"""
Just 2 properties to majors
"""
self.pms_property2 = self.env["pms.property"].create(
{
"name": "Property_2",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
self.property3 = self.env["pms.property"].create(
self.pms_property3 = self.env["pms.property"].create(
{
"name": "Property_3",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
def test_commission_and_partner_correct(self):
# ARRANGE
self.create_common_scenario()
PmsFolio = self.env["pms.folio"]
PmsReservation = self.env["pms.reservation"]
def create_sale_channel_scenario(self):
"""
Method to simplified scenario on sale channel tests:
- create a sale_channel1 like indirect
- create a agency1 like sale_channel1 agency
"""
PmsPartner = self.env["res.partner"]
PmsSaleChannel = self.env["pms.sale.channel"]
# ACT
saleChannel = PmsSaleChannel.create(
self.sale_channel1 = PmsSaleChannel.create(
{"name": "saleChannel1", "channel_type": "indirect"}
)
agency = PmsPartner.create(
self.agency1 = PmsPartner.create(
{
"name": "partner1",
"is_agency": True,
"invoice_to_agency": True,
"default_commission": 15,
"sale_channel_id": saleChannel.id,
"sale_channel_id": self.sale_channel1.id,
}
)
folio = PmsFolio.create(
def create_configuration_accounting_scenario(self):
"""
Method to simplified scenario to payments and accounting:
# REVIEW:
- Use new property with odoo demo data company to avoid account configuration
- Emule SetUp with new property:
- create demo_room_type_double
- Create 2 rooms room_type_double
"""
self.pms_property_demo = self.env["pms.property"].create(
{
"agency_id": agency.id,
"pms_property_id": self.property.id,
"name": "Property Based on Comapany Demo",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
}
)
reservation = PmsReservation.create(
# create room type
self.demo_room_type_double = self.env["pms.room.type"].create(
{
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"agency_id": agency.id,
"folio_id": folio.id,
"pms_property_ids": [self.pms_property_demo.id],
"name": "Double Test",
"default_code": "Demo_DBL_Test",
"class_id": self.room_type_class1.id,
"price": 25,
}
)
commission = 0
for reservation in folio.reservation_ids:
commission += reservation.commission_amount
# ASSERT
self.assertEqual(
folio.commission,
commission,
"Folio commission don't math with his reservation commission",
)
if folio.agency_id:
self.assertEqual(
folio.agency_id, folio.partner_id, "Agency has to be the partner"
)
def test_compute_folio_priority(self):
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
# create rooms
self.double1 = self.env["pms.room"].create(
{
"checkin": fields.date.today(),
"checkout": fields.date.today() + datetime.timedelta(days=1),
"room_type_id": self.room_type_double.id,
"partner_id": self.env.ref("base.res_partner_12").id,
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property_demo.id,
"name": "Double 101",
"room_type_id": self.demo_room_type_double.id,
"capacity": 2,
}
)
self.double2 = self.env["pms.room"].create(
{
"pms_property_id": self.pms_property_demo.id,
"name": "Double 102",
"room_type_id": self.demo_room_type_double.id,
"capacity": 2,
}
)
# TestCases: Sale Channels
def test_default_agency_commission(self):
"""
Check the total commission of a folio with agency based on the
reservation night price and the preconfigured commission in the agency.
-------
Agency with 15% default commision, folio with one reservation
and 3 nights at 20$ by night (60$ total)
"""
# ARRANGE
self.create_sale_channel_scenario()
commission = (20 + 20 + 20) * 0.15
# ACT
folio1 = self.env["pms.folio"].create(
{
"agency_id": self.agency1.id,
"pms_property_id": self.pms_property1.id,
}
)
r1.allowed_checkin = False
self.env["pms.reservation"].create(
{
"folio_id": r1.folio_id.id,
"folio_id": folio1.id,
"room_type_id": self.room_type_double.id,
"reservation_line_ids": [
(
0,
False,
{
"date": fields.date.today(),
"price": 20,
},
),
(
0,
False,
{
"date": fields.date.today() + datetime.timedelta(days=1),
"price": 20,
},
),
(
0,
False,
{
"date": fields.date.today() + datetime.timedelta(days=2),
"price": 20,
},
),
],
}
)
# ASSERT
self.assertEqual(
commission, folio1.commission, "The folio compute commission is wrong"
)
def test_reservation_agency_without_partner(self):
"""
Check that a reservation / folio created with an agency
and without a partner will automatically take the partner.
-------
Create the folio1 and the reservation1, only set agency_id,
and the partner_id should be the agency itself.
"""
# ARRANGE
self.create_sale_channel_scenario()
# ACT
folio1 = self.env["pms.folio"].create(
{
"agency_id": self.agency1.id,
"pms_property_id": self.pms_property1.id,
}
)
reservation1 = self.env["pms.reservation"].create(
{
"room_type_id": self.room_type_double.id,
"checkin": fields.date.today(),
"checkout": fields.date.today() + datetime.timedelta(days=1),
"room_type_id": self.room_type_double.id,
"partner_id": self.env.ref("base.res_partner_12").id,
"pms_property_id": self.property.id,
"folio_id": folio1.id,
}
)
# ASSERT
self.assertEqual(
r1.priority,
r1.folio_id.max_reservation_prior,
"The max. reservation priority on the whole folio is incorrect",
reservation1.agency_id, folio1.partner_id, "Agency has to be the partner"
)
# TestCases: Priority
def test_compute_folio_priority(self):
"""
Check the priority of a folio based on its reservations
#TODO: Commented test waiting to redefine the priority calculation
"""
# reservation1 = self.env["pms.reservation"].create(
# {
# "checkin": fields.date.today(),
# "checkout": fields.date.today() + datetime.timedelta(days=1),
# "room_type_id": self.room_type_double.id,
# "partner_id": self.env.ref("base.res_partner_12").id,
# "pms_property_id": self.property.id,
# }
# )
# reservation1.allowed_checkin = False
# self.env["pms.reservation"].create(
# {
# "folio_id": reservation1.folio_id.id,
# "checkin": fields.date.today(),
# "checkout": fields.date.today() + datetime.timedelta(days=1),
# "room_type_id": self.room_type_double.id,
# "partner_id": self.env.ref("base.res_partner_12").id,
# "pms_property_id": self.property.id,
# }
# )
# self.assertEqual(
# reservation1.priority,
# reservation1.folio_id.max_reservation_prior,
# "The max. reservation priority on the whole folio is incorrect",
# )
# TestCases: Payments
@freeze_time("2000-02-02")
def test_full_pay_folio(self):
# TEST CASE
# Folio is paid after execute
#
"""
After making the payment of a folio for the entire amount,
check that there is nothing pending.
-----
We create a reservation (autocalculates the amounts) and
then make the payment using the do_payment method of the folio,
directly indicating the pending amount on the folio of the newly
created reservation
"""
# ARRANGE
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
self.create_configuration_accounting_scenario()
reservation1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property_demo.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=1),
"adults": 2,
"partner_id": self.env.ref("base.res_partner_12").id,
"room_type_id": self.room_type_double.id,
"room_type_id": self.demo_room_type_double.id,
}
)
# ACTION
self.env["pms.folio"].do_payment(
self.env["account.journal"].browse(
r_test.folio_id.pms_property_id._get_payment_methods().ids[0]
journal=self.env["account.journal"].browse(
reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]
),
self.env["account.journal"]
.browse(r_test.folio_id.pms_property_id._get_payment_methods().ids[0])
receivable_account=self.env["account.journal"]
.browse(reservation1.folio_id.pms_property_id._get_payment_methods().ids[0])
.suspense_account_id,
self.env.user,
r_test.folio_id.pending_amount,
r_test.folio_id,
partner=r_test.partner_id,
user=self.env.user,
amount=reservation1.folio_id.pending_amount,
folio=reservation1.folio_id,
partner=reservation1.partner_id,
date=fields.date.today(),
)
self.assertFalse(r_test.folio_id.pending_amount)
# ASSERT
self.assertFalse(
reservation1.folio_id.pending_amount,
"The pending amount of a folio paid in full has not been zero",
)
@freeze_time("2000-02-02")
def test_partial_pay_folio(self):
# TEST CASE
# Folio is partially paid after execute
#
"""
After making the payment of a folio for the partial amount,
We check that the pending amount is the one that corresponds to it.
-----
We create a reservation (autocalculates the amounts) and
then make the payment using the do_payment method of the folio,
directly indicating the pending amount on the folio MINUS 1$
of the newly created reservation
"""
# ARRANGE
self.create_configuration_accounting_scenario()
left_to_pay = 1
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
reservation1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property_demo.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=1),
"adults": 2,
"partner_id": self.env.ref("base.res_partner_12").id,
"room_type_id": self.room_type_double.id,
"room_type_id": self.demo_room_type_double.id,
}
)
# ACTION
self.env["pms.folio"].do_payment(
self.env["account.journal"].browse(
r_test.folio_id.pms_property_id._get_payment_methods().ids[0]
journal=self.env["account.journal"].browse(
reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]
),
self.env["account.journal"]
.browse(r_test.folio_id.pms_property_id._get_payment_methods().ids[0])
receivable_account=self.env["account.journal"]
.browse(reservation1.folio_id.pms_property_id._get_payment_methods().ids[0])
.suspense_account_id,
self.env.user,
r_test.folio_id.pending_amount - left_to_pay,
r_test.folio_id,
partner=r_test.partner_id,
user=self.env.user,
amount=reservation1.folio_id.pending_amount - left_to_pay,
folio=reservation1.folio_id,
partner=reservation1.partner_id,
date=fields.date.today(),
)
self.assertEqual(r_test.folio_id.pending_amount, left_to_pay)
def test_closure_reason_property(self):
# ASSERT
self.assertEqual(
reservation1.folio_id.pending_amount,
left_to_pay,
"The pending amount on a partially paid folio it \
does not correspond to the amount that it should",
)
# TestCases: Property Consistencies
def test_folio_closure_reason_consistency_properties(self):
"""
Check the multioproperty consistencia between
clousure reasons and folios
-------
create multiproperty scenario (3 properties in total) and
a new clousure reason in pms_property1 and pms_property2, then, create
a new folio in property3 and try to set the clousure_reason
waiting a error property consistency.
"""
# ARRANGE
self.create_multiproperty_scenario()
cl_reason = self.env["room.closure.reason"].create(
{
"name": "closure_reason_test",
"pms_property_ids": [
(4, self.property1.id),
(4, self.property2.id),
(4, self.pms_property1.id),
(4, self.pms_property2.id),
],
}
)
with self.assertRaises(UserError):
# ACTION & ASSERT
with self.assertRaises(
UserError,
msg="Folio created with clousure_reason_id with properties inconsistence",
):
self.env["pms.folio"].create(
{
"pms_property_id": self.property3.id,
"pms_property_id": self.pms_property3.id,
"closure_reason_id": cl_reason.id,
}
)
def _test_compute_currency(self):
self.create_common_scenario()
self.currency1 = self.env["res.currency"].create(
{
"name": "currency1",
"symbol": "C",
}
)
self.pricelist = self.env["product.pricelist"].create(
{
"name": "pricelist 1",
"pms_property_ids": [
(4, self.property.id),
],
"currency_id": self.currency1.id,
}
)
self.reservation1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=1),
"partner_id": self.env.ref("base.res_partner_12").id,
"pricelist_id": self.pricelist.id,
}
)
self.assertEqual(
self.currency1.id,
self.reservation1.folio_id.currency_id.id,
"Currency must match",
)

View File

@@ -6,10 +6,7 @@ from odoo.tests import common
class TestPmsFolioInvoice(common.SavepointCase):
def setUp(self):
super(TestPmsFolioInvoice, self).setUp()
def create_common_scenario(self):
# create a room type availability
# sequences
# create a room type availability and sequences
self.folio_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Folio",
@@ -95,7 +92,6 @@ class TestPmsFolioInvoice(common.SavepointCase):
def test_invoice_full_folio(self):
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
@@ -121,7 +117,6 @@ class TestPmsFolioInvoice(common.SavepointCase):
def test_invoice_partial_folio_by_steps(self):
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
@@ -162,7 +157,6 @@ class TestPmsFolioInvoice(common.SavepointCase):
def test_invoice_partial_folio_diferent_partners(self):
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
@@ -208,7 +202,6 @@ class TestPmsFolioInvoice(common.SavepointCase):
def test_invoice_partial_folio_wrong_qtys(self):
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,

View File

@@ -1,43 +1,31 @@
import datetime
from odoo import fields
from .common import TestPms
class TestPmsFolioSaleLine(TestPms):
def create_common_scenario(self):
# create a room type availability
self.room_type_availability = self.env["pms.availability.plan"].create(
{"name": "Availability plan for TEST"}
)
# create a property
self.property = self.env["pms.property"].create(
{
"name": "MY PMS TEST",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
}
)
# create room type class
self.room_type_class = self.env["pms.room.type.class"].create(
{"name": "Room", "default_code": "ROOM"}
)
def setUp(self):
"""
- common + room_type_avalability_plan
"""
super().setUp()
# create room type
self.room_type_double = self.env["pms.room.type"].create(
{
"pms_property_ids": [self.property.id],
"pms_property_ids": [self.pms_property1.id],
"name": "Double Test",
"default_code": "DBL_Test",
"class_id": self.room_type_class.id,
"class_id": self.room_type_class1.id,
"price": 25,
}
)
# create room
self.room1 = self.env["pms.room"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"name": "Double 101",
"room_type_id": self.room_type_double.id,
"capacity": 2,
@@ -46,21 +34,49 @@ class TestPmsFolioSaleLine(TestPms):
# RESERVATION LINES
def test_comp_fsl_rooms_all_same_group(self):
# TEST CASE
# 2-night reservation and same price, discount & cancel_discount for
# all nights
# should generate just 1 reservation sale line
"""
check the grouping of the reservation lines on the sale line folio
when the price, discount match-
------------
reservation with 3 nights with the same price,
should generate just 1 reservation sale line
"""
# ARRANGE
expected_sale_lines = 1
self.create_common_scenario()
# ACT
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"pms_property_id": self.pms_property1.id,
"reservation_line_ids": [
(
0,
False,
{
"date": fields.date.today(),
"price": 20,
"discount": 10,
},
),
(
0,
False,
{
"date": fields.date.today() + datetime.timedelta(days=1),
"price": 20,
"discount": 10,
},
),
(
0,
False,
{
"date": fields.date.today() + datetime.timedelta(days=2),
"price": 20,
"discount": 10,
},
),
],
"adults": 2,
"room_type_id": self.room_type_double.id,
"partner_id": self.env.ref("base.res_partner_12").id,
@@ -81,10 +97,9 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_sale_lines = 2
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -112,10 +127,9 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_sale_lines = 2
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -143,10 +157,9 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_sale_lines = 2
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -175,10 +188,9 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_sale_lines = 1
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -207,10 +219,9 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same reservation sales line record.
# ARRANGE
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -242,10 +253,9 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same reservation sales line record.
# ARRANGE
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -278,10 +288,9 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same reservation sales line record.
# ARRANGE
self.create_common_scenario()
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -315,7 +324,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_board_service_sale_lines = 1
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -345,7 +353,7 @@ class TestPmsFolioSaleLine(TestPms):
# ACT
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -375,7 +383,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_board_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -404,7 +411,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -435,7 +442,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_board_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -464,7 +470,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -498,7 +504,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_board_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -527,7 +532,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -561,7 +566,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_board_service_sale_lines = 1
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -590,7 +594,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -623,7 +627,6 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same board service sales line record.
# ARRANGE
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -651,7 +654,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -684,7 +687,6 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same board service sales line record.
# ARRANGE
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -716,7 +718,7 @@ class TestPmsFolioSaleLine(TestPms):
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -751,7 +753,6 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same board service sales line record.
# ARRANGE
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -783,7 +784,7 @@ class TestPmsFolioSaleLine(TestPms):
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -821,7 +822,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_extra_service_sale_lines = 1
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -832,7 +832,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -871,7 +871,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_extra_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -882,7 +881,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -923,7 +922,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_extra_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -934,7 +932,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -976,7 +974,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_extra_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -987,7 +984,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -1030,7 +1027,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_extra_service_sale_lines = 1
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -1041,7 +1037,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -1082,7 +1078,6 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same reservation service sales line record.
# ARRANGE
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -1093,7 +1088,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -1134,7 +1129,6 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same reservation service sales line record.
# ARRANGE
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -1145,7 +1139,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -1187,7 +1181,6 @@ class TestPmsFolioSaleLine(TestPms):
# Should keep the same reservation service sales line record.
# ARRANGE
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -1198,7 +1191,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -1220,7 +1213,7 @@ class TestPmsFolioSaleLine(TestPms):
# ACT
r_test.service_ids.filtered(
lambda x: x.id == extra_service
lambda x: x.id == extra_service.id
).service_line_ids.price_unit = 50
r_test.service_ids.service_line_ids.flush()
@@ -1242,7 +1235,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_folio_service_sale_lines = 1
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -1250,7 +1242,7 @@ class TestPmsFolioSaleLine(TestPms):
)
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
@@ -1289,7 +1281,6 @@ class TestPmsFolioSaleLine(TestPms):
# ARRANGE
expected_folio_service_sale_lines = 2
self.create_common_scenario()
product_test1 = self.env["product.product"].create(
{
"name": "Test Product 1",
@@ -1303,7 +1294,7 @@ class TestPmsFolioSaleLine(TestPms):
r_test = self.env["pms.reservation"].create(
{
"pms_property_id": self.property.id,
"pms_property_id": self.pms_property1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,

File diff suppressed because it is too large Load Diff

View File

@@ -1,541 +0,0 @@
import datetime
from freezegun import freeze_time
from odoo.tests import common
class TestPmsPricelistRules(common.SavepointCase):
def create_common_scenario(self):
self.product_template = self.env["product.template"].create(
{"name": "Template1"}
)
self.product_category = self.env["product.category"].create(
{"name": "Category1"}
)
self.availability_plan1 = self.env["pms.availability.plan"].create(
{"name": "Availability 1"}
)
self.availability_plan2 = self.env["pms.availability.plan"].create(
{"name": "Availability"}
)
# sequences
self.folio_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Folio",
"code": "pms.folio",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.reservation_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Reservation",
"code": "pms.reservation",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.checkin_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Checkin",
"code": "pms.checkin.partner",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.property1 = self.env["pms.property"].create(
{
"name": "Property_1",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
self.property2 = self.env["pms.property"].create(
{
"name": "Property_2",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.env.ref("product.list0").id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
self.room_type_class = self.env["pms.room.type.class"].create(
{"name": "Room Class", "default_code": "ROOM"}
)
self.room_type = self.env["pms.room.type"].create(
{
"pms_property_ids": [self.property1.id, self.property2.id],
"name": "Single",
"default_code": "SIN",
"class_id": self.room_type_class.id,
"list_price": 30,
}
)
self.room = self.env["pms.room"].create(
{
"pms_property_id": self.property1.id,
"name": "Single 101",
"room_type_id": self.room_type.id,
}
)
self.room2 = self.env["pms.room"].create(
{
"pms_property_id": self.property2.id,
"name": "Single 102",
"room_type_id": self.room_type.id,
}
)
self.pricelist = self.env["product.pricelist"].create(
{
"name": "pricelist_1",
}
)
self.partner1 = self.env["res.partner"].create({"name": "Carles"})
@freeze_time("2000-01-01")
def test_simple_price_without_items(self):
# TEST CASE : no items applied
# ARRANGE
self.create_common_scenario()
reservation = self.env["pms.reservation"].create(
{
"checkin": datetime.datetime.today(),
"checkout": datetime.datetime.today() + datetime.timedelta(days=3),
"preferred_room_id": self.room.id,
"pms_property_id": self.property1.id,
"partner_id": self.partner1.id,
}
)
# ACT
n_days = (reservation.checkout - reservation.checkin).days
expected_price = self.room.room_type_id.list_price * n_days
# ASSERT
self.assertEqual(
expected_price, reservation.price_subtotal, "The price is not as expected"
)
@freeze_time("2022-01-01")
def test_items_sort(self):
# ARRANGE
self.create_common_scenario()
# - test cases to verify the order for each field considered individually
# - test cases to prioritize fields over other fields:
# 1. applied_on
# 2. date
# 3. date consumption
# 4. num. properties
# 5. id
# - tie
# - no [date_start|date_end|date_start_consumption|date_end_consumption]
properties = self.room_type.product_id.pms_property_ids.ids
test_cases = [
{
"name": "sorting applied_on",
"expected_price": 50 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "2_product_category",
"categ_id": self.product_category.id,
"fixed_price": 60.0,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"fixed_price": 50.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "1_product",
"product_id": self.room_type.product_id.id,
"product_tmpl_id": self.product_template.id,
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "sorting SALE date min range",
"expected_price": 50.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=2),
"fixed_price": 60.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=1),
"fixed_price": 50.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=3),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "sorting CONSUMPTION date min range",
"expected_price": 40.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=6),
"fixed_price": 60.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=10),
"fixed_price": 50.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "sorting num. properties",
"expected_price": 50.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"fixed_price": 60.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"pms_property_ids": [self.property1.id],
"fixed_price": 50.0,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"pms_property_ids": [self.property1.id, self.property2.id],
"fixed_price": 40.0,
},
],
},
{
"name": "sorting by item id",
"expected_price": 40.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"fixed_price": 60.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"fixed_price": 50.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "prioritize applied_on over SALE date",
"expected_price": 60.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=2),
"fixed_price": 60.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"product_id": self.room_type.product_id.id,
"product_tmpl_id": self.product_template.id,
"applied_on": "1_product",
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=1),
"fixed_price": 50.0,
"pms_property_ids": properties,
},
],
},
{
"name": "prioritize SALE date over CONSUMPTION date",
"expected_price": 120.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=10),
"fixed_price": 120.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"fixed_price": 50.0,
"pms_property_ids": properties,
},
],
},
{
"name": "prioritize CONSUMPTION date over min. num. properties",
"expected_price": 50.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"fixed_price": 120.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"pms_property_ids": [self.property1.id, self.property2.id],
"fixed_price": 50.0,
},
],
},
{
"name": "prioritize min. num. properties over item id",
"expected_price": 50.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"fixed_price": 120.0,
"pms_property_ids": properties,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"pms_property_ids": [self.property1.id, self.property2.id],
"fixed_price": 50.0,
},
],
},
{
"name": "tie => order by item id",
"expected_price": 50.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=3),
"pms_property_ids": [self.property1.id, self.property2.id],
"fixed_price": 120.0,
},
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now()
+ datetime.timedelta(days=3),
"date_start": datetime.datetime.now(),
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=3),
"pms_property_ids": [self.property1.id, self.property2.id],
"fixed_price": 50.0,
},
],
},
{
"name": "no SALE DATE START",
"expected_price": 40.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_end": datetime.datetime.now()
+ datetime.timedelta(days=1),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "no SALE DATE END",
"expected_price": 40.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start": datetime.datetime.now(),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "no consumption DATE START",
"expected_price": 40.0 + self.room_type.list_price * 2,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_end_consumption": datetime.datetime.now(),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "no consumption DATE END",
"expected_price": 40.0 * 3,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
{
"name": "only applied consumption in one night",
"expected_price": 40.0 + self.room_type.list_price * 2,
"items": [
{
"pricelist_id": self.pricelist.id,
"applied_on": "0_product_variant",
"product_id": self.room_type.product_id.id,
"date_start_consumption": datetime.datetime.now(),
"date_end_consumption": datetime.datetime.now(),
"fixed_price": 40.0,
"pms_property_ids": properties,
},
],
},
]
for tc in test_cases:
with self.subTest(k=tc):
# ARRANGE
items = []
for item in tc["items"]:
item = self.env["product.pricelist.item"].create(item)
items.append(item.id)
# ACT
reservation = self.env["pms.reservation"].create(
{
"partner_id": self.partner1.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now()
+ datetime.timedelta(days=3),
"preferred_room_id": self.room.id,
"pms_property_id": self.property1.id,
"pricelist_id": self.pricelist.id,
}
)
reservation_price = reservation.price_subtotal
self.env["pms.reservation"].browse(reservation.id).unlink()
self.env["product.pricelist.item"].browse(items).unlink()
# ASSERT
self.assertEqual(tc["expected_price"], reservation_price, tc["name"])

View File

@@ -0,0 +1,55 @@
from odoo.exceptions import ValidationError
from .common import TestPms
class TestPmsPricelistSettings(TestPms):
def test_advanced_pricelist_exists(self):
"""
Check if value of Pricelist parameter in sales settings is Advanced Price Rules.
Find the value of Pricelist parameter
with the key product.product_pricelist_setting and check if is equal to "advanced".
"""
# ARRANGE
key = "product.product_pricelist_setting"
value = "advanced"
# ACT
found_value = self.env["ir.config_parameter"].sudo().get_param(key)
# ASSERT
self.assertEqual(
found_value, value, "Parameter of Pricelist in setting is not 'advanced'"
)
def test_product_pricelist_setting_not_modified(self):
"""
Check that Pricelist parameter 'advanced' cannot be modified.
Set the value of product.product_pricelist_setting to 'basic'
but is not possible because this only can be 'advanced'.
"""
# ARRANGE
key = "product.product_pricelist_setting"
value = "basic"
# ACT & ASSERT
with self.assertRaises(
ValidationError, msg="The Pricelist parameter 'advanced' was modified."
):
self.env["ir.config_parameter"].set_param(key, value)
def test_product_pricelist_setting_not_unlink(self):
"""
Check that Pricelist parameter 'advanced' cannot be unlink.
Try to unlink the parameter product_pricelist with value 'advanced'
but this should be impossible.
"""
# ARRANGE
key = "product.product_pricelist_setting"
value = "advanced"
# ACT & ASSERT
with self.assertRaises(ValidationError), self.cr.savepoint():
self.env["ir.config_parameter"].search(
[("key", "=", key), ("value", "=", value)]
).unlink()

View File

@@ -153,6 +153,84 @@ class TestPmsReservations(common.SavepointCase):
}
)
@freeze_time("1980-11-01")
def test_reservation_dates_not_consecutive(self):
"""
Check the constrain if not consecutive dates
----------------
Create correct reservation set 3 reservation lines consecutives (nights)
"""
# ARRANGE
self.create_common_scenario()
customer = self.env.ref("base.res_partner_12")
today = fields.date.today()
tomorrow = fields.date.today() + datetime.timedelta(days=1)
three_days_later = fields.date.today() + datetime.timedelta(days=3)
# ACT & ASSERT
with self.assertRaises(
ValidationError,
msg="Error, it has been allowed to create a reservation with non-consecutive days",
):
self.env["pms.reservation"].create(
{
"room_type_id": self.room_type_double.id,
"partner_id": customer.id,
"pms_property_id": self.property.id,
"reservation_line_ids": [
(0, False, {"date": today}),
(0, False, {"date": tomorrow}),
(0, False, {"date": three_days_later}),
],
}
)
@freeze_time("1980-11-01")
def test_reservation_dates_compute_checkin_out(self):
"""
Check the reservation creation with specific reservation lines
anc compute checkin checkout
----------------
Create reservation with correct reservation lines and check
the checkin and checkout fields. Take into account that the
checkout of the reservation must be the day after the last night
(view checkout assertEqual)
"""
# ARRANGE
self.create_common_scenario()
customer = self.env.ref("base.res_partner_12")
today = fields.date.today()
tomorrow = fields.date.today() + datetime.timedelta(days=1)
two_days_later = fields.date.today() + datetime.timedelta(days=2)
# ACT
reservation = self.env["pms.reservation"].create(
{
"room_type_id": self.room_type_double.id,
"partner_id": customer.id,
"pms_property_id": self.property.id,
"reservation_line_ids": [
(0, False, {"date": today}),
(0, False, {"date": tomorrow}),
(0, False, {"date": two_days_later}),
],
}
)
# ASSERT
self.assertEqual(
reservation.checkin,
today,
"The calculated checkin of the reservation does \
not correspond to the first day indicated in the dates",
)
self.assertEqual(
reservation.checkout,
two_days_later + datetime.timedelta(days=1),
"The calculated checkout of the reservation does \
not correspond to the last day indicated in the dates",
)
@freeze_time("1980-11-01")
def test_create_reservation_start_date(self):
# TEST CASE

View File

@@ -1,12 +0,0 @@
from odoo.tests.common import SavepointCase
class TestPmsFolioInvoice(SavepointCase):
def setUp(self):
super(TestPmsFolioInvoice, self).setUp()
def test_price_reservation(self):
"""Test create a reservation, and check price and discounts"""
def test_general_discount_reservation(self):
"""Test a discount in reservation head, and check lines"""

View File

@@ -3,7 +3,7 @@
{
"name": "PMS Spanish Adaptation",
"version": "14.0.1.0.1",
"version": "14.0.1.0.2",
"author": "Commit [Sun], Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": True,
@@ -30,6 +30,8 @@
"data/queue_job_function_data.xml",
"security/ir.model.access.csv",
"views/pms_property_views.xml",
"views/res_partner_views.xml",
"views/pms_log_institution_traveller_report_views.xml",
"wizards/traveller_report.xml",
],
"installable": True,

View File

@@ -3,7 +3,7 @@
<data noupdate="0">
<record model="ir.cron" id="autosend_traveller_report">
<field name="name">Automatic Send Traveller Report</field>
<field name="active" eval="True" />
<field name="active" eval="False" />
<field name="interval_number">1</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_type">days</field>
@@ -13,9 +13,9 @@
<field name="model_id" ref="model_traveller_report_wizard" />
<field
name="nextcall"
eval="datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d 03:00:00')"
eval="datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d 16:57:00')"
/>
<field name="code">model.send_file_gc_async()</field>
<field name="code">model.send_file_institution_async()</field>
</record>
</data>
</odoo>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="1">
<record id="channel_gc_file_send" model="queue.job.channel">
<field name="name">gc_file_send</field>
<record id="channel_institution_file_send" model="queue.job.channel">
<field name="name">institution_file_send</field>
<field name="parent_id" ref="queue_job.channel_root" />
</record>
</data>

View File

@@ -6,7 +6,7 @@
>
<field name="model_id" ref="pms_l10n_es.model_traveller_report_wizard" />
<field name="method">send_file_async</field>
<field name="channel_id" ref="pms_l10n_es.channel_gc_file_send" />
<field name="channel_id" ref="pms_l10n_es.channel_institution_file_send" />
<field name="retry_pattern" eval="{1: 10, 5: 30, 10: 60, 15: 300}" />
</record>
</odoo>

View File

@@ -19,25 +19,117 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.0.1\n"
#. module: pms_l10n_es
#: model:ir.actions.server,name:pms_l10n_es.autosend_traveller_report_ir_actions_server
#: model:ir.cron,cron_name:pms_l10n_es.autosend_traveller_report
#: model:ir.cron,name:pms_l10n_es.autosend_traveller_report
msgid "Automatic Send Traveller Report"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__birthdate_date
msgid "Birthdate"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/models/pms_property.py:0
#, python-format
msgid "Connection Established!"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/models/pms_property.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Connection could not be established"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/models/pms_property.py:0
#, python-format
msgid "Connection established succesfully"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model,name:pms_l10n_es.model_res_partner
msgid "Contact"
msgstr "Contacto"
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could last files sent"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get last file sent"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get next file name to send."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get property_info."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get token login."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not upload file."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__create_uid
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__create_uid
msgid "Created by"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__create_date
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__create_date
msgid "Created on"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__document_type__d
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__res_partner__document_type__d
msgid "DNI"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__date
msgid "Date and time"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__file_incidencies_from_institution
msgid "Detailed file"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__txt_incidencies_from_institution
msgid "Detailed message"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_res_partner__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__display_name
msgid "Display Name"
msgstr ""
@@ -90,6 +182,22 @@ msgstr ""
msgid "Driving License"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__error_sending_data
msgid "Error sending data"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Errors (check the pdf file)."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_property__institution__ertxaintxa
msgid "Ertxaintxa (soon)"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__document_type__x
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__res_partner__document_type__x
@@ -106,6 +214,21 @@ msgstr ""
msgid "Female"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_tree
msgid "File"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__txt_binary
msgid "File Download"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__txt_message
msgid "File Preview"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__firstname
#, fuzzy
@@ -117,21 +240,67 @@ msgstr "Nombre"
msgid "Gender"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_property__institution__guardia_civil
msgid "Guardia Civil"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.view_property_form_pms_l10n_es
msgid "Guest information sending settings"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_res_partner__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__id
msgid "ID"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,help:pms_l10n_es.field_pms_property__institution_property_id
msgid "Id provided by institution to send data from property."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__document_type__i
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__res_partner__document_type__i
msgid "Identification Document"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__institution
msgid "Institution"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__institution_password
msgid "Institution password"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__institution_property_id
msgid "Institution property id"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,help:pms_l10n_es.field_pms_property__institution
msgid "Institution to send daily guest data."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__institution_user
msgid "Institution user"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_res_partner____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard____last_update
msgid "Last Modified on"
msgstr ""
@@ -141,11 +310,48 @@ msgstr ""
msgid "Last Name"
msgstr "Apellido"
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__write_uid
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__write_uid
msgid "Last Updated by"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__write_date
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__write_date
msgid "Last Updated on"
msgstr ""
#. module: pms_l10n_es
#: model:ir.ui.menu,name:pms_l10n_es.menu_open_pms_log_institution_traveller_report_form_tree
msgid "Log institution traveller report"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_form
msgid "Log institution traveller report detail"
msgstr ""
#. module: pms_l10n_es
#: model:ir.actions.act_window,name:pms_l10n_es.open_pms_log_institution_traveller_report_form_tree
msgid "Log of sending files to institutions"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__gender__male
msgid "Male"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_property__institution__mossos
msgid "Mossos_d'esquadra (soon)"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__nationality_id
msgid "Nationality ID"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__gender__other
msgid "Other"
@@ -162,6 +368,36 @@ msgstr ""
msgid "Passport"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,help:pms_l10n_es.field_pms_property__institution_password
msgid "Password provided by institution to send the data."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_property__institution__policia_nacional
msgid "Policía Nacional"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.traveller_report_wizard
msgid "Preview file"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model,name:pms_l10n_es.model_pms_property
msgid "Property"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_tree
msgid "Property Ubications"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model,name:pms_l10n_es.model_pms_log_institution_traveller_report
msgid "Report of daily sending files of travellers to institutions."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__lastname2
#, fuzzy
@@ -176,12 +412,71 @@ msgstr "Apellido"
msgid "Select a valid document type"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.traveller_report_wizard
msgid "Send file"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Sent succesfully!"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_tree
msgid "Size"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__document_type__n
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__res_partner__document_type__n
msgid "Spanish residence permit"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Successful file sending"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.view_property_form_pms_l10n_es
msgid "Test user/password"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "The guest information sending settings is not complete."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "The guest information sending settings is not property updated."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: model:ir.actions.act_window,name:pms_l10n_es.action_traveller_report
#: model:ir.model,name:pms_l10n_es.model_traveller_report_wizard
#: model:ir.ui.menu,name:pms_l10n_es.menu_traveller_report
#, python-format
msgid "Traveller Report"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__txt_filename
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__txt_filename
msgid "Txt Filename"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,help:pms_l10n_es.field_pms_property__institution_user
msgid "User provided by institution to send the data."
msgstr ""
#, fuzzy
#~ msgid "Config Settings"
#~ msgstr "res.config.settings"

View File

@@ -26,12 +26,7 @@ msgid "Birthdate"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Check the GC configuration to send the guests info"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/models/pms_property.py:0
#: code:addons/pms_l10n_es/models/pms_property.py:0
#, python-format
msgid "Connection Established!"
@@ -39,6 +34,7 @@ msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/models/pms_property.py:0
#: code:addons/pms_l10n_es/models/pms_property.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Connection could not be established"
@@ -46,6 +42,7 @@ msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/models/pms_property.py:0
#: code:addons/pms_l10n_es/models/pms_property.py:0
#, python-format
msgid "Connection established succesfully"
msgstr ""
@@ -56,11 +53,50 @@ msgid "Contact"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could last files sent"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get last file sent"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get next file name to send."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get property_info."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not get token login."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Could not upload file."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__create_uid
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__create_uid
msgid "Created by"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__create_date
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__create_date
msgid "Created on"
msgstr ""
@@ -71,8 +107,24 @@ msgstr ""
msgid "DNI"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__date
msgid "Date and time"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__file_incidencies_from_institution
msgid "Detailed file"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__txt_incidencies_from_institution
msgid "Detailed message"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_res_partner__display_name
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__display_name
@@ -128,6 +180,17 @@ msgstr ""
msgid "Driving License"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__error_sending_data
msgid "Error sending data"
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Errors (check the pdf file)."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_property__institution__ertxaintxa
msgid "Ertxaintxa (soon)"
@@ -149,6 +212,11 @@ msgstr ""
msgid "Female"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_tree
msgid "File"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__txt_binary
msgid "File Download"
@@ -181,6 +249,7 @@ msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_res_partner__id
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__id
@@ -225,6 +294,7 @@ msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_property____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_res_partner____last_update
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard____last_update
@@ -237,15 +307,32 @@ msgid "Last Name"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__write_uid
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__write_uid
msgid "Last Updated by"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__write_date
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__write_date
msgid "Last Updated on"
msgstr ""
#. module: pms_l10n_es
#: model:ir.ui.menu,name:pms_l10n_es.menu_open_pms_log_institution_traveller_report_form_tree
msgid "Log institution traveller report"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_form
msgid "Log institution traveller report detail"
msgstr ""
#. module: pms_l10n_es
#: model:ir.actions.act_window,name:pms_l10n_es.open_pms_log_institution_traveller_report_form_tree
msgid "Log of sending files to institutions"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__gender__male
msgid "Male"
@@ -284,7 +371,7 @@ msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_property__institution__policia_nacional
msgid "Policía Nacional (soon)"
msgid "Policía Nacional"
msgstr ""
#. module: pms_l10n_es
@@ -297,6 +384,16 @@ msgstr ""
msgid "Property"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_tree
msgid "Property Ubications"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model,name:pms_l10n_es.model_pms_log_institution_traveller_report
msgid "Report of daily sending files of travellers to institutions."
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_checkin_partner__lastname2
msgid "Second Last Name"
@@ -317,10 +414,16 @@ msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Sent succesfully!"
msgstr ""
#. module: pms_l10n_es
#: model_terms:ir.ui.view,arch_db:pms_l10n_es.pms_log_institution_traveller_report_view_tree
msgid "Size"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__pms_checkin_partner__document_type__n
#: model:ir.model.fields.selection,name:pms_l10n_es.selection__res_partner__document_type__n
@@ -329,6 +432,9 @@ msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "Successful file sending"
msgstr ""
@@ -350,12 +456,6 @@ msgstr ""
msgid "The guest information sending settings is not property updated."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#, python-format
msgid "There is no guest information to send."
msgstr ""
#. module: pms_l10n_es
#: code:addons/pms_l10n_es/wizards/traveller_report.py:0
#: model:ir.actions.act_window,name:pms_l10n_es.action_traveller_report
@@ -366,6 +466,7 @@ msgid "Traveller Report"
msgstr ""
#. module: pms_l10n_es
#: model:ir.model.fields,field_description:pms_l10n_es.field_pms_log_institution_traveller_report__txt_filename
#: model:ir.model.fields,field_description:pms_l10n_es.field_traveller_report_wizard__txt_filename
msgid "Txt Filename"
msgstr ""

View File

@@ -1,3 +1,4 @@
# from . import res_partner
# from . import pms_checkin_partner
from . import pms_property
from . import pms_log_institution_traveller_report

View File

@@ -0,0 +1,22 @@
from odoo import fields, models
class PmsLogInstitutionTravellerReport(models.Model):
_name = "pms.log.institution.traveller.report"
_description = "Report of daily sending files of travellers to institutions."
date = fields.Datetime(
string="Date and time",
default=fields.Datetime.now,
)
error_sending_data = fields.Boolean(
string="Error sending data",
required=True,
)
txt_incidencies_from_institution = fields.Text(
string="Detailed message",
)
file_incidencies_from_institution = fields.Binary(
string="Detailed file",
)
txt_filename = fields.Text()

View File

@@ -14,7 +14,7 @@ class PmsProperty(models.Model):
institution = fields.Selection(
[
("guardia_civil", "Guardia Civil"),
("policia_nacional", "Policía Nacional (soon)"),
("policia_nacional", "Policía Nacional"),
("ertxaintxa", "Ertxaintxa (soon)"),
("mossos", "Mossos_d'esquadra (soon)"),
],
@@ -24,7 +24,6 @@ class PmsProperty(models.Model):
)
institution_property_id = fields.Char(
string="Institution property id",
size=10,
help="Id provided by institution to send data from property.",
)
institution_user = fields.Char(
@@ -35,30 +34,27 @@ class PmsProperty(models.Model):
help="Password provided by institution to send the data.",
)
def test_gc_connection(self):
for pms_property in self:
def test_connection(self):
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 "
"Build/MRA58N) AppleWebKit/537.36 (KHTML, like "
"Gecko) Chrome/90.0.4430.93 Mobile Safari/537.36",
}
for record in self:
if (
pms_property.institution == "guardia_civil"
and pms_property.institution_property_id
and pms_property.institution_user
and pms_property.institution_password
record.institution == "guardia_civil"
and record.institution_property_id
and record.institution_user
and record.institution_password
):
url = "https://hospederias.guardiacivil.es/"
login_route = "/hospederias/login.do"
logout_route = "/hospederias/logout.do"
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 "
"Build/MRA58N) AppleWebKit/537.36 (KHTML, like "
"Gecko) Chrome/90.0.4430.93 Mobile Safari/537.36",
}
session = requests.session()
login_payload = {
"usuario": pms_property.institution_user,
"pswd": pms_property.institution_password,
"usuario": record.institution_user,
"pswd": record.institution_password,
}
# login
response_login = session.post(
url + login_route,
@@ -66,7 +62,7 @@ class PmsProperty(models.Model):
data=login_payload,
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
)
time.sleep(1)
time.sleep(0.1)
# logout
session.get(
url + logout_route,
@@ -96,3 +92,62 @@ class PmsProperty(models.Model):
return message
else:
raise ValidationError(_("Connection could not be established"))
elif (
record.institution == "policia_nacional"
and record.institution_property_id
and record.institution_user
and record.institution_password
):
url = "https://webpol.policia.es/e-hotel"
pre_login_route = "/login"
login_route = "/execute_login"
home_route = "/inicio"
logout_route = "/execute_logout"
session = requests.session()
response_pre_login = session.post(
url + pre_login_route,
headers=headers,
verify=False,
)
soup = bs(response_pre_login.text, "html.parser")
token = soup.select("input[name='_csrf']")[0]["value"]
time.sleep(0.1)
login_payload = {
"username": record.institution_user,
"password": record.institution_password,
"_csrf": token,
}
session.post(
url + login_route,
headers=headers,
data=login_payload,
verify=False,
)
time.sleep(0.1)
response_home = session.get(
url + home_route,
headers=headers,
verify=False,
)
soup = bs(response_home.text, "html.parser")
login_correct = soup.select("#datosUsuarioBanner")
if login_correct:
session.post(
url + logout_route,
headers=headers,
verify=False,
data={"_csrf": token},
)
message = {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": _("Connection Established!"),
"message": _("Connection established succesfully"),
"sticky": False,
},
}
return message
else:
raise ValidationError(_("Connection could not be established"))

View File

@@ -1,2 +1,3 @@
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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 user_access_traveller_report_wizard user_access_traveller_report_wizard model_traveller_report_wizard pms.group_pms_user 1 1 1 1
3 user_access_traveller_report_logs user_access_traveller_report_logs model_pms_log_institution_traveller_report pms.group_pms_user 1 1 1 1

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="pms_log_institution_traveller_report_view_form">
<field name="name">pms.log.institution.traveller.report.form</field>
<field name="model">pms.log.institution.traveller.report</field>
<field name="arch" type="xml">
<form string="Log institution traveller report detail">
<sheet>
<group>
<field name="txt_filename" invisible="1" />
<field name="error_sending_data" />
<field name="date" />
<field name="txt_incidencies_from_institution" select="1" />
<field
name="file_incidencies_from_institution"
filename="txt_filename"
readonly="1"
/>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="pms_log_institution_traveller_report_view_tree">
<field name="name">pms.log.institution.traveller.report.tree</field>
<field name="model">pms.log.institution.traveller.report</field>
<field name="arch" type="xml">
<tree
string="Property Ubications"
create="false"
decoration-danger="error_sending_data"
>
<field name="date" />
<field name="error_sending_data" />
<field name="txt_incidencies_from_institution" />
<field name="txt_filename" string="File" />
<field
name="file_incidencies_from_institution"
filename="txt_filename"
readonly="1"
string="Size"
/>
</tree>
</field>
</record>
<record
model="ir.actions.act_window"
id="open_pms_log_institution_traveller_report_form_tree"
>
<field name="name">Log of sending files to institutions</field>
<field name="res_model">pms.log.institution.traveller.report</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem
name="Log institution traveller report"
id="menu_open_pms_log_institution_traveller_report_form_tree"
action="open_pms_log_institution_traveller_report_form_tree"
parent="pms.menu_reservations"
sequence="28"
/>
</odoo>

View File

@@ -27,7 +27,7 @@
<field name="institution_password" password="True" />
</group>
<button
name="test_gc_connection"
name="test_connection"
class="btn btn-primary btn-sm"
type="object"
string="Test user/password"

View File

@@ -1,13 +1,16 @@
import base64
import datetime
import io
import json
import time
from datetime import date
import PyPDF2
import requests
from bs4 import BeautifulSoup as bs
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.exceptions import MissingError, ValidationError
from odoo.modules.module import get_module_resource
@@ -19,24 +22,27 @@ class TravellerReport(models.TransientModel):
txt_binary = fields.Binary(string="File Download")
txt_message = fields.Char(string="File Preview")
def generate_file(self):
def generate_file_from_user_action(self):
# get the active property
pms_property = self.env["pms.property"].search(
[("id", "=", self.env.user.get_active_property_ids()[0])]
)
# check if there's institution settings properly established
if (
not pms_property
or not pms_property.institution_property_id
or not pms_property.institution_user
or not pms_property.institution_password
):
raise ValidationError(
_("The guest information sending settings is not property updated.")
)
# build content
content = self.generate_checkin_list(pms_property.id)
if not pms_property.institution_property_id:
raise ValidationError(
_("The guest information sending settings is not property updated.")
)
elif not content:
raise ValidationError(_("There is no guest information to send."))
else:
# file creation
if content:
txt_binary = self.env["traveller.report.wizard"].create(
{
"txt_filename": pms_property.institution_property_id + ".999",
@@ -62,48 +68,32 @@ class TravellerReport(models.TransientModel):
[
("state", "=", "onboard"),
("arrival", ">=", str(date.today()) + " 0:00:00"),
("arrival", "<=", str(date.today()) + " 23:59:59"),
]
):
# get the active property
pms_property = self.env["pms.property"].search([("id", "=", property_id)])
# check if the GC configuration info is properly set
if not (
pms_property.name
and pms_property.institution_property_id
and pms_property.institution_user
and pms_property.institution_password
):
raise ValidationError(
_("Check the GC configuration to send the guests info")
)
else:
# get checkin partners info to send
lines = self.env["pms.checkin.partner"].search(
[
("state", "=", "onboard"),
("arrival", ">=", str(date.today()) + " 0:00:00"),
("arrival", "<=", str(date.today()) + " 23:59:59"),
]
)
# build the property info record
# 1 | property id | property name | date | nº of checkin partners
content = (
"1|"
+ pms_property.institution_property_id.upper()
+ "|"
+ pms_property.name.upper()
+ "|"
+ datetime.datetime.now().strftime("%Y%m%d|%H%M")
+ "|"
+ str(len(lines))
+ "\n"
)
# get checkin partners info to send
lines = self.env["pms.checkin.partner"].search(
[
("state", "=", "onboard"),
("arrival", ">=", str(date.today()) + " 0:00:00"),
("arrival", "<=", str(date.today()) + " 23:59:59"),
]
)
# build the property info record
# 1 | property id | property name | date | nº of checkin partners
content = (
"1|"
+ pms_property.institution_property_id.upper()
+ "|"
+ pms_property.name.upper()
+ "|"
+ datetime.datetime.now().strftime("%Y%m%d|%H%M")
+ "|"
+ str(len(lines))
+ "\n"
)
# build each checkin partner line's record
# 2|DNI nº|Doc.number|doc.type|exp.date|lastname|lastname2|name|...
# ...gender|birthdate|nation.|checkin
@@ -131,32 +121,13 @@ class TravellerReport(models.TransientModel):
return content
def send_file_gc(self, pms_property=False):
def send_file_gc(self, file_content, called_from_user, pms_property):
url = "https://hospederias.guardiacivil.es/"
login_route = "/hospederias/login.do"
upload_file_route = "/hospederias/cargaFichero.do"
logout_route = "/hospederias/logout.do"
called_from_user = False
if not pms_property:
called_from_user = True
# get the active property
pms_property = self.env["pms.property"].search(
[("id", "=", self.env.user.get_active_property_ids()[0])]
)
if not (
pms_property
and pms_property.institution_property_id
and pms_property.institution_user
and pms_property.institution_password
):
raise ValidationError(
_("The guest information sending settings is not complete.")
)
content = self.generate_checkin_list(pms_property.id)
if content:
if file_content:
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 "
"Build/MRA58N) AppleWebKit/537.36 (KHTML, like "
@@ -167,14 +138,13 @@ class TravellerReport(models.TransientModel):
"usuario": pms_property.institution_user,
"pswd": pms_property.institution_password,
}
# login
response_login = session.post(
url + login_route,
headers=headers,
data=login_payload,
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
)
time.sleep(0.1)
# check if authentication was successful / unsuccessful or the
# resource cannot be accessed
@@ -189,8 +159,7 @@ class TravellerReport(models.TransientModel):
raise ValidationError(_("Connection could not be established"))
# build the file to send
files = {"fichero": (pms_property.institution_user + ".999", content)}
time.sleep(1)
files = {"fichero": (pms_property.institution_user + ".999", file_content)}
# send file
response_file_sent = session.post(
@@ -199,8 +168,8 @@ class TravellerReport(models.TransientModel):
files=files,
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
)
time.sleep(0.1)
time.sleep(1)
# logout & close connection
session.get(
url + logout_route,
@@ -217,8 +186,20 @@ class TravellerReport(models.TransientModel):
for e in errors:
msg += "Error en línea " + e.select("a")[0].text + ": "
msg += e.select("a")[2].text + "\n"
self.env["pms.log.institution.traveller.report"].create(
{
"error_sending_data": True,
"txt_incidencies_from_institution": msg,
}
)
raise ValidationError(msg)
else:
self.env["pms.log.institution.traveller.report"].create(
{
"error_sending_data": False,
"txt_message": _("Successful file sending"),
}
)
if called_from_user:
message = {
"type": "ir.actions.client",
@@ -231,7 +212,261 @@ class TravellerReport(models.TransientModel):
}
return message
def send_file_pn(self, file_content, called_from_user, pms_property):
base_url = "https://webpol.policia.es"
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 "
"Build/MRA58N) AppleWebKit/537.36 (KHTML, like "
"Gecko) Chrome/90.0.4430.93 Mobile Safari/537.36",
}
pre_login_route = "/e-hotel/login"
login_route = "/e-hotel/execute_login"
next_file_name_route = "/e-hotel/hospederia/ficheros/vista/envioFicheros"
upload_file_route = "/e-hotel/hospederia/ficheros/subirFichero"
pre_get_list_files_sent_route = (
"/e-hotel/hospederia/vista/seguimientoHospederia"
)
files_sent_list_route = "/e-hotel/hospederia/listar/ficherosHospederia"
last_file_errors_route = "/e-hotel/hospederia/report/erroresFicheroHospederia"
logout_route = "/e-hotel/execute_logout"
session = requests.session()
# retrieve token
response_pre_login = session.post(
base_url + pre_login_route,
headers=headers,
verify=False,
)
time.sleep(0.1)
token = bs(response_pre_login.text, "html.parser").select(
"input[name='_csrf']"
)[0]["value"]
if not token:
raise MissingError(_("Could not get token login."))
# do login
session.post(
base_url + login_route,
headers=headers,
data={
"username": pms_property.institution_user,
"password": pms_property.institution_password,
"_csrf": token,
},
verify=False,
)
time.sleep(0.1)
headers["x-csrf-token"] = token
# retrieve file name to send
response_name_file_route = session.post(
base_url + next_file_name_route,
headers=headers,
verify=False,
)
time.sleep(0.1)
soup = bs(response_name_file_route.text, "html.parser")
file_name = soup.select("#msjNombreFichero > b > u")[0].text
if not file_name:
raise MissingError(_("Could not get next file name to send."))
# send file
upload_result = session.post(
base_url + upload_file_route,
headers=headers,
verify=False,
data={
"jsonHiddenComunes": "",
"ficheroJ": "",
"_csrf": token,
},
files={"fichero": (file_name, file_content)},
)
if upload_result.status_code != 200:
raise MissingError(_("Could not upload file."))
time.sleep(0.1)
# retrieve property data
response_pre_files_sent_list_route = session.post(
base_url + pre_get_list_files_sent_route,
headers=headers,
verify=False,
data={
"jsonHiddenComunes": "",
"ficheroJ": "",
"_csrf": token,
},
)
if response_pre_files_sent_list_route.status_code != 200:
raise MissingError(_("Could not get property_info."))
time.sleep(0.1)
soup = bs(response_pre_files_sent_list_route.text, "html.parser")
property_specific_data = {
"codigoHospederia": soup.select("#codigoHospederia")[0]["value"],
"nombreHospederia": soup.select("#nombreHospederia")[0]["value"],
"direccionCompleta": soup.select("#direccionCompleta")[0]["value"],
"telefono": soup.select("#telefono")[0]["value"],
"tieneAgrup": soup.select("#tieneAgrup")[0]["value"],
}
common_file_data = {
"jsonHiddenComunes": "",
"fechaDesde": (
datetime.date.today() + datetime.timedelta(days=-1)
).strftime("%d/%m/%Y"),
"fechaHasta": datetime.date.today().strftime("%d/%m/%Y"),
"_csrf": token,
"_search": False,
"nd": str(int(time.time() * 1000)),
"rows": 10,
"page": 1,
"sidx": "",
"sord": "dat_fich.fecha_alta desc",
}
# retrieve list of sent files
file_data = dict()
for _attempt in range(1, 10):
response_files_sent_list_route = session.post(
base_url + files_sent_list_route,
headers=headers,
verify=False,
data={
**property_specific_data,
**common_file_data,
"primeraConsulta": True,
},
)
file_list = json.loads(
str(bs(response_files_sent_list_route.text, "html.parser"))
)["rows"]
file_data = list(
filter(lambda x: x["nombreFichero"] == file_name, file_list)
)
if file_data:
file_data = file_data[0]
break
else:
time.sleep(1)
if not file_data:
raise ValidationError(_("Could not get last file sent"))
else:
response_last_file_errors_route = session.post(
base_url + last_file_errors_route,
headers=headers,
verify=False,
data={
"idFichero": file_data["idFichero"],
"numErroresHuespedes": file_data["numErroresHuespedes"],
"numAvisosHuespedes": file_data["numAvisosHuespedes"],
"nombreFichero": file_data["nombreFichero"],
"fechaAlta": file_data["fechaAlta"],
"envioDesdeAgrupacion": file_data["envioDesdeAgrupacion"],
"envioDesdeAgrupacionImg": "",
"estadoProceso": file_data["estadoProceso"],
"numTotalErrores": file_data["numTotalErrores"],
"numTotalAvisos": file_data["numTotalAvisos"],
"separadorTabla": "",
"numHuespedesInformados": file_data["numHuespedesInformados"],
"numHuespedes": file_data["numHuespedes"],
"primeraConsulta": False,
**property_specific_data,
**common_file_data,
"datosServidor": False,
},
)
if response_last_file_errors_route.status_code != 200:
raise ValidationError(_("Could last files sent"))
time.sleep(0.1)
soup = bs(response_last_file_errors_route.text, "html.parser")
# get file sent pdf report
response_last_file_errors_route = session.get(
base_url + soup.select("#iframePdf")[0].attrs["src"],
headers=headers,
verify=False,
)
if response_last_file_errors_route.status_code != 200:
raise ValidationError(_("Could last files sent"))
time.sleep(0.1)
pdfReader = PyPDF2.PdfFileReader(
io.BytesIO(response_last_file_errors_route.content)
)
if (
pdfReader.getPage(0).extractText().find("ERRORES Y AVISOS HUESPEDES")
== -1
):
message = _("Successful file sending")
error = False
else:
message = _("Errors (check the pdf file).")
error = True
self.env["pms.log.institution.traveller.report"].create(
{
"error_sending_data": error,
"txt_incidencies_from_institution": message,
"file_incidencies_from_institution": base64.b64encode(
response_last_file_errors_route.content
),
"txt_filename": file_name + ".pdf",
}
)
# do logout
session.post(
base_url + logout_route,
headers=headers,
verify=False,
data={"_csrf": token},
)
session.close()
if called_from_user:
message = {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": _("Sent succesfully!"),
"message": _("Successful file sending"),
"sticky": False,
},
}
return message
def send_file_institution(self, pms_property=False):
called_from_user = False
if not pms_property:
called_from_user = True
pms_property = self.env["pms.property"].search(
[("id", "=", self.env.user.get_active_property_ids()[0])]
)
if (
not pms_property
or not pms_property.institution_property_id
or not pms_property.institution_user
or not pms_property.institution_password
):
raise ValidationError(
_("The guest information sending settings is not complete.")
)
file_content = self.generate_checkin_list(pms_property.id)
if pms_property.institution == "policia_nacional":
return self.send_file_pn(file_content, called_from_user, pms_property)
elif pms_property.institution == "guardia_civil":
return self.send_file_gc(file_content, called_from_user, pms_property)
@api.model
def send_file_gc_async(self):
def send_file_institution_async(self):
for prop in self.env["pms.property"].search([]):
self.with_delay().send_file_gc(prop)
self.with_delay().send_file_institution(prop)

View File

@@ -24,7 +24,7 @@
<div class="row ">
<div class="col-3">
<button
name="generate_file"
name="generate_file_from_user_action"
class="btn btn-primary btn-sm"
type="object"
string="Preview file"
@@ -32,7 +32,7 @@
</div>
<div class="col-3">
<button
name="send_file_gc"
name="send_file_institution"
class="btn btn-primary btn-sm"
type="object"
string="Send file"
@@ -55,8 +55,8 @@
<menuitem
id="menu_traveller_report"
name="Traveller Report"
sequence="30"
parent="pms.pms_configuration_menu"
sequence="27"
parent="pms.menu_reservations"
action="action_traveller_report"
/>
</odoo>