diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index d518e9e57..fd6731663 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -367,6 +367,9 @@ class PmsReservation(models.Model): help="Field indicating type of cancellation. " "It can be 'late', 'intime' or 'noshow'", copy=False, + compute="_compute_cancelled_reason", + readonly=False, + store=True, selection=[("late", "Late"), ("intime", "In time"), ("noshow", "No Show")], tracking=True, ) @@ -582,6 +585,16 @@ class PmsReservation(models.Model): compute="_compute_discount", tracking=True, ) + + services_discount = fields.Float( + string="Services discount (€)", + help="Services discount", + readonly=False, + store=True, + digits=("Discount"), + compute="_compute_services_discount", + tracking=True, + ) date_order = fields.Date( string="Date Order", help="Order date of reservation", @@ -1145,10 +1158,12 @@ class PmsReservation(models.Model): for res in self: res.nights = len(res.reservation_line_ids) - @api.depends("service_ids.price_total") + @api.depends("service_ids.price_total", "services_discount") def _compute_price_services(self): for record in self: - record.price_services = sum(record.mapped("service_ids.price_total")) + record.price_services = ( + sum(record.mapped("service_ids.price_total")) - record.services_discount + ) @api.depends("price_services", "price_total") def _compute_price_room_services_set(self): @@ -1156,7 +1171,8 @@ class PmsReservation(models.Model): record.price_room_services_set = record.price_services + record.price_total @api.depends( - "reservation_line_ids.discount", "reservation_line_ids.cancel_discount" + "reservation_line_ids.discount", + "reservation_line_ids.cancel_discount", ) def _compute_discount(self): for record in self: @@ -1166,8 +1182,17 @@ class PmsReservation(models.Model): price = line.price - first_discount cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01) discount += first_discount + cancel_discount + record.discount = discount + @api.depends("service_ids.discount") + def _compute_services_discount(self): + for record in self: + services_discount = 0 + for service in record.service_ids: + services_discount += service.discount + record.services_discount = services_discount + @api.depends("reservation_line_ids.price", "discount", "tax_ids") def _compute_amount_reservation(self): """ @@ -1672,37 +1697,39 @@ class PmsReservation(models.Model): if not record.allowed_cancel: raise UserError(_("This reservation cannot be cancelled")) else: - cancel_reason = ( - "intime" - if self._context.get("no_penalty", False) - else record.compute_cancelation_reason() - ) - if self._context.get("no_penalty", False): - _logger.info("Modified Reservation - No Penalty") - record.write({"state": "cancel", "cancelled_reason": cancel_reason}) - # record._compute_cancel_discount() + record.state = "cancel" record.folio_id._compute_amount() def action_assign(self): for record in self: record.to_assign = False - def compute_cancelation_reason(self): - self.ensure_one() - pricelist = self.pricelist_id - if pricelist and pricelist.cancelation_rule_id: - tz_property = self.pms_property_id.tz - today = fields.Date.context_today(self.with_context(tz=tz_property)) - days_diff = ( - fields.Date.from_string(self.checkin) - fields.Date.from_string(today) - ).days - if days_diff < 0: - return "noshow" - elif days_diff < pricelist.cancelation_rule_id.days_intime: - return "late" - else: - return "intime" - return False + @api.depends("state") + def _compute_cancelled_reason(self): + for record in self: + # self.ensure_one() + if record.state == "cancel": + pricelist = record.pricelist_id + if record._context.get("no_penalty", False): + record.cancelled_reason = "intime" + _logger.info("Modified Reservation - No Penalty") + elif pricelist and pricelist.cancelation_rule_id: + tz_property = record.pms_property_id.tz + today = fields.Date.context_today( + record.with_context(tz=tz_property) + ) + days_diff = ( + fields.Date.from_string(record.checkin) + - fields.Date.from_string(today) + ).days + if days_diff < 0: + record.cancelled_reason = "noshow" + elif days_diff < pricelist.cancelation_rule_id.days_intime: + record.cancelled_reason = "late" + else: + record.cancelled_reason = "intime" + else: + record.cancelled_reason = False def action_reservation_checkout(self): for record in self: diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index fa9623229..b13be9666 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -386,56 +386,52 @@ class PmsReservationLine(models.Model): def _compute_cancel_discount(self): for line in self: line.cancel_discount = 0 - # TODO: Review cancel logic - # reservation = line.reservation_id - # pricelist = reservation.pricelist_id - # if reservation.state == "cancel": - # if ( - # reservation.cancelled_reason - # and pricelist - # and pricelist.cancelation_rule_id - # ): - # date_start_dt = fields.Date.from_string( - # reservation.checkin - # ) - # date_end_dt = fields.Date.from_string( - # reservation.checkout - # ) - # days = abs((date_end_dt - date_start_dt).days) - # rule = pricelist.cancelation_rule_id - # if reservation.cancelled_reason == "late": - # discount = 100 - rule.penalty_late - # if rule.apply_on_late == "first": - # days = 1 - # elif rule.apply_on_late == "days": - # days = rule.days_late - # elif reservation.cancelled_reason == "noshow": - # discount = 100 - rule.penalty_noshow - # if rule.apply_on_noshow == "first": - # days = 1 - # elif rule.apply_on_noshow == "days": - # days = rule.days_late - 1 - # elif reservation.cancelled_reason == "intime": - # discount = 100 + reservation = line.reservation_id + pricelist = reservation.pricelist_id + if reservation.state == "cancel": + if ( + reservation.cancelled_reason + and pricelist + and pricelist.cancelation_rule_id + ): + checkin = fields.Date.from_string(reservation.checkin) + checkout = fields.Date.from_string(reservation.checkout) + days = abs((checkin - checkout).days) + rule = pricelist.cancelation_rule_id + discount = 0 + if reservation.cancelled_reason == "late": + discount = 100 - rule.penalty_late + if rule.apply_on_late == "first": + days = 1 + elif rule.apply_on_late == "days": + days = rule.days_late + elif reservation.cancelled_reason == "noshow": + discount = 100 - rule.penalty_noshow + if rule.apply_on_noshow == "first": + days = 1 + elif rule.apply_on_noshow == "days": + days = rule.days_late - 1 + elif reservation.cancelled_reason == "intime": + discount = 100 - # checkin = reservation.checkin - # dates = [] - # for i in range(0, days): - # dates.append( - # ( - # fields.Date.from_string(checkin) + timedelta(days=i) - # ).strftime(DEFAULT_SERVER_DATE_FORMAT) - # ) - # reservation.reservation_line_ids.filtered( - # lambda r: r.date in dates - # ).update({"cancel_discount": discount}) - # reservation.reservation_line_ids.filtered( - # lambda r: r.date not in dates - # ).update({"cancel_discount": 100}) - # else: - # reservation.reservation_line_ids.update({"cancel_discount": 0}) - # else: - # reservation.reservation_line_ids.update({"cancel_discount": 0}) + dates = [] + for i in range(0, days): + dates.append( + fields.Date.from_string( + fields.Date.from_string(checkin) + + datetime.timedelta(days=i) + ) + ) + reservation.reservation_line_ids.filtered( + lambda r: r.date in dates + ).update({"cancel_discount": discount}) + reservation.reservation_line_ids.filtered( + lambda r: r.date not in dates + ).update({"cancel_discount": 100}) + else: + reservation.reservation_line_ids.update({"cancel_discount": 0}) + else: + reservation.reservation_line_ids.update({"cancel_discount": 0}) @api.depends("room_id", "pms_property_id", "date", "occupies_availability") def _compute_avail_id(self): @@ -471,17 +467,6 @@ class PmsReservationLine(models.Model): if duplicated: raise ValidationError(_("Duplicated reservation line date")) - @api.constrains("state") - def constrains_service_cancel(self): - for record in self: - if record.state == "cancel": - room_services = record.reservation_id.service_ids - for service in room_services: - cancel_lines = service.service_line_ids.filtered( - lambda r: r.date == record.date - ) - cancel_lines.day_qty = 0 - @api.constrains("room_id") def _check_adults(self): for record in self.filtered("room_id"): diff --git a/pms/models/pms_service.py b/pms/models/pms_service.py index fe0f1fba9..3b6fdbd5d 100644 --- a/pms/models/pms_service.py +++ b/pms/models/pms_service.py @@ -168,7 +168,7 @@ class PmsService(models.Model): ) price_total = fields.Monetary( string="Total", - help="Total price without taxes", + help="Total price with taxes", readonly=True, store=True, compute="_compute_amount_service", @@ -181,6 +181,16 @@ class PmsService(models.Model): compute="_compute_amount_service", ) + discount = fields.Float( + string="Discount (€)", + help="Discount of total price", + readonly=False, + store=True, + digits=("Discount"), + compute="_compute_discount", + tracking=True, + ) + # Compute and Search methods @api.depends("product_id") def _compute_tax_ids(self): @@ -380,7 +390,16 @@ class PmsService(models.Model): else: service.service_line_ids = False - # Default methods + @api.depends("service_line_ids.cancel_discount") + def _compute_discount(self): + for record in self: + discount = 0 + for line in record.service_line_ids: + first_discount = line.price_day_total * ((line.discount or 0.0) * 0.01) + price = line.price_day_total - first_discount + cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01) + discount += first_discount + cancel_discount + record.discount = discount def name_get(self): result = [] diff --git a/pms/models/pms_service_line.py b/pms/models/pms_service_line.py index 5ba12f371..7e316d4ae 100644 --- a/pms/models/pms_service_line.py +++ b/pms/models/pms_service_line.py @@ -1,6 +1,8 @@ # Copyright 2017-2018 Alexandre Díaz # Copyright 2017 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import datetime + from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -71,7 +73,7 @@ class PmsServiceLine(models.Model): ) price_day_total = fields.Monetary( string="Total", - help="Total price without taxes", + help="Total price with taxes", readonly=True, store=True, compute="_compute_day_amount_service", @@ -169,60 +171,35 @@ class PmsServiceLine(models.Model): record.discount = 0 # TODO: Refact method and allowed cancelled single days - @api.depends("service_id.reservation_id.cancelled_reason") + @api.depends("service_id.reservation_id.reservation_line_ids.cancel_discount") def _compute_cancel_discount(self): for line in self: line.cancel_discount = 0 - # TODO: Review cancel logic - # reservation = line.reservation_id.reservation_id - # pricelist = reservation.pricelist_id - # if reservation.state == "cancel": - # if ( - # reservation.cancel_reason - # and pricelist - # and pricelist.cancelation_rule_id - # ): - # date_start_dt = fields.Date.from_string( - # reservation.checkin - # ) - # date_end_dt = fields.Date.from_string( - # reservation.checkout - # ) - # days = abs((date_end_dt - date_start_dt).days) - # rule = pricelist.cancelation_rule_id - # if reservation.cancelled_reason == "late": - # discount = 100 - rule.penalty_late - # if rule.apply_on_late == "first": - # days = 1 - # elif rule.apply_on_late == "days": - # days = rule.days_late - # elif reservation.cancelled_reason == "noshow": - # discount = 100 - rule.penalty_noshow - # if rule.apply_on_noshow == "first": - # days = 1 - # elif rule.apply_on_noshow == "days": - # days = rule.days_late - 1 - # elif reservation.cancelled_reason == "intime": - # discount = 100 - - # checkin = reservation.checkin - # dates = [] - # for i in range(0, days): - # dates.append( - # ( - # fields.Date.from_string(checkin) + timedelta(days=i) - # ).strftime(DEFAULT_SERVER_DATE_FORMAT) - # ) - # reservation.reservation_line_ids.filtered( - # lambda r: r.date in dates - # ).update({"cancel_discount": discount}) - # reservation.reservation_line_ids.filtered( - # lambda r: r.date not in dates - # ).update({"cancel_discount": 100}) - # else: - # reservation.reservation_line_ids.update({"cancel_discount": 0}) - # else: - # reservation.reservation_line_ids.update({"cancel_discount": 0}) + reservation = line.reservation_id + if reservation.state == "cancel": + if ( + reservation.cancelled_reason + and reservation.pricelist_id + and reservation.pricelist_id.cancelation_rule_id + and reservation.reservation_line_ids.mapped("cancel_discount") + ): + if line.is_board_service: + consumed_date = ( + line.date + if line.product_id.consumed_on == "before" + else line.date + datetime.timedelta(days=-1) + ) + line.cancel_discount = ( + reservation.reservation_line_ids.filtered( + lambda l: l.date == consumed_date + ).cancel_discount + ) + else: + line.cancel_discount = 100 + else: + line.cancel_discount = 0 + else: + line.cancel_discount = 0 @api.depends("day_qty") def _compute_auto_qty(self): diff --git a/pms/views/pms_reservation_views.xml b/pms/views/pms_reservation_views.xml index 5b83161da..af3fd4b9a 100644 --- a/pms/views/pms_reservation_views.xml +++ b/pms/views/pms_reservation_views.xml @@ -405,6 +405,12 @@ widget="monetary" options="{'currency_field': 'currency_id'}" /> +