mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP]pms: autoinvoicing folio cron improvements
This commit is contained in:
@@ -46,6 +46,8 @@
|
|||||||
"report/invoice.xml",
|
"report/invoice.xml",
|
||||||
# "templates/pms_email_template.xml",
|
# "templates/pms_email_template.xml",
|
||||||
"data/menus.xml",
|
"data/menus.xml",
|
||||||
|
"data/queue_data.xml",
|
||||||
|
"data/queue_job_function_data.xml",
|
||||||
"wizards/wizard_payment_folio.xml",
|
"wizards/wizard_payment_folio.xml",
|
||||||
"wizards/folio_make_invoice_advance_views.xml",
|
"wizards/folio_make_invoice_advance_views.xml",
|
||||||
"wizards/pms_booking_engine_views.xml",
|
"wizards/pms_booking_engine_views.xml",
|
||||||
|
|||||||
9
pms/data/queue_data.xml
Normal file
9
pms/data/queue_data.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="channel_autoinvocing_folios" model="queue.job.channel">
|
||||||
|
<field name="name">autoinvoing folios</field>
|
||||||
|
<field name="parent_id" ref="queue_job.channel_root" />
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
15
pms/data/queue_job_function_data.xml
Normal file
15
pms/data/queue_job_function_data.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo noupdate="1">
|
||||||
|
<record id="autoinvoice_folio_job_function" model="queue.job.function">
|
||||||
|
<field name="model_id" ref="pms.model_pms_property" />
|
||||||
|
<field name="method">autoinvoice_folio</field>
|
||||||
|
<field name="channel_id" ref="pms.channel_autoinvocing_folios" />
|
||||||
|
<field name="retry_pattern" eval="{1: 10, 5: 30, 10: 60, 15: 300}" />
|
||||||
|
</record>
|
||||||
|
<record id="autoinvoice_folio_job_function" model="queue.job.function">
|
||||||
|
<field name="model_id" ref="pms.model_pms_property" />
|
||||||
|
<field name="method">autovalidate_folio_invoice</field>
|
||||||
|
<field name="channel_id" ref="pms.channel_autoinvocing_folios" />
|
||||||
|
<field name="retry_pattern" eval="{1: 10, 5: 30, 10: 60, 15: 300}" />
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
@@ -539,9 +539,9 @@ class FolioSaleLine(models.Model):
|
|||||||
where Total_sol depends on the invoice policy of the product.
|
where Total_sol depends on the invoice policy of the product.
|
||||||
|
|
||||||
Note: Draft invoice are ignored on purpose, the 'to invoice' amount should
|
Note: Draft invoice are ignored on purpose, the 'to invoice' amount should
|
||||||
come only from the SO lines.
|
come only from the folio lines.
|
||||||
"""
|
"""
|
||||||
for line in self:
|
for line in self.filtered("invoice_lines"):
|
||||||
amount_to_invoice = 0.0
|
amount_to_invoice = 0.0
|
||||||
if line.state != "draft":
|
if line.state != "draft":
|
||||||
# Note: do not use price_subtotal field as it returns
|
# Note: do not use price_subtotal field as it returns
|
||||||
|
|||||||
@@ -285,6 +285,13 @@ class PmsFolio(models.Model):
|
|||||||
compute="_compute_get_invoiced",
|
compute="_compute_get_invoiced",
|
||||||
search="_search_invoice_ids",
|
search="_search_invoice_ids",
|
||||||
)
|
)
|
||||||
|
untaxed_amount_to_invoice = fields.Monetary(
|
||||||
|
string="Amount to invoice",
|
||||||
|
help="The amount to invoice",
|
||||||
|
readonly=True,
|
||||||
|
store=True,
|
||||||
|
compute="_compute_untaxed_amount_to_invoice",
|
||||||
|
)
|
||||||
payment_state = fields.Selection(
|
payment_state = fields.Selection(
|
||||||
string="Payment Status",
|
string="Payment Status",
|
||||||
help="The state of the payment",
|
help="The state of the payment",
|
||||||
@@ -493,6 +500,9 @@ class PmsFolio(models.Model):
|
|||||||
"nothin to invoice, even when there may be ordered "
|
"nothin to invoice, even when there may be ordered "
|
||||||
"quantities pending to invoice.",
|
"quantities pending to invoice.",
|
||||||
copy=False,
|
copy=False,
|
||||||
|
compute="_compute_force_nothing_to_invoice",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
)
|
)
|
||||||
internal_comment = fields.Text(
|
internal_comment = fields.Text(
|
||||||
string="Internal Folio Notes",
|
string="Internal Folio Notes",
|
||||||
@@ -555,11 +565,6 @@ class PmsFolio(models.Model):
|
|||||||
compute="_compute_days_to_checkout",
|
compute="_compute_days_to_checkout",
|
||||||
search="_search_days_to_checkout",
|
search="_search_days_to_checkout",
|
||||||
)
|
)
|
||||||
autoinvoice_date = fields.Date(
|
|
||||||
string="Autoinvoice Date",
|
|
||||||
compute="_compute_autoinvoice_date",
|
|
||||||
store=True,
|
|
||||||
)
|
|
||||||
invoice_to_agency = fields.Boolean(
|
invoice_to_agency = fields.Boolean(
|
||||||
string="Invoice Agency",
|
string="Invoice Agency",
|
||||||
help="""Indicates if agency invoices partner
|
help="""Indicates if agency invoices partner
|
||||||
@@ -1024,7 +1029,7 @@ class PmsFolio(models.Model):
|
|||||||
for folio in self:
|
for folio in self:
|
||||||
folio.access_url = "/my/folios/%s" % (folio.id)
|
folio.access_url = "/my/folios/%s" % (folio.id)
|
||||||
|
|
||||||
@api.depends("state", "sale_line_ids.invoice_status")
|
@api.depends("state", "sale_line_ids.invoice_status", "force_nothing_to_invoice")
|
||||||
def _compute_get_invoice_status(self):
|
def _compute_get_invoice_status(self):
|
||||||
"""
|
"""
|
||||||
Compute the invoice status of a Folio. Possible statuses:
|
Compute the invoice status of a Folio. Possible statuses:
|
||||||
@@ -1057,21 +1062,41 @@ class PmsFolio(models.Model):
|
|||||||
line_invoice_status = [
|
line_invoice_status = [
|
||||||
d[1] for d in line_invoice_status_all if d[0] == order.id
|
d[1] for d in line_invoice_status_all if d[0] == order.id
|
||||||
]
|
]
|
||||||
if order.force_nothing_to_invoice:
|
if not order.force_nothing_to_invoice and any(
|
||||||
order.invoice_status = "no"
|
|
||||||
elif any(
|
|
||||||
invoice_status == "to_invoice" for invoice_status in line_invoice_status
|
invoice_status == "to_invoice" for invoice_status in line_invoice_status
|
||||||
):
|
):
|
||||||
order.invoice_status = "to_invoice"
|
order.invoice_status = "to_invoice"
|
||||||
elif any(inv.state == "draft" for inv in order.move_ids):
|
elif any(inv.state == "draft" for inv in order.move_ids):
|
||||||
order.invoice_status = "to_confirm"
|
order.invoice_status = "to_confirm"
|
||||||
elif line_invoice_status and all(
|
elif line_invoice_status and any(
|
||||||
invoice_status == "invoiced" for invoice_status in line_invoice_status
|
invoice_status == "invoiced" for invoice_status in line_invoice_status
|
||||||
):
|
):
|
||||||
order.invoice_status = "invoiced"
|
if (
|
||||||
|
all(
|
||||||
|
invoice_status == "invoiced"
|
||||||
|
for invoice_status in line_invoice_status
|
||||||
|
)
|
||||||
|
or order.force_nothing_to_invoice
|
||||||
|
):
|
||||||
|
order.invoice_status = "invoiced"
|
||||||
|
else:
|
||||||
|
order.invoice_status = "no"
|
||||||
else:
|
else:
|
||||||
order.invoice_status = "no"
|
order.invoice_status = "no"
|
||||||
|
|
||||||
|
@api.depends("untaxed_amount_to_invoice", "amount_total")
|
||||||
|
def _compute_force_nothing_to_invoice(self):
|
||||||
|
# If the invoice amount and amount total are the same,
|
||||||
|
# and the qty to invoice is not 0, we force nothing to invoice
|
||||||
|
for order in self:
|
||||||
|
if (
|
||||||
|
order.untaxed_amount_to_invoice <= 0
|
||||||
|
and sum(order.sale_line_ids.mapped("qty_to_invoice")) != 0
|
||||||
|
):
|
||||||
|
order.force_nothing_to_invoice = True
|
||||||
|
else:
|
||||||
|
order.force_nothing_to_invoice = False
|
||||||
|
|
||||||
@api.depends("partner_id", "partner_id.name", "agency_id", "reservation_type")
|
@api.depends("partner_id", "partner_id.name", "agency_id", "reservation_type")
|
||||||
def _compute_partner_name(self):
|
def _compute_partner_name(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -1174,6 +1199,12 @@ class PmsFolio(models.Model):
|
|||||||
/ sum(folio.reservation_ids.mapped("adults"))
|
/ sum(folio.reservation_ids.mapped("adults"))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _compute_untaxed_amount_to_invoice(self):
|
||||||
|
for folio in self:
|
||||||
|
folio.untaxed_amount_to_invoice = sum(
|
||||||
|
folio.mapped("sale_line_ids.untaxed_amount_to_invoice")
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: Add return_ids to depends
|
# TODO: Add return_ids to depends
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"amount_total",
|
"amount_total",
|
||||||
@@ -1851,11 +1882,9 @@ class PmsFolio(models.Model):
|
|||||||
invoice_vals_list = self._get_group_vals_list(invoice_vals_list)
|
invoice_vals_list = self._get_group_vals_list(invoice_vals_list)
|
||||||
|
|
||||||
partner_invoice = self.env["res.partner"].browse(partner_invoice_id)
|
partner_invoice = self.env["res.partner"].browse(partner_invoice_id)
|
||||||
partner_invoice_policy = (
|
partner_invoice_policy = self.pms_property_id.default_invoicing_policy
|
||||||
self.pms_property_id.default_invoicing_policy
|
if partner_invoice and partner_invoice.invoicing_policy != "property":
|
||||||
if partner_invoice.invoicing_policy == "property"
|
partner_invoice_policy = partner_invoice.invoicing_policy
|
||||||
else partner_invoice.invoicing_policy
|
|
||||||
)
|
|
||||||
invoice_date = False
|
invoice_date = False
|
||||||
if date:
|
if date:
|
||||||
invoice_date = date
|
invoice_date = date
|
||||||
@@ -1899,11 +1928,13 @@ class PmsFolio(models.Model):
|
|||||||
"Please contact your administrator to unlock it."
|
"Please contact your administrator to unlock it."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if invoice_date < datetime.date.today():
|
if invoice_date < datetime.date.today() and not self._context.get(
|
||||||
|
"autoinvoice"
|
||||||
|
):
|
||||||
invoice_date = datetime.date.today()
|
invoice_date = datetime.date.today()
|
||||||
key_field = (
|
key_field = (
|
||||||
"invoice_date"
|
"invoice_date"
|
||||||
if invoice_date == fields.Date.today()
|
if invoice_date <= fields.Date.today()
|
||||||
else "invoice_date_due"
|
else "invoice_date_due"
|
||||||
)
|
)
|
||||||
for vals in invoice_vals_list:
|
for vals in invoice_vals_list:
|
||||||
|
|||||||
@@ -661,7 +661,7 @@ class PmsProperty(models.Model):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def autoinvoicing(self, offset=0):
|
def autoinvoicing(self, offset=0, with_delay=False):
|
||||||
"""
|
"""
|
||||||
This method is used to invoicing automatically the folios
|
This method is used to invoicing automatically the folios
|
||||||
and validate the draft invoices created by the folios
|
and validate the draft invoices created by the folios
|
||||||
@@ -689,58 +689,11 @@ class PmsProperty(models.Model):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
for folio in folios_to_invoice:
|
for folio in folios_to_invoice:
|
||||||
try:
|
if with_delay:
|
||||||
# REVIEW: folio sale line "_compute_auotinvoice_date" sometimes
|
self.with_delay().autoinvoice_folio(folio)
|
||||||
# dont work in services (probably cache issue¿?), we ensure that the date is
|
else:
|
||||||
# set or recompute this
|
self.autoinvoice_folio(folio)
|
||||||
for line in folio.sale_line_ids.filtered(
|
|
||||||
lambda l: not l.autoinvoice_date
|
|
||||||
):
|
|
||||||
line._compute_autoinvoice_date()
|
|
||||||
# REVIEW: Reverse downpayment invoices if the downpayment is not included
|
|
||||||
# in the service invoice (qty_to_invoice < 0)
|
|
||||||
downpayment_invoices = (
|
|
||||||
folio.sale_line_ids.filtered(
|
|
||||||
lambda l: l.is_downpayment and l.qty_to_invoice < 0
|
|
||||||
)
|
|
||||||
.mapped("invoice_lines")
|
|
||||||
.mapped("move_id")
|
|
||||||
.filtered(lambda i: i.is_simplified_invoice)
|
|
||||||
)
|
|
||||||
if downpayment_invoices:
|
|
||||||
downpayment_invoices._reverse_moves(cancel=True)
|
|
||||||
invoices = folio.with_context(autoinvoice=True)._create_invoices(
|
|
||||||
grouped=True,
|
|
||||||
final=True,
|
|
||||||
)
|
|
||||||
for invoice in invoices:
|
|
||||||
if (
|
|
||||||
invoice.amount_total
|
|
||||||
> invoice.pms_property_id.max_amount_simplified_invoice
|
|
||||||
and invoice.journal_id.is_simplified_invoice
|
|
||||||
):
|
|
||||||
hosts_to_invoice = (
|
|
||||||
invoice.folio_ids.partner_invoice_ids.filtered(
|
|
||||||
lambda p: p._check_enought_invoice_data()
|
|
||||||
).mapped("id")
|
|
||||||
)
|
|
||||||
if hosts_to_invoice:
|
|
||||||
invoice.partner_id = hosts_to_invoice[0]
|
|
||||||
invoice.journal_id = (
|
|
||||||
invoice.pms_property_id.journal_normal_invoice_id
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
mens = _(
|
|
||||||
"The total amount of the simplified invoice is higher than the "
|
|
||||||
"maximum amount allowed for simplified invoices, and dont have "
|
|
||||||
"enought data in hosts to create a normal invoice."
|
|
||||||
)
|
|
||||||
if self.folio_ids:
|
|
||||||
self.folio_ids.message_post(body=mens)
|
|
||||||
raise ValidationError(mens)
|
|
||||||
invoice.action_post()
|
|
||||||
except Exception as e:
|
|
||||||
folio.message_post(body=_("Error in autoinvoicing folio: " + str(e)))
|
|
||||||
draft_invoices_to_post = self.env["account.move"].search(
|
draft_invoices_to_post = self.env["account.move"].search(
|
||||||
[
|
[
|
||||||
("state", "=", "draft"),
|
("state", "=", "draft"),
|
||||||
@@ -749,12 +702,67 @@ class PmsProperty(models.Model):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
for invoice in draft_invoices_to_post:
|
for invoice in draft_invoices_to_post:
|
||||||
try:
|
if with_delay:
|
||||||
invoice.action_post()
|
self.with_delay().autovalidate_folio_invoice(invoice)
|
||||||
except Exception as e:
|
else:
|
||||||
invoice.message_post(body=_("Error in autovalidate invoice: " + str(e)))
|
self.autovalidate_folio_invoice(invoice)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def autovalidate_folio_invoice(self, invoice):
|
||||||
|
try:
|
||||||
|
invoice.action_post()
|
||||||
|
except Exception as e:
|
||||||
|
invoice.message_post(body=_("Error in autovalidate invoice: " + str(e)))
|
||||||
|
|
||||||
|
def autoinvoice_folio(self, folio):
|
||||||
|
try:
|
||||||
|
# REVIEW: folio sale line "_compute_auotinvoice_date" sometimes
|
||||||
|
# dont work in services (probably cache issue¿?), we ensure that the date is
|
||||||
|
# set or recompute this
|
||||||
|
for line in folio.sale_line_ids.filtered(lambda l: not l.autoinvoice_date):
|
||||||
|
line._compute_autoinvoice_date()
|
||||||
|
# REVIEW: Reverse downpayment invoices if the downpayment is not included
|
||||||
|
# in the service invoice (qty_to_invoice < 0)
|
||||||
|
downpayment_invoices = (
|
||||||
|
folio.sale_line_ids.filtered(
|
||||||
|
lambda l: l.is_downpayment and l.qty_to_invoice < 0
|
||||||
|
)
|
||||||
|
.mapped("invoice_lines")
|
||||||
|
.mapped("move_id")
|
||||||
|
.filtered(lambda i: i.is_simplified_invoice)
|
||||||
|
)
|
||||||
|
if downpayment_invoices:
|
||||||
|
downpayment_invoices._reverse_moves(cancel=True)
|
||||||
|
invoices = folio.with_context(autoinvoice=True)._create_invoices(
|
||||||
|
grouped=True,
|
||||||
|
final=True,
|
||||||
|
)
|
||||||
|
for invoice in invoices:
|
||||||
|
if (
|
||||||
|
invoice.amount_total
|
||||||
|
> invoice.pms_property_id.max_amount_simplified_invoice
|
||||||
|
and invoice.journal_id.is_simplified_invoice
|
||||||
|
):
|
||||||
|
hosts_to_invoice = invoice.folio_ids.partner_invoice_ids.filtered(
|
||||||
|
lambda p: p._check_enought_invoice_data()
|
||||||
|
).mapped("id")
|
||||||
|
if hosts_to_invoice:
|
||||||
|
invoice.partner_id = hosts_to_invoice[0]
|
||||||
|
invoice.journal_id = (
|
||||||
|
invoice.pms_property_id.journal_normal_invoice_id
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mens = _(
|
||||||
|
"The total amount of the simplified invoice is higher than the "
|
||||||
|
"maximum amount allowed for simplified invoices, and dont have "
|
||||||
|
"enought data in hosts to create a normal invoice."
|
||||||
|
)
|
||||||
|
folio.message_post(body=mens)
|
||||||
|
raise ValidationError(mens)
|
||||||
|
invoice.action_post()
|
||||||
|
except Exception as e:
|
||||||
|
folio.message_post(body=_("Error in autoinvoicing folio: " + str(e)))
|
||||||
|
|
||||||
@api.constrains("journal_normal_invoice_id")
|
@api.constrains("journal_normal_invoice_id")
|
||||||
def _check_journal_normal_invoice(self):
|
def _check_journal_normal_invoice(self):
|
||||||
for pms_property in self.filtered("journal_normal_invoice_id"):
|
for pms_property in self.filtered("journal_normal_invoice_id"):
|
||||||
|
|||||||
Reference in New Issue
Block a user