コード例 #1
0
class UnrealizedCurrencyReportPrinter(TransientModel):
    _name = "unrealized.report.printer"

    _columns = {
        'chart_account_id':
        fields.many2one('account.account',
                        'Chart root',
                        domain=[('parent_id', '=', False)]),
        'period_id':
        fields.many2one('account.period', 'Period to use', required=True)
    }

    def print_report(self, cursor, uid, wid, data, context=None):
        context = context or {}
        # we update form with display account value
        if isinstance(wid, list):
            wid = wid[0]
        current = self.browse(cursor, uid, wid, context)
        form = {}
        form['period_id'] = current.period_id.id
        form['period_name'] = current.period_id.name
        form['account_ids'] = [current.chart_account_id.id]
        data['form'] = form
        print data
        return {
            'type': 'ir.actions.report.xml',
            'report_name': 'currency_unrealized',
            'datas': data
        }
コード例 #2
0
class StockLevelConfig(Model):
    _name = 'stock.level.configuration'
    _columns = {
        'stock_location_id':
        fields.many2one('stock.location', 'Location to use', required=True),
        'product_field':
        fields.many2one('ir.model.fields',
                        'Fields to use',
                        required=True,
                        help="Field of product form to sum")
    }
コード例 #3
0
class OrderpointTemplate(BaseProductConfigTemplate, Model):
    """ Template for orderpoints """
    _name = 'stock.warehouse.orderpoint.template'

    _inherit = 'stock.warehouse.orderpoint'
    _table = 'stock_warehouse_orderpoint_template'
    _clean_mode = 'deactivate'

    _columns = {
        'product_id':
        fields.many2one('product.product',
                        'Product',
                        required=False,
                        ondelete='cascade',
                        domain=[('type', '=', 'product')]),
    }

    def _get_ids_2_clean(self,
                         cursor,
                         uid,
                         template_br,
                         product_ids,
                         context=None):
        """ hook to select model specific objects to clean
        return must return a list of id"""
        model_obj = self._get_model()
        ids_to_del = model_obj.search(cursor, uid,
                                      [('product_id', 'in', product_ids)])
        return ids_to_del
コード例 #4
0
class AccountInvoice(Model):
    """Add a link to a credit control policy on account.account"""

    _inherit = "account.invoice"
    _columns = {
        'credit_policy_id':
        fields.many2one('credit.control.policy',
                        'Credit Control Policy',
                        help=("The Credit Control Policy "
                              "used for this invoice. "
                              "If nothing is defined, "
                              "it will use the account "
                              "setting or the partner "
                              "setting.")),
        'credit_control_line_ids':
        fields.one2many('credit.control.line',
                        'invoice_id',
                        string='Credit Lines',
                        readonly=True)
    }

    def action_move_create(self, cr, uid, ids, context=None):
        """ Write the id of the invoice in the generated moves. """
        res = super(AccountInvoice, self).action_move_create(cr,
                                                             uid,
                                                             ids,
                                                             context=context)
        for inv in self.browse(cr, uid, ids, context=context):
            if inv.move_id:
                for line in inv.move_id.line_id:
                    line.write({'invoice_id': inv.id})
        return res
コード例 #5
0
class AccountInvoice(Model):
    """ Add account.condition_text to invoice"""

    _inherit = "account.invoice"

    def _set_condition(self,
                       cr,
                       uid,
                       inv_id,
                       commentid,
                       key,
                       partner_id=False):
        """Set the text of the notes in invoices"""
        if not commentid:
            return {}
        if not partner_id:
            raise osv.except_osv(
                _('No Customer Defined !'),
                _('Before choosing condition text select a customer.'))
        lang = self.pool.get('res.partner').browse(cr, uid,
                                                   partner_id).lang or 'en_US'
        cond = self.pool.get('account.condition_text').browse(
            cr, uid, commentid, {'lang': lang})
        return {'value': {key: cond.text}}

    def set_header(self, cursor, uid, inv_id, commentid, partner_id=False):
        return self._set_condition(cursor, uid, inv_id, commentid, 'note1',
                                   partner_id)

    def set_footer(self, cursor, uid, inv_id, commentid, partner_id=False):
        return self._set_condition(cursor, uid, inv_id, commentid, 'note2',
                                   partner_id)

    _columns = {
        'text_condition1':
        fields.many2one('account.condition_text',
                        'Header condition',
                        domain=[('type', '=', 'header')]),
        'text_condition2':
        fields.many2one('account.condition_text',
                        'Footer condition',
                        domain=[('type', '=', 'footer')]),
        'note1':
        fields.html('Header'),
        'note2':
        fields.html('Footer'),
    }
コード例 #6
0
ファイル: partner.py プロジェクト: twanda/l10n-switzerland
class ResPartner(Model):
    _inherit = 'res.partner'

    _columns = {
        'supplier_invoice_default_product':
        fields.many2one(
            'product.product',
            'Default product supplier invoice',
            help="Use by the scan BVR wizard, if completed, it'll generate "
            "a line with the proper amount and this specified product"),
    }
コード例 #7
0
ファイル: invoice.py プロジェクト: alephobjects/ao-odoo
class AccountInvoice(Model):
    """ Add account.condition_text to invoice"""

    _inherit = "account.invoice"

    def _set_condition(self, cr, uid, inv_id, commentid, key):
        """Set the text of the notes in invoices"""
        if not commentid:
            return {}
        try:
            lang = self.browse(cr, uid, inv_id)[0].partner_id.lang
        except:
            lang = 'en_US'
        cond = self.pool.get('account.condition_text').browse(
            cr, uid, commentid, {'lang': lang})
        return {'value': {key: cond.text}}

    def set_header(self, cr, uid, inv_id, commentid):
        return self._set_condition(cr, uid, inv_id, commentid, 'note1')

    def set_footer(self, cr, uid, inv_id, commentid):
        return self._set_condition(cr, uid, inv_id, commentid, 'note2')

    _columns = {
        'text_condition1':
        fields.many2one('account.condition_text',
                        'Header condition',
                        domain=[('type', '=', 'header')]),
        'text_condition2':
        fields.many2one('account.condition_text',
                        'Footer condition',
                        domain=[('type', '=', 'footer')]),
        'note1':
        fields.html('Header'),
        'note2':
        fields.html('Footer'),
    }
コード例 #8
0
class ResCompany(Model):

    _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 overriden on partners or invoices.")),
    }

    _defaults = {"credit_control_tolerance": 0.1}
コード例 #9
0
class ResPartner(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',
                                             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)
    }
コード例 #10
0
class AccountInvoice(Model):
    """Inherit account.invoice in order to add bvr
    printing functionnalites. BVR is a Swiss payment vector"""
    _inherit = "account.invoice"

    _compile_get_ref = re.compile('[^0-9]')

    def _get_reference_type(self, cursor, user, context=None):
        """Function use by the function field reference_type in order to initalise available
        BVR Reference Types"""
        res = super(AccountInvoice, self)._get_reference_type(cursor,
                                                              user,
                                                              context=context)
        res.append(('bvr', 'BVR'))
        return res

    def _compute_full_bvr_name(self,
                               cursor,
                               uid,
                               ids,
                               field_names,
                               arg,
                               context=None):
        res = {}
        move_line_obj = self.pool.get('account.move.line')
        account_obj = self.pool.get('account.account')
        tier_account_id = account_obj.search(
            cursor, uid, [('type', 'in', ['receivable', 'payable'])])
        for inv in self.browse(cursor, uid, ids, context=context):
            move_lines = move_line_obj.search(
                cursor, uid, [('move_id', '=', inv.move_id.id),
                              ('account_id', 'in', tier_account_id)])
            if move_lines:
                if len(move_lines) == 1:
                    res[inv.id] = self._space(inv.get_bvr_ref())
                else:
                    refs = []
                    for move_line in move_line_obj.browse(cursor,
                                                          uid,
                                                          move_lines,
                                                          context=context):
                        refs.append(self._space(move_line.get_bvr_ref()))
                    res[inv.id] = ' ; '.join(refs)
        return res

    _columns = {
        ### BVR reference type BVR or FREE
        'reference_type':
        fields.selection(_get_reference_type, 'Reference Type', required=True),
        ### Partner bank link between bank and partner id
        'partner_bank_id':
        fields.many2one(
            'res.partner.bank',
            'Bank Account',
            help=
            'The partner bank account to pay\nKeep empty to use the default'),
        'bvr_reference':
        fields.function(_compute_full_bvr_name,
                        type="char",
                        size=512,
                        string="BVR REF.",
                        store=True,
                        readonly=True)
    }

    def get_bvr_ref(self, cursor, uid, inv_id, context=None):
        """Retrieve ESR/BVR reference form invoice in order to print it"""
        res = ''
        if isinstance(inv_id, list):
            inv_id = inv_id[0]
        inv = self.browse(cursor, uid, inv_id, context=context)
        ## We check if the type is bvr, if not we return false
        if inv.partner_bank_id.state != 'bvr':
            return ''
        ##
        if inv.partner_bank_id.bvr_adherent_num:
            res = inv.partner_bank_id.bvr_adherent_num
        invoice_number = ''
        if inv.number:
            invoice_number = self._compile_get_ref.sub('', inv.number)
        return mod10r(res + invoice_number.rjust(26 - len(res), '0'))

    def _space(self, nbr, nbrspc=5):
        """Spaces * 5.

        Example:
            self._space('123456789012345')
            '12 34567 89012 345'
        """
        return ''.join([' '[(i - 2) % nbrspc:] + c for i, c in enumerate(nbr)])

    def _update_ref_on_account_analytic_line(self,
                                             cr,
                                             uid,
                                             ref,
                                             move_id,
                                             context=None):
        cr.execute(
            'UPDATE account_analytic_line SET ref=%s'
            '   FROM account_move_line '
            ' WHERE account_move_line.move_id = %s '
            '   AND account_analytic_line.move_id = account_move_line.id',
            (ref, move_id))
        return True

    def action_number(self, cr, uid, ids, context=None):
        res = super(AccountInvoice, self).action_number(cr,
                                                        uid,
                                                        ids,
                                                        context=context)
        move_line_obj = self.pool.get('account.move.line')
        account_obj = self.pool.get('account.account')
        tier_account_id = account_obj.search(
            cr, uid, [('type', 'in', ['receivable', 'payable'])])

        for inv in self.browse(cr, uid, ids, context=context):
            if inv.type != 'out_invoice' and inv.partner_bank_id.state != 'bvr':
                continue
            move_lines = move_line_obj.search(
                cr, uid, [('move_id', '=', inv.move_id.id),
                          ('account_id', 'in', tier_account_id)])
            # We keep this branch for compatibility with single BVR report.
            # This should be cleaned when porting to V8
            if move_lines:
                if len(move_lines) == 1:
                    ref = inv.get_bvr_ref()
                    move_id = inv.move_id
                    if move_id:
                        cr.execute(
                            'UPDATE account_move_line SET transaction_ref=%s'
                            '  WHERE move_id=%s', (ref, move_id.id))
                        self._update_ref_on_account_analytic_line(
                            cr, uid, ref, move_id.id)
                else:
                    for move_line in move_line_obj.browse(cr,
                                                          uid,
                                                          move_lines,
                                                          context=context):
                        ref = move_line.get_bvr_ref()
                        if ref:
                            cr.execute(
                                'UPDATE account_move_line SET transaction_ref=%s'
                                '  WHERE id=%s', (ref, move_line.id))
                            self._update_ref_on_account_analytic_line(
                                cr, uid, ref, move_line.move_id.id)
        return res

    def copy(self, cursor, uid, inv_id, default=None, context=None):
        default = default or {}
        default.update({'reference': False})
        return super(AccountInvoice, self).copy(cursor, uid, inv_id, default,
                                                context)
