Skip to content

Commit

Permalink
[ADD]pms: feature touristic taxes
Browse files Browse the repository at this point in the history
  • Loading branch information
DarioLodeiros committed Dec 15, 2024
1 parent f54b8a6 commit 8921d7e
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 3 deletions.
6 changes: 6 additions & 0 deletions pms/models/pms_checkin_partner.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,12 @@ def create(self, vals):
_("Is not possible to create the proposed check-in in this reservation")
)

def write(self, vals):
res = super().write(vals)
for record in self:
record.reservation_id._update_tourist_tax_service()
return res

def unlink(self):
reservations = self.mapped("reservation_id")
res = super().unlink()
Expand Down
125 changes: 122 additions & 3 deletions pms/models/pms_reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -924,9 +925,11 @@ def _compute_allowed_room_ids(self):
room_type_id=False, # Allows to choose any available room
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
class_id=reservation.room_type_id.class_id.id
if reservation.room_type_id
else False,
class_id=(
reservation.room_type_id.class_id.id
if reservation.room_type_id
else False
),
real_avail=True,
)
reservation.allowed_room_ids = pms_property.free_room_ids
Expand Down Expand Up @@ -2152,6 +2155,7 @@ def create(self, vals):
record.action_cancel()

record._check_services(vals)
record._add_tourist_tax_service()
return record

def write(self, vals):
Expand Down Expand Up @@ -2221,6 +2225,8 @@ def write(self, vals):
# that not take access to possible extra beds service in vals
if "adults" in vals:
self._check_capacity()
if "checkin" in vals or "checkout" in vals or "reservation_line_ids" in vals:
self._update_tourist_tax_service()
return res

def _get_folio_vals(self, reservation_vals):
Expand Down Expand Up @@ -2575,3 +2581,116 @@ def preview_reservation(self):
"target": "self",
"url": self.get_portal_url(),
}

def _add_tourist_tax_service(self):
for record in self:
tourist_tax_products = self.env["product.product"].search(
[("is_tourist_tax", "=", True)]
)
for product in tourist_tax_products:
if product.touristic_calculation == "occupancy":
checkins = record.checkin_partner_ids.filtered_domain(

Check warning on line 2592 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2592

Added line #L2592 was not covered by tests
safe_eval(product.occupancy_domain)
)
quantity = len(checkins)

Check warning on line 2595 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2595

Added line #L2595 was not covered by tests
elif product.touristic_calculation == "nights":
if not record.filtered_domain(safe_eval(product.nights_domain)):
continue
quantity = (record.checkout - record.checkin).days

Check warning on line 2599 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2598-L2599

Added lines #L2598 - L2599 were not covered by tests
elif product.touristic_calculation == "occupancyandnights":
checkins = record.checkin_partner_ids.filtered_domain(

Check warning on line 2601 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2601

Added line #L2601 was not covered by tests
safe_eval(product.occupancy_domain)
)
if not record.filtered_domain(safe_eval(product.nights_domain)):
continue
quantity = len(checkins) * (record.checkout - record.checkin).days

Check warning on line 2606 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2605-L2606

Added lines #L2605 - L2606 were not covered by tests
else:
quantity = 1

Check warning on line 2608 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2608

Added line #L2608 was not covered by tests

if quantity == 0:
continue

Check warning on line 2611 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2611

Added line #L2611 was not covered by tests

product = product.with_context(

Check warning on line 2613 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2613

Added line #L2613 was not covered by tests
lang=record.partner_id.lang,
partner=record.partner_id.id,
quantity=quantity,
date=record.date_order,
consumption_date=record.checkin,
pricelist=record.pricelist_id.id,
uom=product.uom_id.id,
property=record.pms_property_id.id,
)
price = self.env["account.tax"]._fix_tax_included_price_company(

Check warning on line 2623 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2623

Added line #L2623 was not covered by tests
product.price,
product.taxes_id,
record.tax_ids,
record.pms_property_id.company_id,
)

self.env["pms.service"].create(

Check warning on line 2630 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2630

Added line #L2630 was not covered by tests
{
"reservation_id": record.id,
"product_id": product.id,
"quantity": quantity,
"price_unit": price,
}
)

