mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP]pms: improvement invoice downpayments
This commit is contained in:
@@ -98,9 +98,25 @@
|
||||
<field name="model_id" ref="model_pms_property" />
|
||||
<field
|
||||
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>
|
||||
</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>
|
||||
</odoo>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# Copyright 2017 Dario Lodeiros
|
||||
# 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):
|
||||
@@ -103,173 +105,98 @@ class AccountPayment(models.Model):
|
||||
}
|
||||
)
|
||||
|
||||
# Business methods
|
||||
|
||||
# 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()
|
||||
def action_draft(self):
|
||||
for payment in self:
|
||||
if (
|
||||
self.folio_ids
|
||||
and self.company_id.pms_invoice_downpayment_policy != "no"
|
||||
and not self.journal_id.avoid_autoinvoice_downpayment
|
||||
):
|
||||
checkout_ref = max(self.folio_ids.mapped("last_checkout"))
|
||||
if (
|
||||
self.company_id.pms_invoice_downpayment_policy == "all"
|
||||
and payment.date < checkout_ref
|
||||
) 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
|
||||
if payment._check_has_downpayment_invoice(payment):
|
||||
downpayment_invoices = payment.reconciled_invoice_ids.filtered(
|
||||
lambda inv: inv._is_downpayment()
|
||||
)
|
||||
if downpayment_invoices.state == "posted":
|
||||
downpayment_invoices._reverse_moves(cancel=True)
|
||||
else:
|
||||
downpayment_invoices.unlink()
|
||||
return super(AccountPayment, self).action_draft()
|
||||
|
||||
# def modify_payment(self):
|
||||
# self.ensure_one()
|
||||
# view_form_id = self.env.ref("pms.account_payment_view_form_folio").id
|
||||
# # moves = self.mapped('move_ids.id')
|
||||
# return {
|
||||
# "name": _("Payment"),
|
||||
# "view_type": "form",
|
||||
# "views": [(view_form_id, "form")],
|
||||
# "view_mode": "tree,form",
|
||||
# "res_model": "account.payment",
|
||||
# "target": "new",
|
||||
# "init_mode": "edit",
|
||||
# "type": "ir.actions.act_window",
|
||||
# "res_id": self.id,
|
||||
# }
|
||||
@api.model
|
||||
def auto_invoice_downpayments(self, offset=0):
|
||||
"""
|
||||
This method is called by a cron job to invoice the downpayments
|
||||
based on the company settings.
|
||||
"""
|
||||
date_reference = fields.Date.today() - relativedelta(days=offset)
|
||||
payments = self._get_downpayments_to_invoice(date_reference)
|
||||
for payment in payments:
|
||||
partner_id = (
|
||||
payment.partner_id.id or self.env.ref("pms.various_pms_partner").id
|
||||
)
|
||||
self._create_downpayment_invoice(
|
||||
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
|
||||
|
||||
@@ -6,6 +6,7 @@ import datetime
|
||||
import time
|
||||
|
||||
import pytz
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
@@ -646,14 +647,15 @@ class PmsProperty(models.Model):
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def autoinvoicing(self):
|
||||
def autoinvoicing(self, offset=0):
|
||||
"""
|
||||
This method is used to invoicing automatically 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(
|
||||
[
|
||||
("sale_line_ids.autoinvoice_date", "=", fields.date.today()),
|
||||
("sale_line_ids.autoinvoice_date", "=", date_reference),
|
||||
("invoice_status", "=", "to_invoice"),
|
||||
]
|
||||
)
|
||||
@@ -679,7 +681,7 @@ class PmsProperty(models.Model):
|
||||
draft_invoices_to_post = self.env["account.move"].search(
|
||||
[
|
||||
("state", "=", "draft"),
|
||||
("invoice_date", "=", fields.date.today()),
|
||||
("invoice_date", "=", date_reference),
|
||||
("folio_ids", "!=", False),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -232,6 +232,8 @@ class FolioAdvancePaymentInv(models.TransientModel):
|
||||
"sequence": order.sale_line_ids
|
||||
and order.sale_line_ids[-1].sequence + 1
|
||||
or 10,
|
||||
"default_invoice_to": self.partner_invoice_id.id
|
||||
or self.env.ref("pms.various_pms_partner").id,
|
||||
}
|
||||
del context
|
||||
return so_values
|
||||
|
||||
Reference in New Issue
Block a user