Commit f2c6db07 authored by Maxime Pommier's avatar Maxime Pommier Committed by Romain Bignon

[boursorama] Added the accounts ownership

Their no real way to be sure of the account ownership for now in Boursorama. We have to use
some information to guess at the best (account position in the page, owner's children names...)
parent b47e1255
......@@ -31,8 +31,9 @@ from weboob.browser.exceptions import LoggedOut, ClientError
from import (
Account, AccountNotFound, TransferError, TransferInvalidAmount,
TransferInvalidEmitter, TransferInvalidLabel, TransferInvalidRecipient,
AddRecipientStep, Recipient, Rate, TransferBankError,
AddRecipientStep, Recipient, Rate, TransferBankError, AccountOwnership,
from weboob.capabilities.base import empty
from import Advisor
from import VirtKeyboardError
from import Value
......@@ -116,6 +117,7 @@ class BoursoramaBrowser(RetryLoginBrowser, StatesMixin):
authentication = URL('/securisation', AuthenticationPage)
iban = URL('/compte/(?P<webid>.*)/rib', IbanPage)
profile = URL('/mon-profil/', ProfilePage)
profile_children = URL('/mon-profil/coordonnees/enfants', ProfilePage)
expert = URL('/compte/derive/', ExpertPage)
......@@ -187,6 +189,39 @@ class BoursoramaBrowser(RetryLoginBrowser, StatesMixin):
if self.authentication.is_here():
raise BrowserIncorrectAuthenticationCode('Invalid PIN code')
def ownership_guesser(self):
ownerless_accounts = [account for account in self.accounts_list if empty(account.ownership)]
# On Boursorama website, all mandatory accounts have the real owner name in their label, and
# children names are findable in the PSU profile.
children_names =
for ownerless_account in ownerless_accounts:
for child_name in children_names:
if child_name in ownerless_account.label:
ownerless_account.ownership = AccountOwnership.ATTORNEY
# If there are two deferred card for with the same parent account, we assume that's the parent checking
# account is a 'CO_OWNER' account
parent_accounts = []
for account in self.accounts_list:
if account.type == Account.TYPE_CARD and empty(account.parent.ownership):
if account.parent in parent_accounts:
account.parent.ownership = AccountOwnership.CO_OWNER
# We set all accounts without ownership as if they belong to the credential owner
for account in self.accounts_list:
if empty(account.ownership) and account.type != Account.TYPE_CARD:
account.ownership = AccountOwnership.OWNER
# Account cards should be set with the same ownership of their parents accounts
for account in self.accounts_list:
if account.type == Account.TYPE_CARD:
account.ownership = account.parent.ownership
def go_cards_number(self, link):
......@@ -268,6 +303,7 @@ class BoursoramaBrowser(RetryLoginBrowser, StatesMixin):
if exc:
raise exc
return self.accounts_list
def get_account(self, id):
......@@ -32,12 +32,13 @@ from weboob.browser.filters.standard import (
CleanText, CleanDecimal, Field, Format,
Regexp, Date, AsyncLoad, Async, Eval, Env,
Currency as CleanCurrency, Map, Coalesce,
MapIn, Lower,
from weboob.browser.filters.json import Dict
from weboob.browser.filters.html import Attr, Link, TableCell
from import (
Account, Investment, Recipient, Transfer, AccountNotFound,
AddRecipientBankError, TransferInvalidAmount, Loan,
AddRecipientBankError, TransferInvalidAmount, Loan, AccountOwnership,
from import create_french_liquidity
from weboob.capabilities.base import NotAvailable, Currency
......@@ -237,6 +238,12 @@ class AccountsPage(LoggedPage, HTMLPage):
'carte': Account.TYPE_CARD,
'Comptes de mes enfants': AccountOwnership.ATTORNEY,
'joint': AccountOwnership.CO_OWNER,
'commun': AccountOwnership.CO_OWNER,
class iter_accounts(ListElement):
item_xpath = '//table[@class="table table--accounts"]/tr[has-class("table__line--account") and count(descendant::td) > 1 and @data-line-account-href]'
......@@ -314,6 +321,23 @@ class AccountsPage(LoggedPage, HTMLPage):
return Account.TYPE_UNKNOWN
def obj_ownership(self):
ownership = Coalesce(
CleanText('../tr[contains(@class, "list--accounts--master")]//h4/text()'),,
return ownership
def obj_url(self):
link = Attr('.//a[has-class("account--name")] | .//a[2] | .//div/a', 'href', default=NotAvailable)(self)
return urljoin(, link)
......@@ -778,6 +802,17 @@ def MySelect(*args, **kwargs):
class ProfilePage(LoggedPage, HTMLPage):
def get_children_firstnames(self):
names = []
for child in self.doc.xpath('//span[@class="transfer__account-name"]'):
name = child.text.split('\n')
assert len(name) > 1, "There is a child without firstname or the html code has changed !"
return names
class get_profile(ItemElement):
klass = Person
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment