Esempio n. 1
0
class CreditControlEmailer(TransientModel):
    """Send emails for each selected credit control lines."""

    _name = "credit.control.emailer"
    _description = """Mass credit line emailer"""
    _rec_name = 'id'

    def _get_line_ids(self, cr, uid, context=None):
        if context is None:
            context = {}
        res = False
        if (context.get('active_model') == 'credit.control.line'
                and context.get('active_ids')):
            res = self._filter_line_ids(cr,
                                        uid,
                                        context['active_ids'],
                                        context=context)
        return res

    _columns = {
        'line_ids':
        fields.many2many(
            'credit.control.line',
            string='Credit Control Lines',
            domain="[('state', '=', 'to_be_sent'), ('channel', '=', 'email')]"
        ),
    }

    _defaults = {
        'line_ids': _get_line_ids,
    }

    def _filter_line_ids(self, cr, uid, active_ids, context=None):
        """filter lines to use in the wizard"""
        line_obj = self.pool.get('credit.control.line')
        domain = [('state', '=', 'to_be_sent'), ('id', 'in', active_ids),
                  ('channel', '=', 'email')]
        return line_obj.search(cr, uid, domain, context=context)

    def email_lines(self, cr, uid, wiz_id, context=None):
        assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
                "wiz_id: only one id expected"
        comm_obj = self.pool.get('credit.control.communication')
        if isinstance(wiz_id, list):
            wiz_id = wiz_id[0]
        form = self.browse(cr, uid, wiz_id, context)

        if not form.line_ids:
            raise except_osv(_('Error'),
                             _('No credit control lines selected.'))

        line_ids = [l.id for l in form.line_ids]
        filtered_ids = self._filter_line_ids(cr, uid, line_ids, context)
        comms = comm_obj._generate_comm_from_credit_line_ids(cr,
                                                             uid,
                                                             filtered_ids,
                                                             context=context)
        comm_obj._generate_emails(cr, uid, comms, context=context)
        return {}
Esempio n. 2
0
class ProductConfigCreator(TransientModel):
    _inherit = 'stock.warehouse.orderpoint.creator'
    _description = 'Orderpoint Creator'

    _columns = {
            'push_flow_template_id': fields.many2many('stock.location.path.template',
                                                      rel='path_creator_rel',
                                                      string='Pushed Flows'),
            'pull_flow_template_id': fields.many2many('product.pulled.flow.template',
                                                      rel='flow_creator_rel',
                                                      string="Pulled Flows")
    }

    def _get_template_register(self):
        """return a list of the field names which defines a template
        This is a hook to allow expending the list of template"""
        template_reg = []
        parent_reg = super(ProductConfigCreator, self)._get_template_register()
        template_reg.extend(parent_reg)
        template_reg.extend(_template_register)
        return list(set(template_reg))
Esempio n. 3
0
class OrderpointCreator(TransientModel):
    _name = 'stock.warehouse.orderpoint.creator'
    _description = 'Orderpoint Creator'

    _columns = {
        'orderpoint_template_id':
        fields.many2many('stock.warehouse.orderpoint.template',
                         rel='order_point_creator_rel',
                         string='Stock rule template')
    }

    def _get_template_register(self):
        """return a list of the field names which defines a template
        This is a hook to allow expending the list of template"""
        return _template_register

    def action_configure(self, cursor, uid, wiz_id, context=None):
        """ action to retrieve wizard data and launch creation of items """

        product_ids = context['active_ids']
        if isinstance(wiz_id, list):
            wiz_id = wiz_id[0]
        current = self.browse(cursor, uid, wiz_id, context=context)
        for template_field in self._get_template_register():
            template_br_list = current[template_field]
            if template_br_list:
                if isinstance(template_br_list, browse_record):
                    template_br_list = [template_br_list]
                template_model = template_br_list[0]._model._name
                template_obj = self.pool.get(template_model)
                template_obj._disable_old_instances(cursor,
                                                    uid,
                                                    template_br_list,
                                                    product_ids,
                                                    context=context)
                for template_br in template_br_list:
                    template_obj.create_instances(cursor,
                                                  uid,
                                                  template_br,
                                                  product_ids,
                                                  context=context)

        return {}
