def _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] account.iban = u'FR76' + info['id'] account._info = info account.label = label account.type = info['acc_type'] if 'acc_type' in info else account_type account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance else self.get_balance(account) account.currency = account.get_currency(balance) account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
def get_list(self): r = self.api_open('/transactionnel/services/rest/Account/accounts') for content in r.json()['content']: if self.accnum != '00000000000' and content['numero'] != self.accnum: continue for poste in content['postes']: a = Account() a._number = content['numeroLong'] iban_response = self.api_open('/transactionnel/services/rest/Account/account/%s/iban' % a._number).json() a.iban = iban_response['content']['iban'] if 'content' in iban_response else NotAvailable a._nature = poste['codeNature'] a._consultable = poste['consultable'] a._univers = self.current_univers a.id = '%s.%s' % (a._number, a._nature) a.type = self.ACCOUNT_TYPES.get(poste['codeNature'], Account.TYPE_UNKNOWN) if 'numeroDossier' in poste and poste['numeroDossier']: a._file_number = poste['numeroDossier'] a.id += '.%s' % a._file_number if poste['postePortefeuille']: a.label = u'Portefeuille Titres' a.balance = Decimal(str(poste['montantTitres']['valeur'])) a.currency = poste['montantTitres']['monnaie']['code'].strip() yield a if 'libelle' not in poste: continue a.label = ' '.join([content['intitule'].strip(), poste['libelle'].strip()]) a.balance = Decimal(str(poste['solde']['valeur'])) a.currency = poste['solde']['monnaie']['code'].strip() yield a
def _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link, accounts) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] if is_rib_valid(info['id']): account.iban = rib2iban(info['id']) account._info = info account.label = label account.type = self.ACCOUNT_TYPES.get(label, info['acc_type'] if 'acc_type' in info else account_type) balance = balance or self.get_balance(account) account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance and balance is not NotAvailable else NotAvailable account.currency = account.get_currency(balance) if balance and balance is not NotAvailable else NotAvailable account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') if account.balance and account.balance is not NotAvailable: a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
def get_list(self): table = self.find_table() for tr in self.parser.select(table, 'tr', 'many'): tds = self.parser.select(tr, 'td') if len(tds) != 6: continue tdlabel, tdid, tdcur, tdupdated, tdbal, tdbalcur = tds account = Account() account.label = to_unicode(tdlabel.text_content().strip()) # this is important - and is also the last part of the id (considering spaces) # we can't use only the link as it does not goes where we want try: link = self.parser.select(tdlabel, 'a', 1) except BrokenPageError: # probably an account we can't display the history account._link_id = None else: account._link_id = parse_qs(link.attrib['href'])['ch4'][0] account.id = to_unicode(tdid.text.strip().replace(' ', '')) account.iban = 'FR76' + account.id # just in case we are showing the converted balances account._main_currency = Account.get_currency(tdcur.text) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if tdbal.text_content().strip() == 'indisponible': continue account.balance = Decimal(Transaction.clean_amount(tdbal.text_content())) account.currency = Account.get_currency(tdbalcur.text) account._updated = datetime.strptime(tdupdated.text, '%d/%m/%Y') yield account
def iter_accounts(self): # Throw error from password value or random error self.maybe_generate_error(8) accounts = [] first_account = Account() first_account.id = 'FR235711131719@fakebank' first_account.label = 'Compte chèque' first_account.currency = Currency.get_currency('42 €') first_account.iban = 'FR235711131719' first_account.balance = Decimal(random.uniform(0, 150)) first_account.type = Account.TYPE_CHECKING accounts.append(first_account) second_account = Account() second_account.id = 'livretA@fakebank' second_account.label = 'Livret A' second_account.currency = Currency.get_currency('$42') second_account.balance = Decimal(500.0) second_account.type = Account.TYPE_SAVINGS accounts.append(second_account) third_account = Account() third_account.id = 'PEL@fakebank' third_account.label = 'Plan Epargne Logement' third_account.balance = Decimal(0.0) third_account.type = Account.TYPE_SAVINGS accounts.append(third_account) return accounts
def _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] account.iban = u'FR76' + info['id'] account._info = info account.label = label account.type = info['acc_type'] if 'acc_type' in info else account_type account.balance = Decimal(FrenchTransaction.clean_amount( balance)) if balance else self.get_balance(account) account.currency = account.get_currency(balance) account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
def get_list(self): r = self.api_open('/transactionnel/services/rest/Account/accounts') for content in r.json()['content']: if self.accnum != '00000000000' and content[ 'numero'] != self.accnum: continue for poste in content['postes']: a = Account() a._number = content['numeroLong'] a._nature = poste['codeNature'] a._consultable = poste['consultable'] a._univers = self.current_univers a.id = '%s.%s' % (a._number, a._nature) a.type = self.ACCOUNT_TYPES.get(poste['codeNature'], Account.TYPE_UNKNOWN) if a.type == Account.TYPE_CHECKING: iban_response = self.api_open( '/transactionnel/services/rest/Account/account/%s/iban' % a._number).json() a.iban = iban_response['content'][ 'iban'] if 'content' in iban_response else NotAvailable else: a.iban = NotAvailable if 'numeroDossier' in poste and poste['numeroDossier']: a._file_number = poste['numeroDossier'] a.id += '.%s' % a._file_number if poste['postePortefeuille']: a.label = u'Portefeuille Titres' a.balance = Decimal(str(poste['montantTitres']['valeur'])) a.currency = poste['montantTitres']['monnaie'][ 'code'].strip() yield a if 'libelle' not in poste: continue a.label = ' '.join( [content['intitule'].strip(), poste['libelle'].strip()]) a.balance = Decimal(str(poste['solde']['valeur'])) a.currency = poste['solde']['monnaie']['code'].strip() yield a
def iter_accounts(self): for classeur in self.doc.get('donnees', {}).get('classeurs', {}): title = classeur['title'] for compte in classeur.get('comptes', []): a = Account() a.label = CleanText().filter(compte['libelle']) a._id = compte['id'] a.type = self.obj_type(a.label) a.number = compte['iban'].replace(' ', '') # for some account that don't have Iban the account number is store under this variable in the Json if not is_iban_valid(a.number): a.iban = NotAvailable else: a.iban = a.number # id based on iban to match ids in database. a.id = a.number[4:-2] if len(a.number) == 27 else a.number a._agency = compte['agenceGestionnaire'] a._title = title yield a
def get_accounts(self): accounts = {} content = self.doc.xpath('//div[@id="moneyPage" or @id="MoneyPage"]')[0] # Multiple accounts lines = content.xpath('(//div[@class="col-md-8 multi-currency"])[1]/ul/li') for li in lines: account = Account() account.iban = NotAvailable account.type = Account.TYPE_CHECKING currency_code = CleanText().filter((li.xpath('./span[@class="currencyUnit"]/span') or li.xpath('./span[1]'))[0]) currency = Currency.get_currency(currency_code) if not currency: self.logger.warning('Unable to find currency %r', currency_code) continue account.id = currency account.currency = currency account.balance = CleanDecimal(replace_dots=True).filter(li.xpath('./span[@class="amount"]/text()')) account.label = u'%s %s*' % (self.browser.username, account.currency) accounts[account.id] = account self.browser.account_currencies.append(account.currency) if not accounts: # Primary currency account primary_account = Account() primary_account.iban = NotAvailable primary_account.type = Account.TYPE_CHECKING try: balance = CleanText('.')(content.xpath('//div[contains(@class, "col-md-6")][contains(@class, "available")]')[0]) except IndexError: primary_account.id = 'EUR' primary_account.currency = u'EUR' primary_account.balance = NotAvailable primary_account.label = u'%s' % (self.browser.username) else: primary_account.currency = Account.get_currency(balance) primary_account.id = unicode(primary_account.currency) primary_account.balance = Decimal(FrenchTransaction.clean_amount(balance)) primary_account.label = u'%s %s*' % (self.browser.username, primary_account.currency) accounts[primary_account.id] = primary_account return accounts
def iter_accounts(self): for classeur in self.doc.get('donnees', {}).get('classeurs', {}): title = classeur['title'] for compte in classeur.get('comptes', []): a = Account() a.label = CleanText().filter(compte['libelle']) a._id = compte['id'] a.iban = compte['iban'].replace(' ', '') # id based on iban to match ids in database. a.id = a.iban[4:-2] if len(a.iban) == 27 else a.iban a.type = self.obj_type(a.label) a._agency = compte['agenceGestionnaire'] a._title = title yield a
def iter_accounts(self): for classeur in self.doc['donnees']['classeurs']: title = classeur['title'] for compte in classeur['comptes']: a = Account() a.label = compte['libelle'] a._id = compte['id'] a.iban = compte['iban'].replace(' ', '') # id based on iban to match ids in database. a.id = a.iban[4:-2] a.type = Account.TYPE_CHECKING a._agency = compte['agenceGestionnaire'] a._title = title yield a
def get_accounts(self): account = self.request('/api/accounts') a = Account() # Number26 only provides a checking account (as of sept 19th 2016). a.type = Account.TYPE_CHECKING a.label = u'Checking account' a.id = account["id"] a.number = NotAvailable a.balance = Decimal(str(account["availableBalance"])) a.iban = account["iban"] a.currency = u'EUR' return [a]
def iter_accounts(self): if self.accounts: for account in self.accounts: yield account return waiting = False for project in self.users['projects']: self.open('/user/%s/project/%s/' % (self.users['userId'], project['projectId']), method="OPTIONS") me = self.request('/user/%s/project/%s/' % (self.users['userId'], project['projectId']), headers=self.request_headers) waiting = (me['status'] in self.waiting_statuses) # Check project in progress if not me['numeroContrat'] or not me['dateAdhesion'] or not me[ 'solde']: continue a = Account() a.id = "".join(me['numeroContrat'].split()) a.number = me['numeroContrat'] a.label = " ".join(me['supportEpargne'].split("_")) a.type = Account.TYPE_LIFE_INSURANCE if "assurance vie" in a.label.lower() else \ Account.TYPE_MARKET if "compte titre" in a.label.lower() else \ Account.TYPE_PEA if "pea" in a.label.lower() else \ Account.TYPE_UNKNOWN a.balance = CleanDecimal().filter(me['solde']) a.currency = u'EUR' # performanceEuro, montantEuro everywhere in Yomoni JSON a.iban = me['ibancompteTitre'] or NotAvailable a._project_id = project['projectId'] a.valuation_diff = CleanDecimal().filter(me['performanceEuro']) a._startbalance = me['montantDepart'] self.accounts.append(a) self.iter_investment(a, me['sousJacents']) yield a if not self.accounts and waiting: raise ActionNeeded( "Le service client Yomoni est en attente d'un retour de votre part." )
def get_list(self, pro=True): accounts = [] for tr in self.document.xpath('//tr[@class="comptes"]'): cols = tr.findall('td') if len(cols) < 5: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]).replace(" ", "") account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.balance = Decimal(self.parser.tocleanstring(cols[self.COL_BALANCE])) try: account.coming = Decimal(self.parser.tocleanstring(cols[self.COL_COMING])) except InvalidOperation: if self.parser.tocleanstring(cols[self.COL_COMING]) != '-': self.logger.warning('Unable to parse coming value', exc_info=True) account.coming = NotAvailable account._link_id = None account._stp = None a = cols[self.COL_LABEL].find('a') if a is not None: url = urlparse(a.attrib['href']) p = dict(parse_qsl(url.query)) account._link_id = p.get('ch4', None) account._stp = p.get('stp', None) for input_tag in tr.xpath('.//input[starts-with(@id, "urlRib")]'): m = re.search('ch4=(\w+)', input_tag.get('value', '')) if m: account.iban = unicode(m.group(1)) accounts.append(account) # If there are also personnal accounts linked, display the page and iter on them. if pro and len(self.document.xpath('//div[@class="onglets"]//a[contains(@href, "afficherComptesPrives")]')) > 0: self.browser.select_form(name='myForm') self.browser.set_all_readonly(False) self.browser['udcAction'] = '/afficherComptesPrives' self.browser.submit() for a in self.browser.page.get_list(False): accounts.append(a) return accounts
def get_list(self): iban = None for tr in self.document.xpath('//table[@class="ca-table"]/tr'): if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols or len(cols) < 7: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency( self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None a = cols[0].find('a') if a is not None: account._link = a.attrib['href'].replace(' ', '%20') page = self.browser.get_page( self.browser.openurl(account._link)) url = page.get_iban_url() if url: page = self.browser.get_page(self.browser.openurl(url)) iban = account.iban = page.get_iban() elif iban: # In case there is no available IBAN on this account (for # example saving account), calculate it from the previous # IBAN. bankcode = iban[4:9] counter = iban[9:14] key = 97 - ((int(bankcode) * 89 + int(counter) * 15 + int(account.id) * 3) % 97) account.iban = iban[: 4] + bankcode + counter + account.id + str( key) yield account
def parse_table(self, what, actype=Account.TYPE_UNKNOWN): tables = self.document.xpath("//table[@id='%s']" % what, smart_strings=False) if len(tables) < 1: return lines = tables[0].xpath(".//tbody/tr") for line in lines: account = Account() tmp = line.xpath("./td//a")[0] account.label = to_unicode(tmp.text) account.type = actype account._link_id = tmp.get("href") if 'BourseEnLigne' in account._link_id: account.type = Account.TYPE_MARKET tmp = line.xpath("./td/span/strong") if len(tmp) >= 2: tmp_id = tmp[0].text tmp_balance = tmp[1].text else: tmp_id = line.xpath("./td//span")[1].text tmp_balance = tmp[0].text account.id = tmp_id account.currency = account.get_currency(tmp_balance) account.balance = Decimal( FrenchTransaction.clean_amount(tmp_balance)) if account.id in self.accounts: a = self.accounts[account.id] a._card_links.append(account._link_id) if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance else: account._card_links = [] self.accounts[account.id] = account page = self.browser.get_page( self.browser.openurl( self.browser.buildurl( '/voscomptes/canalXHTML/comptesCommun/imprimerRIB/init-imprimer_rib.ea', ('compte.numero', account.id)))) account.iban = page.get_iban()
def get_list(self): iban = None for tr in self.document.xpath('//table[@class="ca-table"]/tr'): if not tr.attrib.get('class', '').startswith('colcelligne'): continue cols = tr.findall('td') if not cols or len(cols) < 7: continue account = Account() account.id = self.parser.tocleanstring(cols[self.COL_ID]) account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) account.type = self.TYPES.get(account.label, Account.TYPE_UNKNOWN) balance = self.parser.tocleanstring(cols[self.COL_VALUE]) # we have to ignore those accounts, because using NotAvailable # makes boobank and probably many others crash if balance in ('indisponible', ''): continue account.balance = Decimal(Transaction.clean_amount(balance)) account.currency = account.get_currency(self.parser.tocleanstring(cols[self.COL_CURRENCY])) account._link = None a = cols[0].find('a') if a is not None: account._link = a.attrib['href'].replace(' ', '%20') page = self.browser.get_page(self.browser.openurl(account._link)) url = page.get_iban_url() if url: page = self.browser.get_page(self.browser.openurl(url)) iban = account.iban = page.get_iban() elif iban: # In case there is no available IBAN on this account (for # example saving account), calculate it from the previous # IBAN. bankcode = iban[4:9] counter = iban[9:14] key = 97 - ((int(bankcode) * 89 + int(counter) * 15 + int(account.id) * 3) % 97) account.iban = iban[:4] + bankcode + counter + account.id + str(key) yield account
def iter_accounts(self): if self.accounts: for account in self.accounts: yield account return waiting = False for project in self.users['projects']: me = self.request('/user/%s/project/%s/' % (self.users['userId'], project['projectId'])) waiting = (me['status'] in ('RETURN_CUSTOMER_SERVICE', 'SUBSCRIPTION_STEP_3')) # Check project in progress if not me['numeroContrat'] or not me['dateAdhesion']: continue a = Account() a.id = "".join(me['numeroContrat'].split()) a.label = " ".join(me['supportEpargne'].split("_")) a.type = Account.TYPE_LIFE_INSURANCE if "assurance vie" in a.label.lower() else \ Account.TYPE_MARKET if "compte titre" in a.label.lower() else \ Account.TYPE_UNKNOWN a.balance = CleanDecimal().filter(me['solde']) a.iban = me['ibancompteTitre'] or NotAvailable a.number = project['projectId'] a.valuation_diff = CleanDecimal().filter(me['performanceEuro']) a._startbalance = me['montantDepart'] self.accounts.append(a) self.iter_investment(a, me['sousJacents']) yield a if not self.accounts and waiting: raise ActionNeeded( "Le service client Yomoni est en attente d'un retour de votre part" )
def iter_accounts(self): if self.accounts: for account in self.accounts: yield account return waiting = False for project in self.users['projects']: self.open('/user/%s/project/%s/' % (self.users['userId'], project['projectId']), method="OPTIONS") me = self.request('/user/%s/project/%s/' % (self.users['userId'], project['projectId']), headers=self.request_headers) waiting = (me['status'] in self.waiting_statuses) # Check project in progress if not me['numeroContrat'] or not me['dateAdhesion']: continue a = Account() a.id = "".join(me['numeroContrat'].split()) a.label = " ".join(me['supportEpargne'].split("_")) a.type = Account.TYPE_LIFE_INSURANCE if "assurance vie" in a.label.lower() else \ Account.TYPE_MARKET if "compte titre" in a.label.lower() else \ Account.TYPE_PEA if "pea" in a.label.lower() else \ Account.TYPE_UNKNOWN a.balance = CleanDecimal().filter(me['solde']) a.currency = u'EUR' # performanceEuro, montantEuro everywhere in Yomoni JSON a.iban = me['ibancompteTitre'] or NotAvailable a.number = project['projectId'] a.valuation_diff = CleanDecimal().filter(me['performanceEuro']) a._startbalance = me['montantDepart'] self.accounts.append(a) self.iter_investment(a, me['sousJacents']) yield a if not self.accounts and waiting: raise ActionNeeded("Le service client Yomoni est en attente d'un retour de votre part.")
def parse_table(self, what, actype=Account.TYPE_UNKNOWN): tables = self.document.xpath("//table[@id='%s']" % what, smart_strings=False) if len(tables) < 1: return lines = tables[0].xpath(".//tbody/tr") for line in lines: account = Account() tmp = line.xpath("./td//a")[0] account.label = to_unicode(tmp.text) account.type = actype account._link_id = tmp.get("href") if 'BourseEnLigne' in account._link_id: account.type = Account.TYPE_MARKET tmp = line.xpath("./td/span/strong") if len(tmp) >= 2: tmp_id = tmp[0].text tmp_balance = tmp[1].text else: tmp_id = line.xpath("./td//span")[1].text tmp_balance = tmp[0].text account.id = tmp_id account.currency = account.get_currency(tmp_balance) account.balance = Decimal(FrenchTransaction.clean_amount(tmp_balance)) if account.id in self.accounts: a = self.accounts[account.id] a._card_links.append(account._link_id) if not a.coming: a.coming = Decimal('0.0') a.coming += account.balance else: account._card_links = [] self.accounts[account.id] = account page = self.browser.get_page(self.browser.openurl(self.browser.buildurl('/voscomptes/canalXHTML/comptesCommun/imprimerRIB/init-imprimer_rib.ea', ('compte.numero', account.id)))) account.iban = page.get_iban()
def _add_account(self, accounts, link, label, account_type, balance): info = self._get_account_info(link, accounts) if info is None: self.logger.warning('Unable to parse account %r: %r' % (label, link)) return account = Account() account.id = info['id'] if is_rib_valid(info['id']): account.iban = rib2iban(info['id']) account._info = info account.label = label account.type = self.ACCOUNT_TYPES.get( label, info['acc_type'] if 'acc_type' in info else account_type) balance = balance or self.get_balance(account) account.balance = Decimal( FrenchTransaction.clean_amount(balance) ) if balance and balance is not NotAvailable else NotAvailable account.currency = account.get_currency( balance ) if balance and balance is not NotAvailable else NotAvailable account._card_links = [] if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts: a = accounts[account.id] if not a.coming: a.coming = Decimal('0.0') if account.balance and account.balance is not NotAvailable: a.coming += account.balance a._card_links.append(account._info) return accounts[account.id] = account
def get_list(self): accounts = [] for cpt in self.doc.xpath( '//div[contains(@class, " compte") and not(contains(@class, "compte_selected"))]' ): # ignore auto assurance accounts if 'aut' in cpt.get('class'): continue account = Account() account._history_link = Link( './ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) # this is to test if access to the accounts info is blocked for different reasons page = self.browser.open(account._history_link).page if isinstance(page, LoanPage): account = Loan() account._history_link = Link( './ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) if isinstance(page, LoanPage): account.id = CleanText( '(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]' )(cpt) account.label = CleanText( '(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]' )(cpt) account.type = Account.TYPE_LOAN account_history_page = self.browser.open( account._history_link).page account.total_amount = account_history_page.get_total_amount() account.next_payment_amount = account_history_page.get_next_payment_amount( ) account.next_payment_date = account_history_page.get_next_payment_date( ) account.account_label = account_history_page.get_account_label( ) account.subscription_date = account_history_page.get_subscription_date( ) account.maturity_date = account_history_page.get_maturity_date( ) if len(accounts) == 0: global_error_message = page.doc.xpath( '//div[@id="as_renouvellementMIFID.do_"]/div[contains(text(), "Bonjour")] ' '| //div[@id="as_afficherMessageBloquantMigration.do_"]//div[@class="content_message"] ' '| //p[contains(text(), "Et si vous faisiez de Fortuneo votre banque principale")] ' '| //div[@id="as_renouvellementMotDePasse.do_"]//p[contains(text(), "votre mot de passe")]' '| //div[@id="as_afficherSecuriteForteOTPIdentification.do_"]//span[contains(text(), "Pour valider ")]' ) if global_error_message: raise ActionNeeded(CleanText('.')(global_error_message[0])) local_error_message = page.doc.xpath( '//div[@id="error"]/p[@class="erreur_texte1"]') if local_error_message: raise BrowserUnavailable( CleanText('.')(local_error_message[0])) number = RawText('./a[contains(@class, "numero_compte")]')( cpt).replace(u'N° ', '') account.id = CleanText(None).filter(number).replace(u'N°', '') account._card_links = [] card_link = Link('./ul/li/a[contains(text(), "Carte bancaire")]', default='')(cpt) if len(card_link) > 0: account._card_links.append(card_link) account.label = CleanText( './a[contains(@class, "numero_compte")]/@title')(cpt) for pattern, type in self.ACCOUNT_TYPES.items(): if pattern in account._history_link: account.type = type break if account.type in { Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE }: account._investment_link = Link( './ul/li/a[contains(@id, "portefeuille")]')(cpt) balance = self.browser.open( account._investment_link).page.get_balance(account.type) if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: self.browser.investments[account.id] = list( self.browser.open( account._investment_link).page.get_investments( account)) else: balance = self.browser.open( account._history_link).page.get_balance() if account.type is not Account.TYPE_LOAN: account.coming = self.browser.open( account._history_link).page.get_coming() if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: account.currency = self.browser.open( account._investment_link).page.get_currency() else: account.currency = account.get_currency(balance) account.balance = CleanDecimal(None, replace_dots=True).filter(balance) if account.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS): # Need a token sent by SMS to customers account.iban = NotAvailable if (account.label, account.id, account.balance) not in [ (a.label, a.id, a.balance) for a in accounts ]: accounts.append(account) return accounts
def get_list(self): call_response = self.location( '/transactionnel/services/rest/Account/accounts' ).json().get('content', []) seen = set() accounts_list = [] for content in call_response: if self.accnum != '00000000000' and content['numero'] != self.accnum: continue for poste in content['postes']: a = Account() a._number = content['numeroLong'] a._nature = poste['codeNature'] a._codeSousPoste = poste['codeSousPoste'] if 'codeSousPoste' in poste else None a._consultable = poste['consultable'] a._univers = self.current_univers a.id = '%s.%s' % (a._number, a._nature) if a.id in seen: # some accounts like "compte à terme fidélis" have the same _number and _nature # but in fact are kind of closed, so worthless... self.logger.warning('ignored account id %r (%r) because it is already used', a.id, poste.get('numeroDossier')) continue seen.add(a.id) a.type = self.ACCOUNT_TYPES.get(poste['codeNature'], Account.TYPE_UNKNOWN) if a.type == Account.TYPE_CHECKING: iban_response = self.location( '/transactionnel/services/rest/Account/account/%s/iban' % a._number ).json().get('content', {}) a.iban = iban_response.get('iban', NotAvailable) else: a.iban = NotAvailable if a.type == Account.TYPE_CARD: a.parent = find_object(accounts_list, _number=a._number, type=Account.TYPE_CHECKING) if 'numeroDossier' in poste and poste['numeroDossier']: a._file_number = poste['numeroDossier'] a.id += '.%s' % a._file_number if poste['postePortefeuille']: a.label = u'Portefeuille Titres' a.balance = Decimal(str(poste['montantTitres']['valeur'])) a.currency = poste['montantTitres']['monnaie']['code'].strip() if not a.balance and not a.currency and 'dateTitres' not in poste: continue accounts_list.append(a) if 'libelle' not in poste: continue a.label = ' '.join([content['intitule'].strip(), poste['libelle'].strip()]) a.balance = Decimal(str(poste['solde']['valeur'])) a.currency = poste['solde']['monnaie']['code'].strip() # Some accounts may have balance currency if 'Solde en devises' in a.label and a.currency != u'EUR': a.id += str(poste['monnaie']['codeSwift']) accounts_list.append(a) return accounts_list
def get_list(self): accounts = [] for cpt in self.doc.xpath( '//div[contains(@class, " compte") and not(contains(@class, "compte_selected"))]' ): # ignore auto assurance accounts if 'aut' in cpt.get('class'): continue account = Account() account._history_link = Link( './ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) # this is to test if there is a redirection to a form for recently created profiles if len(accounts) == 0: message = self.browser.open( account._history_link).page.doc.xpath( '//div[@id="as_renouvellementMIFID.do_"]') if message: raise ActionNeeded( CleanText('./div[contains(text(), "Bonjour")]')( message[0])) number = RawText('./a[contains(@class, "numero_compte")]')( cpt).replace(u'N° ', '') account.id = CleanText(None).filter(number).replace(u'N°', '') account._card_links = [] card_link = Link('./ul/li/a[contains(text(), "Carte bancaire")]', default='')(cpt) if len(card_link) > 0: account._card_links.append(card_link) account.label = CleanText( './a[contains(@class, "numero_compte")]/@title')(cpt) for pattern, type in self.ACCOUNT_TYPES.iteritems(): if pattern in account._history_link: account.type = type break if account.type in { Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE }: account._investment_link = Link( './ul/li/a[contains(@id, "portefeuille")]')(cpt) balance = self.browser.open( account._investment_link).page.get_balance(account.type) if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: self.browser.cache["investments"][account.id] = list( self.browser.open( account._investment_link).page.get_investments( account)) else: balance = self.browser.open( account._history_link).page.get_balance() if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: account.currency = self.browser.open( account._investment_link).page.get_currency() else: account.currency = account.get_currency(balance) account.balance = CleanDecimal(None, replace_dots=True).filter(balance) if account.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS): # Need a token sent by SMS to customers account.iban = NotAvailable if (account.label, account.id, account.balance) not in [ (a.label, a.id, a.balance) for a in accounts ]: accounts.append(account) return iter(accounts)
def get_list(self): accounts = [] for cpt in self.doc.xpath('//div[contains(@class, " compte") and not(contains(@class, "compte_selected"))]'): # ignore auto assurance accounts if 'aut' in cpt.get('class'): continue account = Account() account._history_link = Link('./ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) # this is to test if access to the accounts info is blocked for different reasons page = self.browser.open(account._history_link).page if isinstance(page, LoanPage): account = Loan() account._history_link = Link('./ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) if isinstance(page, LoanPage): account.id = CleanText('(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]')(cpt) account.label = CleanText('(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]')(cpt) account.type = Account.TYPE_LOAN account_history_page = page account.total_amount = account_history_page.get_total_amount() account.next_payment_amount = account_history_page.get_next_payment_amount() account.next_payment_date = account_history_page.get_next_payment_date() account.account_label = account_history_page.get_account_label() account.subscription_date = account_history_page.get_subscription_date() account.maturity_date = account_history_page.get_maturity_date() if len(accounts) == 0: global_error_message = page.doc.xpath('//div[@id="as_renouvellementMIFID.do_"]/div[contains(text(), "Bonjour")] ' '| //div[@id="as_afficherMessageBloquantMigration.do_"]//div[@class="content_message"] ' '| //p[contains(text(), "Et si vous faisiez de Fortuneo votre banque principale")] ' '| //div[@id="as_renouvellementMotDePasse.do_"]//p[contains(text(), "votre mot de passe")]' '| //div[@id="as_afficherSecuriteForteOTPIdentification.do_"]//span[contains(text(), "Pour valider ")]') if global_error_message: raise ActionNeeded(CleanText('.')(global_error_message[0])) local_error_message = page.doc.xpath('//div[@id="error"]/p[@class="erreur_texte1"]') if local_error_message: raise BrowserUnavailable(CleanText('.')(local_error_message[0])) number = RawText('./a[contains(@class, "numero_compte")]')(cpt).replace(u'N° ', '') account.id = CleanText(None).filter(number).replace(u'N°', '') account._card_links = [] card_link = Link('./ul/li/a[contains(text(), "Carte bancaire")]', default='')(cpt) if len(card_link) > 0: account._card_links.append(card_link) account.label = CleanText('./a[contains(@class, "numero_compte")]/@title')(cpt) for pattern, type in self.ACCOUNT_TYPES.items(): if pattern in account._history_link: account.type = type break investment_page = None if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE}: account._investment_link = Link('./ul/li/a[contains(@id, "portefeuille")]')(cpt) investment_page = self.browser.open(account._investment_link).page balance = investment_page.get_balance(account.type) if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: self.browser.investments[account.id] = list(self.browser.open(account._investment_link).page.get_investments(account)) else: balance = page.get_balance() if account.type is not Account.TYPE_LOAN: account.coming = page.get_coming() if account.type in {Account.TYPE_PEA, Account.TYPE_MARKET}: account.currency = investment_page.get_currency() else: account.currency = account.get_currency(balance) account.balance = CleanDecimal(None, replace_dots=True).filter(balance) if account.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS): # Need a token sent by SMS to customers account.iban = NotAvailable if (account.label, account.id, account.balance) not in [(a.label, a.id, a.balance) for a in accounts]: accounts.append(account) return accounts
def get_accounts(self): # This is required in case the browser is left in the middle of add_recipient and the session expires. if self.login.is_here(): return self.get_accounts_list() profile_name = self.get_profile_name() if ' ' in profile_name: owner_name = re.search(r' (.+)', profile_name).group(1).upper() else: owner_name = profile_name.upper() # retrieve life insurance accounts self.assurancevie.stay_or_go() if self.no_perm.is_here(): self.logger.warning('Life insurances are unavailable.') else: # retrieve life insurances from popups for a in self.page.get_popup_life_insurance(name=owner_name): self.update_accounts(a) # retrieve life insurances from calie website calie_index = self.page.get_calie_life_insurances_first_index() if calie_index: form = self.page.get_form(id="formRedirectPart") form['INDEX'] = calie_index form.submit() # if only one calie insurance, request directly leads to details on CaliePage if self.calie_detail.is_here(): self.page.check_error() a = Account() a.url = self.url self.page.fill_account(obj=a) self.update_accounts(a) # if several calie insurances, request leads to CalieContractsPage elif self.calie_contracts.is_here(): for a in self.page.iter_calie_life_insurance(): if a.url: self.location(a.url) self.page.fill_account(obj=a) self.update_accounts(a) else: self.logger.warning( '%s has no url to parse detail to' % a) # get back to life insurances list page self.assurancevie.stay_or_go() # retrieve life insurances on special lcl life insurance website if self.page.is_website_life_insurance(): self.go_life_insurance_website() for life_insurance in self.page.iter_life_insurance(): life_insurance = self.update_life_insurance_account( life_insurance) self.update_accounts(life_insurance) self.go_back_from_life_insurance_website() # retrieve accounts on main page self.accounts.go() for a in self.page.get_accounts_list(name=owner_name): if not self.check_accounts(a): continue self.location('/outil/UWRI/Accueil/') if self.no_perm.is_here(): self.logger.warning('RIB is unavailable.') elif self.page.has_iban_choice(): self.rib.go( data={ 'compte': '%s/%s/%s' % (a.id[0:5], a.id[5:11], a.id[11:]) }) if self.rib.is_here(): iban = self.page.get_iban() a.iban = iban if iban and a.id[ 11:] in iban else NotAvailable else: iban = self.page.check_iban_by_account(a.id) a.iban = iban if iban is not None else NotAvailable self.update_accounts(a) # retrieve loans accounts self.loans.stay_or_go() if self.no_perm.is_here(): self.logger.warning('Loans are unavailable.') else: for a in self.page.get_list(): self.update_accounts(a) # retrieve pro loans accounts self.loans_pro.stay_or_go() if self.no_perm.is_here(): self.logger.warning('Loans are unavailable.') else: for a in self.page.get_list(): self.update_accounts(a) if self.connexion_bourse(): for a in self.page.get_list(name=owner_name): self.update_accounts(a) self.deconnexion_bourse() # Disconnecting from bourse portal before returning account list # to be sure that we are on the banque portal # retrieve deposit accounts self.deposit.stay_or_go() if self.no_perm.is_here(): self.logger.warning('Deposits are unavailable.') else: for a in self.page.get_list(name=owner_name): # There is no id on the page listing the 'Compte à terme' # So a form must be submitted to access the id of the contract self.set_deposit_account_id(a) self.update_accounts(a)
def get_list(self): accounts = [] for cpt in self.doc.xpath('//div[contains(@class, " compte") and not(contains(@class, "compte_selected"))]'): # ignore auto assurance accounts if 'aut' in cpt.get('class'): continue account = Account() account._history_link = Link('./ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) # this is to test if access to the accounts info is blocked for different reasons page = self.browser.open(account._history_link).page if isinstance(page, LoanPage): account = Loan() account._history_link = Link('./ul/li/a[contains(@id, "consulter_solde") ' 'or contains(@id, "historique") ' 'or contains(@id, "contrat") ' 'or contains(@id, "assurance_vie_operations")]')(cpt) if isinstance(page, LoanPage): account.id = CleanText('(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]')(cpt) account.label = CleanText('(//p[@id="c_montantEmprunte"]//span[@class="valStatic"]//strong)[1]')(cpt) account.type = Account.TYPE_LOAN account_history_page = page account.total_amount = account_history_page.get_total_amount() account.next_payment_amount = account_history_page.get_next_payment_amount() account.next_payment_date = account_history_page.get_next_payment_date() account.account_label = account_history_page.get_account_label() account.subscription_date = account_history_page.get_subscription_date() account.maturity_date = account_history_page.get_maturity_date() account.ownership = account_history_page.get_owner() if len(accounts) == 0: global_error_message = page.doc.xpath('//div[@id="as_renouvellementMIFID.do_"]/div[contains(text(), "Bonjour")] ' '| //div[@id="as_afficherMessageBloquantMigration.do_"]//div[@class="content_message"] ' '| //p[contains(text(), "Et si vous faisiez de Fortuneo votre banque principale")] ' '| //div[@id="as_renouvellementMotDePasse.do_"]//p[contains(text(), "votre mot de passe")]' '| //div[@id="as_afficherSecuriteForteOTPIdentification.do_"]//span[contains(text(), "Pour valider ")]') if global_error_message: if "Et si vous faisiez de Fortuneo votre banque principale" in CleanText(global_error_message)(self): self.browser.location('/ReloadContext', data={'action': 4}) return raise ActionNeeded(CleanText('.')(global_error_message[0])) local_error_message = page.doc.xpath('//div[@id="error"]/p[@class="erreur_texte1"]') if local_error_message: raise BrowserUnavailable(CleanText('.')(local_error_message[0])) account.id = account.number = CleanText('./a[contains(@class, "numero_compte")]/div')(cpt).replace(u'N° ', '') account._ca = CleanText('./a[contains(@class, "numero_compte")]/@rel')(cpt) account._card_links = [] card_link = Link('./ul/li/a[contains(text(), "Carte bancaire")]', default='')(cpt) if len(card_link) > 0: account._card_links.append(card_link) account.label = CleanText('./a[contains(@class, "numero_compte")]/@title')(cpt) for pattern, type in self.ACCOUNT_TYPES.items(): if pattern in account._history_link: account.type = type break investment_page = None if account.type in (Account.TYPE_PEA, Account.TYPE_MARKET, Account.TYPE_LIFE_INSURANCE): account._investment_link = Link('./ul/li/a[contains(@id, "portefeuille")]')(cpt) investment_page = self.browser.location(account._investment_link).page balance = investment_page.get_balance(account.type) if account.type in (Account.TYPE_PEA, Account.TYPE_MARKET): self.browser.investments[account.id] = list(self.browser.open(account._investment_link).page.get_investments(account)) else: balance = page.get_balance() if account.type is not Account.TYPE_LOAN: account.coming = page.get_coming() if account.type in (Account.TYPE_PEA, Account.TYPE_MARKET): account.currency = investment_page.get_currency() elif balance: account.currency = account.get_currency(balance) account.balance = CleanDecimal.French().filter(balance) if account.type in (Account.TYPE_CHECKING, Account.TYPE_SAVINGS): # Need a token sent by SMS to customers account.iban = NotAvailable if account.type is not Account.TYPE_LOAN: regexp = re.search(r'(m\. |mme\. )(.+)', CleanText('//span[has-class("mon_espace_nom")]')(self.doc), re.IGNORECASE) if regexp and len(regexp.groups()) == 2: gender = regexp.group(1).replace('.', '').rstrip() name = regexp.group(2) label = account.label if re.search(r'(m|mr|me|mme|mlle|mle|ml)\.? (.*)\bou (m|mr|me|mme|mlle|mle|ml)\b(.*)', label, re.IGNORECASE): account.ownership = AccountOwnership.CO_OWNER elif re.search(r'{} {}'.format(gender, name), label, re.IGNORECASE): account.ownership = AccountOwnership.OWNER else: account.ownership = AccountOwnership.ATTORNEY if (account.label, account.id, account.balance) not in [(a.label, a.id, a.balance) for a in accounts]: accounts.append(account) return accounts
def get_list(self): r = self.api_open('/transactionnel/services/rest/Account/accounts') seen = set() for content in r.json()['content']: if self.accnum != '00000000000' and content[ 'numero'] != self.accnum: continue for poste in content['postes']: a = Account() a._number = content['numeroLong'] a._nature = poste['codeNature'] a._consultable = poste['consultable'] a._univers = self.current_univers a.id = '%s.%s' % (a._number, a._nature) if a.id in seen: # some accounts like "compte à terme fidélis" have the same _number and _nature # but in fact are kind of closed, so worthless... self.logger.warning( 'ignored account id %r (%r) because it is already used', a.id, poste.get('numeroDossier')) continue seen.add(a.id) a.type = self.ACCOUNT_TYPES.get(poste['codeNature'], Account.TYPE_UNKNOWN) if a.type == Account.TYPE_CHECKING: iban_response = self.api_open( '/transactionnel/services/rest/Account/account/%s/iban' % a._number).json() a.iban = iban_response['content'][ 'iban'] if 'content' in iban_response else NotAvailable else: a.iban = NotAvailable if 'numeroDossier' in poste and poste['numeroDossier']: a._file_number = poste['numeroDossier'] a.id += '.%s' % a._file_number if poste['postePortefeuille']: a.label = u'Portefeuille Titres' a.balance = Decimal(str(poste['montantTitres']['valeur'])) a.currency = poste['montantTitres']['monnaie'][ 'code'].strip() if not a.balance and not a.currency and 'dateTitres' not in poste: continue yield a if 'libelle' not in poste: continue a.label = ' '.join( [content['intitule'].strip(), poste['libelle'].strip()]) a.balance = Decimal(str(poste['solde']['valeur'])) a.currency = poste['solde']['monnaie']['code'].strip() # Some accounts may have balance currency if 'Solde en devises' in a.label and a.currency != u'EUR': a.id += str(poste['monnaie']['codeSwift']) yield a