def _update_tourist_tax_service(self):
for record in self:
services = self.env["pms.service"].search(
[
("reservation_id", "=", record.id),
("product_id.is_tourist_tax", "=", True),
]
)
for service in services:
product = service.product_id

Check warning on line 2648 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2648

Added line #L2648 was not covered by tests
if product.touristic_calculation == "occupancy":
checkins = record.checkin_partner_ids.filtered_domain(

Check warning on line 2650 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2650

Added line #L2650 was not covered by tests
safe_eval(product.occupancy_domain)
)
quantity = len(checkins)

Check warning on line 2653 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2653

Added line #L2653 was not covered by tests
elif product.touristic_calculation == "nights":
if not record.filtered_domain(safe_eval(product.nights_domain)):
service.unlink()
continue
quantity = (record.checkout - record.checkin).days

Check warning on line 2658 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2656-L2658

Added lines #L2656 - L2658 were not covered by tests
elif product.touristic_calculation == "occupancyandnights":
checkins = record.checkin_partner_ids.filtered_domain(

Check warning on line 2660 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2660

Added line #L2660 was not covered by tests
safe_eval(product.occupancy_domain)
)
if not record.filtered_domain(safe_eval(product.nights_domain)):
service.unlink()
continue
quantity = len(checkins) * (record.checkout - record.checkin).days

Check warning on line 2666 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2664-L2666

Added lines #L2664 - L2666 were not covered by tests
else:
quantity = 1

Check warning on line 2668 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2668

Added line #L2668 was not covered by tests

if quantity == 0:
service.unlink()
continue

Check warning on line 2672 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2671-L2672

Added lines #L2671 - L2672 were not covered by tests

product = product.with_context(

Check warning on line 2674 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2674

Added line #L2674 was not covered by tests
lang=record.partner_id.lang,
partner=record.partner_id.id,
quantity=quantity,
date=record.date_order,
consumption_date=record.checkin,
pricelist=record.pricelist_id.id,
uom=product.uom_id.id,
property=record.pms_property_id.id,
)
price = self.env["account.tax"]._fix_tax_included_price_company(

Check warning on line 2684 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2684

Added line #L2684 was not covered by tests
product.price,
product.taxes_id,
record.tax_ids,
record.pms_property_id.company_id,
)

service.write(

Check warning on line 2691 in pms/models/pms_reservation.py

View check run for this annotation

Codecov / codecov/patch

pms/models/pms_reservation.py#L2691

Added line #L2691 was not covered by tests
{
"quantity": quantity,
"price_unit": price,
}
)
25 changes: 25 additions & 0 deletions pms/models/product_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,31 @@ class ProductTemplate(models.Model):
help="Indicates if that product is available in PMS",
default=True,
)
is_tourist_tax = fields.Boolean(
string="Is tourist tax",
help="Indicates if that product is a tourist tax",
default=False,
)
touristic_calculation = fields.Selection(
string="Touristic calculation",
help="Indicates how the tourist tax is calculated",
selection=[
("occupany", "Occupancy"),
("nights", "Nights"),
("occupancyandnights", "Occupancy and Nights"),
],
default="occupancyandnights",
)
occupancy_domain = fields.Char(
string="Occupancy domain",
help="Domain to filter checkins",
default="",
)
nights_domain = fields.Char(
string="Nights domain",
help="Domain to filter reservations",
default="[('state', '!=', 'cancel')]",
)

@api.depends_context("allowed_pms_property_ids")
def _compute_daily_limit(self):
Expand Down
143 changes: 143 additions & 0 deletions pms/tests/test_tourist_taxes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import datetime

from odoo import fields
from odoo.tests.common import TransactionCase


class TestTouristTaxes(TransactionCase):
def setUp(self):
super(TestTouristTaxes, self).setUp()
self.product_tourist_tax = self.env["product.product"].create(
{
"name": "Tourist Tax",
"is_tourist_tax": True,
"touristic_calculation": "occupancy",
"occupancy_domain": "[('state', '!=', 'cancel')]",
"nights_domain": "[('state', '!=', 'cancel')]",
}
)
self.partner = self.env["res.partner"].create(
{
"name": "Test Partner",
}
)
self.room_type = self.env["pms.room.type"].create(
{
"name": "Test Room Type",
"product_id": self.env["product.product"]
.create(
{
"name": "Room Product",
"type": "service",
}
)
.id,
}
)
self.room = self.env["pms.room"].create(
{
"name": "Test Room",
"room_type_id": self.room_type.id,
}
)
self.reservation = self.env["pms.reservation"].create(
{
"partner_id": self.partner.id,
"room_type_id": self.room_type.id,
"checkin": fields.Date.today(),
"checkout": fields.Date.today() + datetime.timedelta(days=2),
"adults": 2,
}
)

