[IMP]pms: autoinvoicing folio cron improvements

This commit is contained in:
Darío Lodeiros
2023-02-05 20:37:13 +01:00
parent 993b34349a
commit e6063a172b
6 changed files with 142 additions and 77 deletions

View File

@@ -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
View 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>

View 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>

View File

@@ -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

View File

@@ -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:

View File

@@ -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"):