From 4ebb2cbc72e2a8982b4bd53b2b8e08f9412e5ff5 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 11 Nov 2014 11:17:27 +0100 Subject: [PATCH 01/16] 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. --- account_statement_operation_rule/README.rst | 101 +++++++++++++ account_statement_operation_rule/__init__.py | 3 + .../__openerp__.py | 36 +++++ .../model/__init__.py | 3 + .../model/account_statement_operation_rule.py | 138 ++++++++++++++++++ .../static/src/js/account_widgets.js | 37 +++++ .../view/account_statement_operation_rule.xml | 11 ++ .../account_statement_operation_rule_view.xml | 83 +++++++++++ 8 files changed, 412 insertions(+) create mode 100644 account_statement_operation_rule/README.rst create mode 100644 account_statement_operation_rule/__init__.py create mode 100644 account_statement_operation_rule/__openerp__.py create mode 100644 account_statement_operation_rule/model/__init__.py create mode 100644 account_statement_operation_rule/model/account_statement_operation_rule.py create mode 100644 account_statement_operation_rule/static/src/js/account_widgets.js create mode 100644 account_statement_operation_rule/view/account_statement_operation_rule.xml create mode 100644 account_statement_operation_rule/view/account_statement_operation_rule_view.xml diff --git a/account_statement_operation_rule/README.rst b/account_statement_operation_rule/README.rst new file mode 100644 index 00000000..9f6b82d2 --- /dev/null +++ b/account_statement_operation_rule/README.rst @@ -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 + +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. diff --git a/account_statement_operation_rule/__init__.py b/account_statement_operation_rule/__init__.py new file mode 100644 index 00000000..bad9dcdd --- /dev/null +++ b/account_statement_operation_rule/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import model diff --git a/account_statement_operation_rule/__openerp__.py b/account_statement_operation_rule/__openerp__.py new file mode 100644 index 00000000..ab5cd38a --- /dev/null +++ b/account_statement_operation_rule/__openerp__.py @@ -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 . +# +############################################################################## + +{'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, + } diff --git a/account_statement_operation_rule/model/__init__.py b/account_statement_operation_rule/model/__init__.py new file mode 100644 index 00000000..f8bd646b --- /dev/null +++ b/account_statement_operation_rule/model/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import account_statement_operation_rule diff --git a/account_statement_operation_rule/model/account_statement_operation_rule.py b/account_statement_operation_rule/model/account_statement_operation_rule.py new file mode 100644 index 00000000..3500bc32 --- /dev/null +++ b/account_statement_operation_rule/model/account_statement_operation_rule.py @@ -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 . +# +############################################################################## + +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 diff --git a/account_statement_operation_rule/static/src/js/account_widgets.js b/account_statement_operation_rule/static/src/js/account_widgets.js new file mode 100644 index 00000000..8912a7eb --- /dev/null +++ b/account_statement_operation_rule/static/src/js/account_widgets.js @@ -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; + }, + }); +}; diff --git a/account_statement_operation_rule/view/account_statement_operation_rule.xml b/account_statement_operation_rule/view/account_statement_operation_rule.xml new file mode 100644 index 00000000..dacd629c --- /dev/null +++ b/account_statement_operation_rule/view/account_statement_operation_rule.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/account_statement_operation_rule/view/account_statement_operation_rule_view.xml b/account_statement_operation_rule/view/account_statement_operation_rule_view.xml new file mode 100644 index 00000000..c4e9a3a4 --- /dev/null +++ b/account_statement_operation_rule/view/account_statement_operation_rule_view.xml @@ -0,0 +1,83 @@ + + + + + + account.statement.operation.rule.form + account.statement.operation.rule + +
+ +
+
+ + + + + + + +
+
+
+
+ + account.statement.operation.rule.tree + account.statement.operation.rule + + + + + + + + + + + + + account.statement.operation.rule.search + account.statement.operation.rule + + + + + + + + + + + + Statement Operation Rules + account.statement.operation.rule + form + tree,form + + +

+ Click to create a statement operation rule. +

+ Those can be used to automatically create a move line when reconciling + your bank statements. +