Esempio n. 4
0
class CreditControlPolicy(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'),
        '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="[('reconcile', '=', True)]",
                         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]
        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):
        """ Get the due move lines for the policy of the company.

        The set of ids will be reduced and extended according to the specific policies
        defined on partners and invoices.

        Do not use direct SQL in order to respect security rules.

        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()

        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):
        """ Get the move lines related to one model for a policy.

        Do not use direct SQL in order to respect security rules.

        Assume that only the receivable lines have a maturity date and that
        accounts used in the policy are reconcilable.

        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
            the process
        """
        # MARK possible place for a good optimisation
        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)
        to_add_ids = set()
        to_remove_ids = set()

        # 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:
            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))

        # 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:
            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

    def _get_partner_related_lines(self,
                                   cr,
                                   uid,
                                   policy,
                                   controlling_date,
                                   context=None):
        """ 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
            the process
        """
        return self._move_lines_subset(cr,
                                       uid,
                                       policy,
                                       controlling_date,
                                       'res.partner',
                                       'partner_id',
                                       context=context)

    def _get_invoice_related_lines(self,
                                   cr,
                                   uid,
                                   policy,
                                   controlling_date,
                                   context=None):
        """ 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
            the process
        """
        return self._move_lines_subset(cr,
                                       uid,
                                       policy,
                                       controlling_date,
                                       'account.invoice',
                                       'invoice',
                                       context=context)

    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 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)
        # 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)
        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.
        """
        different_lines = set()
        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.execute(
            "SELECT move_line_id FROM credit_control_line"
            "    WHERE policy_id != %s and move_line_id in %s",
            (policy_id, tuple(lines)))
        res = cr.fetchall()
        if res:
            different_lines.update([x[0] for x in res])
        return different_lines
Esempio n. 5
0
class CreditCommunication(TransientModel):
    """Shell class used to provide a base model to email template and reporting.
       Il use this approche in version 7 a browse record will exist even if not saved"""
    _name = "credit.control.communication"
    _description = "credit control communication"
    _rec_name = 'partner_id'
    _columns = {
        'partner_id':
        fields.many2one('res.partner', 'Partner', required=True),
        'current_policy_level':
        fields.many2one('credit.control.policy.level', 'Level', required=True),
        'credit_control_line_ids':
        fields.many2many('credit.control.line',
                         rel='comm_credit_rel',
                         string='Credit Lines'),
        'company_id':
        fields.many2one('res.company', 'Company', required=True),
        'user_id':
        fields.many2one('res.users', 'User')
    }

    _defaults = {
        'company_id':
        lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(
            cr, uid, 'credit.control.policy', context=c),
        'user_id':
        lambda s, cr, uid, c: uid
    }

    def get_email(self, cr, uid, com_id, context=None):
        """Return a valid email for customer"""
        if isinstance(com_id, list):
            assert len(
                com_id) == 1, "get_email only support one id as parameter"
            com_id = com_id[0]
        form = self.browse(cr, uid, com_id, context=context)
        contact = form.get_contact_address()
        return contact.email

    def get_contact_address(self, cr, uid, com_id, context=None):
        pmod = self.pool['res.partner']
        if isinstance(com_id, list):
            com_id = com_id[0]
        form = self.browse(cr, uid, com_id, context=context)
        part = form.partner_id
        add_ids = part.address_get(adr_pref=['invoice']) or {}
        add_id = add_ids.get('invoice', add_ids.get('default', False))
        return pmod.browse(cr, uid, add_id, context)

    def _get_credit_lines(self,
                          cr,
                          uid,
                          line_ids,
                          partner_id,
                          level_id,
                          context=None):
        """Return credit lines related to a partner and a policy level"""
        cr_line_obj = self.pool.get('credit.control.line')
        cr_l_ids = cr_line_obj.search(cr,
                                      uid,
                                      [('id', 'in', line_ids),
                                       ('partner_id', '=', partner_id),
                                       ('policy_level_id', '=', level_id)],
                                      context=context)
        return cr_l_ids

    def _generate_comm_from_credit_line_ids(self,
                                            cr,
                                            uid,
                                            line_ids,
                                            context=None):
        if not line_ids:
            return []
        comms = []
        sql = (
            "SELECT distinct partner_id, policy_level_id, credit_control_policy_level.level"
            " FROM credit_control_line JOIN credit_control_policy_level "
            "   ON (credit_control_line.policy_level_id = credit_control_policy_level.id)"
            " WHERE credit_control_line.id in %s"
            " ORDER by credit_control_policy_level.level")

        cr.execute(sql, (tuple(line_ids), ))
        res = cr.dictfetchall()
        for level_assoc in res:
            data = {}
            data['credit_control_line_ids'] = \
                    [(6, 0, self._get_credit_lines(cr, uid, line_ids,
                                                   level_assoc['partner_id'],
                                                   level_assoc['policy_level_id'],
                                                   context=context))]
            data['partner_id'] = level_assoc['partner_id']
            data['current_policy_level'] = level_assoc['policy_level_id']
            comm_id = self.create(cr, uid, data, context=context)

            comms.append(self.browse(cr, uid, comm_id, context=context))
        return comms

    def _generate_emails(self, cr, uid, comms, context=None):
        """Generate email message using template related to level"""
        cr_line_obj = self.pool.get('credit.control.line')
        email_temp_obj = self.pool.get('email.template')
        email_message_obj = self.pool.get('mail.mail')
        att_obj = self.pool.get('ir.attachment')
        email_ids = []
        essential_fields = ['subject', 'body_html', 'email_from', 'email_to']

        for comm in comms:
            # we want to use a local cr in order to send the maximum
            # of email
            template = comm.current_policy_level.email_template_id.id
            email_values = {}
            cl_ids = [cl.id for cl in comm.credit_control_line_ids]
            email_values = email_temp_obj.generate_email(cr,
                                                         uid,
                                                         template,
                                                         comm.id,
                                                         context=context)
            email_values['body_html'] = email_values['body']
            email_values['type'] = 'email'

            email_id = email_message_obj.create(cr,
                                                uid,
                                                email_values,
                                                context=context)

            state = 'sent'
            # The mail will not be send, however it will be in the pool, in an
            # error state. So we create it, link it with the credit control line
            # and put this latter in a `email_error` state we not that we have a
            # problem with the email
            if any(not email_values.get(field) for field in essential_fields):
                state = 'email_error'

            cr_line_obj.write(cr,
                              uid,
                              cl_ids, {
                                  'mail_message_id': email_id,
                                  'state': state
                              },
                              context=context)
            att_ids = []
            for att in email_values.get('attachments', []):
                attach_fname = att[0]
                attach_datas = att[1]
                data_attach = {
                    'name': attach_fname,
                    'datas': attach_datas,
                    'datas_fname': attach_fname,
                    'res_model': 'mail.mail',
                    'res_id': email_id,
                    'type': 'binary',
                }
                att_ids.append(
                    att_obj.create(cr, uid, data_attach, context=context))
            email_message_obj.write(cr,
                                    uid, [email_id],
                                    {'attachment_ids': [(6, 0, att_ids)]},
                                    context=context)
            email_ids.append(email_id)
        return email_ids

    def _generate_report(self, cr, uid, comms, context=None):
        """Will generate a report by inserting mako template of related policy template"""
        service = netsvc.LocalService('report.credit_control_summary')
        ids = [x.id for x in comms]
        result, format = service.create(cr, uid, ids, {}, {})
        return result

    def _mark_credit_line_as_sent(self, cr, uid, comms, context=None):
        line_ids = []
        for comm in comms:
            line_ids += [x.id for x in comm.credit_control_line_ids]
        l_obj = self.pool.get('credit.control.line')
        l_obj.write(cr, uid, line_ids, {'state': 'sent'}, context=context)
        return line_ids
