Example #1
0
 def _parse_line_1(self, line, statement):
     # Statement details
     if statement['version'] == '1':
         statement['acc_number'] = sanitize_account_number(line[5:17])
         statement['currency'] = rmspaces(line[18:21])
     elif statement['version'] == '2':
         if line[1] == '0':  # Belgian bank account BBAN structure
             statement['acc_number'] = sanitize_account_number(line[5:17])
             statement['currency'] = rmspaces(line[18:21])
         elif line[1] == '1':  # foreign bank account BBAN structure
             raise ValidationError(
                 _('Error R1001: Foreign bank accounts with BBAN '
                   'structure are not supported'))
         elif line[1] == '2':  # Belgian bank account IBAN structure
             statement['acc_number'] = sanitize_account_number(line[5:21])
             statement['currency'] = rmspaces(line[39:42])
         elif line[1] == '3':  # foreign bank account IBAN structure
             raise ValidationError(
                 _('Error R1002: Foreign bank accounts with IBAN structure '
                   'are not supported'))
         else:  # Something else, not supported
             raise ValidationError(
                 _('Error R1003: Unsupported bank account structure'))
     statement['description'] = rmspaces(line[90:125])
     statement['balance_start'] = float(rmspaces(line[43:58])) / 1000
     if line[42] == '1':  # 1 = Debit, the starting balance is negative
         statement['balance_start'] = -statement['balance_start']
     statement['balance_start_date'] = time.strftime(
         tools.DEFAULT_SERVER_DATE_FORMAT,
         time.strptime(rmspaces(line[58:64]), '%d%m%y'))
     statement['accountHolder'] = rmspaces(line[64:90])
     statement['paperSeqNumber'] = rmspaces(line[2:5])
     statement['codaSeqNumber'] = rmspaces(line[125:128])
Example #2
0
 def _parse_line_23(self, line, statement):
     if statement['lines'][-1]['ref'][0:4] != line[2:6]:
         raise ValidationError(
             _('CODA parsing error on movement data record 2.3, seq nr %s!'
               'Please report this issue via your Odoo support channel.') %
             line[2:10])
     if statement['version'] == '1':
         statement['lines'][-1][
             'counterpartyNumber'] = sanitize_account_number(line[10:22])
         statement['lines'][-1]['counterpartyName'] = rmspaces(line[47:73])
         statement['lines'][-1]['counterpartyAddress'] = rmspaces(
             line[73:125])
         statement['lines'][-1]['counterpartyCurrency'] = ''
     else:
         if line[22] == ' ':
             statement['lines'][-1][
                 'counterpartyNumber'] = sanitize_account_number(
                     line[10:22])
             statement['lines'][-1]['counterpartyCurrency'] = rmspaces(
                 line[23:26])
         else:
             statement['lines'][-1][
                 'counterpartyNumber'] = sanitize_account_number(
                     line[10:44])
             statement['lines'][-1]['counterpartyCurrency'] = rmspaces(
                 line[44:47])
         statement['lines'][-1]['counterpartyName'] = rmspaces(line[47:82])
         join_comm(statement['lines'][-1], rmspaces(line[82:125]))
    def _complete_stmts_vals(self, stmts_vals, journal, account_number):
        for st_vals in stmts_vals:
            st_vals['journal_id'] = journal.id
            if not st_vals.get('reference'):
                st_vals['reference'] = self.filename
            if st_vals.get('number'):
                #build the full name like BNK/2016/00135 by just giving the number '135'
                st_vals['name'] = journal.sequence_id.with_context(ir_sequence_date=st_vals.get('date')).get_next_char(st_vals['number'])
                del(st_vals['number'])
            for line_vals in st_vals['transactions']:
                unique_import_id = line_vals.get('unique_import_id')
                if unique_import_id:
                    sanitized_account_number = sanitize_account_number(account_number)
                    line_vals['unique_import_id'] = (sanitized_account_number and sanitized_account_number + '-' or '') + str(journal.id) + '-' + unique_import_id

                if not line_vals.get('bank_account_id'):
                    # Find the partner and his bank account or create the bank account. The partner selected during the
                    # reconciliation process will be linked to the bank when the statement is closed.
                    partner_id = False
                    bank_account_id = False
                    identifying_string = line_vals.get('account_number')
                    if identifying_string:
                        partner_bank = self.env['res.partner.bank'].search([('acc_number', '=', identifying_string)], limit=1)
                        if partner_bank:
                            bank_account_id = partner_bank.id
                            partner_id = partner_bank.partner_id.id
                        else:
                            bank_account_id = self.env['res.partner.bank'].create({'acc_number': line_vals['account_number']}).id
                    line_vals['partner_id'] = partner_id
                    line_vals['bank_account_id'] = bank_account_id

        return stmts_vals
