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. identifying_string = line_vals.get('account_number') if identifying_string: partner_bank = self.env['res.partner.bank']\ .search([('acc_number', '=', identifying_string), ('company_id', 'in', (False, journal.company_id.id))], limit=1) if partner_bank: line_vals['bank_account_id'] = partner_bank.id line_vals[ 'partner_id'] = partner_bank.partner_id.id return stmts_vals
def _create_or_find_mandate(self, iban, partner_id): self.ensure_one() ResPartnerBank = self.env['res.partner.bank'].sudo() partner_bank = ResPartnerBank.search([ ('sanitized_acc_number', '=', sanitize_account_number(iban))], limit=1) if not partner_bank: partner_bank = ResPartnerBank.create({ 'acc_number': iban, 'partner_id': partner_id, }) # avoid duplicate mandate = self.env['sdd.mandate'].sudo().search([ ('state', 'not in', ['closed', 'revoked']), ('start_date', '<=', datetime.now()), '|', ('end_date', '>=', datetime.now()), ('end_date', '=', None), ('partner_id', '=', partner_id), ('partner_bank_id', '=', partner_bank.id), '|', ('one_off', '=', False), ('payment_ids', '=', False)], limit=1) if not mandate: mandate = self.env['sdd.mandate'].sudo().create({ 'partner_id': partner_id, 'partner_bank_id': partner_bank.id, 'start_date': datetime.now(), 'payment_journal_id': self.journal_id.id, 'state': 'draft', }) return mandate
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'] = " ".join( self.attachment_ids.mapped('name')) 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('partner_bank_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. 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: line_vals['partner_bank_id'] = partner_bank.id line_vals[ 'partner_id'] = partner_bank.partner_id.id return stmts_vals
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. 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: line_vals['bank_account_id'] = partner_bank.id line_vals['partner_id'] = partner_bank.partner_id.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 _complete_stmts_vals(self, stmts_vals, journal, account_number): for st_vals in stmts_vals: st_vals["journal_id"] = journal.id for lvals in st_vals["transactions"]: unique_import_id = lvals.get("unique_import_id") if unique_import_id: sanitized_account_number = sanitize_account_number(account_number) lvals["unique_import_id"] = ( ( sanitized_account_number and sanitized_account_number + "-" or "" ) + str(journal.id) + "-" + unique_import_id ) if ( not lvals.get("partner_bank_id") and lvals.get("account_number") and not lvals.get("partner_id") ): # Find the partner from his bank account number # The partner selected during the # reconciliation process will be linked to the bank account # when the statement is closed (code in the account module) self._update_partner_from_account_number(lvals) if not lvals.get("payment_ref"): raise UserError(_("Missing payment_ref on a transaction.")) return stmts_vals
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 _validate_qr_iban(self, qr_iban): # Check first if it's a valid IBAN. validate_iban(qr_iban) # We sanitize first so that _check_qr_iban_range() can extract correct IID from IBAN to validate it. sanitized_qr_iban = sanitize_account_number(qr_iban) # Now, check if it's valid QR-IBAN (based on its IID). if not self._check_qr_iban_range(sanitized_qr_iban): raise ValidationError(_("QR-IBAN '%s' is invalid.") % qr_iban) return True
def _prepare_swiss_code_url_vals(self, amount, currency_name, debtor_partner, reference_type, reference, comment): qr_code_vals = super()._prepare_swiss_code_url_vals( amount, currency_name, debtor_partner, reference_type, reference, comment) # If there is a QR IBAN we use it for the barcode instead of the account number if self.l10n_ch_qr_iban: qr_code_vals[3] = sanitize_account_number(self.l10n_ch_qr_iban) return qr_code_vals
def _l10n_ch_get_qr_vals(self, amount, currency, debtor_partner, free_communication, structured_communication): qr_vals = super()._l10n_ch_get_qr_vals(amount, currency, debtor_partner, free_communication, structured_communication) # If there is a QR IBAN we use it for the barcode instead of the account number if self.l10n_ch_qr_iban: qr_vals[3] = sanitize_account_number(self.l10n_ch_qr_iban) return qr_vals
def _qonto_get_slug(self): self.ensure_one() url = QONTO_ENDPOINT + '/organizations/%7Bid%7D' response = requests.get(url, verify=False, headers=self._qonto_header()) if response.status_code == 200: data = json.loads(response.text) res = {} for account in data.get('organization', {}).get('bank_accounts', []): iban = sanitize_account_number(account.get('iban', '')) res[iban] = account.get('slug') return res raise UserError(_('%s \n\n %s') % (response.status_code, response.text))
def _onchange_set_l10n_ch_postal(self): try: validate_iban(self.bank_acc_number) is_iban = True except ValidationError: is_iban = False if is_iban: self.l10n_ch_postal = self.env[ 'res.partner.bank']._retrieve_l10n_ch_postal( sanitize_account_number(self.bank_acc_number)) else: self.l10n_ch_postal = self.bank_acc_number
def _ponto_get_account_ids(self): url = PONTO_ENDPOINT + "/accounts" response = requests.get( url, params={"limit": 100}, headers=self._ponto_header() ) if response.status_code == 200: data = json.loads(response.text) res = {} for account in data.get("data", []): iban = sanitize_account_number( account.get("attributes", {}).get("reference", "") ) res[iban] = account.get("id") return res raise UserError(_("%s \n\n %s") % (response.status_code, response.text))
def validate_qr_iban(qr_iban): # Check first if it's a valid IBAN. validate_iban(qr_iban) # We sanitize first so that _check_qr_iban_range() can extract correct IID from IBAN to validate it. sanitized_qr_iban = sanitize_account_number(qr_iban) if sanitized_qr_iban[:2] != 'CH': raise ValidationError( _("QR-IBAN numbers are only available in Switzerland.")) # Now, check if it's valid QR-IBAN (based on its IID). if not check_qr_iban_range(sanitized_qr_iban): raise ValidationError(_("QR-IBAN '%s' is invalid.") % qr_iban) return True
def _ponto_get_account_ids(self): url = PONTO_ENDPOINT + '/accounts' response = requests.get(url, verify=False, params={'limit': 100}, headers=self._ponto_header()) if response.status_code == 200: data = json.loads(response.text) res = {} for account in data.get('data', []): iban = sanitize_account_number( account.get('attributes', {}).get('reference', '')) res[iban] = account.get('id') return res raise UserError( _('%s \n\n %s') % (response.status_code, response.text))
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 sepa_direct_debit_s2s_form_process(self, data): if not data.get('mandate_id'): iban = sanitize_account_number(data['iban']) # will raise a ValidationError given an invalid format validate_iban(iban) partner_id = int(data.get('partner_id')) mandate = self._create_or_find_mandate(iban, partner_id) else: mandate = self.env['sdd.mandate'].browse(data['mandate_id']) # since we're in a sudoed env, we need to add a few checks if mandate.partner_id.id != data['partner_id']: raise AccessError(_('Identity mismatch')) payment_token = self.env['payment.token'].sudo().create({ 'sdd_mandate_id': mandate.id, 'name': data['iban'], 'acquirer_ref': mandate.name, 'acquirer_id': int(data['acquirer_id']), 'partner_id': int(data['partner_id']), }) return payment_token
def sepa_direct_debit_s2s_form_process(self, data): partner_id = int(data['partner_id']) if not data.get('mandate_id'): iban = sanitize_account_number(data['iban']) same_iban_acc = self.env['res.partner.bank'].search( [('sanitized_acc_number', '=', iban), ('company_id', '=', self.env.company.id)], limit=1) if same_iban_acc: raise ValidationError( _("This account is not available. Please log in to continue." )) # will raise a ValidationError given an invalid format validate_iban(iban) mandate = self._create_or_find_mandate(iban, partner_id) else: partner = self.env['res.partner'].browse(partner_id).sudo() mandate = self.env['sdd.mandate'].browse(data['mandate_id']) # since we're in a sudoed env, we need to add a few checks if mandate.partner_id != partner.commercial_partner_id: raise AccessError(_('Identity mismatch')) iban_mask = 'X' * (len(data['iban']) - 4) + data['iban'][-4:] payment_token = self.env['payment.token'].sudo().create({ 'sdd_mandate_id': mandate.id, 'name': _('Direct Debit: ') + iban_mask, 'acquirer_ref': mandate.name, 'acquirer_id': int(data['acquirer_id']), 'partner_id': partner_id, }) return payment_token
def set_bank_account(self, acc_number, bank_id=None): """ Create a res.partner.bank (if not exists) and set it as value of the field bank_account_id """ self.ensure_one() res_partner_bank = self.env['res.partner.bank'].search([ ('sanitized_acc_number', '=', sanitize_account_number(acc_number)), ('company_id', '=', self.company_id.id) ], limit=1) if res_partner_bank: self.bank_account_id = res_partner_bank.id else: self.bank_account_id = self.env['res.partner.bank'].create({ 'acc_number': acc_number, 'bank_id': bank_id, 'company_id': self.company_id.id, 'currency_id': self.currency_id.id, 'partner_id': self.company_id.partner_id.id, }).id
def _generate_unique_import_id(self, unique_import_id): self.ensure_one() sanitized_account_number = sanitize_account_number(self.account_number) return (sanitized_account_number and sanitized_account_number + '-' or '') + str(self.journal_id.id) + '-' + unique_import_id
def _sanitize_bank_account_number(self, bank_account_number): """Hook for extension""" self.ensure_one() return sanitize_account_number(bank_account_number)
def _onchange_set_l10n_ch_postal(self): try: validate_iban(self.bank_acc_number) is_iban = True except ValidationError: is_iban = False if is_iban: self.l10n_ch_postal = self.env['res.partner.bank']._retrieve_l10n_ch_postal(sanitize_account_number(self.bank_acc_number)) else: self.l10n_ch_postal = self.bank_acc_number
def build_swiss_code_url(self, amount, currency_name, not_used_anymore_1, debtor_partner, not_used_anymore_2, structured_communication, free_communication): comment = "" if free_communication: comment = ( free_communication[:137] + '...') if len(free_communication) > 140 else free_communication creditor_addr_1, creditor_addr_2 = self._get_partner_address_lines( self.partner_id) debtor_addr_1, debtor_addr_2 = self._get_partner_address_lines( debtor_partner) # Compute reference type (empty by default, only mandatory for QR-IBAN, # and must then be 27 characters-long, with mod10r check digit as the 27th one, # just like ISR number for invoices) reference_type = 'NON' reference = '' if self._is_qr_iban(): # _check_for_qr_code_errors ensures we can't have a QR-IBAN # without a QR-reference here reference_type = 'QRR' reference = structured_communication # If there is a QR IBAN we use it for the barcode instead of the account number acc_number = self.sanitized_acc_number if self.l10n_ch_qr_iban: acc_number = sanitize_account_number(self.l10n_ch_qr_iban) qr_code_vals = [ 'SPC', # QR Type '0200', # Version '1', # Coding Type acc_number, # IBAN 'K', # Creditor Address Type (self.acc_holder_name or self.partner_id.name)[:71], # Creditor Name creditor_addr_1, # Creditor Address Line 1 creditor_addr_2, # Creditor Address Line 2 '', # Creditor Postal Code (empty, since we're using combined addres elements) '', # Creditor Town (empty, since we're using combined addres elements) self.partner_id.country_id.code, # Creditor Country '', # Ultimate Creditor Address Type '', # Name '', # Ultimate Creditor Address Line 1 '', # Ultimate Creditor Address Line 2 '', # Ultimate Creditor Postal Code '', # Ultimate Creditor Town '', # Ultimate Creditor Country '{:.2f}'.format(amount), # Amount currency_name, # Currency 'K', # Ultimate Debtor Address Type debtor_partner.name[:71], # Ultimate Debtor Name debtor_addr_1, # Ultimate Debtor Address Line 1 debtor_addr_2, # Ultimate Debtor Address Line 2 '', # Ultimate Debtor Postal Code (not to be provided for address type K) '', # Ultimate Debtor Postal City (not to be provided for address type K) debtor_partner.country_id.code, # Ultimate Debtor Postal Country reference_type, # Reference Type reference, # Reference comment, # Unstructured Message 'EPD', # Mandatory trailer part ] return '/report/barcode/?type=%s&value=%s&width=%s&height=%s&humanreadable=1' % ( 'QR_quiet', werkzeug.urls.url_quote_plus( '\n'.join(qr_code_vals)), 256, 256)
def _get_payload_params(self): self.ic_ref = self._get_ic_ref() bank_account = "" if self.payment_type == "qr": bank_account = sanitize_account_number( self.invoice_id.invoice_partner_bank_id.l10n_ch_qr_iban or self.invoice_id.invoice_partner_bank_id.acc_number) else: bank_account = (self.invoice_id.invoice_partner_bank_id. l10n_ch_isr_subscription_chf) if bank_account: account_parts = bank_account.split("-") bank_account = (account_parts[0] + account_parts[1].rjust(6, "0") + account_parts[2]) else: bank_account = "" params = { "client_pid": self.service_id.client_pid, "invoice": self.invoice_id, "invoice_lines": self.invoice_id.paynet_invoice_line_ids(), "biller": self.invoice_id.company_id, "customer": self.invoice_id.partner_id, "delivery": self.invoice_id.partner_shipping_id, "pdf_data": self.attachment_id.datas.decode("ascii"), "bank": self.invoice_id.invoice_partner_bank_id, "bank_account": bank_account, "ic_ref": self.ic_ref, "payment_type": self.payment_type, "document_type": DOCUMENT_TYPE[self.invoice_id.type], "format_date": self.format_date, "ebill_account_number": self.ebill_account_number, "discount_template": "", "discount": {}, } amount_by_group = [] # Get the percentage of the tax from the name of the group # Could be improve by searching in the account_tax linked to the group for taxgroup in self.invoice_id.amount_by_group: rate = taxgroup[0].split()[-1:][0][:-1] amount_by_group.append(( rate or "0", taxgroup[1], taxgroup[2], )) params["amount_by_group"] = amount_by_group # Get the invoice due date date_due = None if self.invoice_id.invoice_payment_term_id: terms = self.invoice_id.invoice_payment_term_id.compute( self.invoice_id.amount_total) if terms: # Returns all payment and their date like [('2020-12-07', 430.37), ...] # Get the last payment date in the format "202021207" date_due = terms[-1][0].replace("-", "") if not date_due: date_due = self.format_date(self.invoice_id.invoice_date_due or self.invoice_id.invoice_date) params["date_due"] = date_due return params
def retrieve_transactions(self): if (self.account_online_provider_id.provider_type != 'ponto'): return super(OnlineAccount, self).retrieve_transactions() # actualize the data in ponto # For some reason, ponto has 2 different routes to update the account balance and transactions # however if we try to refresh both one after another or at the same time, an error is received # An error is also received if we call their synchronization route too quickly. Therefore we # only refresh the transactions of the account and don't update the account which means that the # balance of the account won't be up-to-date. However this is not a big problem as the record that # store the balance is hidden for most user. self._ponto_synchronize('accountTransactions') self._ponto_synchronize('accountDetails') transactions = [] # Update account balance url = '/accounts/%s' % (self.online_identifier, ) resp_json = self.account_online_provider_id._ponto_fetch( 'GET', url, {}, {}) end_amount = resp_json.get('data', {}).get('attributes', {}).get('currentBalance', 0) self.balance = end_amount # Fetch transactions. # Transactions are paginated so we need to loop to ensure we have every transactions, we keep # in memory the id of the last transaction fetched in order to start over from there. url = url + '/transactions' paging_forward = True if self.ponto_last_synchronization_identifier: paging_forward = False url = url + '?before=' + self.ponto_last_synchronization_identifier last_sync = fields.Date.to_date( (self.last_sync or fields.Datetime.now() - datetime.timedelta(days=15))) latest_transaction_identifier = False while url: resp_json = self.account_online_provider_id._ponto_fetch( 'GET', url, {}, {}) # 'prev' page contains newer transactions, 'next' page contains older ones. # we read from last known transaction to newer ones when we know such a transaction # else we read from the newest transaction back to our date limit url = resp_json.get('links', {}).get('next' if paging_forward else 'prev', False) data_lines = resp_json.get('data', []) if data_lines: # latest transaction will be in the last page in backward direction, or in the first one in forward direction if ((not paging_forward and not url) or (paging_forward and not latest_transaction_identifier)): # a chunk sent by Ponto always has its most recent transaction first latest_transaction_identifier = data_lines[0].get('id') for transaction in data_lines: # Convert received transaction datetime into Brussel timezone as we receive transaction date in an UTC # format but we store a date (which will loose information about the time) and some banks don't provide # the time of the transactions hence what we receive is a datetime in the form 2019-01-01T23:00:00.000z for a # transaction where the correct date should be of 2019-01-02 # This is not the best fix as we should instead convert to the time of the country where the bank is located # but since ponto only support bank in belgium/france/nl for now this is acceptable. tr_date = dateutil.parser.parse( transaction.get('attributes', {}).get('executionDate')) tr_date = tr_date.astimezone(GMT_BELGIUM) tr_date = fields.Date.to_date(tr_date) if paging_forward and tr_date < last_sync: # Stop fetching transactions because we are paging forward # and the following transactions are older than specified last_sync date. url = False break attributes = transaction.get('attributes', {}) description = attributes.get('description') or '' counterpart = attributes.get('counterpartName') or '' remittanceinfo = attributes.get('remittanceInformation') or '' remittanceinfoType = attributes.get( 'remittanceInformationType') or '' name = '' if remittanceinfoType == 'structured': name = remittanceinfo if not name: name = ' '.join([description, counterpart, remittanceinfo ]) or '/' account_number = transaction.get( 'attributes', {}).get('counterpartReference') trans = { 'online_identifier': transaction.get('id'), 'date': tr_date, 'payment_ref': name, 'amount': transaction.get('attributes', {}).get('amount'), 'account_number': account_number, } if account_number: company_id = self.account_online_provider_id.company_id.id partner_bank = self.env['res.partner.bank'].search( [('sanitized_acc_number', '=', sanitize_account_number(account_number)), '|', ('company_id', '=', company_id), ('company_id', '=', False)], order='company_id', limit=1) if partner_bank: trans['partner_bank_id'] = partner_bank.id trans['partner_id'] = partner_bank.partner_id.id if not trans.get('partner_id') and transaction.get( 'attributes', {}).get('counterpartName'): trans['online_partner_vendor_name'] = transaction[ 'attributes']['counterpartName'] trans['partner_id'] = self._find_partner([ ('online_partner_vendor_name', '=', transaction['attributes']['counterpartName']) ]) transactions.append(trans) if latest_transaction_identifier: self.ponto_last_synchronization_identifier = latest_transaction_identifier # Create the bank statement with the transactions return self.env['account.bank.statement'].online_sync_bank_statement( transactions, self.journal_ids[0], end_amount)
def _match_journal(self, account_number, currency): company = self.env.company journal_obj = self.env["account.journal"] if not account_number: # exemple : QIF if not self.env.context.get("journal_id"): raise UserError( _("The format of this bank statement file doesn't " "contain the bank account number, so you must " "start the wizard from the right bank journal " "in the dashboard.")) journal = journal_obj.browse(self.env.context.get("journal_id")) else: sanitized_account_number = sanitize_account_number(account_number) journal = journal_obj.search( [ ("type", "=", "bank"), ( "bank_account_id.sanitized_acc_number", "ilike", sanitized_account_number, ), ], limit=1, ) if not journal: bank_accounts = self.env["res.partner.bank"].search( [ ("partner_id", "=", company.partner_id.id), ("sanitized_acc_number", "ilike", sanitized_account_number), ], limit=1, ) if bank_accounts: raise UserError( _("The bank account with number '%s' exists in Odoo " "but it is not set on any bank journal. You should " "set it on the related bank journal. If the related " "bank journal doesn't exist yet, you should create " "a new one.") % (account_number, )) else: raise UserError( _("Could not find any bank account with number '%s' " "linked to partner '%s'. You should create the bank " "account and set it on the related bank journal. " "If the related bank journal doesn't exist yet, you " "should create a new one.") % (account_number, company.partner_id.display_name)) # We support multi-file and multi-statement in a file # so self.env.context.get('journal_id') doesn't mean much # I don't think we should really use it journal_currency = journal.currency_id or company.currency_id if journal_currency != currency: raise UserError( _("The currency of the bank statement (%s) is not the same as the " "currency of the journal '%s' (%s).") % (currency.name, journal.display_name, journal_currency.name)) return journal
def _l10n_ch_get_qr_vals(self, amount, currency, debtor_partner, free_communication, structured_communication): comment = "" if free_communication: comment = ( free_communication[:137] + '...') if len(free_communication) > 140 else free_communication creditor_addr_1, creditor_addr_2 = self._get_partner_address_lines( self.partner_id) debtor_addr_1, debtor_addr_2 = self._get_partner_address_lines( debtor_partner) # Compute reference type (empty by default, only mandatory for QR-IBAN, # and must then be 27 characters-long, with mod10r check digit as the 27th one, # just like ISR number for invoices) reference_type = 'NON' reference = '' acc_number = self.sanitized_acc_number if self.l10n_ch_qr_iban: # _check_for_qr_code_errors ensures we can't have a QR-IBAN without a QR-reference here reference_type = 'QRR' reference = structured_communication acc_number = sanitize_account_number(self.l10n_ch_qr_iban) currency = currency or self.currency_id or self.company_id.currency_id return [ 'SPC', # QR Type '0200', # Version '1', # Coding Type acc_number, # IBAN / QR-IBAN 'K', # Creditor Address Type (self.acc_holder_name or self.partner_id.name)[:70], # Creditor Name creditor_addr_1, # Creditor Address Line 1 creditor_addr_2, # Creditor Address Line 2 '', # Creditor Postal Code (empty, since we're using combined addres elements) '', # Creditor Town (empty, since we're using combined addres elements) self.partner_id.country_id.code, # Creditor Country '', # Ultimate Creditor Address Type '', # Name '', # Ultimate Creditor Address Line 1 '', # Ultimate Creditor Address Line 2 '', # Ultimate Creditor Postal Code '', # Ultimate Creditor Town '', # Ultimate Creditor Country '{:.2f}'.format(amount), # Amount currency.name, # Currency 'K', # Ultimate Debtor Address Type debtor_partner.commercial_partner_id. name[:70], # Ultimate Debtor Name debtor_addr_1, # Ultimate Debtor Address Line 1 debtor_addr_2, # Ultimate Debtor Address Line 2 '', # Ultimate Debtor Postal Code (not to be provided for address type K) '', # Ultimate Debtor Postal City (not to be provided for address type K) debtor_partner.country_id.code, # Ultimate Debtor Postal Country reference_type, # Reference Type reference, # Reference comment, # Unstructured Message 'EPD', # Mandatory trailer part ]