diff --git a/account_credit_control/account.py b/account_credit_control/account.py
index 8fe57e27b..0c00a8e23 100644
--- a/account_credit_control/account.py
+++ b/account_credit_control/account.py
@@ -18,27 +18,15 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm, fields
+from openerp import models, fields
-class AccountAccount(orm.Model):
- """Add a link to a credit control policy on account.account"""
+class AccountAccount(models.Model):
+ """ Add a link to a credit control policy on account.account """
_inherit = "account.account"
- _columns = {
- '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):
- if default is None:
- default = {}
- else:
- default = default.copy()
- default['credit_control_line_ids'] = False
- return super(AccountAccount, self).copy_data(
- cr, uid, id, default=default, context=context)
+ credit_control_line_ids = fields.One2many('credit.control.line',
+ 'account_id',
+ string='Credit Lines',
+ readonly=True)
diff --git a/account_credit_control/company.py b/account_credit_control/company.py
index 790fc05f1..cd94ef7c8 100644
--- a/account_credit_control/company.py
+++ b/account_credit_control/company.py
@@ -18,24 +18,20 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm, fields
+from openerp import models, fields
-class ResCompany(orm.Model):
- """Add credit control parameters"""
+class ResCompany(models.Model):
+ """ Add credit control parameters """
_inherit = 'res.company'
- _columns = {
- 'credit_control_tolerance': fields.float('Credit Control Tolerance'),
- # This is not a property on the partner because we cannot search
- # on fields.property (subclass fields.function).
- '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 overridden"
- " on partners or invoices.")
- ),
- }
-
- _defaults = {"credit_control_tolerance": 0.1}
+ credit_control_tolerance = fields.Float(string='Credit Control Tolerance',
+ default=0.1)
+ # This is not a property on the partner because we cannot search
+ # on fields.property (subclass fields.function).
+ credit_policy_id = fields.Many2one('credit.control.policy',
+ string='Credit Control Policy',
+ help="The Credit Control Policy used "
+ "on partners by default. "
+ "This setting can be overridden"
+ " on partners or invoices.")
diff --git a/account_credit_control/line.py b/account_credit_control/line.py
index 964878fa4..02d1dfbd4 100644
--- a/account_credit_control/line.py
+++ b/account_credit_control/line.py
@@ -20,14 +20,14 @@
##############################################################################
import logging
-from openerp.osv import orm, fields
+from openerp import models, fields, api
from openerp.tools.translate import _
logger = logging.getLogger('credit.line.control')
-class CreditControlLine(orm.Model):
- """A credit control line describes an amount due by a customer for a due date.
+class CreditControlLine(models.Model):
+ """ A credit control line describes an amount due by a customer for a due date.
A line is created once the due date of the payment is exceeded.
It is created in "draft" and some actions are available (send by email,
@@ -38,160 +38,105 @@ class CreditControlLine(orm.Model):
_description = "A credit control line"
_rec_name = "id"
_order = "date DESC"
- _columns = {
- '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',
- required=True,
- readonly=True,
- states={'draft': [('readonly', False)]}
- ),
- 'date_entry': fields.related(
- 'move_line_id', 'date',
- type='date',
- string='Entry date',
- store=True, readonly=True
- ),
+ date = fields.Date(string='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(string='Due date',
+ required=True,
+ readonly=True,
+ states={'draft': [('readonly', False)]})
- 'date_sent': fields.date(
- 'Sent date',
- readonly=True,
- states={'draft': [('readonly', False)]}
- ),
+ date_entry = fields.Date(string='Entry date',
+ related='move_line_id.date',
+ store=True,
+ readonly=True)
- 'state': fields.selection(
- [('draft', 'Draft'),
- ('ignored', 'Ignored'),
- ('to_be_sent', 'Ready To Send'),
- ('sent', 'Done'),
- ('error', 'Error'),
- ('email_error', 'Emailing Error')],
- 'State', required=True, readonly=True,
- help=("Draft lines need to be triaged.\n"
- "Ignored lines are lines for which we do "
- "not want to send something.\n"
- "Draft and ignored lines will be "
- "generated again on the next run.")
- ),
+ date_sent = fields.Date(string='Sent date',
+ readonly=True,
+ states={'draft': [('readonly', False)]})
- 'channel': fields.selection(
- [('letter', 'Letter'),
- ('email', 'Email')],
- 'Channel', required=True,
- readonly=True,
- states={'draft': [('readonly', False)]}
- ),
+ state = fields.Selection([('draft', 'Draft'),
+ ('ignored', 'Ignored'),
+ ('to_be_sent', 'Ready To Send'),
+ ('sent', 'Done'),
+ ('error', 'Error'),
+ ('email_error', 'Emailing Error')],
+ 'State',
+ required=True,
+ readonly=True,
+ default='draft',
+ help="Draft lines need to be triaged.\n"
+ "Ignored lines are lines for which we do "
+ "not want to send something.\n"
+ "Draft and ignored lines will be "
+ "generated again on the next run.")
- 'invoice_id': fields.many2one(
- 'account.invoice',
- 'Invoice',
- readonly=True
- ),
+ channel = fields.Selection([('letter', 'Letter'),
+ ('email', 'Email')],
+ string='Channel',
+ required=True,
+ readonly=True,
+ states={'draft': [('readonly', False)]})
- 'partner_id': fields.many2one(
- 'res.partner',
- "Partner",
- required=True
- ),
+ invoice_id = fields.Many2one('account.invoice',
+ string='Invoice',
+ readonly=True)
- 'amount_due': fields.float(
- 'Due Amount Tax incl.',
- required=True,
- readonly=True
- ),
+ partner_id = fields.Many2one('res.partner',
+ string='Partner',
+ required=True)
- 'balance_due': fields.float(
- 'Due balance', required=True,
- readonly=True
- ),
+ amount_due = fields.Float(string='Due Amount Tax incl.',
+ required=True, readonly=True)
- 'mail_message_id': fields.many2one(
- 'mail.mail',
- 'Sent Email',
- readonly=True
- ),
+ balance_due = fields.Float(string='Due balance', required=True,
+ readonly=True)
- 'move_line_id': fields.many2one(
- 'account.move.line',
- 'Move line',
- required=True,
- readonly=True
- ),
+ mail_message_id = fields.Many2one('mail.mail', string='Sent Email',
+ readonly=True)
- 'account_id': fields.related(
- 'move_line_id',
- 'account_id',
- type='many2one',
- relation='account.account',
- string='Account',
- store=True,
- readonly=True
- ),
+ move_line_id = fields.Many2one('account.move.line',
+ string='Move line',
+ required=True,
+ readonly=True)
- 'currency_id': fields.related(
- 'move_line_id',
- 'currency_id',
- type='many2one',
- relation='res.currency',
- string='Currency',
- store=True,
- readonly=True
- ),
+ account_id = fields.Many2one(related='move_line_id.account_id',
+ store=True,
+ readonly=True)
- 'company_id': fields.related(
- 'move_line_id', 'company_id',
- type='many2one',
- relation='res.company',
- string='Company',
- store=True, readonly=True
- ),
+ currency_id = fields.Many2one(related='move_line_id.currency_id',
+ store=True,
+ readonly=True)
- # we can allow a manual change of policy in draft state
- 'policy_level_id': fields.many2one(
- 'credit.control.policy.level',
- 'Overdue Level',
- required=True,
- readonly=True,
- states={'draft': [('readonly', False)]}
- ),
+ company_id = fields.Many2one(related='move_line_id.company_id',
+ store=True,
+ readonly=True)
- 'policy_id': fields.related(
- 'policy_level_id',
- 'policy_id',
- type='many2one',
- relation='credit.control.policy',
- string='Policy',
- store=True,
- readonly=True
- ),
+ # we can allow a manual change of policy in draft state
+ policy_level_id = fields.Many2one('credit.control.policy.level',
+ string='Overdue Level',
+ required=True,
+ readonly=True,
+ states={'draft': [('readonly', False)]})
- 'level': fields.related(
- 'policy_level_id',
- 'level',
- type='integer',
- relation='credit.control.policy',
- string='Level',
- store=True,
- readonly=True
- ),
+ policy_id = fields.Many2one(related='policy_level_id.policy_id',
+ store=True,
+ readonly=True)
- 'manually_overridden': fields.boolean('Manually overridden')
- }
+ level = fields.Integer(related='policy_level_id.level',
+ store=True,
+ readonly=True)
- _defaults = {'state': 'draft'}
+ manually_overridden = fields.Boolean(string='Manually overridden')
- def _prepare_from_move_line(self, cr, uid, move_line,
- level, controlling_date, open_amount,
- context=None):
- """Create credit control line"""
+ @api.model
+ def _prepare_from_move_line(self, move_line, level, controlling_date,
+ open_amount):
+ """ Create credit control line """
data = {}
data['date'] = controlling_date
data['date_due'] = move_line.date_maturity
@@ -208,17 +153,17 @@ class CreditControlLine(orm.Model):
data['move_line_id'] = move_line.id
return data
- def create_or_update_from_mv_lines(self, cr, uid, ids, lines,
- level_id, controlling_date,
- check_tolerance=True, context=None):
- """Create or update line based on levels
+ @api.model
+ def create_or_update_from_mv_lines(self, lines, level_id, controlling_date,
+ check_tolerance=True):
+ """ 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 lines: move.line id recordset
:param level_id: credit.control.policy.level id
:param controlling_date: date string of the credit controlling date.
Generally it should be the same
@@ -228,65 +173,54 @@ class CreditControlLine(orm.Model):
is smaller than company defined
tolerance
- :returns: list of created credit line ids
+ :returns: recordset of created credit lines
"""
- currency_obj = self.pool.get('res.currency')
- level_obj = self.pool.get('credit.control.policy.level')
- ml_obj = self.pool.get('account.move.line')
- user = self.pool.get('res.users').browse(cr, uid, uid)
- currency_ids = currency_obj.search(cr, uid, [], context=context)
+ currency_obj = self.env['res.currency']
+ level_obj = self.env['credit.control.policy.level']
+ user = self.env.user
+ currencies = currency_obj.search([])
tolerance = {}
tolerance_base = user.company_id.credit_control_tolerance
- for c_id in currency_ids:
- tolerance[c_id] = currency_obj.compute(
- cr, uid,
- c_id,
- user.company_id.currency_id.id,
- tolerance_base,
- context=context)
+ user_currency = user.company_id.currency_id
+ for currency in currencies:
+ tolerance[currency.id] = currency.compute(tolerance_base,
+ user_currency)
- level = level_obj.browse(cr, uid, level_id, context)
- line_ids = []
- 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)
+ level = level_obj.browse(level_id)
+ new_lines = self.browse()
+ for move_line in lines:
+ open_amount = move_line.amount_residual_currency
+ cur_tolerance = tolerance.get(move_line.currency_id.id,
+ tolerance_base)
if check_tolerance and open_amount < cur_tolerance:
continue
- vals = self._prepare_from_move_line(cr, uid,
- line,
+ vals = self._prepare_from_move_line(move_line,
level,
controlling_date,
- open_amount,
- context=context)
- line_id = self.create(cr, uid, vals, context=context)
- line_ids.append(line_id)
+ open_amount)
+ line = self.create(vals)
+ new_lines += line
# 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)
+ previous_drafts = self.search([('move_line_id', '=', move_line.id),
+ ('policy_level_id', '=', level.id),
+ ('state', '=', 'draft'),
+ ('id', '!=', line.id)])
+ if previous_drafts:
+ previous_drafts.write({'state': 'ignored'})
- return line_ids
+ return new_lines
- def unlink(self, cr, uid, ids, context=None, check=True):
- for line in self.browse(cr, uid, ids, context=context):
+ @api.multi
+ def unlink(self):
+ for line in self:
if line.state != 'draft':
- raise orm.except_orm(
- _('Error !'),
+ raise api.Warning(
_('You are not allowed to delete a credit control '
'line that is not in draft state.')
)
- return super(CreditControlLine, self).unlink(cr, uid, ids,
- context=context)
+ return super(CreditControlLine, self).unlink()
diff --git a/account_credit_control/mail.py b/account_credit_control/mail.py
index 25614913c..4636c81be 100644
--- a/account_credit_control/mail.py
+++ b/account_credit_control/mail.py
@@ -18,14 +18,12 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm, fields
+from openerp import models, fields
-class Mail(orm.Model):
+class Mail(models.Model):
_inherit = 'mail.mail'
# use HTML fields instead of text
- _columns = {
- 'body_html': fields.html('Rich-text Contents',
- help="Rich-text/HTML message"),
- }
+ body_html = fields.Html('Rich-text Contents',
+ help="Rich-text/HTML message")
diff --git a/account_credit_control/partner.py b/account_credit_control/partner.py
index ebfee0f9e..9255e7ff4 100644
--- a/account_credit_control/partner.py
+++ b/account_credit_control/partner.py
@@ -18,61 +18,41 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm, fields
+from openerp import models, fields, api
-class ResPartner(orm.Model):
- """Add a settings on the credit control policy to use on the partners,
- and links to the credit control lines."""
+class ResPartner(models.Model):
+ """ Add a settings on the credit control policy to use on the partners,
+ and links to the credit control lines.
+ """
_inherit = "res.partner"
- _columns = {
- '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
- )
- }
+ credit_policy_id = fields.Many2one(
+ 'credit.control.policy',
+ string='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
+ @api.constrains('credit_policy_id')
+ def _check_credit_policy(self):
+ """ Ensure that policy on partner are limited to the account policy """
+ for partner in self:
+ if (not partner.property_account_receivable or
+ not partner.credit_policy_id):
+ continue
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:
- default = default.copy()
- default['credit_control_line_ids'] = False
- return super(ResPartner, self).copy_data(
- cr, uid, id, default=default, context=context)
+ policy = partner.credit_policy_id
+ try:
+ policy.check_policy_against_account(account.id)
+ except api.Warning as err:
+ # constrains should raise ValidationError exceptions
+ raise api.ValidationError(err)
diff --git a/account_credit_control/policy.py b/account_credit_control/policy.py
index 0b4a5c8ca..4806cdf95 100644
--- a/account_credit_control/policy.py
+++ b/account_credit_control/policy.py
@@ -18,61 +18,47 @@
# along with this program. If not, see .
#
##############################################################################
-from openerp.osv import orm, fields
+from openerp import models, fields, api
from openerp.tools.translate import _
-class CreditControlPolicy(orm.Model):
- """Define a policy of reminder"""
+class CreditControlPolicy(models.Model):
+ """ Define a policy of reminder """
_name = "credit.control.policy"
_description = """Define a reminder policy"""
- _columns = {
- 'name': fields.char(
- 'Name',
- required=True,
- size=128
- ),
- 'level_ids': fields.one2many(
- 'credit.control.policy.level',
- 'policy_id',
- 'Policy Levels'
- ),
+ name = fields.Char('Name', required=True)
+ level_ids = fields.One2many('credit.control.policy.level',
+ 'policy_id',
+ string='Policy Levels')
+ do_nothing = fields.Boolean('Do nothing',
+ help='For policies which should not '
+ 'generate lines or are obsolete')
+ company_id = fields.Many2one('res.company', string='Company')
+ account_ids = fields.Many2many(
+ 'account.account',
+ string='Accounts',
+ required=True,
+ domain="[('type', '=', 'receivable')]",
+ help="This policy will be active only"
+ " for the selected accounts",
+ )
+ active = fields.Boolean('Active', default=True)
- 'do_nothing': fields.boolean(
- 'Do nothing',
- help='For policies which should not '
- 'generate lines or are obsolete'
- ),
-
- 'company_id': fields.many2one('res.company', 'Company'),
-
- 'account_ids': fields.many2many(
- 'account.account',
- string='Accounts',
- required=True,
- domain="[('type', '=', 'receivable')]",
- help="This policy will be active only"
- " for the selected accounts"
- ),
- 'active': fields.boolean('Active'),
- }
-
- _defaults = {
- 'active': True,
- }
-
- def _move_lines_domain(self, cr, uid, policy, controlling_date,
- context=None):
- """Build the default domain for searching move lines"""
- account_ids = [a.id for a in policy.account_ids]
+ @api.multi
+ def _move_lines_domain(self, controlling_date):
+ """ Build the default domain for searching move lines """
+ self.ensure_one()
+ account_ids = [a.id for a in self.account_ids]
return [('account_id', 'in', account_ids),
('date_maturity', '<=', controlling_date),
('reconcile_id', '=', False),
('partner_id', '!=', False)]
- def _due_move_lines(self, cr, uid, policy, controlling_date, context=None):
+ @api.multi
+ @api.returns('account.move.line')
+ def _due_move_lines(self, controlling_date):
""" Get the due move lines for the policy of the company.
The set of ids will be reduced and extended according
@@ -83,18 +69,17 @@ class CreditControlPolicy(orm.Model):
Assume that only the receivable lines have a maturity date and that
accounts used in the policy are reconcilable.
"""
- move_l_obj = self.pool.get('account.move.line')
- user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
- if user.company_id.credit_policy_id.id != policy.id:
- return set()
+ self.ensure_one()
+ move_l_obj = self.env['account.move.line']
+ user = self.env.user
+ if user.company_id.credit_policy_id.id != self.id:
+ return move_l_obj.browse()
+ domain_line = self._move_lines_domain(controlling_date)
+ return move_l_obj.search(domain_line)
- domain_line = self._move_lines_domain(cr, uid, policy,
- controlling_date,
- context=context)
- return set(move_l_obj.search(cr, uid, domain_line, context=context))
-
- def _move_lines_subset(self, cr, uid, policy, controlling_date,
- model, move_relation_field, context=None):
+ @api.multi
+ @api.returns('account.move.line')
+ def _move_lines_subset(self, controlling_date, model, move_relation_field):
""" Get the move lines related to one model for a policy.
Do not use direct SQL in order to respect security rules.
@@ -104,274 +89,243 @@ class CreditControlPolicy(orm.Model):
The policy relation field must be named credit_policy_id.
- :param browse_record policy: policy
:param str controlling_date: date of credit control
:param str model: name of the model where is defined a credit_policy_id
:param str move_relation_field: name of the field in account.move.line
which is a many2one to `model`
- :return: set of ids to add in the process, set of ids to remove from
+ :return: recordset to add in the process, recordset to remove from
the process
"""
+ self.ensure_one()
# MARK possible place for a good optimisation
- my_obj = self.pool.get(model)
- move_l_obj = self.pool.get('account.move.line')
+ my_obj = self.env[model]
+ move_l_obj = self.env['account.move.line']
+ default_domain = self._move_lines_domain(controlling_date)
- default_domain = self._move_lines_domain(cr, uid,
- policy,
- controlling_date,
- context=context)
- to_add_ids = set()
- to_remove_ids = set()
+ to_add = move_l_obj.browse()
+ to_remove = move_l_obj.browse()
# The lines which are linked to this policy have to be included in the
# run for this policy.
# If another object override the credit_policy_id (ie. invoice after
- add_obj_ids = my_obj.search(
- cr, uid,
- [('credit_policy_id', '=', policy.id)],
- context=context)
- if add_obj_ids:
+ add_objs = my_obj.search([('credit_policy_id', '=', self.id)])
+ if add_objs:
domain = list(default_domain)
- domain.append((move_relation_field, 'in', add_obj_ids))
- to_add_ids = set(move_l_obj.search(cr, uid, domain,
- context=context))
+ domain.append((move_relation_field, 'in', add_objs))
+ to_add = move_l_obj.search(domain)
# The lines which are linked to another policy do not have to be
# included in the run for this policy.
- neg_obj_ids = my_obj.search(
- cr, uid,
- [('credit_policy_id', '!=', policy.id),
- ('credit_policy_id', '!=', False)],
- context=context)
- if neg_obj_ids:
+ neg_objs = my_obj.search([('credit_policy_id', '!=', self.id),
+ ('credit_policy_id', '!=', False)])
+ if neg_objs:
domain = list(default_domain)
- domain.append((move_relation_field, 'in', neg_obj_ids))
- to_remove_ids = set(move_l_obj.search(cr, uid, domain,
- context=context))
- return to_add_ids, to_remove_ids
+ domain.append((move_relation_field, 'in', neg_objs))
+ to_remove = move_l_obj.search(domain)
+ return to_add, to_remove
- def _get_partner_related_lines(self, cr, uid, policy, controlling_date,
- context=None):
+ @api.multi
+ @api.returns('account.move.line')
+ def _get_partner_related_lines(self, controlling_date):
""" Get the move lines for a policy related to a partner.
- :param browse_record policy: policy
:param str controlling_date: date of credit control
:param str model: name of the model where is defined a credit_policy_id
:param str move_relation_field: name of the field in account.move.line
which is a many2one to `model`
- :return: set of ids to add in the process, set of ids to remove from
+ :return: recordset to add in the process, recordset to remove from
the process
"""
- return self._move_lines_subset(cr, uid, policy, controlling_date,
- 'res.partner', 'partner_id',
- context=context)
+ return self._move_lines_subset(controlling_date, 'res.partner',
+ 'partner_id')
- def _get_invoice_related_lines(self, cr, uid, policy, controlling_date,
- context=None):
+ @api.multi
+ @api.returns('account.move.line')
+ def _get_invoice_related_lines(self, controlling_date):
""" Get the move lines for a policy related to an invoice.
- :param browse_record policy: policy
:param str controlling_date: date of credit control
:param str model: name of the model where is defined a credit_policy_id
:param str move_relation_field: name of the field in account.move.line
which is a many2one to `model`
- :return: set of ids to add in the process, set of ids to remove from
+ :return: recordset to add in the process, recordset to remove from
the process
"""
- return self._move_lines_subset(cr, uid, policy, controlling_date,
- 'account.invoice', 'invoice',
- context=context)
+ return self._move_lines_subset(controlling_date, 'account.invoice',
+ 'invoice')
- def _get_move_lines_to_process(self, cr, uid, policy_id, controlling_date,
- context=None):
- """Build a list of move lines ids to include in a run
+ @api.multi
+ @api.returns('account.move.line')
+ def _get_move_lines_to_process(self, controlling_date):
+ """ Build a list of move lines ids to include in a run
for a policy at a given date.
- :param int/long policy: id of the policy
:param str controlling_date: date of credit control
- :return: set of ids to include in the run
- """
- assert not (isinstance(policy_id, list) and len(policy_id) > 1), \
- "policy_id: only one id expected"
- if isinstance(policy_id, list):
- policy_id = policy_id[0]
-
- policy = self.browse(cr, uid, policy_id, context=context)
+ :return: recordset to include in the run
+ """
+ self.ensure_one()
# there is a priority between the lines, depicted by the calls below
- # warning, side effect method called on lines
- lines = self._due_move_lines(cr, uid, policy, controlling_date,
- context=context)
- add_ids, remove_ids = self._get_partner_related_lines(cr, uid, policy,
- controlling_date,
- context=context)
- lines = lines.union(add_ids).difference(remove_ids)
- add_ids, remove_ids = self._get_invoice_related_lines(cr, uid, policy,
- controlling_date,
- context=context)
- lines = lines.union(add_ids).difference(remove_ids)
+ lines = self._due_move_lines(controlling_date)
+ to_add, to_remove = self._get_partner_related_lines(controlling_date)
+ lines = (lines | to_add) - to_remove
+ to_add, to_remove = self._get_invoice_related_lines(controlling_date)
+ lines = (lines | to_add) - to_remove
return lines
- def _lines_different_policy(self, cr, uid, policy_id, lines, context=None):
- """ Return a set of move lines ids for which there is an existing credit line
- but with a different policy.
+ @api.multi
+ @api.returns('account.move.line')
+ def _lines_different_policy(self, lines):
+ """ Return a set of move lines ids for which there is an
+ existing credit line but with a different policy.
"""
- different_lines = set()
+ self.ensure_one()
+ move_line_obj = self.env['account.move.line']
+ different_lines = move_line_obj.browse()
if not lines:
return different_lines
- assert not (isinstance(policy_id, list) and len(policy_id) > 1), \
- "policy_id: only one id expected"
- if isinstance(policy_id, list):
- policy_id = policy_id[0]
+ cr = self._cr
cr.execute("SELECT move_line_id FROM credit_control_line"
" WHERE policy_id != %s and move_line_id in %s"
" AND manually_overridden IS false",
- (policy_id, tuple(lines)))
+ (self.id, tuple(line.id for line in lines)))
res = cr.fetchall()
if res:
- different_lines.update([x[0] for x in res])
+ return move_line_obj.browse([row[0] for row 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)
+ @api.multi
+ def check_policy_against_account(self, account_id):
+ """ Ensure that the policy corresponds to account relation """
+ account = self.env['account.account'].browse(account_id)
+ policies = self.search([])
allowed = [x for x in policies
if account in x.account_ids or x.do_nothing]
- if policy not in allowed:
+ if self not in allowed:
allowed_names = u"\n".join(x.name for x in allowed)
- raise orm.except_orm(
+ raise api.Warning(
_('You can only use a policy set on '
- 'account %s') % account.name,
- _("Please choose one of the following "
- "policies:\n %s") % allowed_names
+ 'account %s.\n'
+ 'Please choose one of the following '
+ 'policies:\n %s') % (account.name, allowed_names)
)
return True
-class CreditControlPolicyLevel(orm.Model):
+class CreditControlPolicyLevel(models.Model):
"""Define a policy level. A level allows to determine if
a move line is due and the level of overdue of the line"""
_name = "credit.control.policy.level"
_order = 'level'
_description = """A credit control policy level"""
- _columns = {
- 'policy_id': fields.many2one('credit.control.policy',
- 'Related Policy', required=True),
- 'name': fields.char('Name', size=128, required=True,
- translate=True),
- 'level': fields.integer('Level', required=True),
- 'computation_mode': fields.selection(
- [('net_days', 'Due Date'),
- ('end_of_month', 'Due Date, End Of Month'),
- ('previous_date', 'Previous Reminder')],
- 'Compute Mode',
- required=True
- ),
-
- 'delay_days': fields.integer('Delay (in days)', required='True'),
- 'email_template_id': fields.many2one('email.template',
- 'Email Template',
- required=True),
- 'channel': fields.selection([('letter', 'Letter'),
- ('email', 'Email')],
- 'Channel', required=True),
- 'custom_text': fields.text('Custom Message',
- required=True,
- translate=True),
- 'custom_mail_text': fields.text('Custom Mail Message',
- required=True, translate=True),
-
- }
-
- def _check_level_mode(self, cr, uid, rids, context=None):
- """ The smallest level of a policy cannot be computed on the
- "previous_date". Return False if this happens. """
- if isinstance(rids, (int, long)):
- rids = [rids]
- for level in self.browse(cr, uid, rids, context):
- smallest_level_id = self.search(
- cr, uid,
- [('policy_id', '=', level.policy_id.id)],
- order='level asc', limit=1, context=context)
- smallest_level = self.browse(cr, uid, smallest_level_id[0],
- context)
- if smallest_level.computation_mode == 'previous_date':
- return False
- return True
+ name = fields.Char(string='Name', required=True, translate=True)
+ policy_id = fields.Many2one('credit.control.policy',
+ string='Related Policy',
+ required=True)
+ level = fields.Integer(string='Level', required=True)
+ computation_mode = fields.Selection(
+ [('net_days', 'Due Date'),
+ ('end_of_month', 'Due Date, End Of Month'),
+ ('previous_date', 'Previous Reminder')],
+ string='Compute Mode',
+ required=True
+ )
+ delay_days = fields.Integer(string='Delay (in days)', required=True)
+ email_template_id = fields.Many2one('email.template',
+ string='Email Template',
+ required=True)
+ channel = fields.Selection([('letter', 'Letter'),
+ ('email', 'Email')],
+ string='Channel',
+ required=True)
+ custom_text = fields.Text(string='Custom Message',
+ required=True,
+ translate=True)
+ custom_mail_text = fields.Text(string='Custom Mail Message',
+ required=True, translate=True)
_sql_constraint = [('unique level',
'UNIQUE (policy_id, level)',
'Level must be unique per policy')]
- _constraints = [(_check_level_mode,
- 'The smallest level can not be of type Previous Reminder',
- ['level'])]
+ @api.one
+ @api.constrains('level', 'computation_mode')
+ def _check_level_mode(self):
+ """ The smallest level of a policy cannot be computed on the
+ "previous_date".
+ """
+ smallest_level = self.search([('policy_id', '=', self.policy_id.id)],
+ order='level asc', limit=1)
+ if smallest_level.computation_mode == 'previous_date':
+ return api.ValidationError(_('The smallest level can not be of '
+ 'type Previous Reminder'))
- def _previous_level(self, cr, uid, policy_level, context=None):
+ @api.multi
+ def _previous_level(self):
""" For one policy level, returns the id of the previous level
If there is no previous level, it returns None, it means that's the
first policy level
- :param browse_record policy_level: policy level
- :return: previous level id or None if there is no previous level
+ :return: previous level or None if there is no previous level
"""
- previous_level_ids = self.search(
- cr,
- uid,
- [('policy_id', '=', policy_level.policy_id.id),
- ('level', '<', policy_level.level)],
- order='level desc',
- limit=1,
- context=context)
- return previous_level_ids[0] if previous_level_ids else None
+ self.ensure_one()
+ previous_levels = self.search([('policy_id', '=', self.policy_id.id),
+ ('level', '<', self.level)],
+ order='level desc',
+ limit=1)
+ if not previous_levels:
+ return None
+ return previous_levels
# ----- sql time related methods ---------
- def _net_days_get_boundary(self):
+ @staticmethod
+ def _net_days_get_boundary():
return (" (mv_line.date_maturity + %(delay)s)::date <= "
"date(%(controlling_date)s)")
- def _end_of_month_get_boundary(self):
+ @staticmethod
+ def _end_of_month_get_boundary():
return ("(date_trunc('MONTH', (mv_line.date_maturity + %(delay)s))+"
"INTERVAL '1 MONTH - 1 day')::date"
"<= date(%(controlling_date)s)")
- def _previous_date_get_boundary(self):
+ @staticmethod
+ def _previous_date_get_boundary():
return "(cr_line.date + %(delay)s)::date <= date(%(controlling_date)s)"
- def _get_sql_date_boundary_for_computation_mode(self, cr, uid, level,
- controlling_date,
- context=None):
- """Return a where clauses statement for the given
- controlling date and computation mode of the level"""
- fname = "_%s_get_boundary" % (level.computation_mode,)
+ @api.multi
+ def _get_sql_date_boundary_for_computation_mode(self, controlling_date):
+ """ Return a where clauses statement for the given controlling
+ date and computation mode of the level
+ """
+ self.ensure_one()
+ fname = "_%s_get_boundary" % (self.computation_mode, )
if hasattr(self, fname):
fnc = getattr(self, fname)
return fnc()
else:
raise NotImplementedError(
_('Can not get function for computation mode: '
- '%s is not implemented') % (fname,)
+ '%s is not implemented') % (fname, )
)
# -----------------------------------------
- def _get_first_level_move_line_ids(self, cr, uid, level, controlling_date,
- lines, context=None):
- """Retrieve all the move lines that are linked to a first level.
- We use Raw SQL for performance. Security rule where applied in
- policy object when the first set of lines were retrieved"""
- level_lines = set()
+ @api.multi
+ @api.returns('account.move.line')
+ def _get_first_level_move_lines(self, controlling_date, lines):
+ """ Retrieve all the move lines that are linked to a first level.
+ We use Raw SQL for performance. Security rule where applied in
+ policy object when the first set of lines were retrieved
+ """
+ self.ensure_one()
+ move_line_obj = self.env['account.move.line']
if not lines:
- return level_lines
+ return move_line_obj.browse()
+ cr = self._cr
sql = ("SELECT DISTINCT mv_line.id\n"
" FROM account_move_line mv_line\n"
" WHERE mv_line.id in %(line_ids)s\n"
@@ -385,26 +339,28 @@ class CreditControlPolicyLevel(orm.Model):
" 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
- )
+ _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode
+ sql += _get_sql_date_part(controlling_date)
data_dict = {'controlling_date': controlling_date,
- 'line_ids': tuple(lines),
- 'delay': level.delay_days}
+ 'line_ids': tuple(line.id for line in lines),
+ 'delay': self.delay_days}
+ print cr.mogrify(sql, data_dict)
cr.execute(sql, data_dict)
res = cr.fetchall()
if res:
- level_lines.update([x[0] for x in res])
- return level_lines
+ return move_line_obj.browse([row[0] for row in res])
+ return move_line_obj.browse()
- def _get_other_level_move_line_ids(self, cr, uid, level, controlling_date,
- lines, context=None):
+ @api.multi
+ @api.returns('account.move.line')
+ def _get_other_level_move_lines(self, controlling_date, lines):
""" Retrieve the move lines for other levels than first level.
"""
- level_lines = set()
+ self.ensure_one()
+ move_line_obj = self.env['account.move.line']
if not lines:
- return level_lines
+ return move_line_obj.browse()
+ cr = self._cr
sql = ("SELECT mv_line.id\n"
" FROM account_move_line mv_line\n"
" JOIN credit_control_line cr_line\n"
@@ -424,41 +380,31 @@ class CreditControlPolicyLevel(orm.Model):
" 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,
- controlling_date,
- context
- )
- previous_level_id = self._previous_level(cr, uid, level,
- context=context)
- previous_level = self.browse(cr, uid, previous_level_id,
- context=context)
+ _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode
+ sql += _get_sql_date_part(controlling_date)
+ previous_level = self._previous_level()
data_dict = {'controlling_date': controlling_date,
- 'line_ids': tuple(lines),
- 'delay': level.delay_days,
+ 'line_ids': tuple(line.id for line in lines),
+ 'delay': self.delay_days,
'previous_level': previous_level.level}
# print cr.mogrify(sql, data_dict)
cr.execute(sql, data_dict)
res = cr.fetchall()
if res:
- level_lines.update([x[0] for x in res])
- return level_lines
+ return move_line_obj.browse([row[0] for row in res])
+ return move_line_obj.browse()
- def get_level_lines(self, cr, uid, level_id, controlling_date, lines,
- context=None):
- """get all move lines in entry lines that match the current level"""
- assert not (isinstance(level_id, list) and len(level_id) > 1), \
- "level_id: only one id expected"
- if isinstance(level_id, list):
- level_id = level_id[0]
- matching_lines = set()
- level = self.browse(cr, uid, level_id, context=context)
- if self._previous_level(cr, uid, level, context=context) is None:
- method = self._get_first_level_move_line_ids
+ @api.multi
+ @api.returns('account.move.line')
+ def get_level_lines(self, controlling_date, lines):
+ """ get all move lines in entry lines that match the current level """
+ self.ensure_one()
+ move_line_obj = self.env['account.move.line']
+ matching_lines = move_line_obj.browse()
+ if self._previous_level() is None:
+ method = self._get_first_level_move_lines
else:
- method = self._get_other_level_move_line_ids
- matching_lines.update(method(cr, uid, level,
- controlling_date, lines,
- context=context))
+ method = self._get_other_level_move_lines
+ matching_lines += method(controlling_date, lines)
return matching_lines
diff --git a/account_credit_control/run.py b/account_credit_control/run.py
index fd32be1dc..e25518f1c 100644
--- a/account_credit_control/run.py
+++ b/account_credit_control/run.py
@@ -20,143 +20,113 @@
##############################################################################
import logging
-from openerp.osv import orm, fields
+from openerp import models, fields, api
from openerp.tools.translate import _
logger = logging.getLogger('credit.control.run')
-class CreditControlRun(orm.Model):
- """Credit Control run generate all credit control lines and reject"""
+class CreditControlRun(models.Model):
+ """ Credit Control run generate all credit control lines and reject """
_name = "credit.control.run"
_rec_name = 'date'
- _description = """Credit control line generator"""
- _columns = {
- 'date': fields.date('Controlling Date', required=True),
- 'policy_ids': fields.many2many(
- 'credit.control.policy',
- rel="credit_run_policy_rel",
- id1='run_id', id2='policy_id',
- string='Policies',
- readonly=True,
- states={'draft': [('readonly', False)]}
- ),
+ _description = "Credit control line generator"
- 'report': fields.text('Report', readonly=True),
+ date = fields.Date(string='Controlling Date', required=True)
- 'state': fields.selection([('draft', 'Draft'),
- ('done', 'Done')],
- string='State',
- required=True,
- readonly=True),
+ @api.model
+ def _get_policies(self):
+ return self.env['credit.control.policy'].search([])
- 'manual_ids': fields.many2many(
- 'account.move.line',
- rel="credit_runreject_rel",
- string='Lines to handle manually',
- help=('If a credit control line has been generated'
- 'on a policy and the policy has been changed '
- 'in the meantime, it has to be handled '
- 'manually'),
- readonly=True
- ),
- }
+ policy_ids = fields.Many2many(
+ 'credit.control.policy',
+ rel="credit_run_policy_rel",
+ id1='run_id', id2='policy_id',
+ string='Policies',
+ readonly=True,
+ states={'draft': [('readonly', False)]},
+ default=_get_policies,
+ )
+ report = fields.Text(string='Report', readonly=True, copy=False)
+ state = fields.Selection([('draft', 'Draft'),
+ ('done', 'Done')],
+ string='State',
+ required=True,
+ readonly=True,
+ default='draft')
+ manual_ids = fields.Many2many(
+ 'account.move.line',
+ rel="credit_runreject_rel",
+ string='Lines to handle manually',
+ help='If a credit control line has been generated'
+ 'on a policy and the policy has been changed '
+ 'in the meantime, it has to be handled '
+ 'manually',
+ readonly=True,
+ copy=False,
+ )
- def copy_data(self, cr, uid, id, default=None, context=None):
- if default is None:
- default = {}
- else:
- default = default.copy()
- default.update({
- 'report': False,
- 'manual_ids': False,
- })
- return super(CreditControlRun, self).copy_data(
- cr, uid, id, default=default, context=context)
-
- def _get_policies(self, cr, uid, context=None):
- return self.pool['credit.control.policy'].search(cr, uid, [],
- context=context)
-
- _defaults = {'state': 'draft',
- 'policy_ids': _get_policies}
-
- def _check_run_date(self, cr, uid, ids, controlling_date, context=None):
- """Ensure that there is no credit line in the future
+ @api.multi
+ def _check_run_date(self, controlling_date):
+ """ Ensure that there is no credit line in the future
using controlling_date
"""
- run_obj = self.pool['credit.control.run']
- runs = run_obj.search(cr, uid, [('date', '>', controlling_date)],
- order='date DESC', limit=1, context=context)
+ runs = self.search([('date', '>', controlling_date)],
+ order='date DESC', limit=1)
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))
+ raise api.Warning(_('A run has already been executed more '
+ 'recently than %s') % (runs.date))
- line_obj = self.pool['credit.control.line']
- lines = line_obj.search(cr, uid, [('date', '>', controlling_date)],
- order='date DESC', limit=1, context=context)
+ line_obj = self.env['credit.control.line']
+ lines = line_obj.search([('date', '>', controlling_date)],
+ order='date DESC', limit=1)
if lines:
- line = line_obj.browse(cr, uid, lines[0], context=context)
- raise orm.except_orm(_('Error'),
- _('A credit control line more '
- 'recent than %s exists at %s') %
- (controlling_date, line.date))
- return True
+ raise api.Warning(_('A credit control line more '
+ 'recent than %s exists at %s') %
+ (controlling_date, lines.date))
- def _generate_credit_lines(self, cr, uid, run_id, context=None):
+ @api.multi
+ @api.returns('credit.control.line')
+ def _generate_credit_lines(self):
""" Generate credit control lines. """
- cr_line_obj = self.pool.get('credit.control.line')
- assert not (isinstance(run_id, list) and len(run_id) > 1), \
- "run_id: only one id expected"
- if isinstance(run_id, list):
- run_id = run_id[0]
+ self.ensure_one()
+ cr_line_obj = self.env['credit.control.line']
+ move_line_obj = self.env['account.move.line']
+ manually_managed_lines = move_line_obj.browse()
+ self._check_run_date(self.date)
- run = self.browse(cr, uid, run_id, context=context)
- manually_managed_lines = set() # line who changed policy
- credit_line_ids = [] # generated lines
- run._check_run_date(run.date, context=context)
-
- policies = run.policy_ids
+ policies = self.policy_ids
if not policies:
- raise orm.except_orm(_('Error'),
- _('Please select a policy'))
+ raise api.Warning(_('Please select a policy'))
report = ''
- generated_ids = []
+ generated = []
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)
- manually_managed_lines.update(manual_lines)
- policy_generated_ids = []
+ lines = policy._get_move_lines_to_process(self.date)
+ manual_lines = policy._lines_different_policy(lines)
+ lines -= manual_lines
+ manually_managed_lines += manual_lines
+ policy_lines_generated = cr_line_obj.browse()
if lines:
# policy levels are sorted by level
# so iteration is in the correct order
for level in reversed(policy.level_ids):
- level_lines = level.get_level_lines(run.date, lines,
- context=context)
- policy_generated_ids += \
+ level_lines = level.get_level_lines(self.date, lines)
+ policy_lines_generated += \
cr_line_obj.create_or_update_from_mv_lines(
- cr, uid,
- [],
- list(level_lines),
+ level_lines,
level.id,
- run.date,
- context=context
+ self.date,
)
- 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))
- credit_line_ids += policy_generated_ids
+ generated += policy_lines_generated
+ if policy_lines_generated:
+ report += (_("Policy \"%s\" has generated %d Credit "
+ "Control Lines.\n") %
+ (policy.name, len(policy_lines_generated)))
else:
report += _(
"Policy \"%s\" has not generated any "
@@ -166,24 +136,24 @@ class CreditControlRun(orm.Model):
vals = {'state': 'done',
'report': report,
'manual_ids': [(6, 0, manually_managed_lines)]}
- run.write(vals, context=context)
- return generated_ids
+ self.write(vals)
+ return generated
- def generate_credit_lines(self, cr, uid, run_id, context=None):
- """Generate credit control lines
+ @api.multi
+ def generate_credit_lines(self):
+ """ Generate credit control lines
Lock the ``credit_control_run`` Postgres table to avoid concurrent
calls of this method.
"""
try:
- cr.execute('SELECT id FROM credit_control_run'
- ' LIMIT 1 FOR UPDATE NOWAIT')
+ self._cr.execute('SELECT id FROM credit_control_run'
+ ' LIMIT 1 FOR UPDATE NOWAIT')
except Exception:
# In case of exception openerp will do a rollback
# for us and free the lock
- raise orm.except_orm(_('Error'),
- _('A credit control run is already running'
- ' in background, please try later.'))
+ raise api.Warning(_('A credit control run is already running'
+ ' in background, please try later.'))
- self._generate_credit_lines(cr, uid, run_id, context)
+ self._generate_credit_lines()
return True