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])
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
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
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
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', } }
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