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:
Guewen Baconnier
2014-11-11 11:17:27 +01:00
parent 86cf2e953a
commit 4ebb2cbc72
8 changed files with 412 additions and 0 deletions

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

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import model

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

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import account_statement_operation_rule

View File

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

View File

@@ -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;
},
});
};

View File

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

View File

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