コード例 #1
0
class account_invoice(Model):
    ''' Inherit account.invoice in order to change BVR ref field type '''
    _inherit = "account.invoice"

    _columns = {
        'bvr_reference': fields.char(
            "BVR REF.",
            size=32,
        )
    }

    def _check_bvr_ref(self, cr, uid, ids, context=None):
        record = self.browse(cr, uid, ids, context=context)
        for data in record:
            if not data.bvr_reference:
                return True  # No check if no reference
            clean_ref = data.bvr_reference.replace(' ', '')
            if not clean_ref.isdigit() or len(clean_ref) > 27:
                return False
            clean_ref = clean_ref.rjust(27, '0')  # Add zeros to the left
            if not clean_ref == mod10r(clean_ref[0:26]):
                return False

        return True

    _constraints = [
        (_check_bvr_ref,
         'Error: BVR ref should only contain number (max. 27) and spaces.',
         ['bvr_reference'])
    ]
コード例 #2
0
class AccountMoveLine(Model):

    _inherit = "account.move.line"

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

    _columns = {
        'transaction_ref': fields.char('Transaction Ref.', size=128),
    }

    def init(self, cr):
        cr.execute('UPDATE account_move_line SET transaction_ref = ref'
                   '  WHERE transaction_ref IS NULL'
                   '   AND ref IS NOT NULL')
        return True

    def get_bvr_ref(self, cursor, uid, move_line_id, context=None):
        """Retrieve ESR/BVR reference from move line in order to print it"""
        res = ''
        if isinstance(move_line_id, (tuple, list)):
            assert len(move_line_id) == 1, "Only 1 ID expected"
            move_line_id = move_line_id[0]
        move_line = self.browse(cursor, uid, move_line_id, context=context)
        ## We check if the type is bvr, if not we return false
        if move_line.invoice.partner_bank_id.state != 'bvr':
            return ''
        ##
        if move_line.invoice.partner_bank_id.bvr_adherent_num:
            res = move_line.invoice.partner_bank_id.bvr_adherent_num
        move_number = ''
        if move_line.invoice.number:
            move_number = self._compile_get_ref.sub(
                '',
                str(move_line.invoice.number) + str(move_line_id))
        return mod10r(res + move_number.rjust(26 - len(res), '0'))
コード例 #3
0
class AccountTaxCode(Model):
    """Inherit account tax code in order
    to add a Case code"""
    _name = 'account.tax.code'
    _inherit = "account.tax.code"
    _columns = {
        'code': fields.char('Case Code', size=512),
    }
コード例 #4
0
class AccountMoveLine(Model):

    _inherit = "account.move.line"

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

    _columns = {
        'transaction_ref': fields.char('Transaction Ref.', size=128),
    }

    def _get_bvr_amount(self, cr, uid, move, rtype=None):
        """Hook to get amount in CHF for BVR"""
        return move.debit

    def get_bvr_ref(self, cr, uid, move_line_id, context=None):
        """Retrieve ESR/BVR reference from move line in order to print it

        Returns False when no BVR reference should be generated.  No
        reference is generated when a transaction reference already
        exists for the line (likely been generated by a payment service).
        """
        if isinstance(move_line_id, (tuple, list)):
            assert len(move_line_id) == 1, "Only 1 ID expected"
            move_line_id = move_line_id[0]

        move_line = self.browse(cr, uid, move_line_id, context=context)

        if not self._is_generate_bvr(cr, uid, move_line.invoice, context):
            return ''

        reference = self._compute_bvr_ref(cr, uid, move_line, context)
        if (move_line.transaction_ref
                and move_line.transaction_ref != reference):
            # the line has already a transaction id and it is not
            # a BVR reference
            return ''
        return reference

    def _is_generate_bvr(self, cr, uid, invoice, context=None):
        ''' Determine if BVR should be generated or not. '''
        # We check if the type is bvr, if not we return false
        return (invoice.partner_bank_id
                and invoice.partner_bank_id.state == 'bvr')

    def _compute_bvr_ref(self, cr, uid, move_line, context=None):
        ''' Default BVR reference computation '''
        res = ''
        if move_line.invoice.partner_bank_id.bvr_adherent_num:
            res = move_line.invoice.partner_bank_id.bvr_adherent_num
        move_number = ''

        if move_line.invoice.number:
            compound = str(move_line.invoice.number) + str(move_line.id)
            move_number = self._compile_get_ref.sub('', compound)
        reference = mod10r(res + move_number.rjust(26 - len(res), '0'))
        return reference
コード例 #5
0
class InvoiceConditionText(Model):
    """add info condition in the invoice"""
    _name = "account.condition_text"
    _description = "Invoices conditions"

    _columns = {
        'name': fields.char('Condition summary', required=True, size=128),
        'type': fields.selection([('header', 'Top condition'),
                                  ('footer', 'Bottom condition')],
                                 'type', required=True),

        'text': fields.html('Condition', translate=True, required=True)}