class CreditControlMarker(TransientModel):
    """Change the state of lines in mass"""

    _name = 'credit.control.marker'
    _description = 'Mass marker'

    def _get_line_ids(self, cr, uid, context=None):
        if context is None:
            context = {}
        res = False
        if (context.get('active_model') == 'credit.control.line'
                and context.get('active_ids')):
            res = self._filter_line_ids(cr,
                                        uid,
                                        context['active_ids'],
                                        context=context)
        return res

    _columns = {
        'name':
        fields.selection([('ignored', 'Ignored'),
                          ('to_be_sent', 'Ready To Send'), ('sent', 'Done')],
                         'Mark as',
                         required=True),
        'line_ids':
        fields.many2many('credit.control.line',
                         string='Credit Control Lines',
                         domain="[('state', '!=', 'sent')]"),
    }

    _defaults = {
        'name': 'to_be_sent',
        'line_ids': _get_line_ids,
    }

    def _filter_line_ids(self, cr, uid, active_ids, context=None):
        """get line to be marked filter done lines"""
        line_obj = self.pool.get('credit.control.line')
        domain = [('state', '!=', 'sent'), ('id', 'in', active_ids)]
        return line_obj.search(cr, uid, domain, context=context)

    def _mark_lines(self, cr, uid, filtered_ids, state, context=None):
        """write hook"""
        line_obj = self.pool.get('credit.control.line')
        if not state:
            raise ValueError(_('state can not be empty'))
        line_obj.write(cr,
                       uid,
                       filtered_ids, {'state': state},
                       context=context)
        return filtered_ids

    def mark_lines(self, cr, uid, wiz_id, context=None):
        """Write state of selected credit lines to the one in entry
        done credit line will be ignored"""
        assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
                "wiz_id: only one id expected"
        if isinstance(wiz_id, list):
            wiz_id = wiz_id[0]
        form = self.browse(cr, uid, wiz_id, context)

        if not form.line_ids:
            raise except_osv(_('Error'),
                             _('No credit control lines selected.'))

        line_ids = [l.id for l in form.line_ids]

        filtered_ids = self._filter_line_ids(cr, uid, line_ids, context)
        if not filtered_ids:
            raise except_osv(
                _('Information'),
                _('No lines will be changed. All the selected lines are already done.'
                  ))

        self._mark_lines(cr, uid, filtered_ids, form.name, context)

        return {
            'domain': unicode([('id', 'in', filtered_ids)]),
            'view_type': 'form',
            'view_mode': 'tree,form',
            'view_id': False,
            'res_model': 'credit.control.line',
            'type': 'ir.actions.act_window'
        }