Example #4
0
 def _compute_sanitized_acc_number(self):
     for bank_account in self:
         if bank_account.bank_id:
             acc_number_format = bank_account.bank_id.acc_number_format \
                 or '%(acc_number)s'
             args = {
                 'bra_number': bank_account.bra_number or '',
                 'bra_number_dig': bank_account.bra_number_dig or '',
                 'acc_number': bank_account.acc_number or '',
                 'acc_number_dig': bank_account.acc_number_dig or ''
             }
             self.sanitized_acc_number = sanitize_account_number(
                 acc_number_format % args)
         else:
             self.sanitized_acc_number = sanitize_account_number(
                 bank_account.acc_number)
    def _find_additional_data(self, currency_code, account_number):
        """ Look for a res.currency and account.journal using values extracted from the
            statement and make sure it's consistent.
        """
        company_currency = self.env.user.company_id.currency_id
        journal_obj = self.env['account.journal']
        currency = None
        sanitized_account_number = sanitize_account_number(account_number)

        if currency_code:
            currency = self.env['res.currency'].search(
                [('name', '=ilike', currency_code)], limit=1)
            if not currency:
                raise UserError(
                    _("No currency found matching '%s'.") % currency_code)
            if currency == company_currency:
                currency = False

        journal = journal_obj.browse(self.env.context.get('journal_id', []))
        if account_number:
            # No bank account on the journal : create one from the account number of the statement
            if journal and not journal.bank_account_id:
                journal.set_bank_account(account_number)
            # No journal passed to the wizard : try to find one using the account number of the statement
            elif not journal:
                journal = journal_obj.search([
                    ('bank_account_id.sanitized_acc_number', '=',
                     sanitized_account_number)
                ])
            # Already a bank account on the journal : check it's the same as on the statement
            else:
                if not self._check_journal_bank_account(
                        journal, sanitized_account_number):
                    raise UserError(
                        _('The account of this statement (%s) is not the same as the journal (%s).'
                          ) %
                        (account_number, journal.bank_account_id.acc_number))

        # If importing into an existing journal, its currency must be the same as the bank statement
        if journal:
            journal_currency = journal.currency_id
            if currency is None:
                currency = journal_currency
            if currency and currency != journal_currency:
                statement_cur_code = not currency and company_currency.name or currency.name
                journal_cur_code = not journal_currency and company_currency.name or journal_currency.name
                raise UserError(
                    _('The currency of the bank statement (%s) is not the same as the currency of the journal (%s) !'
                      ) % (statement_cur_code, journal_cur_code))

        # If we couldn't find / can't create a journal, everything is lost
        if not journal and not account_number:
            raise UserError(
                _('Cannot find in which journal import this statement. Please manually select a journal.'
                  ))

        return currency, journal
