[IMP]pms: improvement invoice downpayments

This commit is contained in:
Darío Lodeiros
2022-11-27 22:53:59 +01:00
parent 56855e6ac2
commit 5acb92c5ae
4 changed files with 120 additions and 173 deletions

View File

@@ -98,9 +98,25 @@
<field name="model_id" ref="model_pms_property" /> <field name="model_id" ref="model_pms_property" />
<field <field
name="nextcall" name="nextcall"
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 04:30:00')" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 23:30:00')"
/> />
<field name="code">model.autoinvoicing()</field> <field name="code">model.autoinvoicing()</field>
</record> </record>
<record model="ir.cron" id="autoinvoicing_downpayments">
<field name="name">Auto Invoicing DownPayments</field>
<field name="interval_number">5</field>
<field name="user_id" ref="base.user_root" />
<field name="active" eval="False" />
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="state">code</field>
<field name="model_id" ref="model_account_payment" />
<field
name="nextcall"
eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d 04:30:00')"
/>
<field name="code">model.auto_invoice_downpayments(offset=1)</field>
</record>
</data> </data>
</odoo> </odoo>

View File

@@ -1,8 +1,10 @@
# Copyright 2017 Dario Lodeiros # Copyright 2017 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 collections import defaultdict
from dateutil.relativedelta import relativedelta
from odoo import _, api, fields, models from odoo import api, fields, models
class AccountPayment(models.Model): class AccountPayment(models.Model):
@@ -103,173 +105,98 @@ class AccountPayment(models.Model):
} }
) )
# Business methods def action_draft(self):
# def modify(self):
# self.cancel()
# vals = {
# "journal_id": self.journal_id,
# "partner_id": self.partner_id,
# "amount": self.amount,
# "payment_date": self.payment_date,
# "communication": self.communication,
# "state": "draft",
# }
# self.update(vals)
# self.with_context({"ignore_notification_post": True}).post()
# self._compute_folio_amount()
# if self.folio_id:
# msg = _("Payment %s modified: \n") % (self.communication)
# if self.save_amount and self.save_amount != self.amount:
# msg += _("Amount from %s to %s %s \n") % (
# self.save_amount,
# self.amount,
# self.currency_id.symbol,
# )
# if self.save_date and self.save_date != self.payment_date:
# msg += _("Date from %s to %s \n") % (self.save_date, self.payment_date)
# if self.save_journal_id and self.save_journal_id != self.journal_id.id:
# msg += _("Journal from %s to %s") % (
# self.env["account.journal"].browse(self.save_journal_id).name,
# self.journal_id.name,
# )
# self.folio_id.message_post(subject=_("Payment"), body=msg)
# def delete(self):
# msg = False
# if self.folio_id:
# msg = _("Deleted payment: %s %s ") % (self.amount, self.currency_id.symbol)
# self.cancel()
# self.move_name = ""
# self.unlink()
# if msg:
# self.folio_id.message_post(subject=_("Payment Deleted"), body=msg)
def action_post(self):
res = super(AccountPayment, self).action_post()
for payment in self: for payment in self:
if ( if payment._check_has_downpayment_invoice(payment):
self.folio_ids downpayment_invoices = payment.reconciled_invoice_ids.filtered(
and self.company_id.pms_invoice_downpayment_policy != "no" lambda inv: inv._is_downpayment()
and not self.journal_id.avoid_autoinvoice_downpayment )
): if downpayment_invoices.state == "posted":
checkout_ref = max(self.folio_ids.mapped("last_checkout")) downpayment_invoices._reverse_moves(cancel=True)
if ( else:
self.company_id.pms_invoice_downpayment_policy == "all" downpayment_invoices.unlink()
and payment.date < checkout_ref return super(AccountPayment, self).action_draft()
) or (
self.company_id.pms_invoice_downpayment_policy
== "checkout_past_month"
and checkout_ref.month > payment.date.month
and checkout_ref.year >= payment.date.year
):
partner_id = (
payment.partner_id.id
or self.env.ref("pms.various_pms_partner").id
)
if payment.payment_type == "inbound":
invoice_wizard = self.env["folio.advance.payment.inv"].create(
{
"partner_invoice_id": partner_id,
"advance_payment_method": "fixed",
"fixed_amount": payment.amount,
}
)
move = invoice_wizard.with_context(
active_ids=self.folio_ids.ids,
return_invoices=True,
).create_invoices()
move.action_post()
move_lines = move.line_ids.filtered(
lambda line: line.account_id.user_type_id.type
in ("receivable", "payable")
)
payment_lines = payment.move_id.line_ids.filtered(
lambda line: line.account_id == move_lines.account_id
)
if not move_lines.reconciled:
(payment_lines + move_lines).reconcile()
else:
# We try to revert the advance payment invoice
advance_invoices = self.env["account.move"].search(
[
("folio_ids", "in", self.folio_ids.ids),
("state", "=", "posted"),
("payment_state", "=", "paid"),
("move_type", "=", "out_invoice"),
]
)
advance_invoices = advance_invoices.filtered(
lambda inv: any(
[
line.folio_line_ids.is_downpayment
for line in inv.invoice_line_ids
]
)
)
if advance_invoices:
match_inv = advance_invoices.filtered(
lambda inv: round(inv.amount_total, 2)
== round(payment.amount, 2)
)
move_reversal = (
self.env["account.move.reversal"]
.with_context(
active_model="account.move",
active_ids=match_inv.ids
if match_inv
else advance_invoices.ids,
)
.create(
{
"date": fields.Date.today(),
"reason": _("Deposit return"),
"refund_method": "refund",
}
)
)
move_reversal.reverse_moves()
move = move_reversal.new_move_ids
invoice_line = move.invoice_line_ids.filtered(
lambda l: not l.display_type
)
move.write(
{
"invoice_line_ids": [
(
1,
invoice_line.id,
{
"price_unit": payment.amount,
},
)
],
}
)
move_lines = move.line_ids.filtered(
lambda line: line.account_id.user_type_id.type
in ("receivable", "payable")
)
payment_lines = payment.move_id.line_ids.filtered(
lambda line: line.account_id == move_lines.account_id
)
move.sudo().action_post()
if not move_lines.reconciled:
(payment_lines + move_lines).reconcile()
return res
# def modify_payment(self): @api.model
# self.ensure_one() def auto_invoice_downpayments(self, offset=0):
# view_form_id = self.env.ref("pms.account_payment_view_form_folio").id """
# # moves = self.mapped('move_ids.id') This method is called by a cron job to invoice the downpayments
# return { based on the company settings.
# "name": _("Payment"), """
# "view_type": "form", date_reference = fields.Date.today() - relativedelta(days=offset)
# "views": [(view_form_id, "form")], payments = self._get_downpayments_to_invoice(date_reference)
# "view_mode": "tree,form", for payment in payments:
# "res_model": "account.payment", partner_id = (
# "target": "new", payment.partner_id.id or self.env.ref("pms.various_pms_partner").id
# "init_mode": "edit", )
# "type": "ir.actions.act_window", self._create_downpayment_invoice(
# "res_id": self.id, payment=payment,
# } partner_id=partner_id,
)
return True
@api.model
def _get_downpayments_to_invoice(self, date_reference):
companys = self.env["res.company"].search([])
payments = self.env["account.payment"]
for company in companys:
if company.pms_invoice_downpayment_policy == "all":
date_ref = fields.Date.today()
elif company.pms_invoice_downpayment_policy == "checkout_past_month":
date_ref = fields.Date.today().replace(
day=1, month=fields.Date.today().month + 1
)
else:
continue
payments += self.search(
[
("state", "=", "posted"),
("partner_type", "=", "customer"),
("company_id", "=", company.id),
("journal_id.avoid_autoinvoice_downpayment", "=", False),
("folio_ids", "!=", False),
("folio_ids.last_checkout", ">=", date_ref),
("date", "<=", date_reference),
]
)
payments = payments.filtered(lambda p: not p.reconciled_invoice_ids)
return payments
@api.model
def _check_has_downpayment_invoice(self, payment):
if (
payment.folio_ids
and payment.partner_type == "customer"
and payment.reconciled_invoice_ids.filtered(
lambda inv: inv._is_downpayment()
)
):
return True
return False
@api.model
def _create_downpayment_invoice(self, payment, partner_id):
invoice_wizard = self.env["folio.advance.payment.inv"].create(
{
"partner_invoice_id": partner_id,
"advance_payment_method": "fixed",
"fixed_amount": payment.amount,
}
)
move = invoice_wizard.with_context(
active_ids=payment.folio_ids.ids,
return_invoices=True,
).create_invoices()
if payment.payment_type == "outbound":
move.action_switch_invoice_into_refund_credit_note()
move.action_post()
for invoice, payment_move in zip(move, payment.move_id):
group = defaultdict(list)
for line in (invoice.line_ids + payment_move.line_ids).filtered(
lambda l: not l.reconciled
):
group[(line.account_id, line.currency_id)].append(line.id)
for (account, _dummy), line_ids in group.items():
if account.reconcile or account.internal_type == "liquidity":
self.env["account.move.line"].browse(line_ids).reconcile()
return move