class CreditControlPrinter(TransientModel):
    """Print lines"""

    _name = "credit.control.printer"
    _rec_name = 'id'
    _description = 'Mass printer'

    def _get_line_ids(self, cr, uid, context=None):
        if context is None:
            context = {}
        res = False
        if (context.get('active_model') == 'credit.control.line' and
                context.get('active_ids')):
            res = context['active_ids']
        return res

    _columns = {
        'mark_as_sent': fields.boolean('Mark letter lines as sent',
                                       help="Only letter lines will be marked."),
        'report_file': fields.binary('Generated Report', readonly=True),
        'state': fields.char('state', size=32),
        'line_ids': fields.many2many(
            'credit.control.line',
            string='Credit Control Lines'),
    }

    _defaults = {
        'mark_as_sent': True,
        'line_ids': _get_line_ids,
    }

    def _filter_line_ids(self, cr, uid, active_ids, context=None):
        """filter lines to use in the wizard"""
        line_obj = self.pool.get('credit.control.line')
        domain = [('state', '=', 'to_be_sent'),
                  ('id', 'in', active_ids),
                  ('channel', '=', 'letter')]
        return line_obj.search(cr, uid, domain, context=context)

    def print_lines(self, cr, uid, wiz_id, context=None):
        assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
                "wiz_id: only one id expected"
        comm_obj = self.pool.get('credit.control.communication')
        if isinstance(wiz_id, list):
            wiz_id = wiz_id[0]
        form = self.browse(cr, uid, wiz_id, context)

        if not form.line_ids and not form.print_all:
            raise except_osv(_('Error'), _('No credit control lines selected.'))

        line_ids = [l.id for l in form.line_ids]
        comms = comm_obj._generate_comm_from_credit_line_ids(
                cr, uid, line_ids, context=context)
        report_file = comm_obj._generate_report(cr, uid, comms, context=context)

        form.write({'report_file': base64.b64encode(report_file), 'state': 'done'})

        if form.mark_as_sent:
            filtered_ids = self._filter_line_ids(cr, uid, line_ids, context)
            comm_obj._mark_credit_line_as_sent(cr, uid, comms, context=context)

        return False  # do not close the window, we need it to download the report
Esempio n. 8
0
class CreditControlRun(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)]}),
        'report':
        fields.text('Report', readonly=True),
        'state':
        fields.selection([
            ('draft', 'Draft'),
            ('done', 'Done'),
        ],
                         string='State',
                         required=True,
                         readonly=True),
        '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 meantime, '
                  'it has to be handled manually'),
            readonly=True),
    }

    def _get_policies(self, cr, uid, context=None):
        return self.pool.get('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 using controlling_date"""
        line_obj = self.pool.get('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 except_osv(
                _('Error'),
                _('A run has already been executed more recently than %s') %
                (line.date))
        return True

    def _generate_credit_lines(self, cr, uid, run_id, context=None):
        """ 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]

        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
        if not policies:
            raise except_osv(_('Error'), _('Please select a policy'))

        report = ''
        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 = []
            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 += cr_line_obj.create_or_update_from_mv_lines(
                        cr,
                        uid, [],
                        list(level_lines),
                        level.id,
                        run.date,
                        context=context)

            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
            else:
                report += _(
                    "Policy \"%s\" has not generated any Credit Control Lines.\n"
                    % policy.name)

        vals = {
            'state': 'done',
            'report': report,
            'manual_ids': [(6, 0, manually_managed_lines)]
        }
        run.write(vals, context=context)

    def generate_credit_lines(self, cr, uid, run_id, context=None):
        """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')
        except Exception, exc:
            # in case of exception openerp will do a rollback for us and free the lock
            raise except_osv(
                _('Error'),
                _('A credit control run is already running'
                  ' in background, please try later.'), str(exc))

        self._generate_credit_lines(cr, uid, run_id, context)
        return True