def test_add_tourist_tax_service(self):
"""
Test that a tourist tax service is created when adding a reservation.
Steps:
1. Add a tourist tax service to the reservation.
2. Search for the created service.
3. Assert that the service is created and the quantity is correct.
"""
self.reservation._add_tourist_tax_service()
service = self.env["pms.service"].search(
[
("reservation_id", "=", self.reservation.id),
("product_id", "=", self.product_tourist_tax.id),
]
)
self.assertEqual(len(service), 1, "Tourist tax service should be created")
self.assertEqual(service.quantity, 2, "Tourist tax quantity should be 2")

def test_update_tourist_tax_service(self):
"""
Test that a tourist tax service is updated when modifying the reservation.
Steps:
1. Add a tourist tax service to the reservation.
2. Update the number of adults in the reservation.
3. Update the tourist tax service.
4. Search for the updated service.
5. Assert that the service is updated and the quantity is correct.
"""
self.reservation._add_tourist_tax_service()
self.reservation.adults = 3
self.reservation._update_tourist_tax_service()
service = self.env["pms.service"].search(
[
("reservation_id", "=", self.reservation.id),
("product_id", "=", self.product_tourist_tax.id),
]
)
self.assertEqual(len(service), 1, "Tourist tax service should be updated")
self.assertEqual(
service.quantity, 3, "Tourist tax quantity should be updated to 3"
)

def test_no_tourist_tax_service_when_quantity_zero(self):
"""
Test that no tourist tax service is created when the quantity is zero.
Steps:
1. Set the tourist tax calculation to 'occupancyandnights'.
2. Add a tourist tax service to the reservation.
3. Search for the created service.
4. Assert that no service is created.
"""
self.product_tourist_tax.touristic_calculation = "occupancyandnights"
self.reservation._add_tourist_tax_service()
service = self.env["pms.service"].search(
[
("reservation_id", "=", self.reservation.id),
("product_id", "=", self.product_tourist_tax.id),
]
)
self.assertEqual(
len(service),
0,
"Tourist tax service should not be created when quantity is zero",
)

def test_remove_tourist_tax_service_when_quantity_zero(self):
"""
Test that a tourist tax service is removed when the quantity becomes zero.
Steps:
1. Set the tourist tax calculation to 'occupancy'.
2. Add a tourist tax service to the reservation.
3. Update the number of adults in the reservation to zero.
4. Update the tourist tax service.
5. Search for the updated service.
6. Assert that the service is removed.
"""
self.product_tourist_tax.touristic_calculation = "occupancy"
self.reservation._add_tourist_tax_service()
self.reservation.adults = 0
self.reservation._update_tourist_tax_service()
service = self.env["pms.service"].search(
[
("reservation_id", "=", self.reservation.id),
("product_id", "=", self.product_tourist_tax.id),
]
)
self.assertEqual(
len(service),
0,
"Tourist tax service should be removed when quantity is zero",
)
19 changes: 19 additions & 0 deletions pms/views/product_template_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<group>
<field name="per_day" />
<field name="per_person" />
<field name="is_tourist_tax" />
<field
name="consumed_on"
widget="radio"
Expand All @@ -36,6 +37,24 @@
</group>
</group>
</page>
<page
string="Tourist tax configuration"
attrs="{'invisible': [('is_tourist_tax', '=', False)]}"
>
<group>
<field name="touristic_calculation" />
<field
name="occupancy_domain"
widget="domain"
options="{'model': 'pms.checkin.partner'}"
/>
<field
name="nights_domain"
widget="domain"
options="{'model': 'pms.reservation'}"
/>
</group>
</page>
</xpath>
</field>
</record>
Expand Down

0 comments on commit 8921d7e

Please sign in to comment.