+
+
+ +
+
From a87fdeb50adba2a5808722b92daefb09b3eed260 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 26 Nov 2014 09:14:18 +0100 Subject: [PATCH 02/16] Add a test suite --- .../model/account_statement_operation_rule.py | 3 + .../tests/__init__.py | 22 +++ .../tests/test_rule.py | 183 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 account_statement_operation_rule/tests/__init__.py create mode 100644 account_statement_operation_rule/tests/test_rule.py diff --git a/account_statement_operation_rule/model/account_statement_operation_rule.py b/account_statement_operation_rule/model/account_statement_operation_rule.py index 3500bc32..d19866cd 100644 --- a/account_statement_operation_rule/model/account_statement_operation_rule.py +++ b/account_statement_operation_rule/model/account_statement_operation_rule.py @@ -113,6 +113,9 @@ class AccountStatementOperationRule(models.Model): balance = statement_line.amount for move_line in move_lines: balance += move_line.credit - move_line.debit + # TODO use is_zero + if not balance: + return self.browse() rules = self.search([]) # return the first applicable rule diff --git a/account_statement_operation_rule/tests/__init__.py b/account_statement_operation_rule/tests/__init__.py new file mode 100644 index 00000000..23fa21ab --- /dev/null +++ b/account_statement_operation_rule/tests/__init__.py @@ -0,0 +1,22 @@ +# -*- 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 . +# +############################################################################## + +from . import test_rule diff --git a/account_statement_operation_rule/tests/test_rule.py b/account_statement_operation_rule/tests/test_rule.py new file mode 100644 index 00000000..480dba02 --- /dev/null +++ b/account_statement_operation_rule/tests/test_rule.py @@ -0,0 +1,183 @@ +# -*- 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 . +# +############################################################################## + +from openerp.tests import common + + +class TestRule(common.TransactionCase): + + def setUp(self): + super(TestRule, self).setUp() + self.operation_obj = self.env['account.statement.operation.template'] + self.rule_obj = self.env['account.statement.operation.rule'] + self.operation_round_1 = self.operation_obj.create({ + 'name': 'Rounding -1.0 to 0.0', + 'label': 'Rounding', + 'account_id': self.ref('account.rsa'), + 'amount_type': 'percentage_of_total', + 'amount': 100.0, + + }) + self.rule_round_1 = self.rule_obj.create({ + 'name': 'Rounding -1.0 to 0.0', + 'rule_type': 'balance', + 'operations': [(6, 0, (self.operation_round_1.id, ))], + 'amount_min': -1.0, + 'amount_max': 0, + 'sequence': 1, + }) + self.operation_round_2 = self.operation_obj.create({ + 'name': 'Rounding -2.0 to -1.0', + 'label': 'Rounding', + 'account_id': self.ref('account.rsa'), + 'amount_type': 'percentage_of_total', + 'amount': 100.0, + + }) + self.rule_round_2 = self.rule_obj.create({ + 'name': 'Rounding -1.0 to 0.0', + 'rule_type': 'balance', + 'operations': [(6, 0, (self.operation_round_2.id, ))], + 'amount_min': -2.0, + 'amount_max': -1.0, + 'sequence': 2, + }) + self.operation_round_3 = self.operation_obj.create({ + 'name': 'Rounding 0.0 to 2.0', + 'label': 'Rounding', + 'account_id': self.ref('account.rsa'), + 'amount_type': 'percentage_of_total', + 'amount': 100.0, + + }) + self.rule_round_3 = self.rule_obj.create({ + 'name': 'Rounding 0.0 to 2.0', + 'rule_type': 'balance', + 'operations': [(6, 0, (self.operation_round_3.id, ))], + 'amount_min': 0, + 'amount_max': 2, + 'sequence': 2, + }) + + def _prepare_statement(self, difference): + amount = 100 + statement_obj = self.env['account.bank.statement'] + statement_line_obj = self.env['account.bank.statement.line'] + move_obj = self.env['account.move'] + move_line_obj = self.env['account.move.line'] + statement = statement_obj.create({ + 'name': '/', + 'journal_id': self.ref('account.cash_journal') + }) + statement_line = statement_line_obj.create({ + 'name': '001', + 'amount': amount + difference, + 'statement_id': statement.id, + }) + move = move_obj.create({ + 'journal_id': self.ref('account.sales_journal') + }) + move_line = move_line_obj.create({ + 'move_id': move.id, + 'name': '001', + 'account_id': self.ref('account.a_recv'), + 'debit': amount, + }) + move_line_obj.create({ + 'move_id': move.id, + 'name': '001', + 'account_id': self.ref('account.a_sale'), + 'credit': amount, + }) + return statement_line, move_line + + def test_rule_round_1(self): + """-0.5 => rule round 1""" + statement_line, move_line = self._prepare_statement(-0.5) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_1) + + def test_rule_round_1_limit(self): + """-1 => rule round 1""" + statement_line, move_line = self._prepare_statement(-1) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_1) + + def test_rule_round_1_near_limit(self): + """-1.0001 => rule round 1""" + statement_line, move_line = self._prepare_statement(-1.0001) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_1) + + def test_rule_round_2(self): + """-1.01 => rule round 2""" + statement_line, move_line = self._prepare_statement(-1.01) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_2) + + def test_rule_round_2_limit(self): + """-2 => rule round 2""" + statement_line, move_line = self._prepare_statement(-2) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_2) + + def test_rule_round_3(self): + """+1.5 => rule round 3""" + statement_line, move_line = self._prepare_statement(1.5) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_3) + + def test_rule_round_3_limit(self): + """+2 => rule round 3""" + statement_line, move_line = self._prepare_statement(2) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_3) + + def test_rule_no_round_below(self): + """-3 => no rule""" + statement_line, move_line = self._prepare_statement(-3) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_rule_no_round_above(self): + """+3 => no rule""" + statement_line, move_line = self._prepare_statement(3) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_rule_no_round_zero(self): + """0 => no rule""" + statement_line, move_line = self._prepare_statement(0) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_rule_no_round_near_zero(self): + """0.0001 => no rule""" + statement_line, move_line = self._prepare_statement(0.0001) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_operations(self): + """test operations_for_reconciliation()""" + statement_line, move_line = self._prepare_statement(-0.5) + ops = self.rule_obj.operations_for_reconciliation(statement_line.id, + move_line.ids) + self.assertEquals(ops, self.operation_round_1) From 630e995ae82770f507f1ef2428f9394171e66c41 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 26 Nov 2014 09:59:41 +0100 Subject: [PATCH 03/16] Use float comparisons --- .../model/account_statement_operation_rule.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/account_statement_operation_rule/model/account_statement_operation_rule.py b/account_statement_operation_rule/model/account_statement_operation_rule.py index d19866cd..72edb00d 100644 --- a/account_statement_operation_rule/model/account_statement_operation_rule.py +++ b/account_statement_operation_rule/model/account_statement_operation_rule.py @@ -55,8 +55,14 @@ class AccountStatementOperationRule(models.Model): @api.multi def _is_valid_balance(self, statement_line, move_lines, balance): - # TODO use float compare - return self.amount_min <= balance <= self.amount_max + currency = (statement_line.currency_id or + statement_line.statement_id.currency) + # FIXME: is_valid_balance must not work with multicurrency + if currency.compare_amounts(balance, self.amount_min) == -1: + return False + if currency.compare_amounts(balance, self.amount_max) == 1: + return False + return True @api.multi def _is_valid_multicurrency(self, statement_line, move_lines, balance): @@ -74,6 +80,7 @@ class AccountStatementOperationRule(models.Model): # 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): + # FIXME: is_valid_balance must not work with multicurrency return self._is_valid_balance(statement_line, move_lines, balance) return False @@ -113,8 +120,10 @@ class AccountStatementOperationRule(models.Model): balance = statement_line.amount for move_line in move_lines: balance += move_line.credit - move_line.debit - # TODO use is_zero - if not balance: + + currency = (statement_line.currency_id or + statement_line.statement_id.currency) + if currency.is_zero(balance): return self.browse() rules = self.search([]) From 2fb222abe91a8b6fb445d228287bce4f7da6aed7 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 26 Nov 2014 10:10:02 +0100 Subject: [PATCH 04/16] The 'balance' rules must not match when we are in multicurrency --- .../model/account_statement_operation_rule.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/account_statement_operation_rule/model/account_statement_operation_rule.py b/account_statement_operation_rule/model/account_statement_operation_rule.py index 72edb00d..dc69f166 100644 --- a/account_statement_operation_rule/model/account_statement_operation_rule.py +++ b/account_statement_operation_rule/model/account_statement_operation_rule.py @@ -54,22 +54,35 @@ class AccountStatementOperationRule(models.Model): ) @api.multi - def _is_valid_balance(self, statement_line, move_lines, balance): - currency = (statement_line.currency_id or - statement_line.statement_id.currency) - # FIXME: is_valid_balance must not work with multicurrency + def _balance_in_range(self, balance, currency): if currency.compare_amounts(balance, self.amount_min) == -1: return False if currency.compare_amounts(balance, self.amount_max) == 1: return False return True + @api.model + def _is_multicurrency(self, statement_line): + currency = (statement_line.currency_id or + statement_line.statement_id.currency) + company_currency = statement_line.company_id.currency_id + return currency != company_currency + + @api.multi + def _is_valid_balance(self, statement_line, move_lines, balance): + if self._is_multicurrency(statement_line): + return False + currency = (statement_line.currency_id or + statement_line.statement_id.currency) + return self._balance_in_range(balance, currency) + @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 + if self._is_multicurrency(statement_line): return False + currency = (statement_line.currency_id or + statement_line.statement_id.currency) amount_currency = statement_line.amount_currency for move_line in move_lines: if move_line.currency_id != statement_line.currency_id: @@ -80,8 +93,7 @@ class AccountStatementOperationRule(models.Model): # 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): - # FIXME: is_valid_balance must not work with multicurrency - return self._is_valid_balance(statement_line, move_lines, balance) + return self._balance_in_range(balance, currency) return False @api.multi From b9015944591e76f1aa8ce2873699609ba5b3e840 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 26 Nov 2014 11:07:34 +0100 Subject: [PATCH 05/16] Extract a helper so it can be reused --- .../tests/common.py | 58 +++++++++++++++++++ .../tests/test_rule.py | 58 +++++-------------- 2 files changed, 72 insertions(+), 44 deletions(-) create mode 100644 account_statement_operation_rule/tests/common.py diff --git a/account_statement_operation_rule/tests/common.py b/account_statement_operation_rule/tests/common.py new file mode 100644 index 00000000..d8cf7d75 --- /dev/null +++ b/account_statement_operation_rule/tests/common.py @@ -0,0 +1,58 @@ +# -*- 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 . +# +############################################################################## + + +def prepare_statement(test, difference): + """ Prepare a bank statement line and a move line + + The difference is applied on the bank statement line relatively to + the move line. + """ + amount = 100 + statement_obj = test.env['account.bank.statement'] + statement_line_obj = test.env['account.bank.statement.line'] + move_obj = test.env['account.move'] + move_line_obj = test.env['account.move.line'] + statement = statement_obj.create({ + 'name': '/', + 'journal_id': test.ref('account.cash_journal') + }) + statement_line = statement_line_obj.create({ + 'name': '001', + 'amount': amount + difference, + 'statement_id': statement.id, + }) + move = move_obj.create({ + 'journal_id': test.ref('account.sales_journal') + }) + move_line = move_line_obj.create({ + 'move_id': move.id, + 'name': '001', + 'account_id': test.ref('account.a_recv'), + 'debit': amount, + }) + move_line_obj.create({ + 'move_id': move.id, + 'name': '001', + 'account_id': test.ref('account.a_sale'), + 'credit': amount, + }) + return statement_line, move_line diff --git a/account_statement_operation_rule/tests/test_rule.py b/account_statement_operation_rule/tests/test_rule.py index 480dba02..8dde1f1a 100644 --- a/account_statement_operation_rule/tests/test_rule.py +++ b/account_statement_operation_rule/tests/test_rule.py @@ -21,6 +21,8 @@ from openerp.tests import common +from .common import prepare_statement + class TestRule(common.TransactionCase): @@ -77,107 +79,75 @@ class TestRule(common.TransactionCase): 'sequence': 2, }) - def _prepare_statement(self, difference): - amount = 100 - statement_obj = self.env['account.bank.statement'] - statement_line_obj = self.env['account.bank.statement.line'] - move_obj = self.env['account.move'] - move_line_obj = self.env['account.move.line'] - statement = statement_obj.create({ - 'name': '/', - 'journal_id': self.ref('account.cash_journal') - }) - statement_line = statement_line_obj.create({ - 'name': '001', - 'amount': amount + difference, - 'statement_id': statement.id, - }) - move = move_obj.create({ - 'journal_id': self.ref('account.sales_journal') - }) - move_line = move_line_obj.create({ - 'move_id': move.id, - 'name': '001', - 'account_id': self.ref('account.a_recv'), - 'debit': amount, - }) - move_line_obj.create({ - 'move_id': move.id, - 'name': '001', - 'account_id': self.ref('account.a_sale'), - 'credit': amount, - }) - return statement_line, move_line - def test_rule_round_1(self): """-0.5 => rule round 1""" - statement_line, move_line = self._prepare_statement(-0.5) + statement_line, move_line = prepare_statement(self, -0.5) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_1) def test_rule_round_1_limit(self): """-1 => rule round 1""" - statement_line, move_line = self._prepare_statement(-1) + statement_line, move_line = prepare_statement(self, -1) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_1) def test_rule_round_1_near_limit(self): """-1.0001 => rule round 1""" - statement_line, move_line = self._prepare_statement(-1.0001) + statement_line, move_line = prepare_statement(self, -1.0001) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_1) def test_rule_round_2(self): """-1.01 => rule round 2""" - statement_line, move_line = self._prepare_statement(-1.01) + statement_line, move_line = prepare_statement(self, -1.01) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_2) def test_rule_round_2_limit(self): """-2 => rule round 2""" - statement_line, move_line = self._prepare_statement(-2) + statement_line, move_line = prepare_statement(self, -2) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_2) def test_rule_round_3(self): """+1.5 => rule round 3""" - statement_line, move_line = self._prepare_statement(1.5) + statement_line, move_line = prepare_statement(self, 1.5) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_3) def test_rule_round_3_limit(self): """+2 => rule round 3""" - statement_line, move_line = self._prepare_statement(2) + statement_line, move_line = prepare_statement(self, 2) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertEquals(rule, self.rule_round_3) def test_rule_no_round_below(self): """-3 => no rule""" - statement_line, move_line = self._prepare_statement(-3) + statement_line, move_line = prepare_statement(self, -3) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertFalse(rule) def test_rule_no_round_above(self): """+3 => no rule""" - statement_line, move_line = self._prepare_statement(3) + statement_line, move_line = prepare_statement(self, 3) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertFalse(rule) def test_rule_no_round_zero(self): """0 => no rule""" - statement_line, move_line = self._prepare_statement(0) + statement_line, move_line = prepare_statement(self, 0) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertFalse(rule) def test_rule_no_round_near_zero(self): """0.0001 => no rule""" - statement_line, move_line = self._prepare_statement(0.0001) + statement_line, move_line = prepare_statement(self, 0.0001) rule = self.rule_obj.find_first_rule(statement_line, [move_line]) self.assertFalse(rule) def test_operations(self): """test operations_for_reconciliation()""" - statement_line, move_line = self._prepare_statement(-0.5) + statement_line, move_line = prepare_statement(self, -0.5) ops = self.rule_obj.operations_for_reconciliation(statement_line.id, move_line.ids) self.assertEquals(ops, self.operation_round_1) From f7074a2664a706430dce3b0506f716a975119156 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 26 Nov 2014 12:00:15 +0100 Subject: [PATCH 06/16] Extract a function which is reused in account_statement_operation_rule_dunning_fees --- .../model/account_statement_operation_rule.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/account_statement_operation_rule/model/account_statement_operation_rule.py b/account_statement_operation_rule/model/account_statement_operation_rule.py index dc69f166..9c616556 100644 --- a/account_statement_operation_rule/model/account_statement_operation_rule.py +++ b/account_statement_operation_rule/model/account_statement_operation_rule.py @@ -53,14 +53,26 @@ class AccountStatementOperationRule(models.Model): help="If several rules match, the first one is used.", ) - @api.multi - def _balance_in_range(self, balance, currency): - if currency.compare_amounts(balance, self.amount_min) == -1: + @staticmethod + def _between_with_bounds(low, value, high, currency): + """ Equivalent to a three way comparison: ``min <= value <= high`` + + The comparisons are done with the currency to use the correct + precision. + """ + if currency.compare_amounts(value, low) == -1: return False - if currency.compare_amounts(balance, self.amount_max) == 1: + if currency.compare_amounts(value, high) == 1: return False return True + @api.multi + def _balance_in_range(self, balance, currency): + amount_min = self.amount_min + amount_max = self.amount_max + return self._between_with_bounds(amount_min, balance, + amount_max, currency) + @api.model def _is_multicurrency(self, statement_line): currency = (statement_line.currency_id or From cac59983c3a8dac0c5e0a88ad64f9e185195b942 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 3 Dec 2014 13:20:11 +0100 Subject: [PATCH 07/16] Add a security group an account.statement.operation.rule --- account_statement_operation_rule/__openerp__.py | 1 + account_statement_operation_rule/security/ir.model.access.csv | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 account_statement_operation_rule/security/ir.model.access.csv diff --git a/account_statement_operation_rule/__openerp__.py b/account_statement_operation_rule/__openerp__.py index ab5cd38a..1302d8da 100644 --- a/account_statement_operation_rule/__openerp__.py +++ b/account_statement_operation_rule/__openerp__.py @@ -30,6 +30,7 @@ 'website': 'http://www.camptocamp.com', 'data': ['view/account_statement_operation_rule.xml', 'view/account_statement_operation_rule_view.xml', + 'security/ir.model.access.csv', ], 'installable': True, 'auto_install': False, diff --git a/account_statement_operation_rule/security/ir.model.access.csv b/account_statement_operation_rule/security/ir.model.access.csv new file mode 100644 index 00000000..f6b7bfb6 --- /dev/null +++ b/account_statement_operation_rule/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_statement_operation_rule,account.statement.operation.rule,model_account_statement_operation_rule,account.group_account_user,1,1,1,1 From 0656b9afa05c517d17680dd70d60c67ca71d0624 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 3 Dec 2014 14:23:36 +0100 Subject: [PATCH 08/16] Rename the basic rule 'Balance' to 'Roundings' --- .../model/account_statement_operation_rule.py | 6 +++--- account_statement_operation_rule/tests/test_rule.py | 6 +++--- .../view/account_statement_operation_rule_view.xml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/account_statement_operation_rule/model/account_statement_operation_rule.py b/account_statement_operation_rule/model/account_statement_operation_rule.py index 9c616556..3de82c85 100644 --- a/account_statement_operation_rule/model/account_statement_operation_rule.py +++ b/account_statement_operation_rule/model/account_statement_operation_rule.py @@ -30,10 +30,10 @@ class AccountStatementOperationRule(models.Model): name = fields.Char() rule_type = fields.Selection( - selection=[('balance', 'Balance'), + selection=[('rounding', 'Roundings'), ('currency', 'Currencies')], string='Type', - default='balance', + default='rounding', required=True, ) operations = fields.Many2many( @@ -126,7 +126,7 @@ class AccountStatementOperationRule(models.Model): rule when called on multiple rules. """ self.ensure_one() - if self.rule_type == 'balance': + if self.rule_type == 'rounding': return self._is_valid_balance(statement_line, move_lines, balance) elif self.rule_type == 'currency': return self._is_valid_multicurrency(statement_line, diff --git a/account_statement_operation_rule/tests/test_rule.py b/account_statement_operation_rule/tests/test_rule.py index 8dde1f1a..2f6ea990 100644 --- a/account_statement_operation_rule/tests/test_rule.py +++ b/account_statement_operation_rule/tests/test_rule.py @@ -40,7 +40,7 @@ class TestRule(common.TransactionCase): }) self.rule_round_1 = self.rule_obj.create({ 'name': 'Rounding -1.0 to 0.0', - 'rule_type': 'balance', + 'rule_type': 'rounding', 'operations': [(6, 0, (self.operation_round_1.id, ))], 'amount_min': -1.0, 'amount_max': 0, @@ -56,7 +56,7 @@ class TestRule(common.TransactionCase): }) self.rule_round_2 = self.rule_obj.create({ 'name': 'Rounding -1.0 to 0.0', - 'rule_type': 'balance', + 'rule_type': 'rounding', 'operations': [(6, 0, (self.operation_round_2.id, ))], 'amount_min': -2.0, 'amount_max': -1.0, @@ -72,7 +72,7 @@ class TestRule(common.TransactionCase): }) self.rule_round_3 = self.rule_obj.create({ 'name': 'Rounding 0.0 to 2.0', - 'rule_type': 'balance', + 'rule_type': 'rounding', 'operations': [(6, 0, (self.operation_round_3.id, ))], 'amount_min': 0, 'amount_max': 2, diff --git a/account_statement_operation_rule/view/account_statement_operation_rule_view.xml b/account_statement_operation_rule/view/account_statement_operation_rule_view.xml index c4e9a3a4..ba34b050 100644 --- a/account_statement_operation_rule/view/account_statement_operation_rule_view.xml +++ b/account_statement_operation_rule/view/account_statement_operation_rule_view.xml @@ -17,7 +17,7 @@ - + - From 32fc96c0d9784a485ce6e48615e7689d34d16462 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Mon, 16 Mar 2015 15:52:12 +0100 Subject: [PATCH 12/16] Add translation template --- .../i18n/account_statement_operation_rule.pot | 152 +++++++++++++++++ account_statement_operation_rule/i18n/fr.po | 158 ++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 account_statement_operation_rule/i18n/account_statement_operation_rule.pot create mode 100644 account_statement_operation_rule/i18n/fr.po diff --git a/account_statement_operation_rule/i18n/account_statement_operation_rule.pot b/account_statement_operation_rule/i18n/account_statement_operation_rule.pot new file mode 100644 index 00000000..430e735e --- /dev/null +++ b/account_statement_operation_rule/i18n/account_statement_operation_rule.pot @@ -0,0 +1,152 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_statement_operation_rule +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-03-16 14:46+0000\n" +"PO-Revision-Date: 2015-03-16 14:46+0000\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_statement_operation_rule +#: model:ir.actions.act_window,help:account_statement_operation_rule.action_account_statement_operation_rule +msgid "