Example #6
0
 def _find_additional_data(self, currency_code, account_number):
     currency, journal = super()._find_additional_data(
         currency_code, account_number)
     if not journal:
         sanitized_account_number = sanitize_account_number(account_number)
         fin_journals = self.env['account.journal'].search([('type', '=',
                                                             'bank')])
         fin_journal = fin_journals.filtered(
             lambda r: sanitized_account_number in r.bank_account_id.
             sanitized_acc_number)
         if len(fin_journal) == 1:
             journal = fin_journal
     return currency, journal
 def _compute_sanitized_acc_number(self):
     self.ensure_one()
     if self.bank_id and self.bank_id.acc_number_format:
         acc_number_format = self.bank_id.acc_number_format\
             or '%(acc_number)s'
         args = {
             'bra_number': self.bra_number or '',
             'bra_number_dig': self.bra_number_dig or '',
             'acc_number': self.acc_number or '',
             'acc_number_dig': self.acc_number_dig or ''
         }
         self.sanitized_acc_number = sanitize_account_number(
             acc_number_format % args)
     else:
         super(ResPartnerBank, self)._compute_sanitized_acc_number()
    def _find_additional_data(self, currency_code, account_number):
        """ Look for a res.currency and account.journal using values extracted from the
            statement and make sure it's consistent.
        """
        company_currency = self.env.user.company_id.currency_id
        journal_obj = self.env['account.journal']
        currency = None
        sanitized_account_number = sanitize_account_number(account_number)

        if currency_code:
            currency = self.env['res.currency'].search([('name', '=ilike', currency_code)], limit=1)
            if not currency:
                raise UserError(_("No currency found matching '%s'.") % currency_code)
            if currency == company_currency:
                currency = False

        journal = journal_obj.browse(self.env.context.get('journal_id', []))
        if account_number:
            # No bank account on the journal : create one from the account number of the statement
            if journal and not journal.bank_account_id:
                journal.set_bank_account(account_number)
            # No journal passed to the wizard : try to find one using the account number of the statement
            elif not journal:
                journal = journal_obj.search([('bank_account_id.sanitized_acc_number', '=', sanitized_account_number)])
            # Already a bank account on the journal : check it's the same as on the statement
            else:
                if not self._check_journal_bank_account(journal, sanitized_account_number):
                    raise UserError(_('The account of this statement (%s) is not the same as the journal (%s).') % (account_number, journal.bank_account_id.acc_number))

        # If importing into an existing journal, its currency must be the same as the bank statement
        if journal:
            journal_currency = journal.currency_id
            if currency is None:
                currency = journal_currency
            if currency and currency != journal_currency:
                statement_cur_code = not currency and company_currency.name or currency.name
                journal_cur_code = not journal_currency and company_currency.name or journal_currency.name
                raise UserError(_('The currency of the bank statement (%s) is not the same as the currency of the journal (%s) !') % (statement_cur_code, journal_cur_code))

        # If we couldn't find / can't create a journal, everything is lost
        if not journal and not account_number:
            raise UserError(_('Cannot find in which journal import this statement. Please manually select a journal.'))

        return currency, journal
Example #9
0
 def create_journal(self):
     print '_c_saltedge_transactions'
     account_number = sanitize_account_number(self.synchronise_account_name)
     """ Calls a wizard that allows the user to carry on with journal creation """
     return {
         'name': 'Journal Creation',
         'type': 'ir.actions.act_window',
         # 'res_model': 'account.bank.statement.import.journal.creation',
         'res_model': 'account.journal',
         'view_type': 'form',
         'view_mode': 'form',
         'target': 'new',
         'context': {
             # 'statement_import_transient_id': self.ids[0],
             'default_bank_acc_number': account_number,
             'default_name': 'Bank' + ' ' + account_number,
             'default_currency_id': False,
             'default_type': 'bank',
         }
     }
