diff --git a/account_credit_control_dunning_fees/__init__.py b/account_credit_control_dunning_fees/__init__.py new file mode 100644 index 000000000..6ac610f01 --- /dev/null +++ b/account_credit_control_dunning_fees/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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 model diff --git a/account_credit_control_dunning_fees/__openerp__.py b/account_credit_control_dunning_fees/__openerp__.py new file mode 100644 index 000000000..36ceb3944 --- /dev/null +++ b/account_credit_control_dunning_fees/__openerp__.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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': 'Credit control dunning fees', + 'version': '0.1.0', + 'author': 'Camptocamp', + 'maintainer': 'Camptocamp', + 'category': 'Accounting', + 'complexity': 'normal', + 'depends': ['account_credit_control'], + 'description': """ +Dunning Fees for Credit Control +=============================== + +This extention of credit control adds the notion of dunning fees +on credit control lines. + +Configuration +------------- +For release 0.1 only fixed fees are supported. + +You can specifiy a fees fixed a product and a currency +on the credit control level form. + +The amount will be used as fees values the currency will determine +the currency of the fee. If the credit control line has not the +same currency as the fees currency, fees will be converted to +the credit control line currency. + +The product is used to compute taxes in reconciliation process. + +Run +--- +Fees are automatically computed on credit run and saved +on the generated credit lines. + +Fees can be manually edited as long credit line is draft + +To Come +------- +Support of fees price list + +""", + 'website': 'http://www.camptocamp.com', + 'data': ['view/policy_view.xml', + 'view/line_view.xml'], + 'demo': [], + 'test': [], + 'installable': True, + 'auto_install': False, + 'license': 'AGPL-3', + 'application': False, +} diff --git a/account_credit_control_dunning_fees/model/__init__.py b/account_credit_control_dunning_fees/model/__init__.py new file mode 100644 index 000000000..c9a69427a --- /dev/null +++ b/account_credit_control_dunning_fees/model/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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 line +from . import policy +from . import run +from . import dunning diff --git a/account_credit_control_dunning_fees/model/dunning.py b/account_credit_control_dunning_fees/model/dunning.py new file mode 100644 index 000000000..dc383c6dd --- /dev/null +++ b/account_credit_control_dunning_fees/model/dunning.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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.osv import orm +from openerp.tools.translate import _ + + +class FeesComputer(orm.TransientModel): + _name = 'credit.control.dunning.fees.computer' + _auto = False + _log_access = True + + def _get_compute_fun(self, level_fees_type): + if level_fees_type == 'fixed': + return self.compute_fixed_fees + else: + raise NotImplementedError('fees type %s is not supported' % level_fees_type) + + def _compute_fees(self, cr, uid, credit_line_ids, context=None): + if context is None: + context = {} + if not credit_line_ids: + return credit_line_ids + c_model = self.pool['credit.control.line'] + credit_lines = c_model.browse(cr, uid, credit_line_ids, context=context) + for credit in credit_lines: + # if there is no dependence between generated credit lines + # this could be threaded + self._compute(cr, uid, credit, context=context), + return credit_line_ids + + def _compute(self, cr, uid, credit_line, context=None): + """Compute fees for a given credit line""" + fees_type = credit_line.policy_level_id.dunning_fees_type + compute = self._get_compute_fun(fees_type) + fees = compute(cr, uid, credit_line, context=context) + if fees: + credit_line.write({'dunning_fees_amount': fees}, + context=context) + + def compute_fixed_fees(self, cr, uid, credit_line, context=None): + currency_model = self.pool['res.currency'] + credit_currency = credit_line.currency_id + level = credit_line.policy_level_id + fees_amount = level.dunning_fixed_amount + if not fees_amount: + return 0.0 + fees_currency = level.dunning_currency_id + if fees_currency == credit_currency: + return fees_amount + else: + return currency_model.compute(cr, uid, fees_currency.id, + credit_currency.id, fees_amount, + context=context) diff --git a/account_credit_control_dunning_fees/model/line.py b/account_credit_control_dunning_fees/model/line.py new file mode 100644 index 000000000..9cc853326 --- /dev/null +++ b/account_credit_control_dunning_fees/model/line.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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.osv import orm, fields + + +class credit_control_line(orm.Model): + """Add fees computations""" + + _inherit = "credit.control.line" + + _columns = {'dunning_fees_amount': fields.float('Fees')} diff --git a/account_credit_control_dunning_fees/model/policy.py b/account_credit_control_dunning_fees/model/policy.py new file mode 100644 index 000000000..1cf3c699b --- /dev/null +++ b/account_credit_control_dunning_fees/model/policy.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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.osv import orm, fields + + +class credit_control_policy(orm.Model): + """ADD dunning fees fields""" + + _inherit = "credit.control.policy.level" + _columns = {'dunning_product_id': fields.many2one('product.product', + 'Fees Product'), + 'dunning_fixed_amount': fields.float('Fees Fixed Amount'), + 'dunning_currency_id': fields.many2one('res.currency', + 'Fees currency'), + # planned type are fixed, percent, compound + 'dunning_fees_type': fields.selection([('fixed', 'Fixed')])} + + _defaults = {'dunning_fees_type': 'fixed'} diff --git a/account_credit_control_dunning_fees/model/run.py b/account_credit_control_dunning_fees/model/run.py new file mode 100644 index 000000000..a01b43f61 --- /dev/null +++ b/account_credit_control_dunning_fees/model/run.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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.osv import orm, fields + + +class credit_control_run(orm.Model): + """Add computation of fees""" + + _inherit = "credit.control.run" + + def _generate_credit_lines(self, cr, uid, run_id, context=None): + + credit_line_ids = super(credit_control_run, self)._generate_credit_lines( + cr, + uid, + run_id, + context=context + ) + fees_model = self.pool['credit.control.dunning.fees.computer'] + fees_model._compute_fees(cr, uid, credit_line_ids, context=context) + return credit_line_ids diff --git a/account_credit_control_dunning_fees/tests/__init__.py b/account_credit_control_dunning_fees/tests/__init__.py new file mode 100644 index 000000000..db75f16d2 --- /dev/null +++ b/account_credit_control_dunning_fees/tests/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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_fees_generation + +checks = [test_fees_generation] diff --git a/account_credit_control_dunning_fees/tests/test_fees_generation.py b/account_credit_control_dunning_fees/tests/test_fees_generation.py new file mode 100644 index 000000000..bb5ff3609 --- /dev/null +++ b/account_credit_control_dunning_fees/tests/test_fees_generation.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Nicolas Bessi +# 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 mock import MagicMock +import openerp.tests.common as test_common + + +class FixedFeesTester(test_common.TransactionCase): + + def setUp(self): + """Initialaize level mock to test fees computations""" + super(FixedFeesTester, self).setUp() + self.currency_model = self.registry('res.currency') + self.euro = self.currency_model.search(self.cr, self.uid, + [('name', '=', 'EUR')]) + self.assertTrue(self.euro) + self.euro = self.registry('res.currency').browse(self.cr, + self.uid, + self.euro[0]) + + self.usd = self.currency_model.search(self.cr, self.uid, + [('name', '=', 'USD')]) + self.assertTrue(self.usd) + self.usd = self.registry('res.currency').browse(self.cr, + self.uid, + self.usd[0]) + + self.euro_level = MagicMock(name='Euro policy level') + self.euro_level.dunning_fixed_amount = 5.0 + self.euro_level.dunning_currency_id = self.euro + self.euro_level.dunning_type = 'fixed' + + self.usd_level = MagicMock(name='Euro policy level') + self.usd_level.dunning_fixed_amount = 5.0 + self.usd_level.dunning_currency_id = self.usd + self.usd_level.dunning_type = 'fixed' + self.dunning_model = self.registry('credit.control.dunning.fees.computer') + + def test_type_getter(self): + """Test that correct compute function is returned for "fixed" type""" + c_fun = self.dunning_model._get_compute_fun('fixed') + self.assertEqual(c_fun, self.dunning_model.compute_fixed_fees) + + def test_unknow_type(self): + """Test that non implemented error is raised if invalide fees type""" + with self.assertRaises(NotImplementedError): + self.dunning_model._get_compute_fun('bang') + + def test_computation_same_currency(self): + """Test that fees are correctly computed with same currency""" + credit_line = MagicMock(name='Euro credit line') + credit_line.policy_level_id = self.euro_level + credit_line.currency_id = self.euro + fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid, + credit_line, + {}) + self.assertEqual(fees, self.euro_level.dunning_fixed_amount) + + def test_computation_different_currency(self): + """Test that fees are correctly computed with different currency""" + credit_line = MagicMock(name='USD credit line') + credit_line.policy_level_id = self.euro_level + credit_line.currency_id = self.usd + fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid, + credit_line, + {}) + self.assertNotEqual(fees, self.euro_level.dunning_fixed_amount) + + def test_no_fees(self): + """Test that fees are not generated if no amount defined on level""" + credit_line = MagicMock(name='USD credit line') + credit_line.policy_level_id = self.euro_level + self.euro_level.dunning_fixed_amount = 0.0 + credit_line.currency_id = self.usd + fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid, + credit_line, + {}) + self.assertEqual(fees, 0.0) diff --git a/account_credit_control_dunning_fees/view/line_view.xml b/account_credit_control_dunning_fees/view/line_view.xml new file mode 100644 index 000000000..9d4688bd5 --- /dev/null +++ b/account_credit_control_dunning_fees/view/line_view.xml @@ -0,0 +1,30 @@ + + + + + + add fees on credit control line + credit.control.line + + + + + + + + + + add fees on credit control line form + credit.control.line + + + + + + + + + + diff --git a/account_credit_control_dunning_fees/view/policy_view.xml b/account_credit_control_dunning_fees/view/policy_view.xml new file mode 100644 index 000000000..02e9ac618 --- /dev/null +++ b/account_credit_control_dunning_fees/view/policy_view.xml @@ -0,0 +1,26 @@ + + + + + add dunning fees on policy + credit.control.policy + + + + + + + + + + + + + + + + + +