Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions account_payment_mode_auto_reconcile/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
===================================
Account Payment Mode Auto Reconcile
===================================

.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--reconcile-lightgray.png?logo=github
:target: https://github.com/OCA/account-reconcile/tree/16.0/account_payment_mode_auto_reconcile
:alt: OCA/account-reconcile
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/account-reconcile-16-0/account-reconcile-16-0-account_payment_mode_auto_reconcile
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/account-reconcile&target_branch=16.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module adds a checkbox `auto_reconcile_outstanding_credits` on account
payment modes to allow automatic reconciliation on account invoices if it is
checked.

Automatic reconciliation of outstanding credits will only happen on customer
invoices at validation if the payment mode is set or when the payment mode is
changed on an open invoice. If a payment mode using auto-reconcile is removed
from an open invoice, the existing auto reconciled payments will be removed.

Another option `auto_reconcile_allow_partial` on account payment mode defines
if outstanding credits can be partially used for the auto reconciliation.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-reconcile/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/account-reconcile/issues/new?body=module:%20account_payment_mode_auto_reconcile%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Camptocamp

Contributors
~~~~~~~~~~~~

* Akim Juillerat <akim.juillerat@camptocamp.com>

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/account-reconcile <https://github.com/OCA/account-reconcile/tree/16.0/account_payment_mode_auto_reconcile>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions account_payment_mode_auto_reconcile/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
22 changes: 22 additions & 0 deletions account_payment_mode_auto_reconcile/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
{
"name": "Account Payment Mode Auto Reconcile",
"summary": "Reconcile outstanding credits according to payment mode",
"version": "16.0.1.0.0",
"category": "Banking addons",
"website": "https://github.com/OCA/account-reconcile",
"author": "Camptocamp, Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"depends": [
"account_payment_partner",
],
"data": [
"views/account_invoice.xml",
"views/account_payment_mode.xml",
],
"demo": [
"demo/account_payment_mode.xml",
],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record
id="account_payment_mode.payment_mode_inbound_dd1"
model="account.payment.mode"
>
<field name="auto_reconcile_outstanding_credits" eval="True" />
</record>
</odoo>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_payment_mode_auto_reconcile
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,field_description:account_payment_mode_auto_reconcile.field_account_payment_mode_auto_reconcile_allow_partial
msgid "Allow partial"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,help:account_payment_mode_auto_reconcile.field_account_payment_mode_auto_reconcile_allow_partial
msgid "Allows automatic partial reconciliation of outstanding credits"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,field_description:account_payment_mode_auto_reconcile.field_account_payment_mode_auto_reconcile_outstanding_credits
msgid "Auto reconcile"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.ui.view,arch_db:account_payment_mode_auto_reconcile.account_payment_mode_form_inherit
msgid "Auto reconcile outstanding credits"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: code:addons/account_payment_mode_auto_reconcile/models/account_invoice.py:160
#, python-format
msgid "Changing payment mode will reconcile outstanding credits."
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: code:addons/account_payment_mode_auto_reconcile/models/account_invoice.py:150
#, python-format
msgid "Changing payment mode will unreconcile existing auto reconciled payments."
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,field_description:account_payment_mode_auto_reconcile.field_account_invoice_display_payment_mode_warning
msgid "Display payment mode warning"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model,name:account_payment_mode_auto_reconcile.model_account_invoice
msgid "Invoice"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,help:account_payment_mode_auto_reconcile.field_account_payment_mode_auto_reconcile_same_journal
msgid "Only reconcile payment in the same journal than the invoice"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,field_description:account_payment_mode_auto_reconcile.field_account_payment_mode_auto_reconcile_same_journal
msgid "Only same journal"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model,name:account_payment_mode_auto_reconcile.model_account_partial_reconcile
msgid "Partial Reconcile"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model,name:account_payment_mode_auto_reconcile.model_account_payment_mode
msgid "Payment Modes"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,field_description:account_payment_mode_auto_reconcile.field_account_partial_reconcile_payment_mode_auto_reconcile
msgid "Payment mode auto reconcile"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,field_description:account_payment_mode_auto_reconcile.field_account_invoice_payment_mode_warning
msgid "Payment mode warning"
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: model:ir.model.fields,help:account_payment_mode_auto_reconcile.field_account_payment_mode_auto_reconcile_outstanding_credits
msgid "Reconcile automatically outstanding credits when an invoice using this payment mode is validated, or when this payment mode is defined on an open invoice."
msgstr ""

#. module: account_payment_mode_auto_reconcile
#: code:addons/account_payment_mode_auto_reconcile/models/account_invoice.py:140
#, python-format
msgid "Validating invoices with this payment mode will reconcile any outstanding credits."
msgstr ""

3 changes: 3 additions & 0 deletions account_payment_mode_auto_reconcile/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import account_move
from . import account_partial_reconcile
from . import account_payment_mode
174 changes: 174 additions & 0 deletions account_payment_mode_auto_reconcile/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from operator import itemgetter

from odoo import _, api, fields, models


class AccountMove(models.Model):

_inherit = "account.move"

# Allow changing payment mode in open state
# TODO: Check if must be done in account_payment_partner instead
payment_mode_id = fields.Many2one(
states={"draft": [("readonly", False)], "posted": [("readonly", False)]}
)
payment_mode_warning = fields.Char(
compute="_compute_payment_mode_warning",
)
display_payment_mode_warning = fields.Boolean(
compute="_compute_payment_mode_warning",
)

