14.0 pms service price day (#57)

WIP: compute folio_sale_line without (5,0,0)
WIP: boardservices pricelist item sql search

* [IMP]pms: Service day with prices

* [ADD]pms: New Product price base type: Board Service

* [WIP]pms: pricelist item rule board service

* [WIP]pms: pricelist boardservice sql

* [WIP]pms: pricelist boardservice sql

* [IMP] service price per day

* [FIX] compute board_service reservation change

* [FIX] Views

* [IMP]pms: add default user_id on reservation and folio

* [IMP]pms: aler change prices reservation

* [FIX]pms: recompute reservation services board

* [DEL]pms: pricelist field on board_service_room_type

* [RFC] sale_line_ids model
This commit is contained in:
Darío Lodeiros
2021-03-30 19:34:53 +02:00
committed by GitHub
parent 5d29d69fa9
commit ec841374cf
33 changed files with 605 additions and 1143 deletions

View File

@@ -7,7 +7,6 @@ import time
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare, float_is_zero
_logger = logging.getLogger(__name__)
@@ -83,6 +82,12 @@ class PmsReservation(models.Model):
copy=False,
check_company=True,
)
sale_line_ids = fields.One2many(
comodel_name="folio.sale.line",
inverse_name="reservation_id",
string="Sale Lines",
copy=False,
)
board_service_room_id = fields.Many2one(
"pms.board.service.room.type",
string="Board Service",
@@ -184,7 +189,8 @@ class PmsReservation(models.Model):
)
user_id = fields.Many2one(
related="folio_id.user_id",
depends=["folio_id"],
depends=["folio_id.user_id"],
default=lambda self: self.env.user.id,
readonly=False,
store=True,
)
@@ -274,14 +280,6 @@ class PmsReservation(models.Model):
ondelete="restrict",
domain=["|", ("active", "=", False), ("active", "=", True)],
)
move_line_ids = fields.Many2many(
"account.move.line",
"reservation_move_rel",
"reservation_id",
"move_line_id",
string="Invoice Lines",
copy=False,
)
localizator = fields.Char(
string="Localizator",
compute="_compute_localizator",
@@ -446,32 +444,6 @@ class PmsReservation(models.Model):
readonly=True,
default="no",
)
qty_to_invoice = fields.Float(
compute="_compute_qty_to_invoice",
string="To Invoice Quantity",
store=True,
readonly=True,
digits=("Product Unit of Measure"),
)
qty_invoiced = fields.Float(
compute="_compute_qty_invoiced",
string="Invoiced Quantity",
store=True,
readonly=True,
digits=("Product Unit of Measure"),
)
untaxed_amount_invoiced = fields.Monetary(
"Untaxed Invoiced Amount",
compute="_compute_untaxed_amount_invoiced",
compute_sudo=True,
store=True,
)
untaxed_amount_to_invoice = fields.Monetary(
"Untaxed Amount To Invoice",
compute="_compute_untaxed_amount_to_invoice",
compute_sudo=True,
store=True,
)
analytic_tag_ids = fields.Many2many(
"account.analytic.tag",
string="Analytic Tags",
@@ -552,20 +524,15 @@ class PmsReservation(models.Model):
if reservation.pricelist_id and reservation.room_type_id:
board_service_default = self.env["pms.board.service.room.type"].search(
[
"&",
"&",
("pms_room_type_id", "=", reservation.room_type_id.id),
("by_default", "=", True),
"|",
("pricelist_id", "=", reservation.pricelist_id.id),
("pricelist_id", "=", False),
]
)
if len(board_service_default) > 1:
reservation.board_service_room_id = board_service_default.filtered(
lambda b: b.pricelist_id == reservation.pricelist_id
)
else:
if (
not reservation.board_service_room_id
or not reservation.board_service_room_id.pms_room_type_id
== reservation.room_type_id
):
reservation.board_service_room_id = (
board_service_default.id if board_service_default else False
)
@@ -693,6 +660,13 @@ class PmsReservation(models.Model):
("is_board_service", "=", True),
]
)
# Avoid recalculating services if the boardservice has not changed
if (
old_board_lines
and reservation.board_service_room_id
== reservation._origin.board_service_room_id
):
return
if reservation.board_service_room_id:
board = self.env["pms.board.service.room.type"].browse(
reservation.board_service_room_id.id
@@ -707,6 +681,8 @@ class PmsReservation(models.Model):
board_services.append((0, False, res))
reservation.service_ids -= old_board_lines
reservation.service_ids = board_services
elif old_board_lines:
reservation.service_ids -= old_board_lines
@api.depends("partner_id", "agency_id")
def _compute_pricelist_id(self):
@@ -727,13 +703,19 @@ class PmsReservation(models.Model):
reservation.pms_property_id.default_pricelist_id.id
)
@api.depends("pricelist_id")
@api.depends("pricelist_id", "room_type_id")
def _compute_show_update_pricelist(self):
for reservation in self:
if (
sum(reservation.reservation_line_ids.mapped("price")) > 0
and reservation.pricelist_id
and reservation._origin.pricelist_id != reservation.pricelist_id
and (
reservation.pricelist_id
and reservation._origin.pricelist_id != reservation.pricelist_id
)
or (
reservation.room_type_id
and reservation._origin.room_type_id != reservation.room_type_id
)
):
reservation.show_update_pricelist = True
else:
@@ -1036,183 +1018,28 @@ class PmsReservation(models.Model):
if room_ids:
reservation.preferred_room_id = room_ids[0]
@api.depends("state", "qty_to_invoice", "qty_invoiced")
@api.depends(
"sale_line_ids",
"sale_line_ids.invoice_status",
)
def _compute_invoice_status(self):
"""
Compute the invoice status of a Reservation. Possible statuses:
- no: if the Folio is not in status 'sale' or 'done', we consider
that there is nothing to invoice. This is also hte default value
if the conditions of no other status is met.
- to invoice: we refer to the quantity to invoice of the line.
Refer to method `_compute_get_to_invoice_qty()` for more information
on how this quantity is calculated.
- invoiced: the quantity invoiced is larger or equal to the
quantity ordered.
"""
precision = self.env["decimal.precision"].precision_get(
"Product Unit of Measure"
)
for line in self:
if line.state == "draft":
line.invoice_status = "no"
elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
line.invoice_status = "to invoice"
elif (
float_compare(
line.qty_invoiced,
len(line.reservation_line_ids),
precision_digits=precision,
)
>= 0
):
line.invoice_status = "invoiced"
else:
line.invoice_status = "no"
@api.depends("qty_invoiced", "nights", "folio_id.state")
def _compute_qty_to_invoice(self):
"""
Compute the quantity to invoice. The quantity to invoice is
calculated from the nights quantity.
Base on folio sale line invoice status
"""
for line in self:
if line.folio_id.state not in ["draft"]:
line.qty_to_invoice = len(line.reservation_line_ids) - line.qty_invoiced
else:
line.qty_to_invoice = 0
@api.depends(
"move_line_ids.move_id.state",
"move_line_ids.quantity",
"untaxed_amount_to_invoice",
)
def _compute_qty_invoiced(self):
"""
Compute the quantity invoiced. If case of a refund, the quantity
invoiced is decreased. We must check day per day and sum or
decreased on 1 unit per invoice_line
"""
for record in self:
qty_invoiced = 0.0
for line in record.reservation_line_ids:
invoice_lines = line.move_line_ids.filtered(
lambda r: r.move_id.state != "cancel"
)
qty_invoiced += len(
invoice_lines.filtered(
lambda r: r.move_id.move_type == "out_invoice"
)
) - len(
invoice_lines.filtered(
lambda r: r.move_id.move_type == "out_refund"
)
)
record.qty_invoiced = qty_invoiced
@api.depends(
"move_line_ids",
"move_line_ids.price_total",
"move_line_ids.move_id.state",
"move_line_ids.move_id.move_type",
)
def _compute_untaxed_amount_invoiced(self):
"""Compute the untaxed amount already invoiced from the reservation,
taking the refund attached
the reservation into account. This amount is computed as
SUM(inv_line.price_subtotal) - SUM(ref_line.price_subtotal)
where
`inv_line` is a customer invoice line linked to the reservation
`ref_line` is a customer credit note (refund)
line linked to the reservation
"""
for line in self:
amount_invoiced = 0.0
for invoice_line in line.move_line_ids:
if invoice_line.move_id.state == "posted":
invoice_date = (
invoice_line.move_id.invoice_date or fields.Date.today()
)
if invoice_line.move_id.move_type == "out_invoice":
amount_invoiced += invoice_line.currency_id._convert(
invoice_line.price_subtotal,
line.currency_id,
line.company_id,
invoice_date,
)
elif invoice_line.move_id.move_type == "out_refund":
amount_invoiced -= invoice_line.currency_id._convert(
invoice_line.price_subtotal,
line.currency_id,
line.company_id,
invoice_date,
)
line.untaxed_amount_invoiced = amount_invoiced
@api.depends(
"state", "discount", "price_total", "room_type_id", "untaxed_amount_invoiced"
)
def _compute_untaxed_amount_to_invoice(self):
"""Total of remaining amount to invoice on the reservation (taxes excl.) as
total_sol - amount already invoiced
Note: Draft invoice are ignored on purpose, the 'to invoice' amount should
come only from the reservation.
"""
for line in self:
amount_to_invoice = 0.0
if line.state not in ["draft"]:
price_subtotal = 0.0
price_subtotal = line.price_subtotal
if len(line.tax_ids.filtered(lambda tax: tax.price_include)) > 0:
# As included taxes are not excluded from the computed subtotal,
# `compute_all()` method
# has to be called to retrieve the subtotal without them.
price_subtotal = line.tax_ids.compute_all(
price_subtotal,
currency=line.currency_id,
quantity=line.nights,
product=line.room_type_id.product_id,
)["total_excluded"]
if any(
line.move_line_ids.mapped(lambda i: i.discount != line.discount)
):
# In case of re-invoicing with different discount we
# try to calculate manually the
# remaining amount to invoice
amount = 0
for move in line.move_line_ids:
if (
len(move.tax_ids.filtered(lambda tax: tax.price_include))
> 0
):
amount += move.tax_ids.compute_all(
move.currency_id._convert(
move.price_unit,
line.currency_id,
line.company_id,
move.date or fields.Date.today(),
round=False,
)
* move.quantity
)["total_excluded"]
else:
amount += (
move.currency_id._convert(
move.price_unit,
line.currency_id,
line.company_id,
move.date or fields.Date.today(),
round=False,
)
* move.quantity
)
amount_to_invoice = max(price_subtotal - amount, 0)
states = list(set(line.sale_line_ids.mapped("invoice_status")))
if len(states) == 1:
line.invoice_status = states[0]
elif len(states) >= 1:
if "to_invoice" in states:
line.invoice_status = "to_invoice"
elif "invoiced" in states:
line.invoice_status = "invoiced"
else:
amount_to_invoice = price_subtotal - line.untaxed_amount_invoiced
line.untaxed_amount_to_invoice = amount_to_invoice
line.invoice_status = "no"
else:
line.invoice_status = "no"
@api.depends("reservation_line_ids")
def _compute_nights(self):
@@ -1529,8 +1356,10 @@ class PmsReservation(models.Model):
self.show_update_pricelist = False
self.message_post(
body=_(
"Prices have been recomputed according to pricelist <b>%s<b> ",
"""Prices have been recomputed according to pricelist <b>%s</b>
and room type <b>%s</b>""",
self.pricelist_id.display_name,
self.room_type_id.name,
)
)