コード例 #11
0
class CreditControlPolicyLevel(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

    _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'])
    ]

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

    # ----- sql time related methods ---------

    def _net_days_get_boundary(self):
        return " (mv_line.date_maturity + %(delay)s)::date <= date(%(controlling_date)s)"

    def _end_of_month_get_boundary(self):
        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):
        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, )
        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, ))

    # -----------------------------------------

    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()
        if not lines:
            return level_lines
        sql = (
            "SELECT DISTINCT mv_line.id\n"
            " FROM account_move_line mv_line\n"
            " WHERE mv_line.id in %(line_ids)s\n"
            " AND NOT EXISTS (SELECT id\n"
            "                 FROM credit_control_line\n"
            "                 WHERE move_line_id = mv_line.id\n"
            # lines from a previous level with a draft or ignored state
            # have to be generated again for the previous level
            "                 AND state not in ('draft', 'ignored'))")
        sql += " AND"
        sql += self._get_sql_date_boundary_for_computation_mode(
            cr, uid, level, controlling_date, context)
        data_dict = {
            'controlling_date': controlling_date,
            'line_ids': tuple(lines),
            'delay': level.delay_days
        }
        cr.execute(sql, data_dict)
        res = cr.fetchall()
        if res:
            level_lines.update([x[0] for x in res])
        return level_lines

    def _get_other_level_move_line_ids(self,
                                       cr,
                                       uid,
                                       level,
                                       controlling_date,
                                       lines,
                                       context=None):
        """ Retrieve the move lines for other levels than first level.
        """
        level_lines = set()
        if not lines:
            return level_lines
        sql = (
            "SELECT mv_line.id\n"
            " FROM account_move_line mv_line\n"
            " JOIN credit_control_line cr_line\n"
            " ON (mv_line.id = cr_line.move_line_id)\n"
            " 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'"
            "                              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
            # have to be generated again for the previous level
            " 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)
        data_dict = {
            'controlling_date': controlling_date,
            'line_ids': tuple(lines),
            'delay': level.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

    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
        else:
            method = self._get_other_level_move_line_ids
        matching_lines.update(
            method(cr, uid, level, controlling_date, lines, context=context))
        return matching_lines
コード例 #12
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
コード例 #13
0
ファイル: scan_bvr.py プロジェクト: twanda/l10n-switzerland
class scan_bvr(TransientModel):

    _name = "scan.bvr"
    _description = "BVR/ESR Scanning Wizard"

    _columns = {
        'journal_id': fields.many2one('account.journal',
                                      string="Invoice journal"),
        'bvr_string': fields.char(size=128,
                                  string='BVR String'),
        'partner_id': fields.many2one('res.partner',
                                      string="Partner"),
        'bank_account_id': fields.many2one('res.partner.bank',
                                           string="Partner Bank Account"),
        'state': fields.selection(
            [
                ('new', 'New'),
                ('valid', 'valid'),
                ('need_extra_info', 'Need extra information'),
            ],
            'State'
        ),
    }

    def _default_journal(self, cr, uid, context=None):
        pool = pooler.get_pool(cr.dbname)
        user = pool.get('res.users').browse(cr, uid, uid, context=context)
        if user.company_id:
            # We will get purchase journal linked with this company
            journal_ids = pool.get('account.journal').search(
                cr,
                uid,
                [('type', '=', 'purchase'),
                 ('company_id', '=', user.company_id.id)],
                context=context
            )
            if len(journal_ids) == 1:
                return journal_ids[0]
            else:
                return False
        else:
            return False

    _defaults = {
        'state': 'new',
        'journal_id': _default_journal,
    }

    def _check_number(self, part_validation):
        nTab = [0, 9, 4, 6, 8, 2, 7, 1, 3, 5]
        resultnumber = 0
        for number in part_validation:
            resultnumber = nTab[(resultnumber + int(number) - 0) % 10]
        return (10 - resultnumber) % 10

    def _construct_bvrplus_in_chf(self, bvr_string):

            if len(bvr_string) != 43:
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in first part')
                )
            elif self._check_number(bvr_string[0:2]) != int(bvr_string[2]):
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in second part')
                )
            elif self._check_number(bvr_string[4:30]) != int(bvr_string[30]):
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in third part')
                )
            elif self._check_number(bvr_string[33:41]) != int(bvr_string[41]):
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in fourth part')
                )
            else:
                bvr_struct = {
                    'type': bvr_string[0:2],
                    'amount': 0.0,
                    'reference': bvr_string[4:31],
                    'bvrnumber': bvr_string[4:10],
                    'beneficiaire': self._create_bvr_account(
                        bvr_string[33:42]
                    ),
                    'domain': '',
                    'currency': ''
                }
                return bvr_struct

    def _construct_bvr_in_chf(self, bvr_string):
            if len(bvr_string) != 53:
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in first part')
                )
            elif self._check_number(bvr_string[0:12]) != int(bvr_string[12]):
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in second part')
                )
            elif self._check_number(bvr_string[14:40]) != int(bvr_string[40]):
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in third part')
                )
            elif self._check_number(bvr_string[43:51]) != int(bvr_string[51]):
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in fourth part')
                )
            else:
                bvr_struct = {
                    'type': bvr_string[0:2],
                    'amount': float(bvr_string[2:12]) / 100,
                    'reference': bvr_string[14:41],
                    'bvrnumber': bvr_string[14:20],
                    'beneficiaire': self._create_bvr_account(
                        bvr_string[43:52]
                    ),
                    'domain': '',
                    'currency': ''
                }
                return bvr_struct

    def _construct_bvr_postal_in_chf(self, bvr_string):
            if len(bvr_string) != 42:
                raise orm.except_orm(
                    _('Validation Error'),
                    _('BVR CheckSum Error in first part')
                )
            else:

                bvr_struct = {
                    'type': bvr_string[0:2],
                    'amount': float(bvr_string[2:12]) / 100,
                    'reference': bvr_string[14:30],
                    'bvrnumber': '',
                    'beneficiaire': self._create_bvr_account(
                        bvr_string[32:41]
                    ),
                    'domain': '',
                    'currency': ''
                }
                return bvr_struct

    def _construct_bvr_postal_other_in_chf(self, bvr_string):
        if len(bvr_string) != 41:
            raise orm.except_orm(
                _('Validation Error'),
                _('BVR CheckSum Error in first part')
            )
        else:

            bvr_struct = {
                'type': bvr_string[0:2],
                'amount': float(bvr_string[7:16]) / 100,
                'reference': bvr_string[18:33],
                'bvrnumber': '000000',
                'beneficiaire': self._create_bvr_account(
                    bvr_string[34:40]
                ),
                'domain': '',
                'currency': ''
            }
            return bvr_struct

    def _create_invoice_line(self, cr, uid, ids, data, context):
            invoice_line_ids = False
            pool = pooler.get_pool(cr.dbname)
            invoice_line_obj = pool.get('account.invoice.line')
            # First we write partner_id
            self.write(cr, uid, ids, {'partner_id': data['partner_id']})
            # We check that this partner have a default product
            accounts_data = pool.get('res.partner').read(
                cr, uid,
                data['partner_id'],
                ['supplier_invoice_default_product'],
                context=context
            )
            if accounts_data['supplier_invoice_default_product']:
                product_onchange_result = invoice_line_obj.product_id_change(
                    cr, uid, ids,
                    accounts_data['supplier_invoice_default_product'][0],
                    uom_id=False,
                    qty=0,
                    name='',
                    type='in_invoice',
                    partner_id=data['partner_id'],
                    fposition_id=False,
                    price_unit=False,
                    currency_id=False,
                    context=context,
                    company_id=None
                )
                # We will check that the tax specified
                # on the product is price include or amount is 0
                if product_onchange_result['value']['invoice_line_tax_id']:
                    taxes = pool.get('account.tax').browse(
                        cr, uid,
                        product_onchange_result['value']['invoice_line_tax_id']
                    )
                    for taxe in taxes:
                        if not taxe.price_include and taxe.amount != 0.0:
                            raise orm.except_orm(
                                _('Error !'),
                                _('The default product in this partner has '
                                  'wrong taxes configuration')
                            )
                prod = accounts_data['supplier_invoice_default_product'][0]
                account = product_onchange_result['value']['account_id']
                taxes = product_onchange_result['value']['invoice_line_tax_id']
                invoice_line_vals = {
                    'product_id': prod,
                    'account_id': account,
                    'name': product_onchange_result['value']['name'],
                    'uos_id': product_onchange_result['value']['uos_id'],
                    'price_unit': data['bvr_struct']['amount'],
                    'invoice_id': data['invoice_id'],
                    'invoice_line_tax_id': [(6, 0, taxes)],
                }
                invoice_line_ids = invoice_line_obj.create(
                    cr, uid, invoice_line_vals, context=context)
            return invoice_line_ids

    def _create_direct_invoice(self, cr, uid, ids, data, context):
        pool = pooler.get_pool(cr.dbname)
        # We will call the function, that create invoice line
        account_invoice_obj = pool.get('account.invoice')
        account_invoice_tax_obj = pool.get('account.invoice.tax')
        if data['bank_account']:
            account_info = pool.get('res.partner.bank').browse(
                cr, uid, data['bank_account'],
                context=context
            )
        # We will now search the currency_id
        currency_search = pool.get('res.currency').search(
            cr, uid,
            [('name',
              '=',
              data['bvr_struct']['currency'])],
            context=context
        )
        currency_id = pool.get('res.currency').browse(cr, uid,
                                                      currency_search[0],
                                                      context=context)
        # Account Modification
        if data['bvr_struct']['domain'] == 'name':
            pool.get('res.partner.bank').write(
                cr, uid,
                data['bank_account'],
                {'post_number': data['bvr_struct']['beneficiaire']},
                context=context
            )
        else:
            pool.get('res.partner.bank').write(
                cr, uid,
                data['bank_account'],
                {'bvr_adherent_num': data['bvr_struct']['bvrnumber'],
                 'bvr_number': data['bvr_struct']['beneficiaire']},
                context=context
            )
        date_due = time.strftime('%Y-%m-%d')
        # We will now compute the due date and fixe the payment term
        payment_term_id = (account_info.partner_id.property_payment_term and
                           account_info.partner_id.property_payment_term.id or
                           False)
        if payment_term_id:
            # We Calculate due_date
            inv_mod = pool.get('account.invoice')
            res = inv_mod.onchange_payment_term_date_invoice(
                cr, uid, [],
                payment_term_id,
                time.strftime('%Y-%m-%d')
            )
            date_due = res['value']['date_due']

        curr_invoice = {
            'name': time.strftime('%Y-%m-%d'),
            'partner_id': account_info.partner_id.id,
            'account_id': account_info.partner_id.property_account_payable.id,
            'date_due': date_due,
            'date_invoice': time.strftime('%Y-%m-%d'),
            'payment_term': payment_term_id,
            'reference_type': 'bvr',
            'reference': data['bvr_struct']['reference'],
            'amount_total': data['bvr_struct']['amount'],
            'check_total': data['bvr_struct']['amount'],
            'partner_bank_id': account_info.id,
            'comment': '',
            'currency_id': currency_id.id,
            'journal_id': data['journal_id'],
            'type': 'in_invoice',
        }

        last_invoice = account_invoice_obj.create(
            cr, uid,
            curr_invoice,
            context=context
        )
        data['invoice_id'] = last_invoice
        self._create_invoice_line(cr, uid, ids, data, context)
        # Now we create taxes lines
        computed_tax = account_invoice_tax_obj.compute(cr,
                                                       uid,
                                                       last_invoice,
                                                       context=context)
        inv = account_invoice_obj.browse(cr,
                                         uid,
                                         last_invoice,
                                         context=context)
        account_invoice_obj.check_tax_lines(cr,
                                            uid,
                                            inv,
                                            computed_tax,
                                            account_invoice_tax_obj)
        action = {
            'domain': "[('id','=', " + str(last_invoice) + ")]",
            'name': 'Invoices',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'account.invoice',
            'view_id': False,
            'context': "{'type':'out_invoice'}",
            'type': 'ir.actions.act_window',
            'res_id': last_invoice
        }
        return action

    def _create_bvr_account(self, account_unformated):
        acc_len = len(account_unformated)
        account_formated = "%s-%s-%s" % (
            account_unformated[0:2],
            str(int(account_unformated[2:acc_len - 1])),
            account_unformated[acc_len - 1:acc_len]
        )
        return account_formated

    def _get_bvr_structurated(self, bvr_string):
        if bvr_string is not False:
            # We will get the 2 frist digit of the BVr string in order
            # to now the BVR type of this account
            bvr_type = bvr_string[0:2]
            if bvr_type == '01' and len(bvr_string) == 42:
                # This BVR is the type of BVR in CHF
                # WE will call the function and Call
                bvr_struct = self._construct_bvr_postal_in_chf(bvr_string)
                # We will test if the BVR have an Adherent Number if not we
                # will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'CHF'
            elif bvr_type == '01':
                # This BVR is the type of BVR in CHF
                # We will call the function and Call
                bvr_struct = self._construct_bvr_in_chf(bvr_string)
                # We will test if the BVR have an Adherent Number if not
                # we will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'CHF'
            elif bvr_type == '03':
                # It will be (At this time) the same work
                # as for a standard BVR with 01 code
                bvr_struct = self._construct_bvr_postal_in_chf(bvr_string)
                # We will test if the BVR have an Adherent Number
                # if not we will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'CHF'
            elif bvr_type == '04':
                # It the BVR postal in CHF
                bvr_struct = self._construct_bvrplus_in_chf(bvr_string)
                # We will test if the BVR have an Adherent Number
                # if not we will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'CHF'
            elif bvr_type == '21':
                # It for a BVR in Euro
                bvr_struct = self._construct_bvr_in_chf(bvr_string)
                # We will test if the BVR have an Adherent Number if
                # not we will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'EUR'
            ##
            elif bvr_type == '31':
                # It the BVR postal in CHF
                bvr_struct = self._construct_bvrplus_in_chf(bvr_string)
                # We will test if the BVR have an Adherent Number if not
                # we will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'EUR'

            elif bvr_type[0:1] == '<' and len(bvr_string) == 41:
                # It the BVR postal in CHF
                bvr_struct = self._construct_bvr_postal_other_in_chf(
                    bvr_string)
                # We will test if the BVR have an Adherent Number
                # if not we will make the search of the account base on
                # his name non base on the BVR adherent number
                if (bvr_struct['bvrnumber'] == '000000'):
                    bvr_struct['domain'] = 'name'
                else:
                    bvr_struct['domain'] = 'bvr_adherent_num'
                # We will set the currency , in this case it's allways CHF
                bvr_struct['currency'] = 'CHF'
            else:
                raise orm.except_orm(_('BVR Type error'),
                                     _('This kind of BVR is not supported '
                                       'at this time'))
            return bvr_struct

    def validate_bvr_string(self, cr, uid, ids, context):
        # We will now retrive result
        bvr_data = self.browse(cr, uid, ids, context)[0]
        # BVR Standrard
        # 0100003949753>120000000000234478943216899+ 010001628>
        # BVR without BVr Reference
        # 0100000229509>000000013052001000111870316+ 010618955>
        # BVR + In CHF
        # 042>904370000000000000007078109+ 010037882>
        # BVR In euro
        # 2100000440001>961116900000006600000009284+ 030001625>
        # <060001000313795> 110880150449186+ 43435>
        # <010001000165865> 951050156515104+ 43435>
        # <010001000060190> 052550152684006+ 43435>
        #
        # Explode and check  the BVR Number and structurate it
        #
        data = {}
        data['bvr_struct'] = self._get_bvr_structurated(
            bvr_data.bvr_string)
        # We will now search the account linked with this BVR
        if data['bvr_struct']['domain'] == 'name':
            domain = [('acc_number', '=', data['bvr_struct']['beneficiaire'])]
            partner_bank_search = self.pool.get('res.partner.bank').search(
                cr,
                uid,
                domain,
                context=context
            )
        else:
            domain = [
                ('bvr_adherent_num', '=', data['bvr_struct']['bvrnumber'])
            ]
            partner_bank_search = self.pool.get('res.partner.bank').search(
                cr,
                uid,
                domain,
                context=context
            )
        # We will need to know if we need to create invoice line
        if partner_bank_search:
            # We have found the account corresponding to the
            # bvr_adhreent_number
            # so we can directly create the account
            partner_bank_result = self.pool.get('res.partner.bank').browse(
                cr,
                uid,
                partner_bank_search[0],
                context=context
            )
            data['id'] = bvr_data.id
            data['partner_id'] = partner_bank_result.partner_id.id
            data['bank_account'] = partner_bank_result.id
            data['journal_id'] = bvr_data.journal_id.id
            action = self._create_direct_invoice(cr, uid, ids, data, context)
            return action
        elif bvr_data.bank_account_id:
            data['id'] = bvr_data.id
            data['partner_id'] = bvr_data.partner_id.id
            data['journal_id'] = bvr_data.journal_id.id
            data['bank_account'] = bvr_data.bank_account_id.id
            action = self._create_direct_invoice(cr, uid, ids, data, context)
            return action
        else:
            # we haven't found a valid bvr_adherent_number
            # we will need to create or update a bank account
            self.write(cr, uid, ids, {'state': 'need_extra_info'})
            return {
                'type': 'ir.actions.act_window',
                'res_model': 'scan.bvr',
                'view_mode': 'form',
                'view_type': 'form',
                'res_id': bvr_data.id,
                'views': [(False, 'form')],
                'target': 'new',
            }
