diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py
index ef5369f00..0f8871bf0 100644
--- a/account_credit_control/__openerp__.py
+++ b/account_credit_control/__openerp__.py
@@ -19,7 +19,7 @@
#
##############################################################################
{'name': 'Account Credit Control',
- 'version': '0.1.1',
+ 'version': '0.2.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
@@ -69,6 +69,7 @@ On each generated line, you have many choices:
"wizard/credit_control_emailer_view.xml",
"wizard/credit_control_marker_view.xml",
"wizard/credit_control_printer_view.xml",
+ "wizard/credit_control_policy_changer_view.xml",
"security/ir.model.access.csv"],
'demo_xml': ["credit_control_demo.xml"],
'tests': [],
diff --git a/account_credit_control/account.py b/account_credit_control/account.py
index 9e8011b0b..7a2ef4071 100644
--- a/account_credit_control/account.py
+++ b/account_credit_control/account.py
@@ -27,11 +27,11 @@ class AccountAccount(orm.Model):
_inherit = "account.account"
_columns = {
- 'credit_control_line_ids':
- fields.one2many('credit.control.line',
- 'account_id',
- string='Credit Lines',
- readonly=True),
+ 'credit_control_line_ids': fields.one2many(
+ 'credit.control.line',
+ 'account_id',
+ string='Credit Lines',
+ readonly=True),
}
def copy_data(self, cr, uid, id, default=None, context=None):
@@ -42,34 +42,3 @@ class AccountAccount(orm.Model):
default['credit_control_line_ids'] = False
return super(AccountAccount, self).copy_data(
cr, uid, id, default=default, context=context)
-
-
-class AccountInvoice(orm.Model):
- """Add a link to a credit control policy on account.account"""
-
- _inherit = "account.invoice"
- _columns = {
- 'credit_policy_id':
- fields.many2one('credit.control.policy',
- 'Credit Control Policy',
- help=("The Credit Control Policy used for this "
- "invoice. If nothing is defined, it will "
- "use the account setting or the partner "
- "setting.")
- ),
- 'credit_control_line_ids':
- fields.one2many('credit.control.line',
- 'invoice_id',
- string='Credit Lines',
- readonly=True),
- }
-
- def copy_data(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- else:
- default = default.copy()
- default = default.copy()
- default['credit_control_line_ids'] = False
- return super(AccountInvoice, self).copy_data(
- cr, uid, id, default=default, context=context)
diff --git a/account_credit_control/company.py b/account_credit_control/company.py
index eb8187a4c..893b33bb5 100644
--- a/account_credit_control/company.py
+++ b/account_credit_control/company.py
@@ -31,7 +31,7 @@ class ResCompany(orm.Model):
'credit_policy_id': fields.many2one('credit.control.policy',
'Credit Control Policy',
help=("The Credit Control Policy used on partners"
- " by default. This setting can be overriden"
+ " by default. This setting can be overridden"
" on partners or invoices.")),
}
diff --git a/account_credit_control/data.xml b/account_credit_control/data.xml
index aa46023da..09fc3cced 100644
--- a/account_credit_control/data.xml
+++ b/account_credit_control/data.xml
@@ -25,6 +25,21 @@
+
+
+ No follow
+
+ net_days
+
+
+
+ email
+ Manual no follow
+
+ Manual no follow
+
+
diff --git a/account_credit_control/i18n/de.po b/account_credit_control/i18n/de.po
index 761cfb60a..6f3eaaf27 100644
--- a/account_credit_control/i18n/de.po
+++ b/account_credit_control/i18n/de.po
@@ -984,10 +984,10 @@ msgstr "60 days last reminder"
#: help:res.company,credit_policy_id:0
msgid ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
msgstr ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
#. module: account_credit_control
#: field:credit.control.line,policy_level_id:0
diff --git a/account_credit_control/i18n/en.po b/account_credit_control/i18n/en.po
index a485dd7c4..4ab005650 100644
--- a/account_credit_control/i18n/en.po
+++ b/account_credit_control/i18n/en.po
@@ -984,10 +984,10 @@ msgstr "60 days last reminder"
#: help:res.company,credit_policy_id:0
msgid ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
msgstr ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
#. module: account_credit_control
#: field:credit.control.line,policy_level_id:0
@@ -1042,4 +1042,4 @@ msgstr "Currency"
#. module: account_credit_control
#: report:addons/account_credit_control/report/credit_control_summary.html.mako:211
msgid "If you have any question, do not hesitate to contact us."
-msgstr "If you have any question, do not hesitate to contact us."
\ No newline at end of file
+msgstr "If you have any question, do not hesitate to contact us."
diff --git a/account_credit_control/i18n/es.po b/account_credit_control/i18n/es.po
index 8c6ef96d9..76d47c904 100644
--- a/account_credit_control/i18n/es.po
+++ b/account_credit_control/i18n/es.po
@@ -982,10 +982,10 @@ msgstr "60 days last reminder"
#: help:res.company,credit_policy_id:0
msgid ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
msgstr ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
#. module: account_credit_control
#: field:credit.control.line,policy_level_id:0
@@ -1040,4 +1040,4 @@ msgstr "Currency"
#. module: account_credit_control
#: report:addons/account_credit_control/report/credit_control_summary.html.mako:211
msgid "If you have any question, do not hesitate to contact us."
-msgstr "If you have any question, do not hesitate to contact us."
\ No newline at end of file
+msgstr "If you have any question, do not hesitate to contact us."
diff --git a/account_credit_control/i18n/fr.po b/account_credit_control/i18n/fr.po
index 85867283b..6f0e83980 100644
--- a/account_credit_control/i18n/fr.po
+++ b/account_credit_control/i18n/fr.po
@@ -965,7 +965,7 @@ msgstr "Dernier rappel à 60 jours"
#: help:res.company,credit_policy_id:0
msgid ""
"The Credit Control Policy used on partners by default. This setting can be "
-"overriden on partners or invoices."
+"overridden on partners or invoices."
msgstr ""
"Politique de relance par défaut du client. (Ce paramétrage peut être "
"défini plus spécifiquement au niveau de la facture)."
diff --git a/account_credit_control/invoice.py b/account_credit_control/invoice.py
index c296854ea..078df90f4 100644
--- a/account_credit_control/invoice.py
+++ b/account_credit_control/invoice.py
@@ -18,7 +18,7 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm
+from openerp.osv import orm, fields
from openerp.tools.translate import _
@@ -26,7 +26,36 @@ class AccountInvoice(orm.Model):
"""Check on cancelling of an invoice"""
_inherit = 'account.invoice'
+ _columns = {
+ 'credit_policy_id':
+ fields.many2one('credit.control.policy',
+ 'Credit Control Policy',
+ help=("The Credit Control Policy used for this "
+ "invoice. If nothing is defined, it will "
+ "use the account setting or the partner "
+ "setting."),
+ readonly=True,
+ ),
+ 'credit_control_line_ids':
+ fields.one2many('credit.control.line',
+ 'invoice_id',
+ string='Credit Lines',
+ readonly=True),
+ }
+
+ def copy_data(self, cr, uid, id, default=None, context=None):
+ """Ensure that credit lines and policy are not copied"""
+ if default is None:
+ default = {}
+ else:
+ default = default.copy()
+ default['credit_control_line_ids'] = False
+ default['credit_policy_id'] = False
+ return super(AccountInvoice, self).copy_data(
+ cr, uid, id, default=default, context=context)
+
def action_cancel(self, cr, uid, ids, context=None):
+ """Prevent to cancel invoice related to credit line"""
# We will search if this invoice is linked with credit
cc_line_obj = self.pool.get('credit.control.line')
for invoice_id in ids:
diff --git a/account_credit_control/line.py b/account_credit_control/line.py
index d450e0697..ffb940c98 100644
--- a/account_credit_control/line.py
+++ b/account_credit_control/line.py
@@ -38,9 +38,11 @@ class CreditControlLine(orm.Model):
_name = "credit.control.line"
_description = "A credit control line"
_rec_name = "id"
-
+ _order = "date DESC"
_columns = {
- 'date': fields.date('Controlling date', required=True),
+ 'date': fields.date('Controlling date',
+ required=True,
+ select=True),
# maturity date of related move line we do not use a related field in order to
# allow manual changes
'date_due': fields.date('Due date',
@@ -116,6 +118,7 @@ class CreditControlLine(orm.Model):
string='Level',
store=True,
readonly=True),
+ 'manually_overridden': fields.boolean('Manually overridden')
}
@@ -141,8 +144,27 @@ class CreditControlLine(orm.Model):
return data
def create_or_update_from_mv_lines(self, cr, uid, ids, lines,
- level_id, controlling_date, context=None):
- """Create or update line based on levels"""
+ level_id, controlling_date,
+ check_tolerance=True, context=None):
+ """Create or update line based on levels
+
+ if check_tolerance is true credit line will not be
+ created if open amount is too small.
+ eg. we do not want to send a letter for 10 cents
+ of open amount.
+
+ :param lines: move.line id list
+ :param level_id: credit.control.policy.level id
+ :param controlling_date: date string of the credit controlling date.
+ Generally it should be the same
+ as create date
+ :param check_tolerance: boolean if True credit line
+ will not be generated if open amount
+ is smaller than company defined
+ tolerance
+
+ :returns: list of created credit line ids
+ """
currency_obj = self.pool.get('res.currency')
level_obj = self.pool.get('credit.control.policy.level')
ml_obj = self.pool.get('account.move.line')
@@ -164,26 +186,31 @@ class CreditControlLine(orm.Model):
for line in ml_obj.browse(cr, uid, lines, context):
open_amount = line.amount_residual_currency
+ cur_tolerance = tolerance.get(line.currency_id.id, tolerance_base)
+ if check_tolerance and open_amount < cur_tolerance:
+ continue
+ vals = self._prepare_from_move_line(cr, uid,
+ line,
+ level,
+ controlling_date,
+ open_amount,
+ context=context)
+ line_id = self.create(cr, uid, vals, context=context)
+ line_ids.append(line_id)
- if open_amount > tolerance.get(line.currency_id.id, tolerance_base):
- vals = self._prepare_from_move_line(
- cr, uid, line, level, controlling_date, open_amount, context=context)
- line_id = self.create(cr, uid, vals, context=context)
- line_ids.append(line_id)
-
- # when we have lines generated earlier in draft,
- # on the same level, it means that we have left
- # them, so they are to be considered as ignored
- previous_draft_ids = self.search(
- cr, uid,
- [('move_line_id', '=', line.id),
- ('level', '=', level.id),
- ('state', '=', 'draft'),
- ('id', '!=', line_id)],
- context=context)
- if previous_draft_ids:
- self.write(cr, uid, previous_draft_ids,
- {'state': 'ignored'}, context=context)
+ # when we have lines generated earlier in draft,
+ # on the same level, it means that we have left
+ # them, so they are to be considered as ignored
+ previous_draft_ids = self.search(
+ cr, uid,
+ [('move_line_id', '=', line.id),
+ ('policy_level_id', '=', level.id),
+ ('state', '=', 'draft'),
+ ('id', '!=', line_id)],
+ context=context)
+ if previous_draft_ids:
+ self.write(cr, uid, previous_draft_ids,
+ {'state': 'ignored'}, context=context)
return line_ids
diff --git a/account_credit_control/line_view.xml b/account_credit_control/line_view.xml
index f48708d2f..370a9a136 100644
--- a/account_credit_control/line_view.xml
+++ b/account_credit_control/line_view.xml
@@ -10,6 +10,7 @@
+
@@ -32,7 +33,7 @@
search
-
+
@@ -48,6 +49,9 @@
+
@@ -89,6 +93,8 @@
+
@@ -103,6 +109,7 @@
+
diff --git a/account_credit_control/partner.py b/account_credit_control/partner.py
index 0e4605081..078e2da51 100644
--- a/account_credit_control/partner.py
+++ b/account_credit_control/partner.py
@@ -19,6 +19,7 @@
#
##############################################################################
from openerp.osv import orm, fields
+from openerp.tools.translate import _
class ResPartner(orm.Model):
@@ -28,21 +29,47 @@ class ResPartner(orm.Model):
_inherit = "res.partner"
_columns = {
- 'credit_policy_id':
- fields.many2one('credit.control.policy',
- 'Credit Control Policy',
- help=("The Credit Control Policy used for this "
- "partner. This setting can be forced on the "
- "invoice. If nothing is defined, it will use "
- "the company setting.")),
- 'credit_control_line_ids':
- fields.one2many('credit.control.line',
- 'invoice_id',
- string='Credit Control Lines',
- readonly=True)
+ 'credit_policy_id': fields.many2one(
+ 'credit.control.policy',
+ 'Credit Control Policy',
+ domain="[('account_ids', 'in', property_account_receivable)]",
+ help=("The Credit Control Policy used for this "
+ "partner. This setting can be forced on the "
+ "invoice. If nothing is defined, it will use "
+ "the company setting.")
+ ),
+ 'credit_control_line_ids': fields.one2many(
+ 'credit.control.line',
+ 'invoice_id',
+ string='Credit Control Lines',
+ readonly=True
+ )
}
+ def _check_credit_policy(self, cr, uid, part_ids, context=None):
+ """Ensure that policy on partner are limited to the account policy"""
+ if isinstance(part_ids, (int, long)):
+ part_ids = [part_ids]
+ policy_obj = self.pool['credit.control.policy']
+ for partner in self.browse(cr, uid, part_ids, context):
+ if not partner.property_account_receivable or \
+ not partner.credit_policy_id:
+ return True
+ account = partner.property_account_receivable
+ policy_obj.check_policy_against_account(
+ cr, uid,
+ account.id,
+ partner.credit_policy_id.id,
+ context=context
+ )
+ return True
+
+ _constraints = [(_check_credit_policy,
+ 'The policy must be related to the receivable account',
+ ['credit_policy_id'])]
+
def copy_data(self, cr, uid, id, default=None, context=None):
+ """Remove credit lines when copying partner"""
if default is None:
default = {}
else:
diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py
index 1940ea672..34b8bc69c 100644
--- a/account_credit_control/policy.py
+++ b/account_credit_control/policy.py
@@ -18,11 +18,11 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv.orm import Model, fields
+from openerp.osv import orm, fields
from openerp.tools.translate import _
-class CreditControlPolicy(Model):
+class CreditControlPolicy(orm.Model):
"""Define a policy of reminder"""
_name = "credit.control.policy"
@@ -42,7 +42,7 @@ class CreditControlPolicy(Model):
'account_ids': fields.many2many('account.account',
string='Accounts',
required=True,
- domain="[('reconcile', '=', True)]",
+ domain="[('type', '=', 'receivable')]",
help="This policy will be active only"
" for the selected accounts"),
'active': fields.boolean('Active'),
@@ -103,7 +103,10 @@ class CreditControlPolicy(Model):
my_obj = self.pool.get(model)
move_l_obj = self.pool.get('account.move.line')
- default_domain = self._move_lines_domain(cr, uid, policy, controlling_date, context=context)
+ default_domain = self._move_lines_domain(cr, uid,
+ policy,
+ controlling_date,
+ context=context)
to_add_ids = set()
to_remove_ids = set()
@@ -198,15 +201,34 @@ class CreditControlPolicy(Model):
if isinstance(policy_id, list):
policy_id = policy_id[0]
cr.execute("SELECT move_line_id FROM credit_control_line"
- " WHERE policy_id != %s and move_line_id in %s",
+ " WHERE policy_id != %s and move_line_id in %s"
+ " AND manually_overridden IS false",
(policy_id, tuple(lines)))
res = cr.fetchall()
if res:
different_lines.update([x[0] for x in res])
return different_lines
+ def check_policy_against_account(self, cr, uid, account_id, policy_id,
+ context=None):
+ """Ensure that the policy corresponds to account relation"""
+ policy = self.browse(cr, uid, policy_id, context=context)
+ account = self.pool['account.account'].browse(cr, uid, account_id,
+ context=context)
+ policies_id = self.search(cr, uid, [],
+ context=context)
+ policies = self.browse(cr, uid, policies_id, context=context)
+ allowed = [x for x in policies
+ if account in x.account_ids or x.do_nothing]
+ if policy not in allowed:
+ allowed_names = u"\n".join(x.name for x in allowed)
+ raise orm.except_orm(
+ _('You can only use a policy set on account %s') % account.name,
+ _("Please choose one of the following policies:\n %s") % allowed_names)
+ return True
-class CreditControlPolicyLevel(Model):
+
+class CreditControlPolicyLevel(orm.Model):
"""Define a policy level. A level allows to determine if
a move line is due and the level of overdue of the line"""
@@ -319,8 +341,11 @@ class CreditControlPolicyLevel(Model):
" FROM credit_control_line\n"
" WHERE move_line_id = mv_line.id\n"
# lines from a previous level with a draft or ignored state
+ # or manually overridden
# have to be generated again for the previous level
- " AND state not in ('draft', 'ignored'))")
+ " AND NOT manually_overridden\n"
+ " AND state NOT IN ('draft', 'ignored'))"
+ " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n")
sql += " AND"
sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
controlling_date, context)
@@ -346,11 +371,15 @@ class CreditControlPolicyLevel(Model):
" WHERE cr_line.id = (SELECT credit_control_line.id FROM credit_control_line\n"
" WHERE credit_control_line.move_line_id = mv_line.id\n"
" AND state != 'ignored'"
+ " AND NOT manually_overridden"
" ORDER BY credit_control_line.level desc limit 1)\n"
" AND cr_line.level = %(previous_level)s\n"
+ " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n"
# lines from a previous level with a draft or ignored state
+ # or manually overridden
# have to be generated again for the previous level
- " AND cr_line.state not in ('draft', 'ignored')\n"
+ " AND NOT manually_overridden\n"
+ " AND cr_line.state NOT IN ('draft', 'ignored')\n"
" AND mv_line.id in %(line_ids)s\n")
sql += " AND "
sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
diff --git a/account_credit_control/run.py b/account_credit_control/run.py
index 1fc739b9f..91b4f63ba 100644
--- a/account_credit_control/run.py
+++ b/account_credit_control/run.py
@@ -81,14 +81,24 @@ class CreditControlRun(orm.Model):
def _check_run_date(self, cr, uid, ids, controlling_date, context=None):
"""Ensure that there is no credit line in the future using controlling_date"""
- line_obj = self.pool.get('credit.control.line')
+ run_obj = self.pool['credit.control.run']
+ runs = run_obj.search(cr, uid, [('date', '>', controlling_date)],
+ order='date DESC', limit=1, context=context)
+ if runs:
+ run = run_obj.browse(cr, uid, runs[0], context=context)
+ raise orm.except_orm(_('Error'),
+ _('A run has already been executed more '
+ 'recently than %s') % (run.date))
+
+ line_obj = self.pool['credit.control.line']
lines = line_obj.search(cr, uid, [('date', '>', controlling_date)],
order='date DESC', limit=1, context=context)
if lines:
line = line_obj.browse(cr, uid, lines[0], context=context)
raise orm.except_orm(_('Error'),
- _('A run has already been executed more '
- 'recently than %s') % (line.date))
+ _('A credit control line more '
+ 'recent than %s exists at %s') %
+ (controlling_date, line.date))
return True
def _generate_credit_lines(self, cr, uid, run_id, context=None):
@@ -110,10 +120,10 @@ class CreditControlRun(orm.Model):
_('Please select a policy'))
report = ''
+ generated_ids = []
for policy in policies:
if policy.do_nothing:
continue
-
lines = policy._get_move_lines_to_process(run.date, context=context)
manual_lines = policy._lines_different_policy(lines, context=context)
lines.difference_update(manual_lines)
@@ -125,7 +135,7 @@ class CreditControlRun(orm.Model):
level_lines = level.get_level_lines(run.date, lines, context=context)
policy_generated_ids += cr_line_obj.create_or_update_from_mv_lines(
cr, uid, [], list(level_lines), level.id, run.date, context=context)
-
+ generated_ids.extend(policy_generated_ids)
if policy_generated_ids:
report += _("Policy \"%s\" has generated %d Credit Control Lines.\n") % \
(policy.name, len(policy_generated_ids))
@@ -138,6 +148,7 @@ class CreditControlRun(orm.Model):
'report': report,
'manual_ids': [(6, 0, manually_managed_lines)]}
run.write(vals, context=context)
+ return generated_ids
def generate_credit_lines(self, cr, uid, run_id, context=None):
"""Generate credit control lines
@@ -147,7 +158,7 @@ class CreditControlRun(orm.Model):
"""
try:
cr.execute('SELECT id FROM credit_control_run'
- ' LIMIT 1 FOR UPDATE NOWAIT')
+ ' LIMIT 1 FOR UPDATE NOWAIT')
except Exception as exc:
# in case of exception openerp will do a rollback for us and free the lock
raise orm.except_orm(_('Error'),
diff --git a/account_credit_control/run_view.xml b/account_credit_control/run_view.xml
index 2ca3e6827..59c99b9a5 100644
--- a/account_credit_control/run_view.xml
+++ b/account_credit_control/run_view.xml
@@ -19,7 +19,8 @@
form
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..d219bb7ce
--- /dev/null
+++ b/account_credit_control_dunning_fees/__openerp__.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 .
+#
+##############################################################################
+{'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 fixed fees amount, 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
+
+Credit control Summary report includes a new fees column.
+-------
+Support of fees price list
+
+""",
+ 'website': 'http://www.camptocamp.com',
+ 'data': ['view/policy_view.xml',
+ 'view/line_view.xml',
+ 'report/report.xml'],
+ 'demo': [],
+ 'test': [],
+ 'installable': True,
+ 'auto_install': False,
+ 'license': 'AGPL-3',
+ 'application': False,
+}
diff --git a/account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot b/account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot
new file mode 100644
index 000000000..6282315c6
--- /dev/null
+++ b/account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot
@@ -0,0 +1,81 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * account_credit_control_dunning_fees
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-05-07 11:44+0000\n"
+"PO-Revision-Date: 2014-05-07 11:44+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_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_line
+#, python-format
+msgid "A credit control line"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_policy_level
+#, python-format
+msgid "A credit control policy level"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_run
+#, python-format
+msgid "Credit control line generator"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.line,dunning_fees_amount:0
+#: view:credit.control.policy:0
+msgid "Fees"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_fixed_amount:0
+msgid "Fees Fixed Amount"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_product_id:0
+msgid "Fees Product"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_currency_id:0
+msgid "Fees currency"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: selection:credit.control.policy.level,dunning_fees_type:0
+msgid "Fixed"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: view:credit.control.policy:0
+msgid "Mail and reporting"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_dunning_fees_computer
+#, python-format
+msgid "credit.control.dunning.fees.computer"
+msgstr ""
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_fees_type:0
+msgid "unknown"
+msgstr ""
+
diff --git a/account_credit_control_dunning_fees/i18n/fr.po b/account_credit_control_dunning_fees/i18n/fr.po
new file mode 100644
index 000000000..df2e9e0ba
--- /dev/null
+++ b/account_credit_control_dunning_fees/i18n/fr.po
@@ -0,0 +1,80 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * account_credit_control_dunning_fees
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-04-16 07:11+0000\n"
+"PO-Revision-Date: 2014-04-16 07:11+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_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_line
+#, python-format
+msgid "A credit control line"
+msgstr "Ligne de relance"
+
+#. module: account_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_policy_level
+#, python-format
+msgid "A credit control policy level"
+msgstr "Une politique de relance"
+
+#. module: account_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_run
+#, python-format
+msgid "Credit control line generator"
+msgstr "Générateur de relance"
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.line,dunning_fees_amount:0
+#: view:credit.control.policy:0
+msgid "Fees"
+msgstr "Frais de relance"
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_fixed_amount:0
+msgid "Fees Fixed Amount"
+msgstr "Montant des frais"
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_product_id:0
+msgid "Fees Product"
+msgstr "Article lié"
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_currency_id:0
+msgid "Fees currency"
+msgstr "Devises"
+
+#. module: account_credit_control_dunning_fees
+#: selection:credit.control.policy.level,dunning_fees_type:0
+msgid "Fixed"
+msgstr "Fixe"
+
+#. module: account_credit_control_dunning_fees
+#: view:credit.control.policy:0
+msgid "Mail and reporting"
+msgstr "Lettres et e-mails"
+
+#. module: account_credit_control_dunning_fees
+#: code:_description:0
+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_dunning_fees_computer
+#, python-format
+msgid "credit.control.dunning.fees.computer"
+msgstr "credit.control.dunning.fees.computer"
+
+#. module: account_credit_control_dunning_fees
+#: field:credit.control.policy.level,dunning_fees_type:0
+msgid "unknown"
+msgstr "inconnu"
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..b0a75cdd2
--- /dev/null
+++ b/account_credit_control_dunning_fees/model/dunning.py
@@ -0,0 +1,120 @@
+# -*- 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
+
+
+class FeesComputer(orm.BaseModel):
+ """Model that compute dunnig fees.
+
+ This class does not need any database storage as
+ it contains pure logic.
+
+ It inherits form ``orm.BaseModel`` to benefit of orm facility
+
+ Similar to AbstractModel but log access and actions
+ """
+
+ _name = 'credit.control.dunning.fees.computer'
+ _auto = False
+ _log_access = True
+ _register = True
+ _transient = False
+
+ def _get_compute_fun(self, level_fees_type):
+ """Retrieve function of class that should compute the fees based on type
+
+ :param level_fee_type: type exisiting in model `credit.control.policy.level`
+ for field dunning_fees_type
+
+ :returns: a function of class :class:`FeesComputer` with following signature
+ self, cr, uid, credit_line (record), context
+
+ """
+ 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):
+ """Compute fees for `credit_line_ids` parameter
+
+ Fees amount is written on credit line in field dunning_fees_amount
+
+ :param credit_line_ids: list of `credit.control.line` ids
+
+ :returns: `credit_line_ids` list of `credit.control.line` ids
+
+ """
+ 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 amount is written on credit line in field dunning_fees_amount
+
+ :param credit_line: credit line record
+
+ :returns: `credit_line` record
+ """
+ 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)
+ return credit_line
+
+ def compute_fixed_fees(self, cr, uid, credit_line, context=None):
+ """Compute fees amount for fixed fees.
+ Correspond to the fixed dunning fees type
+
+ if currency of the fees is not the same as the currency
+ of the credit line, fees amount is converted to
+ currency of credit line.
+
+ :param credit_line: credit line record
+
+ :return: fees amount float (in credit line currency)
+
+ """
+ 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..5e4a3f782
--- /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 dunning_fees_amount_fees field"""
+
+ _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..2d6585e58
--- /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):
+ """Override method to add fees computation"""
+ 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/report/credit_control_summary.html.mako b/account_credit_control_dunning_fees/report/credit_control_summary.html.mako
new file mode 100644
index 000000000..985a93403
--- /dev/null
+++ b/account_credit_control_dunning_fees/report/credit_control_summary.html.mako
@@ -0,0 +1,246 @@
+## -*- coding: utf-8 -*-
+
+
+
+
+
+
+ %for comm in objects :
+ <% setLang(comm.get_contact_address().lang) %>
+
+
+ <%
+ add = comm.get_contact_address()
+ %>
+ %if comm.partner_id.id == add.id:
+ | ${comm.partner_id.title and comm.partner_id.title.name or ''} ${comm.partner_id.name } |
+ <% address_lines = comm.partner_id.contact_address.split("\n") %>
+
+ %else:
+ | ${comm.partner_id.name or ''} |
+ | ${add.title and add.title.name or ''} ${add.name} |
+ <% address_lines = add.contact_address.split("\n")[1:] %>
+ %endif
+ %for part in address_lines:
+ %if part:
+ | ${part} |
+ %endif
+ %endfor
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${_('Reminder')}: ${comm.current_policy_level.name or '' }
+
+
+
${_('Dear')},
+
${comm.current_policy_level.custom_text.replace('\n', '
')}
+
+
+
+
${_('Summary')}
+
+
+ | ${_('Invoice number')} |
+ ${_('Invoice date')} |
+ ${_('Date due')} |
+ ${_('Invoiced amount')} |
+ ${_('Open amount')} |
+ ${_('Fees')} |
+ ${_('Currency')} |
+
+
+%for line in comm.credit_control_line_ids:
+
+ %if line.invoice_id:
+ ${line.invoice_id.number}
+ %if line.invoice_id.name:
+
+ ${line.invoice_id.name}
+ %endif
+ |
+ %else:
+ ${line.move_line_id.name} |
+ %endif
+ ${line.date_entry} |
+ ${line.date_due} |
+ ${line.amount_due} |
+ ${line.balance_due} |
+ ${line.dunning_fees_amount} |
+ ${line.currency_id.name or comm.company_id.currency_id.name} |
+
+%endfor
+
+
+
+<%doc>
+
+
${_('If you have any question, do not hesitate to contact us.')}
+
+
${comm.user_id.name} ${comm.user_id.email and '<%s>'%(comm.user_id.email) or ''}
+ ${comm.company_id.name}
+ % if comm.company_id.street:
+ ${comm.company_id.street or ''}
+
+ % endif
+
+ % if comm.company_id.street2:
+ ${comm.company_id.street2}
+ % endif
+ % if comm.company_id.city or comm.company_id.zip:
+ ${comm.company_id.zip or ''} ${comm.company_id.city or ''}
+ % endif
+ % if comm.company_id.country_id:
+ ${comm.company_id.state_id and ('%s, ' % comm.company_id.state_id.name) or ''} ${comm.company_id.country_id.name or ''}
+ % endif
+ % if comm.company_id.phone:
+ Phone: ${comm.company_id.phone}
+ % endif
+ % if comm.company_id.website:
+ ${comm.company_id.website or ''}
+ % endif
+%doc>
+
+
+ %endfor
+
+
+
diff --git a/account_credit_control_dunning_fees/report/report.xml b/account_credit_control_dunning_fees/report/report.xml
new file mode 100644
index 000000000..89a9874a6
--- /dev/null
+++ b/account_credit_control_dunning_fees/report/report.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
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..0d7037a9a
--- /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):
+ """Initialize credit control 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='USD 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/async_move_line_importer/__openerp__.py b/async_move_line_importer/__openerp__.py
index 526f337a8..097191d9a 100644
--- a/async_move_line_importer/__openerp__.py
+++ b/async_move_line_importer/__openerp__.py
@@ -19,7 +19,7 @@
#
##############################################################################
{'name': 'Asynchronous move/move line CSV importer',
- 'version': '0.1.1',
+ 'version': '0.1.2',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Accounting',
diff --git a/async_move_line_importer/model/move_line_importer.py b/async_move_line_importer/model/move_line_importer.py
index 2ae7c725c..f515bf8fe 100644
--- a/async_move_line_importer/model/move_line_importer.py
+++ b/async_move_line_importer/model/move_line_importer.py
@@ -41,7 +41,7 @@ class move_line_importer(orm.Model):
It will parse the saved CSV file using orm.BaseModel.load
in a thread. If you set bypass_orm to True then the load function
- will use a totally overriden create function that is a lot faster
+ will use a totally overridden create function that is a lot faster
but that totally bypass the ORM
"""
@@ -303,7 +303,7 @@ class move_line_importer(orm.Model):
Will generate an success/failure report and generate some
maile threads. It uses BaseModel.load to lookup CSV.
If you set bypass_orm to True then the load function
- will use a totally overriden create function that is a lot faster
+ will use a totally overridden create function that is a lot faster
but that totally bypass the ORM
"""