\n" +" Click to create a statement operation rule.\n" +"

\n" +" Those can be used to automatically create a move line when reconciling\n" +" your bank statements.\n" +"

\n" +" " +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "And" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "And the currency is one of" +msgstr "" + +#. module: account_statement_operation_rule +#: model:ir.model,name:account_statement_operation_rule.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,create_uid:0 +msgid "Created by" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,create_date:0 +msgid "Created on" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,currencies:0 +#: selection:account.statement.operation.rule,rule_type:0 +msgid "Currencies" +msgstr "" + +#. module: account_statement_operation_rule +#: help:account.statement.operation.rule,currencies:0 +msgid "For 'Currencies' rules, you can choose for which currencies the rule will be applicable." +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,id:0 +msgid "ID" +msgstr "" + +#. module: account_statement_operation_rule +#: help:account.statement.operation.rule,sequence:0 +msgid "If several rules match, the first one is used." +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,amount_max:0 +msgid "Max. Amount" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,amount_min:0 +msgid "Min. Amount" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,name:0 +msgid "Name" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,operations:0 +msgid "Operations" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Result" +msgstr "" + +#. module: account_statement_operation_rule +#: selection:account.statement.operation.rule,rule_type:0 +msgid "Roundings" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Rule" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Statement Operation Rule" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_search +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_tree +#: model:ir.actions.act_window,name:account_statement_operation_rule.action_account_statement_operation_rule +#: model:ir.ui.menu,name:account_statement_operation_rule.menu_action_account_statement_operation_rule +msgid "Statement Operation Rules" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Then the following operations will be applied:" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,rule_type:0 +msgid "Type" +msgstr "" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "When the balance is between" +msgstr "" + diff --git a/account_statement_operation_rule/i18n/fr.po b/account_statement_operation_rule/i18n/fr.po new file mode 100644 index 00000000..4e22afe3 --- /dev/null +++ b/account_statement_operation_rule/i18n/fr.po @@ -0,0 +1,158 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_statement_operation_rule +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-03-16 14:46+0000\n" +"PO-Revision-Date: 2015-03-16 14:46+0000\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_statement_operation_rule +#: model:ir.actions.act_window,help:account_statement_operation_rule.action_account_statement_operation_rule +msgid "