Example #10
0
    def _complete_stmts_vals(self, stmts_vals, journal, account_number):
        ResPB = self.env['res.partner.bank']
        for st_vals in stmts_vals:
            st_vals['journal_id'] = journal.id

            for line_vals in st_vals['transactions']:
                unique_import_id = line_vals.get('unique_import_id')
                if unique_import_id:
                    sanitized_account_number = sanitize_account_number(
                        account_number)
                    line_vals['unique_import_id'] = \
                        (sanitized_account_number and
                            sanitized_account_number + '-' or '') + \
                        str(journal.id) + '-' + unique_import_id

                if not line_vals.get('bank_account_id'):
                    # Find the partner and his bank account or create
                    # the bank account. The partner selected during the
                    # reconciliation process will be linked to the bank
                    # when the statement is closed.
                    partner_id = False
                    bank_account_id = False
                    identifying_string = line_vals.get('account_number')
                    if identifying_string:
                        partner_bank = ResPB.search(
                            [('acc_number', '=', identifying_string)], limit=1)
                        if partner_bank:
                            bank_account_id = partner_bank.id
                            partner_id = partner_bank.partner_id.id
                        else:
                            bank_account_id = ResPB.create({
                                'acc_number':
                                line_vals['account_number']
                            }).id
                    line_vals['partner_id'] = partner_id
                    line_vals['bank_account_id'] = bank_account_id

        return stmts_vals
 def _sanitize_bank_account_number(self, bank_account_number):
     """Hook for extension"""
     self.ensure_one()
     return sanitize_account_number(bank_account_number)
    def initialize_refresh(self):
        try:
            self.synchronise_item.write({'synchronise_error': ''})
            app = SaltEdge(self.settings.settings_client_id,
                           self.settings.settings_app_id,
                           self.settings.settings_service_secret)
            inactive_accounts = self._get_inactive_accounts()
            payload = json.dumps({
                'data': {
                    "fetch_scopes": ["accounts", "transactions"],
                    "exclude_accounts": inactive_accounts
                }
            })
            payload = self.__add_unique_id(
                payload, self.settings.settings_environment_url)

            active_accounts = self.synchronise_item.env[
                'fit.saltedge.account'].search(
                    [['account_status', '=', 'Active']], limit=1)
            if len(active_accounts) == 0:
                stage = self.synchronise_item.env[
                    'fit.saltedge.synchronise.stage'].sudo().search(
                        [('synchronise_stage_id', '=', 'done')], limit=1)
                self.synchronise_item.write({
                    'synchronise_status': 'No active accounts',
                    'synchronise_state': stage.id
                })
            else:
                _m_active_account = active_accounts[0]
                print _m_active_account.account_name
                print _m_active_account.account_status
                print _m_active_account.account_id
                print _m_active_account.account_login_id
                print _m_active_account.account_latest_sync
                synchronise_status = str(fields.Datetime.now(
                )) + ' Initializing account refresh, please wait.'
                self.synchronise_item.write({
                    'synchronise_account_name':
                    _m_active_account.account_name,
                    'synchronise_account_id':
                    _m_active_account.account_id,
                    'synchronise_account':
                    _m_active_account.id,
                    'synchronise_client_id':
                    self.settings.settings_client_id,
                    'synchronise_customer_id':
                    self.settings.settings_customer_id,
                    'synchronise_login_id':
                    _m_active_account.account_login_id,
                    'synchronise_status':
                    synchronise_status
                })

                _account_number = sanitize_account_number(
                    _m_active_account.account_name)
                currency, journal = self.synchronise_item._find_additional_data(
                    self.currency_code, _account_number)
                if not journal:
                    self.synchronise_item.write({
                        'synchronise_journal_fail':
                        True,
                        'synchronise_error':
                        _('Journal for bank account: %s not available, please create journal '
                          'first.') % (_account_number)
                    })
                    return False

                payload = self._add_from_and_to_date(payload,
                                                     _m_active_account)
                response = app.put(
                    'https://www.saltedge.com/api/v4/logins/' +
                    str(_m_active_account.account_login_id) + '/refresh',
                    payload)

                synchronise_status = str(
                    self.synchronise_item.synchronise_status)
                synchronise_status += '\n' + str(fields.Datetime.now())

                if response.status_code == 200:
                    _logger.info('Login refresh success: ' +
                                 str(response.content))
                    synchronise_status += ' Successfully initialized refresh, waiting for response from bank.'
                    self.synchronise_item.write(
                        {'synchronise_status': synchronise_status})
                else:
                    synchronise_status += ' Error while initializing refresh.'
                    synchronise_error = 'ERROR:  \n'
                    synchronise_error += json.loads(
                        response.text)[u'error_message']

                    stage = self.synchronise_item.env[
                        'fit.saltedge.synchronise.stage'].sudo().search(
                            [('synchronise_stage_id', '=', 'error')], limit=1)
                    self.synchronise_item.write({
                        'synchronise_status':
                        synchronise_status,
                        'synchronise_state':
                        stage.id,
                        'synchronise_error':
                        synchronise_error
                    })
        except UserError as e:
            raise e
        except Exception as e:
            self._handle_error(e)
    def _handle_transactions(self, json_data, current_id):
        _logger.info('Start handling of transactions')

        if not current_id:
            self.transactions = json_data[u'data']
        else:
            self.transactions.extend(json_data[u'data'])

        if json_data[u'meta'][u'next_id']:
            self.retrieve_transactions(json_data[u'meta'][u'next_id'])
        else:
            print 'retrieved all transactions, # total: ' + str(
                len(self.transactions))
            result_transactions = []
            for transaction in self.transactions:
                if str(transaction[u'account_id']) == str(
                        self.synchronise_item.synchronise_account_id):
                    print 'transaction details: ' + str(transaction)
                    if self.account_number is None:
                        self.account_number = sanitize_account_number(
                            self.synchronise_item.synchronise_account_name)
                    self.account_balance = transaction[u'extra'][
                        u'account_balance_snapshot']

                    result_transaction = {}
                    result_transaction['partner_name'] = transaction[
                        u'description']
                    result_transaction['name'] = transaction[u'description']
                    # created_date = strptime(transaction.made_on, "%Y%m%dT%H:%M:%SZ").date()
                    result_transaction['date'] = transaction[u'made_on']
                    result_transaction['amount'] = transaction[u'amount']
                    result_transaction['unique_import_id'] = str(
                        transaction[u'id'])
                    result_transaction['note'] = transaction[u'description']

                    if u'information' in transaction[u'extra']:
                        if self.__is_valid_iban(
                                transaction[u'extra'][u'information']):
                            result_transaction[
                                'account_number'] = self.__get_compact_iban(
                                    transaction[u'extra'][u'information'])
                        else:
                            result_transaction['name'] = transaction[u'extra'][
                                u'information']
                            # result_transaction['account_number'] = transaction[u'extra'][u'information'].replace(" ", "")
                    if u'payee' in transaction[u'extra']:
                        if self.__is_valid_iban(
                                transaction[u'extra'][u'payee']):
                            result_transaction[
                                'account_number'] = self.__get_compact_iban(
                                    transaction[u'extra'][u'payee'])
                        else:
                            result_transaction['name'] = transaction[u'extra'][
                                u'payee']
                            # result_transaction['account_number'] = transaction[u'extra'][u'payee']
                    # if u'payee' in transaction[u'extra']:
                    #     result_transaction['partner_name'] = transaction[u'extra'][u'payee']
                    if u'record_number' in transaction:
                        result_transaction['ref'] = transaction[
                            u'record_number']
                    if u'additional' in transaction[u'extra']:
                        result_transaction['name'] = transaction[u'extra'][
                            u'additional']
                    print 'result transaction: ' + str(result_transaction)
                    result_transactions.append(result_transaction)

            data = [{
                'name': self._generate_unique_name(),
                'date': strftime('%Y-%m-%d'),
                'balance_end_real': self.account_balance,
                'transactions': result_transactions
            }]

            currency, journal = self.synchronise_item._find_additional_data(
                self.currency_code, self.account_number)
            if not journal:
                self.synchronise_item.write({
                    'synchronise_journal_fail':
                    True,
                    'synchronise_error':
                    _('Journal for bank account: %s not available, please create journal '
                      'first.') % (self.account_number, )
                })
                return False, '', '', 0
            if not journal.default_debit_account_id or not journal.default_credit_account_id:
                self.synchronise_item.write({
                    'synchronise_journal_fail':
                    True,
                    'synchronise_error':
                    _('You have to set a Default Debit Account and a Default Credit Account for the journal: %s'
                      ) % (journal.name, )
                })
                return False, '', '', 0

            try:
                last_bnk_stmt = self.synchronise_item.env[
                    'account.bank.statement'].search(
                        [('journal_id', '=', journal.id)], limit=1)
                data[0]['balance_start'] = last_bnk_stmt.balance_end_real
                # if last_bnk_stmt:
                # return last_bnk_stmt.balance_end

                # Prepare statement data to be used for bank statements creation
                _max_unique_id = max(
                    [x['unique_import_id'] for x in data[0]['transactions']])
                stmts_vals = self.synchronise_item._complete_stmts_vals(
                    data, journal, self.account_number)
                # _max_unique_id = max([x['unique_import_id'] for x in stmts_vals[0]['transactions']])

                # Create the bank statements
                statement_ids, notifications = self.synchronise_item._create_bank_statements(
                    stmts_vals)

                # Now that the import worked out, set it as the bank_statements_source of the journal
                journal.bank_statements_source = 'file_import'

                return True, statement_ids, notifications, _max_unique_id
            except UserError as e:
                # if e[0] == u'U hebt dit bestand reeds ge\xefmporteerd.':
                #     stage = self.synchronise_item.env['fit.saltedge.synchronise.stage'].sudo().search([('synchronise_stage_id', '=', 'done')],
                #                                                                                       limit=1)
                #     self.synchronise_item.write({'synchronise_journal_fail': False,
                #                                  'synchronise_state': stage.id,
                #                                  'synchronise_error': _('All transactions already processed, nothing to do.')})
                # else:
                stage = self.synchronise_item.env[
                    'fit.saltedge.synchronise.stage'].sudo().search(
                        [('synchronise_stage_id', '=', 'error')], limit=1)
                self.synchronise_item.write({
                    'synchronise_journal_fail': False,
                    'synchronise_state': stage.id,
                    'synchronise_error': _(e[0])
                })
                return False, '', '', 0
            except BaseException as e:
                stage = self.synchronise_item.env[
                    'fit.saltedge.synchronise.stage'].sudo().search(
                        [('synchronise_stage_id', '=', 'error')], limit=1)
                self.synchronise_item.write({
                    'synchronise_journal_fail': False,
                    'synchronise_state': stage.id,
                    'synchronise_error': _(e)
                })
                return False, '', '', 0