mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP]pms: multiple autoinvoicing improvements
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
# Copyright 2020 Dario Lodeiros
|
# Copyright 2020 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
|
||||||
import babel.dates
|
import babel.dates
|
||||||
|
from dateutil import relativedelta
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.osv import expression
|
from odoo.osv import expression
|
||||||
@@ -44,6 +46,15 @@ class FolioSaleLine(models.Model):
|
|||||||
comodel_name="pms.service",
|
comodel_name="pms.service",
|
||||||
ondelete="cascade",
|
ondelete="cascade",
|
||||||
)
|
)
|
||||||
|
pms_property_id = fields.Many2one(
|
||||||
|
string="Property",
|
||||||
|
help="Property with access to the element;",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
comodel_name="pms.property",
|
||||||
|
related="folio_id.pms_property_id",
|
||||||
|
check_pms_properties=True,
|
||||||
|
)
|
||||||
is_board_service = fields.Boolean(
|
is_board_service = fields.Boolean(
|
||||||
string="Board Service",
|
string="Board Service",
|
||||||
help="Indicates if the service included in "
|
help="Indicates if the service included in "
|
||||||
@@ -51,7 +62,6 @@ class FolioSaleLine(models.Model):
|
|||||||
store=True,
|
store=True,
|
||||||
related="service_id.is_board_service",
|
related="service_id.is_board_service",
|
||||||
)
|
)
|
||||||
|
|
||||||
name = fields.Text(
|
name = fields.Text(
|
||||||
string="Description",
|
string="Description",
|
||||||
help="Description of folio sale line",
|
help="Description of folio sale line",
|
||||||
@@ -259,13 +269,6 @@ class FolioSaleLine(models.Model):
|
|||||||
index=True,
|
index=True,
|
||||||
related="folio_id.company_id",
|
related="folio_id.company_id",
|
||||||
)
|
)
|
||||||
folio_partner_id = fields.Many2one(
|
|
||||||
string="Customer",
|
|
||||||
help="Related customer with Folio Sale Line",
|
|
||||||
readonly=False,
|
|
||||||
store=True,
|
|
||||||
related="folio_id.partner_id",
|
|
||||||
)
|
|
||||||
origin_agency_id = fields.Many2one(
|
origin_agency_id = fields.Many2one(
|
||||||
string="Origin Agency",
|
string="Origin Agency",
|
||||||
help="The agency where the folio sale line originates",
|
help="The agency where the folio sale line originates",
|
||||||
@@ -330,14 +333,38 @@ class FolioSaleLine(models.Model):
|
|||||||
store=True,
|
store=True,
|
||||||
compute="_compute_date_order",
|
compute="_compute_date_order",
|
||||||
)
|
)
|
||||||
|
default_invoice_to = fields.Many2one(
|
||||||
|
string="Invoice to",
|
||||||
|
help="""Indicates the contact to which this line will be
|
||||||
|
billed by default, if it is not established,
|
||||||
|
a guest or the generic contact will be used instead""",
|
||||||
|
comodel_name="res.partner",
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
autoinvoice_date = fields.Date(
|
||||||
|
string="Autoinvoice Date",
|
||||||
|
compute="_compute_autoinvoice_date",
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
|
"folio_id.agency_id",
|
||||||
"reservation_line_ids",
|
"reservation_line_ids",
|
||||||
"reservation_id.agency_id",
|
"service_line_ids",
|
||||||
)
|
)
|
||||||
def _compute_origin_agency_id(self):
|
def _compute_origin_agency_id(self):
|
||||||
|
"""
|
||||||
|
Set the origin agency if the origin lines channel
|
||||||
|
match with the agency's channel
|
||||||
|
"""
|
||||||
for rec in self:
|
for rec in self:
|
||||||
rec.origin_agency_id = rec.folio_id.agency_id
|
# TODO: ServiceLines agency
|
||||||
|
if rec.folio_id.agency_id and list(
|
||||||
|
set(rec.reservation_line_ids.mapped("sale_channel_id.id"))
|
||||||
|
) == rec.folio_id.agency_id.mapped("sale_channel_id.id"):
|
||||||
|
rec.origin_agency_id = rec.folio_id.agency_id
|
||||||
|
else:
|
||||||
|
rec.origin_agency_id = False
|
||||||
|
|
||||||
@api.depends("qty_to_invoice")
|
@api.depends("qty_to_invoice")
|
||||||
def _compute_service_order(self):
|
def _compute_service_order(self):
|
||||||
@@ -370,6 +397,54 @@ class FolioSaleLine(models.Model):
|
|||||||
else:
|
else:
|
||||||
record.date_order = 0
|
record.date_order = 0
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
"default_invoice_to",
|
||||||
|
"invoice_status",
|
||||||
|
"folio_id.last_checkout",
|
||||||
|
"reservation_id.checkout",
|
||||||
|
"service_id.reservation_id.checkout",
|
||||||
|
)
|
||||||
|
def _compute_autoinvoice_date(self):
|
||||||
|
self.autoinvoice_date = False
|
||||||
|
for record in self.filtered(lambda r: r.invoice_status == "to_invoice"):
|
||||||
|
record.autoinvoice_date = record._get_to_invoice_date()
|
||||||
|
|
||||||
|
def _get_to_invoice_date(self):
|
||||||
|
self.ensure_one()
|
||||||
|
partner = self.default_invoice_to
|
||||||
|
if self.reservation_id:
|
||||||
|
last_checkout = self.reservation_id.checkout
|
||||||
|
elif self.service_id and self.service_id.reservation_id:
|
||||||
|
last_checkout = self.service_id.reservation_id.checkout
|
||||||
|
else:
|
||||||
|
last_checkout = self.folio_id.last_checkout
|
||||||
|
invoicing_policy = (
|
||||||
|
self.pms_property_id.default_invoicing_policy
|
||||||
|
if not partner or partner.invoicing_policy == "property"
|
||||||
|
else partner.invoicing_policy
|
||||||
|
)
|
||||||
|
if invoicing_policy == "manual":
|
||||||
|
return False
|
||||||
|
if invoicing_policy == "checkout":
|
||||||
|
margin_days = (
|
||||||
|
self.pms_property_id.margin_days_autoinvoice
|
||||||
|
if not partner or partner.invoicing_policy == "property"
|
||||||
|
else partner.margin_days_autoinvoice
|
||||||
|
)
|
||||||
|
return last_checkout + timedelta(days=margin_days)
|
||||||
|
if invoicing_policy == "month_day":
|
||||||
|
month_day = (
|
||||||
|
self.pms_property_id.invoicing_month_day
|
||||||
|
if not partner or partner.invoicing_policy == "property"
|
||||||
|
else partner.invoicing_month_day
|
||||||
|
)
|
||||||
|
if last_checkout.day <= month_day:
|
||||||
|
self.autoinvoice_date = last_checkout.replace(day=month_day)
|
||||||
|
else:
|
||||||
|
self.autoinvoice_date = (
|
||||||
|
last_checkout + relativedelta.relativedelta(months=1)
|
||||||
|
).replace(day=month_day)
|
||||||
|
|
||||||
@api.depends("date_order")
|
@api.depends("date_order")
|
||||||
def _compute_reservation_order(self):
|
def _compute_reservation_order(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
|||||||
@@ -4,11 +4,8 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
|
||||||
from dateutil import relativedelta
|
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import AccessError, UserError, ValidationError
|
from odoo.exceptions import AccessError, UserError, ValidationError
|
||||||
from odoo.tools import float_compare, float_is_zero
|
from odoo.tools import float_compare, float_is_zero
|
||||||
@@ -625,13 +622,11 @@ class PmsFolio(models.Model):
|
|||||||
folio_lines_to_invoice = folio.sale_line_ids.filtered(
|
folio_lines_to_invoice = folio.sale_line_ids.filtered(
|
||||||
lambda l: l.id in list(lines_to_invoice.keys())
|
lambda l: l.id in list(lines_to_invoice.keys())
|
||||||
)
|
)
|
||||||
folio_partner_invoice_id = partner_invoice_id
|
folio._set_default_partner_invoice_id(
|
||||||
if not folio_partner_invoice_id:
|
folio_lines_to_invoice, partner_invoice_id
|
||||||
folio_partner_invoice_id = folio._get_default_partner_invoice_id()
|
)
|
||||||
|
|
||||||
groups_invoice_lines = folio._get_groups_invoice_lines(
|
groups_invoice_lines = folio._get_groups_invoice_lines(
|
||||||
lines_to_invoice=folio_lines_to_invoice,
|
lines_to_invoice=folio_lines_to_invoice,
|
||||||
partner_invoice_id=folio_partner_invoice_id,
|
|
||||||
)
|
)
|
||||||
for group in groups_invoice_lines:
|
for group in groups_invoice_lines:
|
||||||
folio = folio.with_company(folio.company_id)
|
folio = folio.with_company(folio.company_id)
|
||||||
@@ -698,60 +693,49 @@ class PmsFolio(models.Model):
|
|||||||
invoice_vals_list.append(invoice_vals)
|
invoice_vals_list.append(invoice_vals)
|
||||||
return invoice_vals_list
|
return invoice_vals_list
|
||||||
|
|
||||||
def _get_groups_invoice_lines(self, lines_to_invoice, partner_invoice_id):
|
def _get_groups_invoice_lines(self, lines_to_invoice):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
target_lines = lines_to_invoice
|
groups_invoice_lines = []
|
||||||
if self._context.get("lines_auto_add") and partner_invoice_id:
|
partners = lines_to_invoice.mapped("default_invoice_to")
|
||||||
folio_partner_invoice = self.env["res.partner"].browse(partner_invoice_id)
|
for partner in partners:
|
||||||
if folio_partner_invoice.default_invoice_lines == "overnights":
|
|
||||||
target_lines = target_lines.filtered(
|
|
||||||
lambda r: r.is_board_service
|
|
||||||
or (r.reservation_line_ids and r.reservation_id.overnight_room)
|
|
||||||
)
|
|
||||||
elif folio_partner_invoice.default_invoice_lines == "reservations":
|
|
||||||
target_lines = target_lines.filtered(
|
|
||||||
lambda r: r.is_board_service or r.reservation_line_ids
|
|
||||||
)
|
|
||||||
elif folio_partner_invoice.default_invoice_lines == "services":
|
|
||||||
target_lines = target_lines.filtered(
|
|
||||||
lambda r: not r.is_board_service or r.service_line_ids
|
|
||||||
)
|
|
||||||
groups_invoice_lines = [
|
|
||||||
{
|
|
||||||
"partner_id": partner_invoice_id,
|
|
||||||
"lines": target_lines,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
if (
|
|
||||||
self.autoinvoice_date
|
|
||||||
and self.autoinvoice_date <= fields.Date.today()
|
|
||||||
and len(target_lines) < len(lines_to_invoice)
|
|
||||||
):
|
|
||||||
other_partner_to_invoice = self.partner_invoice_ids.filtered(
|
|
||||||
lambda p: p.id != partner_invoice_id
|
|
||||||
)
|
|
||||||
if not other_partner_to_invoice:
|
|
||||||
other_partner_to_invoice = self.env.ref("pms.various_pms_partner")
|
|
||||||
groups_invoice_lines.append(
|
groups_invoice_lines.append(
|
||||||
{
|
{
|
||||||
"partner_id": other_partner_to_invoice.id,
|
"partner_id": partner.id,
|
||||||
"lines": lines_to_invoice - target_lines,
|
"lines": lines_to_invoice.filtered(
|
||||||
|
lambda l: l.default_invoice_to == partner
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return groups_invoice_lines
|
return groups_invoice_lines
|
||||||
|
|
||||||
def _get_default_partner_invoice_id(self):
|
def _set_default_partner_invoice_id(
|
||||||
|
self, lines_to_invoice, folio_partner_invoice_id=False
|
||||||
|
):
|
||||||
|
# By priotiy:
|
||||||
|
# 1º- Partner set in parameter,
|
||||||
|
# 2º- Partner in default_invoice_to in line
|
||||||
|
# 3º- Partner in folio,
|
||||||
|
# 4º- Partner in checkins,
|
||||||
|
# 5º- Generic various partner
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
folio_partner_invoice_id = False
|
for line in lines_to_invoice:
|
||||||
if self.partner_id and self.partner_id.vat:
|
if not folio_partner_invoice_id and line.default_invoice_to:
|
||||||
folio_partner_invoice_id = self.partner_id.id
|
folio_partner_invoice_id = line.default_invoice_to
|
||||||
if not folio_partner_invoice_id:
|
if (
|
||||||
folio_partner_invoice_id = (
|
not folio_partner_invoice_id
|
||||||
self.partner_invoice_ids[0].id if self.partner_invoice_ids else False
|
and self.partner_id
|
||||||
|
and self.partner_id._check_enought_invoice_data()
|
||||||
|
and not self.partner_id.is_agency
|
||||||
|
):
|
||||||
|
folio_partner_invoice_id = self.partner_id.id
|
||||||
|
checkin_invoice_partner = self.checkin_partner_ids.filtered(
|
||||||
|
lambda c: c.partner_id and c.partner_id._check_enought_invoice_data()
|
||||||
)
|
)
|
||||||
if not folio_partner_invoice_id:
|
if not folio_partner_invoice_id and checkin_invoice_partner:
|
||||||
folio_partner_invoice_id = self.env.ref("pms.various_pms_partner").id
|
folio_partner_invoice_id = checkin_invoice_partner[0].partner_id
|
||||||
return folio_partner_invoice_id
|
if not folio_partner_invoice_id:
|
||||||
|
folio_partner_invoice_id = self.env.ref("pms.various_pms_partner").id
|
||||||
|
line.default_invoice_to = folio_partner_invoice_id
|
||||||
|
|
||||||
def _get_tax_amount_by_group(self):
|
def _get_tax_amount_by_group(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -789,42 +773,6 @@ class PmsFolio(models.Model):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@api.depends("partner_id", "invoice_status", "last_checkout", "partner_invoice_ids")
|
|
||||||
def _compute_autoinvoice_date(self):
|
|
||||||
self.autoinvoice_date = False
|
|
||||||
for record in self.filtered(lambda r: r.invoice_status == "to_invoice"):
|
|
||||||
record.autoinvoice_date = record._get_to_invoice_date()
|
|
||||||
|
|
||||||
def _get_to_invoice_date(self):
|
|
||||||
self.ensure_one()
|
|
||||||
partner = self.partner_id
|
|
||||||
invoicing_policy = (
|
|
||||||
self.pms_property_id.default_invoicing_policy
|
|
||||||
if not partner or partner.invoicing_policy == "property"
|
|
||||||
else partner.invoicing_policy
|
|
||||||
)
|
|
||||||
if invoicing_policy == "manual":
|
|
||||||
return False
|
|
||||||
if invoicing_policy == "checkout":
|
|
||||||
margin_days = (
|
|
||||||
self.pms_property_id.margin_days_autoinvoice
|
|
||||||
if not partner or partner.invoicing_policy == "property"
|
|
||||||
else partner.margin_days_autoinvoice
|
|
||||||
)
|
|
||||||
return self.last_checkout + timedelta(days=margin_days)
|
|
||||||
if invoicing_policy == "month_day":
|
|
||||||
month_day = (
|
|
||||||
self.pms_property_id.invoicing_month_day
|
|
||||||
if not partner or partner.invoicing_policy == "property"
|
|
||||||
else partner.invoicing_month_day
|
|
||||||
)
|
|
||||||
if self.last_checkout.day <= month_day:
|
|
||||||
self.autoinvoice_date = self.last_checkout.replace(day=month_day)
|
|
||||||
else:
|
|
||||||
self.autoinvoice_date = (
|
|
||||||
self.last_checkout + relativedelta.relativedelta(months=1)
|
|
||||||
).replace(day=month_day)
|
|
||||||
|
|
||||||
@api.depends("reservation_ids", "reservation_ids.state")
|
@api.depends("reservation_ids", "reservation_ids.state")
|
||||||
def _compute_number_of_rooms(self):
|
def _compute_number_of_rooms(self):
|
||||||
for folio in self:
|
for folio in self:
|
||||||
@@ -848,6 +796,7 @@ class PmsFolio(models.Model):
|
|||||||
"reservation_ids",
|
"reservation_ids",
|
||||||
"service_ids",
|
"service_ids",
|
||||||
"service_ids.reservation_id",
|
"service_ids.reservation_id",
|
||||||
|
"service_ids.default_invoice_to",
|
||||||
"service_ids.service_line_ids.price_day_total",
|
"service_ids.service_line_ids.price_day_total",
|
||||||
"service_ids.service_line_ids.discount",
|
"service_ids.service_line_ids.discount",
|
||||||
"service_ids.service_line_ids.cancel_discount",
|
"service_ids.service_line_ids.cancel_discount",
|
||||||
@@ -857,6 +806,7 @@ class PmsFolio(models.Model):
|
|||||||
"reservation_ids.reservation_line_ids.price",
|
"reservation_ids.reservation_line_ids.price",
|
||||||
"reservation_ids.reservation_line_ids.discount",
|
"reservation_ids.reservation_line_ids.discount",
|
||||||
"reservation_ids.reservation_line_ids.cancel_discount",
|
"reservation_ids.reservation_line_ids.cancel_discount",
|
||||||
|
"reservation_ids.reservation_line_ids.default_invoice_to",
|
||||||
"reservation_ids.tax_ids",
|
"reservation_ids.tax_ids",
|
||||||
)
|
)
|
||||||
def _compute_sale_line_ids(self):
|
def _compute_sale_line_ids(self):
|
||||||
@@ -1054,7 +1004,7 @@ class PmsFolio(models.Model):
|
|||||||
"reservation_ids",
|
"reservation_ids",
|
||||||
"reservation_ids.sale_channel_ids",
|
"reservation_ids.sale_channel_ids",
|
||||||
"service_ids",
|
"service_ids",
|
||||||
"service_ids.sale_channel_ids",
|
"service_ids.sale_channel_origin_id",
|
||||||
)
|
)
|
||||||
def _compute_sale_channel_ids(self):
|
def _compute_sale_channel_ids(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -1063,8 +1013,6 @@ class PmsFolio(models.Model):
|
|||||||
for sale in record.reservation_ids.mapped("sale_channel_ids.id"):
|
for sale in record.reservation_ids.mapped("sale_channel_ids.id"):
|
||||||
sale_channel_ids.append(sale)
|
sale_channel_ids.append(sale)
|
||||||
if record.service_ids:
|
if record.service_ids:
|
||||||
# si es un board service que mire sale_channel_ids
|
|
||||||
# y si es un servicio a secas entonces que coja el origen
|
|
||||||
for sale in record.service_ids.mapped("sale_channel_origin_id.id"):
|
for sale in record.service_ids.mapped("sale_channel_origin_id.id"):
|
||||||
sale_channel_ids.append(sale)
|
sale_channel_ids.append(sale)
|
||||||
sale_channel_ids = list(set(sale_channel_ids))
|
sale_channel_ids = list(set(sale_channel_ids))
|
||||||
@@ -1910,9 +1858,17 @@ class PmsFolio(models.Model):
|
|||||||
self = self.with_context(lines_auto_add=True)
|
self = self.with_context(lines_auto_add=True)
|
||||||
lines_to_invoice = dict()
|
lines_to_invoice = dict()
|
||||||
for line in self.sale_line_ids:
|
for line in self.sale_line_ids:
|
||||||
lines_to_invoice[line.id] = (
|
if not self._context.get("autoinvoice"):
|
||||||
0 if line.display_type else line.qty_to_invoice
|
lines_to_invoice[line.id] = (
|
||||||
)
|
0 if line.display_type else line.qty_to_invoice
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
line.autoinvoice_date
|
||||||
|
and line.autoinvoice_date <= fields.Date.today()
|
||||||
|
):
|
||||||
|
lines_to_invoice[line.id] = (
|
||||||
|
0 if line.display_type else line.qty_to_invoice
|
||||||
|
)
|
||||||
invoice_vals_list = self.get_invoice_vals_list(
|
invoice_vals_list = self.get_invoice_vals_list(
|
||||||
final=final,
|
final=final,
|
||||||
lines_to_invoice=lines_to_invoice,
|
lines_to_invoice=lines_to_invoice,
|
||||||
@@ -2375,8 +2331,8 @@ class PmsFolio(models.Model):
|
|||||||
("reservation_id", "=", reservation.id),
|
("reservation_id", "=", reservation.id),
|
||||||
("cancel_discount", "<", 100),
|
("cancel_discount", "<", 100),
|
||||||
],
|
],
|
||||||
["price", "discount", "cancel_discount"],
|
["price", "discount", "cancel_discount", "default_invoice_to"],
|
||||||
["price", "discount", "cancel_discount"],
|
["price", "discount", "cancel_discount", "default_invoice_to"],
|
||||||
lazy=False,
|
lazy=False,
|
||||||
)
|
)
|
||||||
current_sale_line_ids = reservation.sale_line_ids.filtered(
|
current_sale_line_ids = reservation.sale_line_ids.filtered(
|
||||||
@@ -2391,13 +2347,17 @@ class PmsFolio(models.Model):
|
|||||||
final_discount = self.concat_discounts(
|
final_discount = self.concat_discounts(
|
||||||
item["discount"], item["cancel_discount"]
|
item["discount"], item["cancel_discount"]
|
||||||
)
|
)
|
||||||
|
partner_invoice = lines_to.mapped("default_invoice_to")
|
||||||
if current_sale_line_ids and index <= (len(current_sale_line_ids) - 1):
|
if current_sale_line_ids and index <= (len(current_sale_line_ids) - 1):
|
||||||
|
|
||||||
current = {
|
current = {
|
||||||
"price_unit": item["price"],
|
"price_unit": item["price"],
|
||||||
"discount": final_discount,
|
"discount": final_discount,
|
||||||
"reservation_line_ids": [(6, 0, lines_to.ids)],
|
"reservation_line_ids": [(6, 0, lines_to.ids)],
|
||||||
"sequence": sequence,
|
"sequence": sequence,
|
||||||
|
"default_invoice_to": partner_invoice[0].id
|
||||||
|
if partner_invoice
|
||||||
|
else current_sale_line_ids[index].default_invoice_to,
|
||||||
}
|
}
|
||||||
sale_reservation_vals.append(
|
sale_reservation_vals.append(
|
||||||
(1, current_sale_line_ids[index].id, current)
|
(1, current_sale_line_ids[index].id, current)
|
||||||
@@ -2412,6 +2372,9 @@ class PmsFolio(models.Model):
|
|||||||
"tax_ids": [(6, 0, reservation.tax_ids.ids)],
|
"tax_ids": [(6, 0, reservation.tax_ids.ids)],
|
||||||
"reservation_line_ids": [(6, 0, lines_to.ids)],
|
"reservation_line_ids": [(6, 0, lines_to.ids)],
|
||||||
"sequence": sequence,
|
"sequence": sequence,
|
||||||
|
"default_invoice_to": partner_invoice[0].id
|
||||||
|
if partner_invoice
|
||||||
|
else False,
|
||||||
}
|
}
|
||||||
sale_reservation_vals.append((0, 0, new))
|
sale_reservation_vals.append((0, 0, new))
|
||||||
folio_sale_lines_to_remove = []
|
folio_sale_lines_to_remove = []
|
||||||
@@ -2434,8 +2397,8 @@ class PmsFolio(models.Model):
|
|||||||
("service_id", "=", service.id),
|
("service_id", "=", service.id),
|
||||||
("cancel_discount", "<", 100),
|
("cancel_discount", "<", 100),
|
||||||
],
|
],
|
||||||
["price_unit", "discount", "cancel_discount"],
|
["price_unit", "discount", "cancel_discount", "default_invoice_to"],
|
||||||
["price_unit", "discount", "cancel_discount"],
|
["price_unit", "discount", "cancel_discount", "default_invoice_to"],
|
||||||
lazy=False,
|
lazy=False,
|
||||||
)
|
)
|
||||||
current_sale_service_ids = reservation.sale_line_ids.filtered(
|
current_sale_service_ids = reservation.sale_line_ids.filtered(
|
||||||
@@ -2449,7 +2412,7 @@ class PmsFolio(models.Model):
|
|||||||
final_discount = self.concat_discounts(
|
final_discount = self.concat_discounts(
|
||||||
item["discount"], item["cancel_discount"]
|
item["discount"], item["cancel_discount"]
|
||||||
)
|
)
|
||||||
|
partner_invoice = lines_to.mapped("default_invoice_to")
|
||||||
if current_sale_service_ids and index <= (
|
if current_sale_service_ids and index <= (
|
||||||
len(current_sale_service_ids) - 1
|
len(current_sale_service_ids) - 1
|
||||||
):
|
):
|
||||||
@@ -2458,6 +2421,9 @@ class PmsFolio(models.Model):
|
|||||||
"discount": final_discount,
|
"discount": final_discount,
|
||||||
"service_line_ids": [(6, 0, lines_to.ids)],
|
"service_line_ids": [(6, 0, lines_to.ids)],
|
||||||
"sequence": sequence,
|
"sequence": sequence,
|
||||||
|
"default_invoice_to": partner_invoice[0].id
|
||||||
|
if partner_invoice
|
||||||
|
else current_sale_service_ids[index].default_invoice_to,
|
||||||
}
|
}
|
||||||
sale_service_vals.append(
|
sale_service_vals.append(
|
||||||
(1, current_sale_service_ids[index].id, current)
|
(1, current_sale_service_ids[index].id, current)
|
||||||
@@ -2473,6 +2439,9 @@ class PmsFolio(models.Model):
|
|||||||
"product_id": service.product_id.id,
|
"product_id": service.product_id.id,
|
||||||
"tax_ids": [(6, 0, service.tax_ids.ids)],
|
"tax_ids": [(6, 0, service.tax_ids.ids)],
|
||||||
"sequence": sequence,
|
"sequence": sequence,
|
||||||
|
"default_invoice_to": partner_invoice[0].id
|
||||||
|
if partner_invoice
|
||||||
|
else False,
|
||||||
}
|
}
|
||||||
sale_service_vals.append((0, 0, new))
|
sale_service_vals.append((0, 0, new))
|
||||||
sequence = sequence + 1
|
sequence = sequence + 1
|
||||||
|
|||||||
@@ -607,7 +607,7 @@ class PmsProperty(models.Model):
|
|||||||
"""
|
"""
|
||||||
folios = self.env["pms.folio"].search(
|
folios = self.env["pms.folio"].search(
|
||||||
[
|
[
|
||||||
("autoinvoice_date", "=", fields.date.today()),
|
("sale_line_ids.autoinvoice_date", "=", fields.date.today()),
|
||||||
("invoice_status", "=", "to_invoice"),
|
("invoice_status", "=", "to_invoice"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1635,12 +1635,10 @@ class PmsReservation(models.Model):
|
|||||||
else:
|
else:
|
||||||
record.lang = self.env["res.lang"].get_installed()
|
record.lang = self.env["res.lang"].get_installed()
|
||||||
|
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"reservation_line_ids",
|
"reservation_line_ids",
|
||||||
"reservation_line_ids.sale_channel_id",
|
"reservation_line_ids.sale_channel_id",
|
||||||
"service_ids",
|
"service_ids",
|
||||||
"service_ids.sale_channel_ids",
|
|
||||||
"service_ids.sale_channel_origin_id",
|
"service_ids.sale_channel_origin_id",
|
||||||
)
|
)
|
||||||
def _compute_sale_channel_ids(self):
|
def _compute_sale_channel_ids(self):
|
||||||
@@ -1650,7 +1648,7 @@ class PmsReservation(models.Model):
|
|||||||
for sale in record.reservation_line_ids.mapped("sale_channel_id.id"):
|
for sale in record.reservation_line_ids.mapped("sale_channel_id.id"):
|
||||||
sale_channel_ids.append(sale)
|
sale_channel_ids.append(sale)
|
||||||
if record.service_ids:
|
if record.service_ids:
|
||||||
for sale in record.service_ids.mapped("sale_channel_ids.id"):
|
for sale in record.service_ids.mapped("sale_channel_origin_id.id"):
|
||||||
sale_channel_ids.append(sale)
|
sale_channel_ids.append(sale)
|
||||||
sale_channel_ids = list(set(sale_channel_ids))
|
sale_channel_ids = list(set(sale_channel_ids))
|
||||||
record.sale_channel_ids = [(6, 0, sale_channel_ids)]
|
record.sale_channel_ids = [(6, 0, sale_channel_ids)]
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ class PmsReservationLine(models.Model):
|
|||||||
)
|
)
|
||||||
pms_property_id = fields.Many2one(
|
pms_property_id = fields.Many2one(
|
||||||
string="Property",
|
string="Property",
|
||||||
help="Property with access to the element;"
|
help="Property with access to the element",
|
||||||
" if not set, all properties can access",
|
|
||||||
readonly=True,
|
readonly=True,
|
||||||
store=True,
|
store=True,
|
||||||
comodel_name="pms.property",
|
comodel_name="pms.property",
|
||||||
@@ -119,6 +118,17 @@ class PmsReservationLine(models.Model):
|
|||||||
comodel_name="pms.sale.channel",
|
comodel_name="pms.sale.channel",
|
||||||
check_pms_properties=True,
|
check_pms_properties=True,
|
||||||
)
|
)
|
||||||
|
default_invoice_to = fields.Many2one(
|
||||||
|
string="Invoice to",
|
||||||
|
help="""Indicates the contact to which this line will be
|
||||||
|
billed by default, if it is not established,
|
||||||
|
a guest or the generic contact will be used instead""",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_default_invoice_to",
|
||||||
|
comodel_name="res.partner",
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
(
|
(
|
||||||
@@ -502,6 +512,19 @@ class PmsReservationLine(models.Model):
|
|||||||
)
|
)
|
||||||
return records
|
return records
|
||||||
|
|
||||||
|
@api.depends("sale_channel_id", "reservation_id.agency_id")
|
||||||
|
def _compute_default_invoice_to(self):
|
||||||
|
for record in self:
|
||||||
|
agency = record.reservation_id.agency_id
|
||||||
|
if (
|
||||||
|
agency
|
||||||
|
and agency.invoice_to_agency == "always"
|
||||||
|
and agency.sale_channel_id == record.sale_channel_id
|
||||||
|
):
|
||||||
|
record.default_invoice_to = agency
|
||||||
|
elif not record.default_invoice_to:
|
||||||
|
record.default_invoice_to = False
|
||||||
|
|
||||||
# Constraints and onchanges
|
# Constraints and onchanges
|
||||||
@api.constrains("date")
|
@api.constrains("date")
|
||||||
def constrains_duplicated_date(self):
|
def constrains_duplicated_date(self):
|
||||||
|
|||||||
@@ -146,14 +146,6 @@ class PmsService(models.Model):
|
|||||||
("no", "Nothing to Invoice"),
|
("no", "Nothing to Invoice"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
sale_channel_ids = fields.Many2many(
|
|
||||||
string="Sale Channels",
|
|
||||||
help="Sale Channels through which service lines were managed",
|
|
||||||
store=True,
|
|
||||||
compute="_compute_sale_channel_ids",
|
|
||||||
comodel_name="pms.sale.channel",
|
|
||||||
check_pms_properties=True,
|
|
||||||
)
|
|
||||||
sale_channel_origin_id = fields.Many2one(
|
sale_channel_origin_id = fields.Many2one(
|
||||||
string="Sale Channel Origin",
|
string="Sale Channel Origin",
|
||||||
help="Sale Channel through which service was created, the original",
|
help="Sale Channel through which service was created, the original",
|
||||||
@@ -199,6 +191,17 @@ class PmsService(models.Model):
|
|||||||
""",
|
""",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
default_invoice_to = fields.Many2one(
|
||||||
|
string="Invoice to",
|
||||||
|
help="""Indicates the contact to which this line will be
|
||||||
|
billed by default, if it is not established,
|
||||||
|
a guest or the generic contact will be used instead""",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_default_invoice_to",
|
||||||
|
comodel_name="res.partner",
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
|
||||||
# Compute and Search methods
|
# Compute and Search methods
|
||||||
@api.depends("product_id")
|
@api.depends("product_id")
|
||||||
@@ -427,12 +430,18 @@ class PmsService(models.Model):
|
|||||||
line.discount = record.discount
|
line.discount = record.discount
|
||||||
line.cancel_discount = 0
|
line.cancel_discount = 0
|
||||||
|
|
||||||
@api.depends("service_line_ids", "service_line_ids.sale_channel_id")
|
@api.depends("sale_channel_origin_id", "folio_id.agency_id")
|
||||||
def _compute_sale_channel_ids(self):
|
def _compute_default_invoice_to(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.sale_channel_ids = [
|
agency = record.folio_id.agency_id
|
||||||
(6, 0, record.mapped("service_line_ids.sale_channel_id.id"))
|
if (
|
||||||
]
|
agency
|
||||||
|
and agency.invoice_to_agency == "always"
|
||||||
|
and agency.sale_channel_id == record.sale_channel_origin_id
|
||||||
|
):
|
||||||
|
record.default_invoice_to = agency
|
||||||
|
elif not record.default_invoice_to:
|
||||||
|
record.default_invoice_to = False
|
||||||
|
|
||||||
def name_get(self):
|
def name_get(self):
|
||||||
result = []
|
result = []
|
||||||
@@ -566,7 +575,6 @@ class PmsService(models.Model):
|
|||||||
lines_to_update_channel = self.env["pms.service.line"]
|
lines_to_update_channel = self.env["pms.service.line"]
|
||||||
if "sale_channel_origin_id" in vals:
|
if "sale_channel_origin_id" in vals:
|
||||||
folios_to_update_channel = self.get_folios_to_update_channel(vals)
|
folios_to_update_channel = self.get_folios_to_update_channel(vals)
|
||||||
lines_to_update_channel = self.get_service_lines_to_update_channel(vals)
|
|
||||||
res = super(PmsService, self).write(vals)
|
res = super(PmsService, self).write(vals)
|
||||||
if folios_to_update_channel:
|
if folios_to_update_channel:
|
||||||
folios_to_update_channel.sale_channel_origin_id = vals[
|
folios_to_update_channel.sale_channel_origin_id = vals[
|
||||||
@@ -590,13 +598,3 @@ class PmsService(models.Model):
|
|||||||
):
|
):
|
||||||
folios_to_update_channel += folio
|
folios_to_update_channel += folio
|
||||||
return folios_to_update_channel
|
return folios_to_update_channel
|
||||||
|
|
||||||
def get_service_lines_to_update_channel(self, vals):
|
|
||||||
lines_to_update_channel = self.env["pms.service.line"]
|
|
||||||
for record in self:
|
|
||||||
for service_line in record.service_line_ids:
|
|
||||||
if service_line.sale_channel_id == self.sale_channel_origin_id and (
|
|
||||||
vals["sale_channel_origin_id"] != service_line.sale_channel_id.id
|
|
||||||
):
|
|
||||||
lines_to_update_channel += service_line
|
|
||||||
return lines_to_update_channel
|
|
||||||
|
|||||||
@@ -126,12 +126,6 @@ class PmsServiceLine(models.Model):
|
|||||||
readonly=True,
|
readonly=True,
|
||||||
store=True,
|
store=True,
|
||||||
)
|
)
|
||||||
sale_channel_id = fields.Many2one(
|
|
||||||
string="Sale Channel",
|
|
||||||
help="Sale Channel through which service line was created",
|
|
||||||
comodel_name="pms.sale.channel",
|
|
||||||
check_pms_properties=True,
|
|
||||||
)
|
|
||||||
auto_qty = fields.Boolean(
|
auto_qty = fields.Boolean(
|
||||||
string="Qty automated setted",
|
string="Qty automated setted",
|
||||||
help="Show if the day qty was calculated automatically",
|
help="Show if the day qty was calculated automatically",
|
||||||
@@ -139,6 +133,16 @@ class PmsServiceLine(models.Model):
|
|||||||
readonly=False,
|
readonly=False,
|
||||||
store=True,
|
store=True,
|
||||||
)
|
)
|
||||||
|
default_invoice_to = fields.Many2one(
|
||||||
|
string="Invoice to",
|
||||||
|
help="""Indicates the contact to which this line will be
|
||||||
|
billed by default, if it is not established,
|
||||||
|
a guest or the generic contact will be used instead""",
|
||||||
|
comodel_name="res.partner",
|
||||||
|
store=True,
|
||||||
|
related="service_id.default_invoice_to",
|
||||||
|
ondelete="restrict",
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends("day_qty", "discount", "price_unit", "tax_ids")
|
@api.depends("day_qty", "discount", "price_unit", "tax_ids")
|
||||||
def _compute_day_amount_service(self):
|
def _compute_day_amount_service(self):
|
||||||
@@ -255,15 +259,6 @@ class PmsServiceLine(models.Model):
|
|||||||
% (record.service_id.product_id.name, record.date)
|
% (record.service_id.product_id.name, record.date)
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.model
|
|
||||||
def create(self, vals):
|
|
||||||
if vals.get("service_id") and not vals.get("sale_channel_id"):
|
|
||||||
service = self.env["pms.service"].browse(vals["service_id"])
|
|
||||||
if service.sale_channel_origin_id:
|
|
||||||
vals["sale_channel_id"] = service.sale_channel_origin_id.id
|
|
||||||
record = super(PmsServiceLine, self).create(vals)
|
|
||||||
return record
|
|
||||||
|
|
||||||
# Business methods
|
# Business methods
|
||||||
def _cancel_discount(self):
|
def _cancel_discount(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
|||||||
@@ -192,22 +192,6 @@ class ResPartner(models.Model):
|
|||||||
string="Days from Checkout",
|
string="Days from Checkout",
|
||||||
help="Days from Checkout to generate the invoice",
|
help="Days from Checkout to generate the invoice",
|
||||||
)
|
)
|
||||||
default_invoice_lines = fields.Selection(
|
|
||||||
string="Invoice...",
|
|
||||||
help="""Use to preconfigure the sale lines to autoinvoice
|
|
||||||
for this partner. All (invoice reservations and services),
|
|
||||||
Only overnights to invoice only the reservations
|
|
||||||
with overnight and board services(exclude parkings, salon, etc...),
|
|
||||||
All reservations to include all reservations,
|
|
||||||
and Services only include services not boards""",
|
|
||||||
selection=[
|
|
||||||
("all", "All"),
|
|
||||||
("overnights", "Only Overnights"),
|
|
||||||
("reservations", "All reservations"),
|
|
||||||
("services", "Services"),
|
|
||||||
],
|
|
||||||
default="all",
|
|
||||||
)
|
|
||||||
vat_document_type = fields.Selection(
|
vat_document_type = fields.Selection(
|
||||||
string="Document Type",
|
string="Document Type",
|
||||||
help="""The vat document type of the partner,
|
help="""The vat document type of the partner,
|
||||||
|
|||||||
@@ -239,7 +239,6 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
r1.folio_id.invoice_status,
|
r1.folio_id.invoice_status,
|
||||||
"The status after a partial invoicing is not correct",
|
"The status after a partial invoicing is not correct",
|
||||||
)
|
)
|
||||||
|
|
||||||
# qty to 2 to 1st folio sale line
|
# qty to 2 to 1st folio sale line
|
||||||
dict_lines[
|
dict_lines[
|
||||||
r1.folio_id.sale_line_ids.filtered(lambda l: not l.display_type)[0].id
|
r1.folio_id.sale_line_ids.filtered(lambda l: not l.display_type)[0].id
|
||||||
@@ -699,9 +698,9 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# ASSERT
|
# ASSERT
|
||||||
self.assertEqual(
|
self.assertIn(
|
||||||
datetime.date.today() + datetime.timedelta(days=3),
|
datetime.date.today() + datetime.timedelta(days=3),
|
||||||
self.reservation1.folio_id.autoinvoice_date,
|
self.reservation1.folio_id.mapped("sale_line_ids.autoinvoice_date"),
|
||||||
"The autoinvoice date in folio with property checkout policy is wrong",
|
"The autoinvoice date in folio with property checkout policy is wrong",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -729,18 +728,21 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
self.reservation1.reservation_line_ids.default_invoice_to = self.partner_id
|
||||||
|
|
||||||
# ASSERT
|
# ASSERT
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
datetime.date.today() + datetime.timedelta(days=5),
|
datetime.date.today() + datetime.timedelta(days=5),
|
||||||
self.reservation1.folio_id.autoinvoice_date,
|
self.reservation1.folio_id.sale_line_ids.filtered(
|
||||||
|
lambda l: l.invoice_status == "to_invoice"
|
||||||
|
)[0].autoinvoice_date,
|
||||||
"The autoinvoice date in folio with property checkout policy is wrong",
|
"The autoinvoice date in folio with property checkout policy is wrong",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_autoinvoice_paid_folio_overnights_partner_policy(self):
|
def test_autoinvoice_paid_folio_overnights_partner_policy(self):
|
||||||
"""
|
"""
|
||||||
Test create and invoice the cron by partner preconfig automation
|
Test create and invoice the cron by partner preconfig automation
|
||||||
with only overnights reservations (included board services)
|
with partner setted as default invoiced to in reservation lines
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
Set partner invoicing_policy to checkout, create a reservation
|
Set partner invoicing_policy to checkout, create a reservation
|
||||||
with room, board service and normal service, run autoinvoicing
|
with room, board service and normal service, run autoinvoicing
|
||||||
@@ -750,9 +752,18 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
"""
|
"""
|
||||||
# ARRANGE
|
# ARRANGE
|
||||||
self.create_configuration_accounting_scenario()
|
self.create_configuration_accounting_scenario()
|
||||||
|
self.partner_id2 = self.env["res.partner"].create(
|
||||||
|
{
|
||||||
|
"name": "Sara",
|
||||||
|
"vat": "ES123456787",
|
||||||
|
"country_id": self.env.ref("base.es").id,
|
||||||
|
"city": "Madrid",
|
||||||
|
"zip": "28013",
|
||||||
|
"street": "Street 321",
|
||||||
|
}
|
||||||
|
)
|
||||||
self.partner_id.invoicing_policy = "checkout"
|
self.partner_id.invoicing_policy = "checkout"
|
||||||
self.partner_id.margin_days_autoinvoice = 0
|
self.partner_id.margin_days_autoinvoice = 0
|
||||||
self.partner_id.default_invoice_lines = "overnights"
|
|
||||||
self.product1 = self.env["product.product"].create(
|
self.product1 = self.env["product.product"].create(
|
||||||
{
|
{
|
||||||
"name": "Test Product 1",
|
"name": "Test Product 1",
|
||||||
@@ -796,7 +807,7 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
"checkout": datetime.date.today(),
|
"checkout": datetime.date.today(),
|
||||||
"adults": 2,
|
"adults": 2,
|
||||||
"room_type_id": self.demo_room_type_double.id,
|
"room_type_id": self.demo_room_type_double.id,
|
||||||
"partner_id": self.partner_id.id,
|
"partner_id": self.partner_id2.id,
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
"board_service_room_id": self.board_service_room_type1.id,
|
||||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||||
}
|
}
|
||||||
@@ -810,6 +821,11 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
)
|
)
|
||||||
folio = self.reservation1.folio_id
|
folio = self.reservation1.folio_id
|
||||||
reservation1 = self.reservation1
|
reservation1 = self.reservation1
|
||||||
|
reservation1.reservation_line_ids.default_invoice_to = self.partner_id
|
||||||
|
reservation1.service_ids.filtered(
|
||||||
|
"is_board_service"
|
||||||
|
).default_invoice_to = self.partner_id
|
||||||
|
|
||||||
folio.do_payment(
|
folio.do_payment(
|
||||||
journal=self.env["account.journal"].browse(
|
journal=self.env["account.journal"].browse(
|
||||||
reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]
|
reservation1.folio_id.pms_property_id._get_payment_methods().ids[0]
|
||||||
@@ -850,7 +866,6 @@ class TestPmsFolioInvoice(TestPms):
|
|||||||
# ARRANGE
|
# ARRANGE
|
||||||
self.partner_id.invoicing_policy = "checkout"
|
self.partner_id.invoicing_policy = "checkout"
|
||||||
self.partner_id.margin_days_autoinvoice = 0
|
self.partner_id.margin_days_autoinvoice = 0
|
||||||
self.partner_id.default_invoice_lines = "overnights"
|
|
||||||
self.product1 = self.env["product.product"].create(
|
self.product1 = self.env["product.product"].create(
|
||||||
{
|
{
|
||||||
"name": "Test Product 1",
|
"name": "Test Product 1",
|
||||||
|
|||||||
@@ -116,270 +116,6 @@ class TestPmsService(TestPms):
|
|||||||
"sale_channel_origin of board_Service must be the same as its reservation",
|
"sale_channel_origin of board_Service must be the same as its reservation",
|
||||||
)
|
)
|
||||||
|
|
||||||
@freeze_time("2002-01-03")
|
|
||||||
def test_board_service_origin_as_sale_channel_in_lines(self):
|
|
||||||
"""
|
|
||||||
The lines of board_service when created have as sale_channel_id
|
|
||||||
the sale_channel_origin_id of its board_service
|
|
||||||
|
|
||||||
Board_service ---> sale_channel_origin = Door
|
|
||||||
|
|
|
||||||
---> service_line.sale_channel? It must be DOOR
|
|
||||||
"""
|
|
||||||
# ARRANGE
|
|
||||||
self.product1 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service1 = self.env["pms.board.service"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Board Service 1",
|
|
||||||
"default_code": "CB1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.board_service_line1 = self.env["pms.board.service.line"].create(
|
|
||||||
{
|
|
||||||
"product_id": self.product1.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
"amount": 10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service_room_type1 = self.env["pms.board.service.room.type"].create(
|
|
||||||
{
|
|
||||||
"pms_room_type_id": self.room_type_double.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ACT
|
|
||||||
self.reservation = self.env["pms.reservation"].create(
|
|
||||||
{
|
|
||||||
"checkin": fields.date.today(),
|
|
||||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
|
||||||
"room_type_id": self.room_type_double.id,
|
|
||||||
"partner_id": self.partner1.id,
|
|
||||||
"pms_property_id": self.pms_property1.id,
|
|
||||||
"pricelist_id": self.pricelist1.id,
|
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
|
||||||
"sale_channel_origin_id": self.sale_channel_door.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ASSERT
|
|
||||||
self.assertEqual(
|
|
||||||
self.reservation.service_ids.sale_channel_origin_id,
|
|
||||||
self.reservation.service_ids.service_line_ids.mapped("sale_channel_id"),
|
|
||||||
"sale_channel_origin of board_Service must be the same as its service_lines",
|
|
||||||
)
|
|
||||||
|
|
||||||
@freeze_time("2002-01-05")
|
|
||||||
def test_sale_channel_in_new_line_of_board_service(self):
|
|
||||||
"""
|
|
||||||
When adding a line to a board_service already created, by default,
|
|
||||||
the sale_channel of the line will be the same as the sale_channel_origin_id
|
|
||||||
of its board_service
|
|
||||||
Reservation --> sale_channel_ids = { Door, Phone }
|
|
||||||
|
|
|
||||||
--> board_service.sale_channel_origin = Door
|
|
||||||
|
|
|
||||||
--> board_service.service_line[0].sale_channel = Door
|
|
||||||
--> NEW board_service.service_line[1].sale_channel?
|
|
||||||
It must be Door --CHECKING THIS--
|
|
||||||
"""
|
|
||||||
# ARRANGE
|
|
||||||
self.product1 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service1 = self.env["pms.board.service"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Board Service 1",
|
|
||||||
"default_code": "CB1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.board_service_line1 = self.env["pms.board.service.line"].create(
|
|
||||||
{
|
|
||||||
"product_id": self.product1.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
"amount": 10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service_room_type1 = self.env["pms.board.service.room.type"].create(
|
|
||||||
{
|
|
||||||
"pms_room_type_id": self.room_type_double.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.reservation = self.env["pms.reservation"].create(
|
|
||||||
{
|
|
||||||
"checkin": fields.date.today(),
|
|
||||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
|
||||||
"room_type_id": self.room_type_double.id,
|
|
||||||
"partner_id": self.partner1.id,
|
|
||||||
"pms_property_id": self.pms_property1.id,
|
|
||||||
"pricelist_id": self.pricelist1.id,
|
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
|
||||||
"sale_channel_origin_id": self.sale_channel_door.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ACT
|
|
||||||
self.env["pms.service.line"].create(
|
|
||||||
{
|
|
||||||
"service_id": self.reservation.service_ids.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ASSERT
|
|
||||||
self.assertEqual(
|
|
||||||
self.reservation.service_ids.sale_channel_origin_id,
|
|
||||||
self.reservation.service_ids.service_line_ids.mapped("sale_channel_id"),
|
|
||||||
"sale_channel_origin of board_Service must corresponds to the"
|
|
||||||
"sale_channel_id of its new service_line",
|
|
||||||
)
|
|
||||||
|
|
||||||
@freeze_time("2002-01-07")
|
|
||||||
def test_sale_channel_different_in_lines_check_sale_channel_ids(self):
|
|
||||||
"""
|
|
||||||
Check that sale_channel_ids is calculated well (in board_service, folio and
|
|
||||||
reservation) when a board service has lines from different sale_channels
|
|
||||||
|
|
||||||
Reservation --> sale_channel_ids = { Door, Phone } --CHECKING THIS--
|
|
||||||
|
|
|
||||||
--> board_service.sale_channel_origin = Door
|
|
||||||
|
|
|
||||||
--> board_service.service_line[0].sale_channel = Door
|
|
||||||
--> board_service.service_line[1].sale_channel = Phone
|
|
||||||
|
|
||||||
reservation.folio_id.sale_channel_ids = { Door, Phone } --CHECKING THIS--
|
|
||||||
"""
|
|
||||||
# ARRANGE
|
|
||||||
self.product1 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service1 = self.env["pms.board.service"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Board Service 1",
|
|
||||||
"default_code": "CB1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.board_service_line1 = self.env["pms.board.service.line"].create(
|
|
||||||
{
|
|
||||||
"product_id": self.product1.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
"amount": 10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service_room_type1 = self.env["pms.board.service.room.type"].create(
|
|
||||||
{
|
|
||||||
"pms_room_type_id": self.room_type_double.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.reservation = self.env["pms.reservation"].create(
|
|
||||||
{
|
|
||||||
"checkin": fields.date.today(),
|
|
||||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
|
||||||
"room_type_id": self.room_type_double.id,
|
|
||||||
"partner_id": self.partner1.id,
|
|
||||||
"pms_property_id": self.pms_property1.id,
|
|
||||||
"pricelist_id": self.pricelist1.id,
|
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
|
||||||
"sale_channel_origin_id": self.sale_channel_door.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ACT
|
|
||||||
self.env["pms.service.line"].create(
|
|
||||||
{
|
|
||||||
"service_id": self.reservation.service_ids.id,
|
|
||||||
"sale_channel_id": self.sale_channel_phone.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
sale_channel_ids = [
|
|
||||||
self.reservation.folio_id.sale_channel_ids.ids,
|
|
||||||
self.reservation.sale_channel_ids.ids,
|
|
||||||
self.reservation.service_ids.sale_channel_ids.ids,
|
|
||||||
]
|
|
||||||
|
|
||||||
expected_sale_channel_ids = [
|
|
||||||
self.sale_channel_door.id,
|
|
||||||
self.sale_channel_phone.id,
|
|
||||||
]
|
|
||||||
# ASSERT
|
|
||||||
for sale_channel in sale_channel_ids:
|
|
||||||
with self.subTest(k=sale_channel):
|
|
||||||
self.assertItemsEqual(
|
|
||||||
sale_channel,
|
|
||||||
expected_sale_channel_ids,
|
|
||||||
"sale_channel_ids must contain sale_channel_id of all board_service_lines",
|
|
||||||
)
|
|
||||||
|
|
||||||
@freeze_time("2002-01-09")
|
|
||||||
def test_sale_channel_equal_in_lines_check_sale_channel_ids(self):
|
|
||||||
"""
|
|
||||||
Check that sale_channel_ids of service is calculated correctly when its lines
|
|
||||||
have the sale sale_channel_id
|
|
||||||
"""
|
|
||||||
# ARRANGE
|
|
||||||
self.product1 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service1 = self.env["pms.board.service"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Board Service 1",
|
|
||||||
"default_code": "CB1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.board_service_line1 = self.env["pms.board.service.line"].create(
|
|
||||||
{
|
|
||||||
"product_id": self.product1.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
"amount": 10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service_room_type1 = self.env["pms.board.service.room.type"].create(
|
|
||||||
{
|
|
||||||
"pms_room_type_id": self.room_type_double.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ACT
|
|
||||||
self.reservation = self.env["pms.reservation"].create(
|
|
||||||
{
|
|
||||||
"checkin": fields.date.today(),
|
|
||||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
|
||||||
"room_type_id": self.room_type_double.id,
|
|
||||||
"partner_id": self.partner1.id,
|
|
||||||
"pms_property_id": self.pms_property1.id,
|
|
||||||
"pricelist_id": self.pricelist1.id,
|
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
|
||||||
"sale_channel_origin_id": self.sale_channel_door.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.env["pms.service.line"].create(
|
|
||||||
{
|
|
||||||
"service_id": self.reservation.service_ids.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ASSERT
|
|
||||||
self.assertEqual(
|
|
||||||
self.reservation.service_ids.sale_channel_ids,
|
|
||||||
self.reservation.service_ids.service_line_ids.mapped("sale_channel_id"),
|
|
||||||
"""sale_channel_ids of board_Service must correspond
|
|
||||||
to the sale_channel_id set of its service_lines""",
|
|
||||||
)
|
|
||||||
|
|
||||||
@freeze_time("2002-01-11")
|
@freeze_time("2002-01-11")
|
||||||
def test_change_origin_board_service_not_change_reservation_origin(self):
|
def test_change_origin_board_service_not_change_reservation_origin(self):
|
||||||
"""
|
"""
|
||||||
@@ -447,158 +183,6 @@ class TestPmsService(TestPms):
|
|||||||
with sale_channel_origin_id of its reservation""",
|
with sale_channel_origin_id of its reservation""",
|
||||||
)
|
)
|
||||||
|
|
||||||
@freeze_time("2002-01-13")
|
|
||||||
def test_change_origin_board_service_change_in_lines(self):
|
|
||||||
"""
|
|
||||||
Check that when changing sale_channel_origin_id of board_service they
|
|
||||||
change their lines matching the changed channel
|
|
||||||
|
|
||||||
Service --> sale_channel_origin_id = Door sale_channel_ids = {Door, Phone}
|
|
||||||
|
|
|
||||||
--> service_line[0].sale_channel_id = Door
|
|
||||||
|
|
|
||||||
--> service_line[1].sale_channel_id = Phone
|
|
||||||
|
|
||||||
Change service origin to mail, expected results:
|
|
||||||
Service --> sale_channel_origin_id = Mail sale_channel_ids = {Mail, Phone}
|
|
||||||
|
|
|
||||||
--> service_line[0].sale_channel_id = Mail -----------CHECKING THIS---
|
|
||||||
|
|
|
||||||
--> service_line[1].sale_channel_id = Phone
|
|
||||||
"""
|
|
||||||
# ARRANGE
|
|
||||||
self.product1 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service1 = self.env["pms.board.service"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Board Service 1",
|
|
||||||
"default_code": "CB1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.board_service_line1 = self.env["pms.board.service.line"].create(
|
|
||||||
{
|
|
||||||
"product_id": self.product1.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
"amount": 10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service_room_type1 = self.env["pms.board.service.room.type"].create(
|
|
||||||
{
|
|
||||||
"pms_room_type_id": self.room_type_double.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.reservation = self.env["pms.reservation"].create(
|
|
||||||
{
|
|
||||||
"checkin": fields.date.today(),
|
|
||||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
|
||||||
"room_type_id": self.room_type_double.id,
|
|
||||||
"partner_id": self.partner1.id,
|
|
||||||
"pms_property_id": self.pms_property1.id,
|
|
||||||
"pricelist_id": self.pricelist1.id,
|
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
|
||||||
"sale_channel_origin_id": self.sale_channel_door.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.env["pms.service.line"].create(
|
|
||||||
{
|
|
||||||
"service_id": self.reservation.service_ids.id,
|
|
||||||
"sale_channel_id": self.sale_channel_phone.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ACT
|
|
||||||
self.reservation.service_ids.sale_channel_origin_id = self.sale_channel_mail
|
|
||||||
|
|
||||||
# ASSERT
|
|
||||||
self.assertIn(
|
|
||||||
self.sale_channel_mail,
|
|
||||||
self.reservation.service_ids.service_line_ids.mapped("sale_channel_id"),
|
|
||||||
"""new sale_channel_origin_id of board service
|
|
||||||
must be changed in lines with same sale_channel""",
|
|
||||||
)
|
|
||||||
|
|
||||||
@freeze_time("2002-01-15")
|
|
||||||
def test_change_origin_board_service_no_change_in_lines(self):
|
|
||||||
"""
|
|
||||||
Check that when changing sale_channel_origin_id of board_service, those of its
|
|
||||||
service_lines that didn't match with the changed origin shouldn't be changed
|
|
||||||
|
|
||||||
Service --> sale_channel_origin_id = Door sale_channel_ids = {Door, Phone}
|
|
||||||
|
|
|
||||||
--> service_line[0].sale_channel_id = Door
|
|
||||||
|
|
|
||||||
--> service_line[1].sale_channel_id = Phone
|
|
||||||
|
|
||||||
Change service origin to mail, expected results:
|
|
||||||
Service --> sale_channel_origin_id = Mail sale_channel_ids = {Mail, Phone}
|
|
||||||
|
|
|
||||||
--> service_line[0].sale_channel_id = Mail
|
|
||||||
|
|
|
||||||
--> service_line[1].sale_channel_id = Phone -----------CHECKING THIS---
|
|
||||||
"""
|
|
||||||
# ARRANGE
|
|
||||||
self.product1 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service1 = self.env["pms.board.service"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Board Service 1",
|
|
||||||
"default_code": "CB1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.board_service_line1 = self.env["pms.board.service.line"].create(
|
|
||||||
{
|
|
||||||
"product_id": self.product1.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
"amount": 10,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.board_service_room_type1 = self.env["pms.board.service.room.type"].create(
|
|
||||||
{
|
|
||||||
"pms_room_type_id": self.room_type_double.id,
|
|
||||||
"pms_board_service_id": self.board_service1.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
self.reservation = self.env["pms.reservation"].create(
|
|
||||||
{
|
|
||||||
"checkin": fields.date.today(),
|
|
||||||
"checkout": fields.date.today() + datetime.timedelta(days=3),
|
|
||||||
"room_type_id": self.room_type_double.id,
|
|
||||||
"partner_id": self.partner1.id,
|
|
||||||
"pms_property_id": self.pms_property1.id,
|
|
||||||
"pricelist_id": self.pricelist1.id,
|
|
||||||
"board_service_room_id": self.board_service_room_type1.id,
|
|
||||||
"sale_channel_origin_id": self.sale_channel_door.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.env["pms.service.line"].create(
|
|
||||||
{
|
|
||||||
"service_id": self.reservation.service_ids.id,
|
|
||||||
"sale_channel_id": self.sale_channel_phone.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ACT
|
|
||||||
self.reservation.service_ids.sale_channel_origin_id = self.sale_channel_mail
|
|
||||||
|
|
||||||
# ASSERT
|
|
||||||
self.assertIn(
|
|
||||||
self.sale_channel_phone,
|
|
||||||
self.reservation.service_ids.service_line_ids.mapped("sale_channel_id"),
|
|
||||||
"""new sale_channel_origin_id of board service
|
|
||||||
mustn't changed in lines with different sale_channel""",
|
|
||||||
)
|
|
||||||
|
|
||||||
@freeze_time("2002-01-17")
|
@freeze_time("2002-01-17")
|
||||||
def test_change_origin_board_service_in_sale_channels(self):
|
def test_change_origin_board_service_in_sale_channels(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -500,6 +500,7 @@
|
|||||||
attrs="{'column_invisible': [('parent.state','!=','cancel')]}"
|
attrs="{'column_invisible': [('parent.state','!=','cancel')]}"
|
||||||
/>
|
/>
|
||||||
<field name="sale_channel_id" />
|
<field name="sale_channel_id" />
|
||||||
|
<field name="default_invoice_to" />
|
||||||
<field name="pms_property_id" invisible="1" />
|
<field name="pms_property_id" invisible="1" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
@@ -564,6 +565,10 @@
|
|||||||
force_save="1"
|
force_save="1"
|
||||||
/>
|
/>
|
||||||
<field name="sale_channel_origin_id" />
|
<field name="sale_channel_origin_id" />
|
||||||
|
<field
|
||||||
|
name="default_invoice_to"
|
||||||
|
optional="hide"
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
type="object"
|
type="object"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
<field name="product_id" />
|
<field name="product_id" />
|
||||||
<field name="day_qty" />
|
<field name="day_qty" />
|
||||||
<field name="date" />
|
<field name="date" />
|
||||||
<field name="sale_channel_id" />
|
|
||||||
<field name="price_unit" />
|
<field name="price_unit" />
|
||||||
<field
|
<field
|
||||||
name="discount"
|
name="discount"
|
||||||
@@ -30,7 +29,6 @@
|
|||||||
<field name="product_id" />
|
<field name="product_id" />
|
||||||
<field name="day_qty" />
|
<field name="day_qty" />
|
||||||
<field name="date" />
|
<field name="date" />
|
||||||
<field name="sale_channel_id" />
|
|
||||||
<field name="price_unit" />
|
<field name="price_unit" />
|
||||||
<field
|
<field
|
||||||
name="discount"
|
name="discount"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
force_save="1"
|
force_save="1"
|
||||||
/>
|
/>
|
||||||
<field name="sale_channel_origin_id" />
|
<field name="sale_channel_origin_id" />
|
||||||
<field name="sale_channel_ids" widget="many2many_tags" />
|
<field name="default_invoice_to" />
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<field name="tax_ids" widget="many2many_tags" invisible="1" />
|
<field name="tax_ids" widget="many2many_tags" invisible="1" />
|
||||||
@@ -38,7 +38,6 @@
|
|||||||
<field name="service_line_ids" nolabel="1">
|
<field name="service_line_ids" nolabel="1">
|
||||||
<tree string="Days" editable="bottom">
|
<tree string="Days" editable="bottom">
|
||||||
<field name="date" />
|
<field name="date" />
|
||||||
<field name="sale_channel_id" />
|
|
||||||
<field name="day_qty" />
|
<field name="day_qty" />
|
||||||
<field name="price_unit" />
|
<field name="price_unit" />
|
||||||
<field
|
<field
|
||||||
@@ -91,7 +90,7 @@
|
|||||||
force_save="1"
|
force_save="1"
|
||||||
/>
|
/>
|
||||||
<field name="sale_channel_origin_id" />
|
<field name="sale_channel_origin_id" />
|
||||||
<field name="sale_channel_ids" />
|
<field name="default_invoice_to" />
|
||||||
<button
|
<button
|
||||||
type="object"
|
type="object"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
@@ -106,7 +105,6 @@
|
|||||||
<field name="service_line_ids" invisible="1">
|
<field name="service_line_ids" invisible="1">
|
||||||
<tree string="Days">
|
<tree string="Days">
|
||||||
<field name="date" />
|
<field name="date" />
|
||||||
<field name="sale_channel_id" />
|
|
||||||
<field name="day_qty" />
|
<field name="day_qty" />
|
||||||
<field
|
<field
|
||||||
name="discount"
|
name="discount"
|
||||||
|
|||||||
@@ -157,7 +157,6 @@
|
|||||||
name="margin_days_autoinvoice"
|
name="margin_days_autoinvoice"
|
||||||
attrs="{'invisible': [('invoicing_policy', '!=', 'checkout')]}"
|
attrs="{'invisible': [('invoicing_policy', '!=', 'checkout')]}"
|
||||||
/>
|
/>
|
||||||
<field name="default_invoice_lines" />
|
|
||||||
</group>
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
Reference in New Issue
Block a user