\n" +" Click to create a statement operation rule.\n" +"

\n" +" Those can be used to automatically create a move line when reconciling\n" +" your bank statements.\n" +"

\n" +" " +msgstr "

\n" +" Cliquer pour créer une nouvelle règle d'opération relevé.\n" +"

\n" +" Elles peuvent être utilisées pour automatiser la création de lignes " +" quand vous réconciliez des relevés bancaires." +"

\n" +" " + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "And" +msgstr "Et" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "And the currency is one of" +msgstr "Et la devise est une des suivantes" + +#. module: account_statement_operation_rule +#: model:ir.model,name:account_statement_operation_rule.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "Ligne de relevé bancaire" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,create_uid:0 +msgid "Created by" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,create_date:0 +msgid "Created on" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,currencies:0 +#: selection:account.statement.operation.rule,rule_type:0 +msgid "Currencies" +msgstr "Devises" + +#. module: account_statement_operation_rule +#: help:account.statement.operation.rule,currencies:0 +msgid "For 'Currencies' rules, you can choose for which currencies the rule will be applicable." +msgstr "Pour les règles 'Devises', vous pouvez sélectionner les devises pour lesquelles la règle s'applique." + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,id:0 +msgid "ID" +msgstr "" + +#. module: account_statement_operation_rule +#: help:account.statement.operation.rule,sequence:0 +msgid "If several rules match, the first one is used." +msgstr "Si plusieurs règles correspondent, la première est utilisée." + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,write_uid:0 +msgid "Last Updated by" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,write_date:0 +msgid "Last Updated on" +msgstr "" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,amount_max:0 +msgid "Max. Amount" +msgstr "Montant max." + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,amount_min:0 +msgid "Min. Amount" +msgstr "Montant min." + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,name:0 +msgid "Name" +msgstr "Nom" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,operations:0 +msgid "Operations" +msgstr "Opérations" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Result" +msgstr "Résultat" + +#. module: account_statement_operation_rule +#: selection:account.statement.operation.rule,rule_type:0 +msgid "Roundings" +msgstr "Arrondis" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Rule" +msgstr "Règle" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,sequence:0 +msgid "Sequence" +msgstr "Séquence" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Statement Operation Rule" +msgstr "Règle d'opération de relevé" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_search +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_tree +#: model:ir.actions.act_window,name:account_statement_operation_rule.action_account_statement_operation_rule +#: model:ir.ui.menu,name:account_statement_operation_rule.menu_action_account_statement_operation_rule +msgid "Statement Operation Rules" +msgstr "Règles d'opération de relevé" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "Then the following operations will be applied:" +msgstr "Alors l'opération suivant sera appliquée :" + +#. module: account_statement_operation_rule +#: field:account.statement.operation.rule,rule_type:0 +msgid "Type" +msgstr "Type" + +#. module: account_statement_operation_rule +#: view:account.statement.operation.rule:account_statement_operation_rule.view_account_statement_operation_rule_form +msgid "When the balance is between" +msgstr "Quand la balance est entre" + From 1b3031491e1e8d13da76a28b1caf4afa2757edfe Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 25 Nov 2014 09:16:18 +0100 Subject: [PATCH 13/16] Add account_statement_operation_rule_dunning_fees Rules based on credit control dunning fees --- .../README.rst | 85 +++++++++ .../__init__.py | 3 + .../__openerp__.py | 36 ++++ .../model/__init__.py | 3 + .../model/account_statement_operation_rule.py | 100 ++++++++++ .../tests/__init__.py | 23 +++ .../tests/test_rule_dunning_fees.py | 171 ++++++++++++++++++ .../tests/test_rule_sequence.py | 83 +++++++++ .../account_statement_operation_rule_view.xml | 18 ++ 9 files changed, 522 insertions(+) create mode 100644 account_statement_operation_rule_dunning_fees/README.rst create mode 100644 account_statement_operation_rule_dunning_fees/__init__.py create mode 100644 account_statement_operation_rule_dunning_fees/__openerp__.py create mode 100644 account_statement_operation_rule_dunning_fees/model/__init__.py create mode 100644 account_statement_operation_rule_dunning_fees/model/account_statement_operation_rule.py create mode 100644 account_statement_operation_rule_dunning_fees/tests/__init__.py create mode 100644 account_statement_operation_rule_dunning_fees/tests/test_rule_dunning_fees.py create mode 100644 account_statement_operation_rule_dunning_fees/tests/test_rule_sequence.py create mode 100644 account_statement_operation_rule_dunning_fees/view/account_statement_operation_rule_view.xml diff --git a/account_statement_operation_rule_dunning_fees/README.rst b/account_statement_operation_rule_dunning_fees/README.rst new file mode 100644 index 00000000..29fec194 --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/README.rst @@ -0,0 +1,85 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License + +Bank Statement Operation Rules with Dunning Fees +================================================ + +Extends the *Bank Statement Operation Rules* with a new rule, the +**Dunning Fees** rule. It allows to automatically create a write-off +entry for the amount paid by the customers when they received dunning +fees (using the **Account Credit Control** module). + +Configuration +------------- + +As this module aims to automatize the ``Statement Operation Templates``, +you first want to ensure that you have an operation configured for the +dunning fees. +You can find them in ``Invoicing > Configuration > Miscellaneous > +Statement Operation Templates``. An example of operation is (the account +is where the amount received for the dunning fees will be input): + +=================== ========================== ======= ============ +Account Amount Type Amount Label +=================== ========================== ======= ============ +Depends of the l10n Percentage of open balance 100.0 % Dunning Fees +=================== ========================== ======= ============ + +The configuration of the rules themselves happens in ``Invoicing > +Configuration > Miscellaneous > Statement Operation Rules``. + +There is no conditions to setup on this rule. It will be applied if the +amount in the bank statement line is above the journal entries amount +and if the difference is comprised in the amount of the dunning fees for +the journal entries. + +Example: + +======================= ====== +Document Amount +======================= ====== +Journal Entry (invoice) 100.- +Dunning Fees no1 5.- +Dunning Fees no2 10.- +Dunning Fees no3 15.- +======================= ====== + +The customer received 3 times dunning fees, with a increasing amount. +The customer might pay from 100.- to 115.-. The difference between +100.- and what the customer paid above goes to the write-off account +configured on the operation. If the customer pays 99.- or 116.-, the +Dunning Fees rule is not valid and the other rules will be evaluated. + +.. note:: The Dunning Fees rule must be placed before the Roundings + rules, otherwise the fees might be confused with roundings. + +.. 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 + +Dependencies +------------ + +This module only works with the ``account_credit_control_dunning_fees`` +module in the project: https://github.com/OCA/account-financial-tools + +Credits +======= + +Contributors +------------ + +* Guewen Baconnier + +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. diff --git a/account_statement_operation_rule_dunning_fees/__init__.py b/account_statement_operation_rule_dunning_fees/__init__.py new file mode 100644 index 00000000..bad9dcdd --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import model diff --git a/account_statement_operation_rule_dunning_fees/__openerp__.py b/account_statement_operation_rule_dunning_fees/__openerp__.py new file mode 100644 index 00000000..96a16b24 --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/__openerp__.py @@ -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 . +# +############################################################################## + +{'name': 'Bank Statement Operation Rules with Dunning Fees', + 'version': '8.0.1.0.0', + 'author': 'Camptocamp', + 'maintainer': 'Camptocamp', + 'license': 'AGPL-3', + 'category': 'Accounting & Finance', + 'depends': ['account_statement_operation_rule', + 'account_credit_control_dunning_fees', + ], + 'website': 'http://www.camptocamp.com', + 'data': ['view/account_statement_operation_rule_view.xml', + ], + 'installable': True, + 'auto_install': True, + } diff --git a/account_statement_operation_rule_dunning_fees/model/__init__.py b/account_statement_operation_rule_dunning_fees/model/__init__.py new file mode 100644 index 00000000..f8bd646b --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/model/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import account_statement_operation_rule diff --git a/account_statement_operation_rule_dunning_fees/model/account_statement_operation_rule.py b/account_statement_operation_rule_dunning_fees/model/account_statement_operation_rule.py new file mode 100644 index 00000000..78099f2e --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/model/account_statement_operation_rule.py @@ -0,0 +1,100 @@ +# -*- 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 . +# +############################################################################## + +from openerp import models, fields, api, exceptions, _ + + +class AccountStatementOperationRule(models.Model): + _inherit = 'account.statement.operation.rule' + + rule_type = fields.Selection( + selection_add=[('dunning_fees', 'Dunning Fees')], + ) + + @api.multi + def _is_valid_dunning_fees(self, statement_line, move_lines, balance): + control_line_obj = self.env['credit.control.line'] + control_lines = None + for line in move_lines: + domain = [('move_line_id', '=', line.id), + ('state', '=', 'sent')] + line_control_lines = control_line_obj.search(domain) + if line_control_lines and control_lines: + # Several lines have credit control lines, use case + # not covered, needs to be handled manually, dunning + # fees rules not applied + control_lines = None + break + elif line_control_lines: + control_lines = line_control_lines + if control_lines: + # If we have an amount of 100.- with 3 credit control + # lines, with the following fees amounts: + # * 1st level: 5.- + # * 2nd level: 10.- + # * 3rd level: 15.- + # The customer might pay from 100.- to 115.-, the rest + # goes to the writeoff account configured on the operation. + max_fees = max(control_lines.mapped('dunning_fees_amount')) + # only use the dunning rule if the balance is between -fees and 0 + currency = statement_line.currency_for_rules() + return self._between_with_bounds(0, balance, max_fees, currency) + 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. + """ + if self.rule_type == 'dunning_fees': + return self._is_valid_dunning_fees(statement_line, + move_lines, + balance) + else: + _super = super(AccountStatementOperationRule, self) + return _super.is_valid(statement_line, move_lines, balance) + + @api.constrains('sequence') + def check_dunning_before_rounding(self): + if self.rule_type == 'dunning_fees': + operator = '<=' + other_type = 'rounding' + elif self.rule_type == 'rounding': + operator = '>' + other_type = 'dunning_fees' + else: + return + message = _('The Dunning Fees rule must be before the Rounding Rules') + if self.search([('sequence', operator, self.sequence), + ('rule_type', '=', other_type)], limit=1): + raise exceptions.ValidationError(message) diff --git a/account_statement_operation_rule_dunning_fees/tests/__init__.py b/account_statement_operation_rule_dunning_fees/tests/__init__.py new file mode 100644 index 00000000..62079f42 --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/tests/__init__.py @@ -0,0 +1,23 @@ +# -*- 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 . +# +############################################################################## + +from . import test_rule_dunning_fees +from . import test_rule_sequence diff --git a/account_statement_operation_rule_dunning_fees/tests/test_rule_dunning_fees.py b/account_statement_operation_rule_dunning_fees/tests/test_rule_dunning_fees.py new file mode 100644 index 00000000..6e68563b --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/tests/test_rule_dunning_fees.py @@ -0,0 +1,171 @@ +# -*- 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 . +# +############################################################################## + +from openerp.tests import common +from openerp.addons.account_statement_operation_rule.tests.common import ( + prepare_statement +) + + +def prepare_statement_with_dunning_fees(test, difference, fees): + """ Prepare a bank statement line and a move line + + The difference is applied on the bank statement line relatively to + the move line. + + The fees is a list of dunning fees (amounts) applied on the move line. + """ + statement_line, move_line = prepare_statement(test, difference) + control_lines = test.env['credit.control.line'].browse() + for fee in fees: + values = { + 'date': move_line.date, + 'date_due': move_line.date, + 'state': 'sent', + 'channel': 'letter', + 'partner_id': test.ref('base.res_partner_1'), + 'amount_due': move_line.credit, + 'balance_due': move_line.credit, + 'policy_level_id': test.ref('account_credit_control.3_time_1'), + 'company_id': move_line.company_id.id, + 'move_line_id': move_line.id, + 'dunning_fees_amount': fee, + } + control_lines += test.env['credit.control.line'].create(values) + return statement_line, move_line, control_lines + + +class TestDunningRule(common.TransactionCase): + + def setUp(self): + super(TestDunningRule, self).setUp() + self.operation_obj = self.env['account.statement.operation.template'] + self.rule_obj = self.env['account.statement.operation.rule'] + self.operation_dunning = self.operation_obj.create({ + 'name': 'Dunning Fees', + 'label': 'Dunning Fees', + 'account_id': self.ref('account.rsa'), + 'amount_type': 'percentage_of_total', + 'amount': 100.0, + + }) + self.rule_dunning = self.rule_obj.create({ + 'name': 'Dunning Fees', + 'rule_type': 'dunning_fees', + 'operations': [(6, 0, (self.operation_dunning.id, ))], + 'sequence': 1, + }) + self.operation_round_1 = self.operation_obj.create({ + 'name': 'Rounding -1.0 to 0.0', + 'label': 'Rounding', + 'account_id': self.ref('account.rsa'), + 'amount_type': 'percentage_of_total', + 'amount': 100.0, + + }) + self.rule_round_1 = self.rule_obj.create({ + 'name': 'Rounding -1.0 to 0.0', + 'rule_type': 'rounding', + 'operations': [(6, 0, (self.operation_round_1.id, ))], + 'amount_min': -1.0, + 'amount_max': 0, + 'sequence': 2, + }) + + def test_paid_dunning_fees(self): + """Customer paid the dunning fees of 10.-""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, 10, [10] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_dunning) + + def test_no_paid_dunning_fees(self): + """Customer paid the dunning fees of 10.-""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, 0, [10] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_paid_part_of_dunning_fees(self): + """Customer paid only 5.- of the dunning fees of 10.-""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, 5, [10] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_dunning) + + def test_paid_too_much_dunning_fees(self): + """Customer paid 15.- of the dunning fees of 10.-""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, 15, [10] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_paid_no_dunning_fees_and_less_amount(self): + """Customer paid 0.- of the dunning fees of 10.- and 1.- less""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, -1, [10] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_round_1) + + def test_paid_dunning_fees_several(self): + """Customer paid 15.- of the dunning fees of 5.-, 10.- and 15.-""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, 15, [5, 10, 15] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_dunning) + + def test_paid_too_much_dunning_fees_several(self): + """Customer paid 16.- of the dunning fees of 5.-, 10.- and 15.-""" + statement_line, move_line, __ = prepare_statement_with_dunning_fees( + self, 16, [5, 10, 15] + ) + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_ignored_credit_control_line(self): + """Customer paid 15.- of the fees of 5.-, 10.- and draft 15.-""" + prepare = prepare_statement_with_dunning_fees + statement_line, move_line, control_lines = prepare( + self, 15, [5, 10, 15] + ) + for control_line in control_lines: + if control_line.dunning_fees_amount == 15: + control_line.state = 'draft' + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertFalse(rule) + + def test_ignored_credit_control_line_take_other(self): + """Customer paid 10.- of the fees of 5.-, 10.- and draft 15.-""" + prepare = prepare_statement_with_dunning_fees + statement_line, move_line, control_lines = prepare( + self, 10, [5, 10, 15] + ) + for control_line in control_lines: + if control_line.dunning_fees_amount == 15: + control_line.state = 'draft' + rule = self.rule_obj.find_first_rule(statement_line, [move_line]) + self.assertEquals(rule, self.rule_dunning) diff --git a/account_statement_operation_rule_dunning_fees/tests/test_rule_sequence.py b/account_statement_operation_rule_dunning_fees/tests/test_rule_sequence.py new file mode 100644 index 00000000..335ba796 --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/tests/test_rule_sequence.py @@ -0,0 +1,83 @@ +# -*- 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 . +# +############################################################################## + +from openerp import exceptions +from openerp.tests import common + + +class TestRuleSequence(common.TransactionCase): + + def setUp(self): + super(TestRuleSequence, self).setUp() + self.operation_obj = self.env['account.statement.operation.template'] + self.rule_obj = self.env['account.statement.operation.rule'] + self.rule_dunning = self.rule_obj.create({ + 'name': 'Dunning Fees', + 'rule_type': 'dunning_fees', + 'sequence': 5, + }) + self.rule_round_1 = self.rule_obj.create({ + 'name': 'Rounding -1.0 to 0.0', + 'rule_type': 'rounding', + 'amount_min': -1.0, + 'amount_max': 0, + 'sequence': 10, + }) + self.rule_round_2 = self.rule_obj.create({ + 'name': 'Rounding -2.0 to -1.0', + 'rule_type': 'rounding', + 'amount_min': -2.0, + 'amount_max': -1.0, + 'sequence': 15, + }) + self.rule_currency = self.rule_obj.create({ + 'name': 'Currency', + 'rule_type': 'currency', + 'amount_min': -2.0, + 'amount_max': -1.0, + 'sequence': 20, + }) + + def test_dunning_first(self): + """ Dunning rule can be the first """ + self.rule_dunning.sequence = 1 + self.rule_round_1.sequence = 2 + self.rule_round_2.sequence = 3 + self.rule_currency.sequence = 4 + + def test_dunning_after_rounding(self): + """ Dunning rule cannot be after a rounding rule """ + with self.assertRaises(exceptions.ValidationError): + self.rule_dunning.sequence = 30 + + def test_dunning_equal_rounding(self): + """ Dunning rule cannot be equal to a rounding rule """ + with self.assertRaises(exceptions.ValidationError): + self.rule_dunning.sequence = 10 + + def test_rounding_before_dunning(self): + """ Rounding cannot be before dunning """ + with self.assertRaises(exceptions.ValidationError): + self.rule_round_1.sequence = 1 + + def test_currency_before_dunning(self): + """ Currency can be before dunning""" + self.rule_currency.sequence = 1 diff --git a/account_statement_operation_rule_dunning_fees/view/account_statement_operation_rule_view.xml b/account_statement_operation_rule_dunning_fees/view/account_statement_operation_rule_view.xml new file mode 100644 index 00000000..5f3e3409 --- /dev/null +++ b/account_statement_operation_rule_dunning_fees/view/account_statement_operation_rule_view.xml @@ -0,0 +1,18 @@ + + + + + + account.statement.operation.rule.form + account.statement.operation.rule + + + + + + + + + + From 2cb162dc567cdbc3c06e1569e525397131746481 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 26 Nov 2014 13:23:32 +0100 Subject: [PATCH 14/16] Add account-financial-tools in repo's dependencies --- account_statement_operation_rule_dunning_fees/__openerp__.py | 1 + oca_dependencies.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/account_statement_operation_rule_dunning_fees/__openerp__.py b/account_statement_operation_rule_dunning_fees/__openerp__.py index 96a16b24..16e70692 100644 --- a/account_statement_operation_rule_dunning_fees/__openerp__.py +++ b/account_statement_operation_rule_dunning_fees/__openerp__.py @@ -26,6 +26,7 @@ 'license': 'AGPL-3', 'category': 'Accounting & Finance', 'depends': ['account_statement_operation_rule', + # in https://github.com/OCA/account-financial-tools 'account_credit_control_dunning_fees', ], 'website': 'http://www.camptocamp.com', diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 34f82ae9..9a273770 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1 +1,2 @@ +account-financial-tools bank-payment From 70bf845404afe238622a211b13aad5abb14db928 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 4 Dec 2014 10:15:38 +0100 Subject: [PATCH 15/16] Improve usability by splitting the view in 2 parts: rule, result --- .../account_statement_operation_rule_view.xml | 24 ++++++++++--------- .../account_statement_operation_rule_view.xml | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/account_statement_operation_rule/view/account_statement_operation_rule_view.xml b/account_statement_operation_rule/view/account_statement_operation_rule_view.xml index d2d12577..8025126d 100644 --- a/account_statement_operation_rule/view/account_statement_operation_rule_view.xml +++ b/account_statement_operation_rule/view/account_statement_operation_rule_view.xml @@ -17,18 +17,20 @@ - -