コード例 #14
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
コード例 #15
0
class AccountMoveLine(Model):

    _inherit = "account.move.line"

    _columns = {'invoice_id': fields.many2one('account.invoice', 'Invoice')}
コード例 #16
0
class CreditControlLine(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,
    print, ...)
    """

    _name = "credit.control.line"
    _description = "A credit control line"
    _rec_name = "id"

    _columns = {
        'date':
        fields.date('Controlling date', required=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_sent':
        fields.date('Sent date',
                    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,
                         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.")),
        'channel':
        fields.selection([('letter', 'Letter'), ('email', 'Email')],
                         'Channel',
                         required=True,
                         readonly=True,
                         states={'draft': [('readonly', False)]}),
        'invoice_id':
        fields.many2one('account.invoice', 'Invoice', readonly=True),
        'partner_id':
        fields.many2one('res.partner', "Partner", required=True),
        'amount_due':
        fields.float('Due Amount Tax incl.', required=True, readonly=True),
        'balance_due':
        fields.float('Due balance', required=True, readonly=True),
        'mail_message_id':
        fields.many2one('mail.message', 'Sent Email', readonly=True),
        'move_line_id':
        fields.many2one('account.move.line',
                        'Move line',
                        required=True,
                        readonly=True),
        'account_id':
        fields.related('move_line_id',
                       'account_id',
                       type='many2one',
                       relation='account.account',
                       string='Account',
                       store=True,
                       readonly=True),
        'currency_id':
        fields.related('move_line_id',
                       'currency_id',
                       type='many2one',
                       relation='res.currency',
                       string='Currency',
                       store=True,
                       readonly=True),
        'company_id':
        fields.related('move_line_id',
                       'company_id',
                       type='many2one',
                       relation='res.company',
                       string='Company',
                       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)]}),
        'policy_id':
        fields.related('policy_level_id',
                       'policy_id',
                       type='many2one',
                       relation='credit.control.policy',
                       string='Policy',
                       store=True,
                       readonly=True),
        'level':
        fields.related('policy_level_id',
                       'level',
                       type='integer',
                       relation='credit.control.policy',
                       string='Level',
                       store=True,
                       readonly=True),
    }

    _defaults = {'state': 'draft'}

    def _prepare_from_move_line(self,
                                cr,
                                uid,
                                move_line,
                                level,
                                controlling_date,
                                open_amount,
                                context=None):
        """Create credit control line"""
        data = {}
        data['date'] = controlling_date
        data['date_due'] = move_line.date_maturity
        data['state'] = 'draft'
        data['channel'] = level.channel
        data[
            'invoice_id'] = move_line.invoice_id.id if move_line.invoice_id else False
        data['partner_id'] = move_line.partner_id.id
        data['amount_due'] = (move_line.amount_currency or move_line.debit
                              or move_line.credit)
        data['balance_due'] = open_amount
        data['policy_level_id'] = level.id
        data['company_id'] = move_line.company_id.id
        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,
                                       context=None):
        """Create or update line based on levels"""
        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)

        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)

        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

            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)

        return line_ids

    def unlink(self, cr, uid, ids, context=None, check=True):
        for line in self.browse(cr, uid, ids, context=context):
            if line.state != 'draft':
                raise osv.except_osv(
                    _('Error !'),
                    _('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)
コード例 #17
0
class AccountInvoice(Model):
    """Inherit account.invoice in order to add bvr
    printing functionnalites. BVR is a Swiss payment vector"""
    _inherit = "account.invoice"

    _compile_get_ref = re.compile('[^0-9]')

    def _get_reference_type(self, cr, user, context=None):
        """Function used by the function field 'reference_type'
        in order to initalise available BVR Reference Types
        """
        res = super(AccountInvoice, self)._get_reference_type(cr,
                                                              user,
                                                              context=context)
        res.append(('bvr', 'BVR'))
        return res

    def _compute_full_bvr_name(self,
                               cr,
                               uid,
                               ids,
                               field_names,
                               arg,
                               context=None):
        res = {}
        move_line_obj = self.pool.get('account.move.line')
        account_obj = self.pool.get('account.account')
        tier_account_id = account_obj.search(
            cr,
            uid, [('type', 'in', ['receivable', 'payable'])],
            context=context)
        for inv in self.browse(cr, uid, ids, context=context):
            move_lines = move_line_obj.search(
                cr,
                uid, [('move_id', '=', inv.move_id.id),
                      ('account_id', 'in', tier_account_id)],
                context=context)
            if move_lines:
                refs = []
                for move_line in move_line_obj.browse(cr,
                                                      uid,
                                                      move_lines,
                                                      context=context):
                    refs.append(AccountInvoice._space(move_line.get_bvr_ref()))
                res[inv.id] = ' ; '.join(refs)
        return res

    _columns = {
        # BVR reference type BVR or FREE
        'reference_type':
        fields.selection(_get_reference_type, 'Reference Type', required=True),

        # Partner bank link between bank and partner id
        'partner_bank_id':
        fields.many2one('res.partner.bank',
                        'Bank Account',
                        help='The partner bank account to pay\n'
                        'Keep empty to use the default'),
        'bvr_reference':
        fields.function(_compute_full_bvr_name,
                        type="char",
                        size=512,
                        string="BVR REF.",
                        store=True,
                        readonly=True)
    }

    @staticmethod
    def _space(nbr, nbrspc=5):
        """Spaces * 5.

        Example:
            AccountInvoice._space('123456789012345')
            '12 34567 89012 345'
        """
        return ''.join([' '[(i - 2) % nbrspc:] + c for i, c in enumerate(nbr)])

    def _update_ref_on_account_analytic_line(self,
                                             cr,
                                             uid,
                                             ref,
                                             move_id,
                                             context=None):
        """Propagate reference on analytic line"""
        cr.execute(
            'UPDATE account_analytic_line SET ref=%s'
            '   FROM account_move_line '
            ' WHERE account_move_line.move_id = %s '
            '   AND account_analytic_line.move_id = account_move_line.id',
            (ref, move_id))
        return True

    def _action_bvr_number_move_line(self,
                                     cr,
                                     uid,
                                     invoice,
                                     move_line,
                                     ref,
                                     context=None):
        """Propagate reference on move lines and analytic lines"""
        if not ref:
            return
        cr.execute(
            'UPDATE account_move_line SET transaction_ref=%s'
            '  WHERE id=%s', (ref, move_line.id))
        self._update_ref_on_account_analytic_line(cr, uid, ref,
                                                  move_line.move_id.id)

    def action_number(self, cr, uid, ids, context=None):
        """ Copy the BVR/ESR reference in the transaction_ref of move lines.

        For customers invoices: the BVR reference is computed using
        ``get_bvr_ref()`` on the invoice or move lines.

        For suppliers invoices: the BVR reference is stored in the reference
        field of the invoice.

        """
        res = super(AccountInvoice, self).action_number(cr,
                                                        uid,
                                                        ids,
                                                        context=context)
        move_line_obj = self.pool.get('account.move.line')

        for inv in self.browse(cr, uid, ids, context=context):
            move_line_ids = move_line_obj.search(
                cr,
                uid, [('move_id', '=', inv.move_id.id),
                      ('account_id', '=', inv.account_id.id)],
                context=context)
            if not move_line_ids:
                continue
            move_lines = move_line_obj.browse(cr,
                                              uid,
                                              move_line_ids,
                                              context=context)
            for move_line in move_lines:
                if inv.type in ('out_invoice', 'out_refund'):
                    ref = move_line.get_bvr_ref()
                elif inv.reference_type == 'bvr' and inv.reference:
                    ref = inv.reference
                else:
                    ref = False
                self._action_bvr_number_move_line(cr,
                                                  uid,
                                                  inv,
                                                  move_line,
                                                  ref,
                                                  context=context)
        return res

    def copy(self, cr, uid, inv_id, default=None, context=None):
        default = default or {}
        default.update({'reference': False})
        return super(AccountInvoice, self).copy(cr, uid, inv_id, default,
                                                context)