def action_post(self):
res = super(AccountMove, self).action_post()
for invoice in self:
if invoice.move_type != "out_invoice":
continue
if not invoice.payment_mode_id.auto_reconcile_outstanding_credits:
continue
partial = invoice.payment_mode_id.auto_reconcile_allow_partial
invoice.with_context(
_payment_mode_auto_reconcile=True
).auto_reconcile_credits(partial_allowed=partial)
return res

def write(self, vals):
res = super(AccountMove, self).write(vals)
if "payment_mode_id" in vals or "state" in vals:
for invoice in self:
# Do not auto reconcile anything else than open customer inv
if invoice.state != "posted" or invoice.move_type != "out_invoice":
continue
invoice_lines = invoice.line_ids.filtered(
lambda line: line.account_type == "asset_receivable"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about that one, but somehow you're restricting this feature to customer invoices. I guess it would make more sense to use the filtering on payment term lines?

invoice_lines = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term')

)
# Auto reconcile if payment mode sets it
payment_mode = invoice.payment_mode_id
if payment_mode and payment_mode.auto_reconcile_outstanding_credits:
partial = payment_mode.auto_reconcile_allow_partial
invoice.with_context(
_payment_mode_auto_reconcile=True
).auto_reconcile_credits(partial_allowed=partial)
# If the payment mode is not using auto reconcile we remove
# the existing reconciliations
elif any(
[
invoice_lines.mapped("matched_credit_ids"),
invoice_lines.mapped("matched_debit_ids"),
]
):
invoice.auto_unreconcile_credits()
return res

def auto_reconcile_credits(self, partial_allowed=True):
for invoice in self:
invoice._compute_payments_widget_to_reconcile_info()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird to me that you have to explicitely call this before accessing the value 😕


if not invoice.invoice_has_outstanding:
continue
credits_info = invoice.invoice_outstanding_credits_debits_widget
# Get outstanding credits in chronological order
# (using reverse because aml is sorted by date desc as default)
credits_dict = credits_info.get("content", False)
if invoice.payment_mode_id.auto_reconcile_same_journal:
credits_dict = invoice._filter_payment_same_journal(credits_dict)
sorted_credits = self._sort_credits_dict(credits_dict)
for credit in sorted_credits:
if (
not partial_allowed
and credit.get("amount") > invoice.amount_residual
):
continue
invoice.js_assign_outstanding_line(credit.get("id"))

@api.model
def _sort_credits_dict(self, credits_dict):
"""Sort credits dict according to their id (oldest recs first)"""
return sorted(credits_dict, key=itemgetter("id"))

def _filter_payment_same_journal(self, credits_dict):
"""Keep only credits on the same journal than the invoice."""
self.ensure_one()
line_ids = [credit["id"] for credit in credits_dict]
lines = self.env["account.move.line"].search(
[("id", "in", line_ids), ("journal_id", "=", self.journal_id.id)]
)
return [credit for credit in credits_dict if credit["id"] in lines.ids]

def auto_unreconcile_credits(self):
for invoice in self:
payments_info = invoice.invoice_payments_widget
for payment in payments_info.get("content", []):
payment_aml = (
self.env["account.payment"]
.browse(payment.get("account_payment_id"))
.line_ids
)

aml = payment_aml.filtered(lambda l: l.matched_debit_ids)
for apr in aml.matched_debit_ids:
if apr.amount != payment.get("amount"):
continue
if (
apr.payment_mode_auto_reconcile
and apr.debit_move_id.move_id == invoice
):
aml.remove_move_reconcile()

@api.depends(
"move_type", "payment_mode_id", "payment_id", "state", "invoice_has_outstanding"
)
def _compute_payment_mode_warning(self):
# TODO Improve me but watch out
for invoice in self:
existed_reconciliations = any(
[
invoice.line_ids.mapped("matched_credit_ids"),
invoice.line_ids.mapped("matched_debit_ids"),
]
)
if invoice.move_type != "out_invoice" or (
invoice.state == "posted" and invoice.payment_state != "paid"
):
invoice.payment_mode_warning = ""
invoice.display_payment_mode_warning = False
continue
invoice.display_payment_mode_warning = True
if (
invoice.state != "posted"
and invoice.payment_mode_id
and invoice.payment_mode_id.auto_reconcile_outstanding_credits
):
invoice.payment_mode_warning = _(
"Validating invoices with this payment mode will reconcile"
" any outstanding credits."
)
elif (
invoice.state == "posted"
and invoice.payment_state != "paid"
and existed_reconciliations
and (
not invoice.payment_mode_id
or not invoice.payment_mode_id.auto_reconcile_outstanding_credits
)
):
invoice.payment_mode_warning = _(
"Changing payment mode will unreconcile existing auto "
"reconciled payments."
)
elif (
invoice.state == "posted"
and invoice.payment_state != "paid"
and not existed_reconciliations
and invoice.payment_mode_id
and invoice.payment_mode_id.auto_reconcile_outstanding_credits
and invoice.invoice_has_outstanding
):
invoice.payment_mode_warning = _(
"Changing payment mode will reconcile outstanding credits."
)
else:
invoice.payment_mode_warning = ""
invoice.display_payment_mode_warning = False
Loading