コード例 #6
0
ファイル: invoice.py プロジェクト: elmerjc/barcode_and_extras
class AccountMoveLine(Model):

    _inherit = "account.move.line"

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

    _columns = {
        'transaction_ref': fields.char('Transaction Ref.', size=128),
    }

    def get_bvr_ref(self, cursor, uid, move_line_id, context=None):
        """Retrieve ESR/BVR reference from move line in order to print it

        Returns False when no BVR reference should be generated.  No
        reference is generated when a transaction reference already
        exists for the line (likely been generated by a payment service).
        """
        res = ''
        if isinstance(move_line_id, (tuple, list)):
            assert len(move_line_id) == 1, "Only 1 ID expected"
            move_line_id = move_line_id[0]
        move_line = self.browse(cursor, uid, move_line_id, context=context)
        ## We check if the type is bvr, if not we return false
        if move_line.invoice.partner_bank_id.state != 'bvr':
            return ''
        ##
        if move_line.invoice.partner_bank_id.bvr_adherent_num:
            res = move_line.invoice.partner_bank_id.bvr_adherent_num
        move_number = ''
        if move_line.invoice.number:
            move_number = self._compile_get_ref.sub(
                '',
                str(move_line.invoice.number) + str(move_line_id))
        reference = mod10r(res + move_number.rjust(26 - len(res), '0'))
        if (move_line.transaction_ref
                and move_line.transaction_ref != reference):
            # the line has already a transaction id and it is not
            # a BVR reference
            return ''
        return reference
コード例 #7
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
コード例 #8
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
コード例 #9
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',
            }
コード例 #10
0
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
コード例 #11
0
class ResPartner(Model):
    """Adds lastname and firstname, name become a stored function field"""

    _inherit = 'res.partner'

    def init(self, cursor):
        cursor.execute(
            'SELECT id FROM res_partner WHERE lastname IS NOT NULL Limit 1')
        if not cursor.fetchone():
            cursor.execute(
                'UPDATE res_partner set lastname = name WHERE name IS NOT NULL'
            )
            # Create Sql constraint if table is not empty
            cursor.execute('SELECT id FROM res_partner Limit 1')
            if cursor.fetchone():
                cursor.execute(
                    'ALTER TABLE res_partner ALTER COLUMN lastname SET NOT NULL'
                )

    def _prepare_name_custom(self, cursor, uid, partner, context=None):
        """
        This function is designed to be inherited in a custom module
        """
        names = (partner.lastname, partner.firstname)
        fullname = " ".join([s for s in names if s])
        return fullname

    def _compute_name_custom(self, cursor, uid, ids, fname, arg, context=None):
        res = {}
        for partner in self.browse(cursor, uid, ids, context=context):
            res[partner.id] = self._prepare_name_custom(cursor,
                                                        uid,
                                                        partner,
                                                        context=context)
        return res

    def _write_name(self,
                    cursor,
                    uid,
                    partner_id,
                    field_name,
                    field_value,
                    arg,
                    context=None):
        """
        Try to reverse the effect of _compute_name_custom:
        * if the partner is not a company and the firstname does not change in the new name
          then firstname remains untouched and lastname is updated accordingly
        * otherwise lastname=new name and firstname=False
        In addition an heuristic avoids to keep a firstname without a non-blank lastname
        """
        field_value = field_value and not field_value.isspace(
        ) and field_value or False
        vals = {'lastname': field_value, 'firstname': False}
        if field_value:
            flds = self.read(cursor,
                             uid, [partner_id], ['firstname', 'is_company'],
                             context=context)[0]
            if not flds['is_company']:
                to_check = ' %s' % flds['firstname']
                if field_value.endswith(to_check):
                    ln = field_value[:-len(to_check)].strip()
                    if ln:
                        vals['lastname'] = ln
                        del (vals['firstname'])
                    else:
                        # If the lastname is deleted from the new name
                        # then the firstname becomes the lastname
                        vals['lastname'] = flds['firstname']

        return self.write(cursor, uid, partner_id, vals, context=context)

    def copy_data(self, cr, uid, _id, default=None, context=None):
        """
        Avoid to replicate the firstname into the name when duplicating a partner
        """
        default = default or {}
        if not default.get('lastname'):
            default = default.copy()
            default['lastname'] = _('%s (copy)') % self.read(
                cr, uid, [_id], ['lastname'], context=context)[0]['lastname']
            if default.get('name'):
                del (default['name'])
        return super(ResPartner, self).copy_data(cr,
                                                 uid,
                                                 _id,
                                                 default,
                                                 context=context)

    def create(self, cursor, uid, vals, context=None):
        """
        To support data backward compatibility we have to keep this overwrite even if we
        use fnct_inv: otherwise we can't create entry because lastname is mandatory and module
        will not install if there is demo data
        """
        to_use = vals
        if 'name' in vals:
            corr_vals = vals.copy()
            corr_vals['lastname'] = corr_vals['name']
            del (corr_vals['name'])
            to_use = corr_vals
        return super(ResPartner, self).create(cursor,
                                              uid,
                                              to_use,
                                              context=context)

    _columns = {
        'name':
        fields.function(_compute_name_custom,
                        string="Name",
                        type="char",
                        store=True,
                        select=True,
                        readonly=True,
                        fnct_inv=_write_name),
        'firstname':
        fields.char("Firstname"),
        'lastname':
        fields.char("Lastname", required=True)
    }