View File

@@ -6,6 +6,7 @@ import datetime
import time import time
import pytz import pytz
from dateutil.relativedelta import relativedelta
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
@@ -646,14 +647,15 @@ class PmsProperty(models.Model):
return True return True
@api.model @api.model
def autoinvoicing(self): def autoinvoicing(self, offset=0):
""" """
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
""" """
date_reference = fields.Date.today() - relativedelta(days=offset)
folios = self.env["pms.folio"].search( folios = self.env["pms.folio"].search(
[ [
("sale_line_ids.autoinvoice_date", "=", fields.date.today()), ("sale_line_ids.autoinvoice_date", "=", date_reference),
("invoice_status", "=", "to_invoice"), ("invoice_status", "=", "to_invoice"),
] ]
) )
@@ -679,7 +681,7 @@ class PmsProperty(models.Model):
draft_invoices_to_post = self.env["account.move"].search( draft_invoices_to_post = self.env["account.move"].search(
[ [
("state", "=", "draft"), ("state", "=", "draft"),
("invoice_date", "=", fields.date.today()), ("invoice_date", "=", date_reference),
("folio_ids", "!=", False), ("folio_ids", "!=", False),
] ]
) )

View File

@@ -232,6 +232,8 @@ class FolioAdvancePaymentInv(models.TransientModel):
"sequence": order.sale_line_ids "sequence": order.sale_line_ids
and order.sale_line_ids[-1].sequence + 1 and order.sale_line_ids[-1].sequence + 1
or 10, or 10,
"default_invoice_to": self.partner_invoice_id.id
or self.env.ref("pms.various_pms_partner").id,
} }
del context del context
return so_values return so_values