mirror of
https://github.com/OCA/account-reconcile.git
synced 2025-01-20 12:27:39 +02:00
Add module account_statement_operation_rule
This module allows to automatically apply Statement Operations (or "presets") on the statement lines reconciliations according to rules, for instance based on the balance.
This commit is contained in:
101
account_statement_operation_rule/README.rst
Normal file
101
account_statement_operation_rule/README.rst
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||||
|
:alt: License
|
||||||
|
|
||||||
|
Bank Statement Operation Rules
|
||||||
|
==============================
|
||||||
|
|
||||||
|
This module complements the Reconciliation of the bank statements. When
|
||||||
|
the bank statement matches one or more journal entry for a line and
|
||||||
|
there is a remaining balance, Odoo proposes you to click on buttons that
|
||||||
|
will generate write-off entries according to pre-configured *Statement
|
||||||
|
Operation Templates*. The aim of this module is to automatically click
|
||||||
|
for you on these buttons (i.e. create the write-off journal entries)
|
||||||
|
when some rules are respected, rules that you can configure.
|
||||||
|
|
||||||
|
It contains 2 types of rules (but can be extended with additional rules),
|
||||||
|
described below:
|
||||||
|
|
||||||
|
Roundings
|
||||||
|
The most basic rule: when the remaining balance is within a range, 1
|
||||||
|
or more operations are applied.
|
||||||
|
|
||||||
|
Currencies
|
||||||
|
When the remaining balance is within a range and the currency of all
|
||||||
|
the lines is the same but different from the company's, and the amount
|
||||||
|
currency is the same, 1 or more operations are applied.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
As this module aims to automatize the ``Statement Operation Templates``,
|
||||||
|
you first want to ensure that you have at least one operation configured.
|
||||||
|
You can find them in ``Invoicing > Configuration > Miscellaneous >
|
||||||
|
Statement Operation Templates``. An example of a common operation is:
|
||||||
|
|
||||||
|
=================== ========================== ======= ========
|
||||||
|
Account Amount Type Amount Label
|
||||||
|
=================== ========================== ======= ========
|
||||||
|
Depends of the l10n Percentage of open balance 100.0 % Rounding
|
||||||
|
=================== ========================== ======= ========
|
||||||
|
|
||||||
|
The configuration of the rules themselves happens in ``Invoicing >
|
||||||
|
Configuration > Miscellaneous > Statement Operation Rules``. Refer to
|
||||||
|
the description of the types of rules above in case of doubt. The form
|
||||||
|
is divided in 2 parts: **Rule** and **Result**. The rule part is where
|
||||||
|
you will set the conditions and the result part is what operations will
|
||||||
|
be done if the conditions are valid.
|
||||||
|
|
||||||
|
For the **Roundings** rules, you will set a min. and a max. amount. It
|
||||||
|
can be negative or positive. The amount is compared to the remaining
|
||||||
|
balance when lines are matched in the bank statement. Example: if you
|
||||||
|
want to create a move line in a loss account when you received 1.- not
|
||||||
|
enough, you can create a rule with an min. amount of -1.0 and a max.
|
||||||
|
amount of 0.0.
|
||||||
|
|
||||||
|
For the **Currencies** rules, the min. and max. amount have the same
|
||||||
|
properties, but you will also set the currencies for which the rule
|
||||||
|
applies. Setting the currency allows to configure different amounts
|
||||||
|
according to the currencies.
|
||||||
|
|
||||||
|
Only the first rule matching the current situation is used, so if you
|
||||||
|
have several rules overlapping for some reason, be sure to order them
|
||||||
|
appropriately in the list view.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
When you use the *Reconcile* button of a bank statement, Odoo
|
||||||
|
automatically proposes you matching journal entries for each statement
|
||||||
|
line. This module automatically adds journal entries generated from the
|
||||||
|
*Statement Operation Templates* if a rule matches with the current
|
||||||
|
situation, so there is nothing special to do once the rules are
|
||||||
|
configured.
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||||
|
:alt: Try me on Runbot
|
||||||
|
:target: https://runbot.odoo-community.org/runbot/98/8.0
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. image:: http://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: http://odoo-community.org
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
To contribute to this module, please visit
|
||||||
|
http://odoo-community.org.
|
||||||
3
account_statement_operation_rule/__init__.py
Normal file
3
account_statement_operation_rule/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import model
|
||||||
36
account_statement_operation_rule/__openerp__.py
Normal file
36
account_statement_operation_rule/__openerp__.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Author: Guewen Baconnier
|
||||||
|
# Copyright 2014 Camptocamp SA
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
{'name': 'Bank Statement Operation Rules',
|
||||||
|
'version': '8.0.1.0.0',
|
||||||
|
'author': 'Camptocamp',
|
||||||
|
'maintainer': 'Camptocamp',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'category': 'Accounting & Finance',
|
||||||
|
'depends': ['account',
|
||||||
|
],
|
||||||
|
'website': 'http://www.camptocamp.com',
|
||||||
|
'data': ['view/account_statement_operation_rule.xml',
|
||||||
|
'view/account_statement_operation_rule_view.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
3
account_statement_operation_rule/model/__init__.py
Normal file
3
account_statement_operation_rule/model/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import account_statement_operation_rule
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Author: Guewen Baconnier
|
||||||
|
# Copyright 2014 Camptocamp SA
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from openerp import models, fields, api
|
||||||
|
from openerp.addons import decimal_precision as dp
|
||||||
|
|
||||||
|
|
||||||
|
class AccountStatementOperationRule(models.Model):
|
||||||
|
_name = 'account.statement.operation.rule'
|
||||||
|
|
||||||
|
_order = 'sequence ASC, id ASC'
|
||||||
|
|
||||||
|
name = fields.Char()
|
||||||
|
rule_type = fields.Selection(
|
||||||
|
selection=[('balance', 'Balance'),
|
||||||
|
('currency', 'Currencies')],
|
||||||
|
string='Type',
|
||||||
|
default='balance',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
operations = fields.Many2many(
|
||||||
|
comodel_name='account.statement.operation.template',
|
||||||
|
relation='account_statement_oper_rule_rel',
|
||||||
|
)
|
||||||
|
amount_min = fields.Float(
|
||||||
|
string='Min. Amount',
|
||||||
|
digits=dp.get_precision('Account'),
|
||||||
|
)
|
||||||
|
amount_max = fields.Float(
|
||||||
|
string='Max. Amount',
|
||||||
|
digits=dp.get_precision('Account'),
|
||||||
|
)
|
||||||
|
sequence = fields.Integer(
|
||||||
|
default=20,
|
||||||
|
help="If several rules match, the first one is used.",
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _is_valid_balance(self, statement_line, move_lines, balance):
|
||||||
|
# TODO use float compare
|
||||||
|
return self.amount_min <= balance <= self.amount_max
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _is_valid_multicurrency(self, statement_line, move_lines, balance):
|
||||||
|
# FIXME: surely wrong
|
||||||
|
if statement_line.currency_id == statement_line.company_id.currency_id:
|
||||||
|
# not multicurrency
|
||||||
|
return False
|
||||||
|
amount_currency = statement_line.amount_currency
|
||||||
|
for move_line in move_lines:
|
||||||
|
if move_line.currency_id != statement_line.currency_id:
|
||||||
|
# use case not supported, no rule found
|
||||||
|
return self.browse()
|
||||||
|
amount_currency -= move_line.amount_currency
|
||||||
|
|
||||||
|
# amount in currency is the same, so the balance is
|
||||||
|
# a difference due to currency rates
|
||||||
|
if statement_line.currency_id.is_zero(amount_currency):
|
||||||
|
return self._is_valid_balance(statement_line, move_lines, balance)
|
||||||
|
return False
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def is_valid(self, statement_line, move_lines, balance):
|
||||||
|
""" Returns True if a rule applies to a group of statement_line +
|
||||||
|
move lines.
|
||||||
|
|
||||||
|
This is the public method where the rule is evaluated whatever
|
||||||
|
its type is. When a rule returns True, it means that it is a
|
||||||
|
candidate for the current reconciliation. The rule with the lowest
|
||||||
|
number in the ``sequence`` field is chosen.
|
||||||
|
|
||||||
|
:param statement_line: the line to reconcile
|
||||||
|
:param move_lines: the selected move lines for reconciliation
|
||||||
|
:param balance: the balance between the statement_line and the
|
||||||
|
move_lines. It could be computed here but it is
|
||||||
|
computed before to avoid to compute it for each
|
||||||
|
rule when called on multiple rules.
|
||||||
|
"""
|
||||||
|
self.ensure_one()
|
||||||
|
if self.rule_type == 'balance':
|
||||||
|
return self._is_valid_balance(statement_line, move_lines, balance)
|
||||||
|
elif self.rule_type == 'currency':
|
||||||
|
return self._is_valid_multicurrency(statement_line,
|
||||||
|
move_lines,
|
||||||
|
balance)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def find_first_rule(self, statement_line, move_lines):
|
||||||
|
""" Find the rules that apply to a statement line and
|
||||||
|
a selection of move lines.
|
||||||
|
|
||||||
|
:param statement_line: the line to reconcile
|
||||||
|
:param move_lines: the selected move lines for reconciliation
|
||||||
|
"""
|
||||||
|
balance = statement_line.amount
|
||||||
|
for move_line in move_lines:
|
||||||
|
balance += move_line.credit - move_line.debit
|
||||||
|
|
||||||
|
rules = self.search([])
|
||||||
|
# return the first applicable rule
|
||||||
|
for rule in rules:
|
||||||
|
if rule.is_valid(statement_line, move_lines, balance):
|
||||||
|
return rule
|
||||||
|
return self.browse()
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
@api.returns('account.statement.operation.template')
|
||||||
|
def operations_for_reconciliation(self, statement_line_id, move_line_ids):
|
||||||
|
""" Find the rule for the current reconciliation and returns the
|
||||||
|
``account.statement.operation.template`` of the found rule.
|
||||||
|
|
||||||
|
Called from the javascript reconciliation view.
|
||||||
|
|
||||||
|
"""
|
||||||
|
line_obj = self.env['account.bank.statement.line']
|
||||||
|
move_line_obj = self.env['account.move.line']
|
||||||
|
statement_line = line_obj.browse(statement_line_id)
|
||||||
|
move_lines = move_line_obj.browse(move_line_ids)
|
||||||
|
rules = self.find_first_rule(statement_line, move_lines)
|
||||||
|
return rules.operations
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
openerp.account_statement_operation_rule = function (instance) {
|
||||||
|
|
||||||
|
var _t = instance.web._t,
|
||||||
|
_lt = instance.web._lt;
|
||||||
|
var QWeb = instance.web.qweb;
|
||||||
|
|
||||||
|
instance.web.account_statement_operation_rule = instance.web.account_statement_operation_rule || {};
|
||||||
|
|
||||||
|
instance.web.account.bankStatementReconciliationLine.include({
|
||||||
|
operation_rules: function() {
|
||||||
|
var self = this;
|
||||||
|
var model_operation_rule = new instance.web.Model("account.statement.operation.rule");
|
||||||
|
model_operation_rule.call("operations_for_reconciliation",
|
||||||
|
[self.st_line.id,
|
||||||
|
_.pluck(self.get("mv_lines_selected"), 'id')])
|
||||||
|
.then(function (operations) {
|
||||||
|
_.each(operations, function(operation_id) {
|
||||||
|
preset_btn = self.$("button.preset[data-presetid='" + operation_id + "']");
|
||||||
|
preset_btn.click();
|
||||||
|
self.addLineBeingEdited();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
deferred = this._super();
|
||||||
|
if (deferred) {
|
||||||
|
deferred.done(this.operation_rules());
|
||||||
|
}
|
||||||
|
return deferred;
|
||||||
|
},
|
||||||
|
restart: function() {
|
||||||
|
deferred = this._super();
|
||||||
|
deferred.done(this.operation_rules());
|
||||||
|
return deferred;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
<template id="assets_backend" name="account assets" inherit_id="account.assets_backend">
|
||||||
|
<xpath expr="." position="inside">
|
||||||
|
<script type="text/javascript" src="/account_statement_operation_rule/static/src/js/account_widgets.js"></script>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="0">
|
||||||
|
|
||||||
|
<record id="view_account_statement_operation_rule_form" model="ir.ui.view">
|
||||||
|
<field name="name">account.statement.operation.rule.form</field>
|
||||||
|
<field name="model">account.statement.operation.rule</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Statement Operation Rule">
|
||||||
|
<sheet>
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name" class="oe_edit_only"/>
|
||||||
|
<h1>
|
||||||
|
<field name="name"/>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<group name="options">
|
||||||
|
<field name="rule_type"/>
|
||||||
|
</group>
|
||||||
|
<group name="amount" string="Balance" attrs="{'invisible': [('rule_type', 'not in', ('balance', 'currency'))]}">
|
||||||
|
<label for="amount" string="When the balance is between"/>
|
||||||
|
<div>
|
||||||
|
<field name="amount_min" class="oe_inline" /> And
|
||||||
|
<field name="amount_max" class="oe_inline" />
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group name="operations">
|
||||||
|
<label string="Then the following operations will be applied:" colspan="2"/>
|
||||||
|
<field name="operations" nolabel="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_account_statement_operation_rule_tree" model="ir.ui.view">
|
||||||
|
<field name="name">account.statement.operation.rule.tree</field>
|
||||||
|
<field name="model">account.statement.operation.rule</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Statement Operation Rules">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="rule_type"/>
|
||||||
|
<field name="amount_min"/>
|
||||||
|
<field name="amount_max"/>
|
||||||
|
<field name="operations"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="view_account_statement_operation_rule_search" model="ir.ui.view">
|
||||||
|
<field name="name">account.statement.operation.rule.search</field>
|
||||||
|
<field name="model">account.statement.operation.rule</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Statement Operation Rules">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="rule_type"/>
|
||||||
|
<field name="amount_min"/>
|
||||||
|
<field name="amount_max"/>
|
||||||
|
<field name="operations"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<record id="action_account_statement_operation_rule" model="ir.actions.act_window">
|
||||||
|
<field name="name">Statement Operation Rules</field>
|
||||||
|
<field name="res_model">account.statement.operation.rule</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="search_view_id" ref="view_account_statement_operation_rule_search"/>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="oe_view_nocontent_create">
|
||||||
|
Click to create a statement operation rule.
|
||||||
|
</p><p>
|
||||||
|
Those can be used to automatically create a move line when reconciling
|
||||||
|
your bank statements.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<menuitem action="action_account_statement_operation_rule"
|
||||||
|
id="menu_action_account_statement_operation_rule"
|
||||||
|
parent="account.menu_configuration_misc"
|
||||||
|
name="Statement Operation Rules"
|
||||||
|
sequence="22"/>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
Reference in New Issue
Block a user