diff --git a/account_credit_control/__openerp__.py b/account_credit_control/__openerp__.py
index ef5369f00..5fd55c159 100644
--- a/account_credit_control/__openerp__.py
+++ b/account_credit_control/__openerp__.py
@@ -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/line.py b/account_credit_control/line.py
index d450e0697..e2c63cf78 100644
--- a/account_credit_control/line.py
+++ b/account_credit_control/line.py
@@ -116,6 +116,7 @@ class CreditControlLine(orm.Model):
string='Level',
store=True,
readonly=True),
+ 'manually_overriden': fields.boolean('Manually overriden')
}
@@ -141,8 +142,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 +184,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),
+ ('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)
return line_ids
diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py
index 1940ea672..5e36c6bce 100644
--- a/account_credit_control/policy.py
+++ b/account_credit_control/policy.py
@@ -319,8 +319,10 @@ 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 overriden
# have to be generated again for the previous level
- " AND state not in ('draft', 'ignored'))")
+ " AND NOT manually_overriden\n"
+ " AND state NOT IN ('draft', 'ignored'))")
sql += " AND"
sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
controlling_date, context)
@@ -346,11 +348,14 @@ 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_overriden"
" ORDER BY credit_control_line.level desc limit 1)\n"
" AND cr_line.level = %(previous_level)s\n"
# lines from a previous level with a draft or ignored state
+ # or manually overriden
# have to be generated again for the previous level
- " AND cr_line.state not in ('draft', 'ignored')\n"
+ " AND NOT manually_overriden\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/wizard/__init__.py b/account_credit_control/wizard/__init__.py
index ca0a1758a..f12dea14d 100644
--- a/account_credit_control/wizard/__init__.py
+++ b/account_credit_control/wizard/__init__.py
@@ -22,3 +22,4 @@ from . import credit_control_emailer
from . import credit_control_marker
from . import credit_control_printer
from . import credit_control_communication
+from . import credit_control_policy_changer
diff --git a/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py
new file mode 100644
index 000000000..4876b9df7
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_policy_changer.py
@@ -0,0 +1,106 @@
+# -*- 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 itertools import chain
+from openerp.osv import orm, fields
+
+
+class credit_control_policy_changer(orm.TransientModel):
+ """Wizard that is run from invoices and allows to set manually a policy
+ Policy are actually apply to related move lines availabe
+ in selection widget
+
+ """
+ _name = "credit.control.policy.changer"
+ _columns = {
+ 'new_policy_id': fields.many2one('credit.control.policy',
+ 'New Policy to Apply',
+ required=True),
+ 'new_policy_level_id': fields.many2one('credit.control.policy.level',
+ 'New level to apply',
+ required=True),
+ 'move_line_ids': fields.many2many('account.move.line',
+ rel='credit_changer_ml_rel',
+ string='Move line to change'),
+ }
+
+ def _get_default_lines(self, cr, uid, context=None):
+ if context is None:
+ context = {}
+ active_ids = context.get('active_ids')
+ selected_line_ids = []
+ inv_model = self.pool['account.invoice']
+ move_line_model = self.pool['account.move.line']
+ if not active_ids:
+ return False
+ # raise ValueError('No active_ids passed in context')
+ for invoice in inv_model.browse(cr, uid, active_ids, context=context):
+ domain = [('account_id', '=', invoice.account_id.id),
+ ('move_id', '=', invoice.move_id.id),
+ ('reconcile_id', '=', False)]
+ move_ids = move_line_model.search(cr, uid, domain, context=context)
+ selected_line_ids.extend(move_ids)
+ return selected_line_ids
+
+ _defaults = {'move_line_ids': _get_default_lines}
+
+ def _mark_as_overriden(self, cr, uid, move_lines, context=None):
+ credit_model = self.pool['credit.control.line']
+ domain = [('id', 'in', [x.id for x in move_lines])]
+ credits_ids = credit_model.search(cr, uid, domain, context=context)
+ credit_model.write(cr, uid,
+ credits_ids,
+ {'manually_overriden': True},
+ context)
+ return credits_ids
+
+ def set_new_policy(self, cr, uid, wizard_id, context=None):
+ assert len(wizard_id) == 1, "Only one id expected"
+ wizard_id = wizard_id[0]
+
+ credit_line_model = self.pool['credit.control.line']
+ ir_model = self.pool['ir.model.data']
+ ui_act_model = self.pool['ir.actions.act_window']
+ wizard = self.browse(cr, uid, wizard_id, context=context)
+ controlling_date = fields.date.today()
+ self._mark_as_overriden(cr,
+ uid,
+ wizard.move_line_ids,
+ context=context)
+ # As disscused with business expert
+ # draft line should be passed to ignored
+ # if same level as the new one
+ # As it is a manual action
+ # We also ignore rounding tolerance
+ generated_ids = credit_line_model.create_or_update_from_mv_lines(
+ cr, uid, [],
+ [x.id for x in wizard.move_line_ids],
+ wizard.new_policy_level_id.id,
+ controlling_date,
+ check_tolerance=False,
+ context=None
+ )
+ view_id = ir_model.get_object_reference(cr, uid,
+ "account_credit_control",
+ "credit_control_line_action")
+ assert view_id, 'No view found'
+ action = ui_act_model.read(cr, uid, view_id[1], context=context)
+ action['domain'] = [('id', 'in', generated_ids)]
+ return action
diff --git a/account_credit_control/wizard/credit_control_policy_changer_view.xml b/account_credit_control/wizard/credit_control_policy_changer_view.xml
new file mode 100644
index 000000000..ae383d8ec
--- /dev/null
+++ b/account_credit_control/wizard/credit_control_policy_changer_view.xml
@@ -0,0 +1,62 @@
+
+
+
+
+ credit control policy form
+ credit.control.policy.changer
+
+
+
+
+
+
+
+
+ Change current credit policy
+ credit.control.policy.changer
+ account.invoice
+ form
+ form
+
+ new
+ Allows to manually change credit level
+
+
+
+
+
+
+