Commit 24659b1a authored by Quentin Defenouillere's avatar Quentin Defenouillere Committed by Romain Bignon

[bnporc-entreprises] Split market account from checking account

The market accounts were not scraped yet, the BNP website seems to have
changed recently.
This patch enables scraping of the market accounts.
The iter_investments method already worked as such, however the
pagination is not yet handled.
When there is only one account on the "invests" page, the checking and
the market account are fused into one account, the market balance is not
scraped, nor are the investments.
This patch enables both accounts to be visible with their respective
balances and the investments related to the market account.

Closes: 6827@zendesk
parent c7ef2ae6
......@@ -19,14 +19,13 @@
from __future__ import unicode_literals
import re
from datetime import datetime
from dateutil.rrule import rrule, MONTHLY
from dateutil.relativedelta import relativedelta
from weboob.browser import LoginBrowser, need_login
from weboob.capabilities.base import find_object
from weboob.capabilities.bank import Account
from weboob.exceptions import BrowserIncorrectPassword, BrowserForbidden
from weboob.browser.url import URL
......@@ -34,7 +33,7 @@ from weboob.tools.capabilities.bank.transactions import sorted_transactions
from .pages import (
LoginPage, AuthPage, AccountsPage, AccountHistoryViewPage, AccountHistoryPage,
ActionNeededPage, TransactionPage, TokenPage, InvestPage
ActionNeededPage, TransactionPage, MarketPage, InvestPage
)
......@@ -61,8 +60,8 @@ class BNPEnterprise(LoginBrowser):
transaction_detail = URL(r'/NCCPresentationWeb/e21/getOptBDDF.do', TransactionPage)
invest = URL(r'/opcvm/lister-composition/afficher.do', InvestPage)
# the token page is used only if there are several type market accounts
token_inv = URL(r'/opcvm/lister-portefeuilles/afficher.do', TokenPage)
# The Market page is used only if there are several market accounts
market = URL(r'/opcvm/lister-portefeuilles/afficher.do', MarketPage)
renew_pass = URL('/sommaire/PseRedirectPasswordConnect', ActionNeededPage)
......@@ -72,7 +71,7 @@ class BNPEnterprise(LoginBrowser):
def do_login(self):
self.login.go()
if self.login.is_here() is False:
if not self.login.is_here():
return
data = {}
......@@ -89,22 +88,26 @@ class BNPEnterprise(LoginBrowser):
@need_login
def get_accounts_list(self):
accounts = []
try:
self.token_inv.go()
if self.token_inv.is_here():
marketaccount = self.page.market_search()
else:
# redirected to invest page
# it means that there is only 1 market account among the ones showed
marketaccount = [self.page.get_market_account_label()]
except BrowserForbidden:
marketaccount = []
# Fetch checking accounts:
for account in self.accounts.stay_or_go().iter_accounts():
label_tmp = re.search(r'(\d{4,})', account.label)
if (label_tmp and label_tmp.group(0) in marketaccount) or account.label in marketaccount:
account.type = Account.TYPE_MARKET
accounts.append(account)
# Fetch market accounts:
try:
self.market.go()
if self.market.is_here():
for market_account in self.page.iter_market_accounts():
market_account.parent = find_object(accounts, label=market_account._parent)
accounts.append(market_account)
elif self.invest.is_here():
# Redirected to invest page, meaning there is only 1 market account.
# We thus create an Account object for this unique market account.
account = self.page.get_unique_market_account()
account.parent = find_object(accounts, label=account._parent)
accounts.append(account)
except BrowserForbidden:
pass
return accounts
......@@ -116,6 +119,9 @@ class BNPEnterprise(LoginBrowser):
@need_login
def iter_history(self, account):
# There is no available history for market accounts
if account.type == Account.TYPE_MARKET:
return []
return self._iter_history_base(account)
def _iter_history_base(self, account):
......@@ -144,24 +150,30 @@ class BNPEnterprise(LoginBrowser):
@need_login
def iter_coming_operations(self, account):
# There is no available coming operation for market accounts
if account.type == Account.TYPE_MARKET:
return []
self.account_coming_view.go(identifiant=account.iban)
self.account_coming.go(identifiant=account.iban)
return self.page.iter_coming()
@need_login
def iter_investment(self, account):
if account.type == Account.TYPE_MARKET:
self.token_inv.go()
if self.token_inv.is_here():
if account.type != Account.TYPE_MARKET:
return
self.market.go()
# If there is more than one market account, we must fetch the account params:
if not account._unique:
if self.market.is_here():
token = self.page.get_token()
id_invest = self.page.get_id(label=account.label)
data = {"numeroCompte": id_invest, "_csrf": token}
self.location('/opcvm/lister-composition/redirect-afficher.do', data=data)
else:
self.invest.go()
for tr in self.page.iter_investment():
yield tr
for inv in self.page.iter_investment():
yield inv
@need_login
def get_profile(self):
......
......@@ -30,7 +30,7 @@ from weboob.browser.filters.html import TableCell, Attr
from weboob.browser.elements import DictElement, ItemElement, method, TableElement
from weboob.browser.filters.standard import (
CleanText, CleanDecimal, Date, Regexp, Format, Eval, BrowserURL, Field,
Async,
Async, Currency,
)
from weboob.capabilities.bank import Transaction, Account, Investment
from weboob.capabilities.profile import Person
......@@ -105,8 +105,10 @@ class ActionNeededPage(HTMLPage):
class AccountsPage(LoggedPage, JsonPage):
TYPES = {u'Compte chèque': Account.TYPE_CHECKING,
u'Compte à vue': Account.TYPE_CHECKING}
TYPES = {
'Compte chèque': Account.TYPE_CHECKING,
'Compte à vue': Account.TYPE_CHECKING,
}
@method
class iter_accounts(DictElement):
......@@ -333,7 +335,40 @@ class TransactionPage(LoggedPage, JsonPage):
return self.doc['carteNum']
class TokenPage(LoggedPage, HTMLPage):
class MarketPage(LoggedPage, HTMLPage):
TYPES = {
'comptes de titres': Account.TYPE_MARKET,
}
@method
class iter_market_accounts(TableElement):
item_xpath = '//table[@id="table-portefeuille"]/tbody[@class="main-content"]/tr'
head_xpath = '//table[@id="table-portefeuille"]/thead/tr/th/label'
col_label = 'Portefeuille-titres'
col_balance = re.compile('Valorisation')
col__parent = re.compile('Compte courant')
class item(ItemElement):
klass = Account
# Market accounts have no IBAN so we use the account number and specify
# "MARKET" at the end to differentiate from its parent account.
obj_id = Format('%sMARKET', Regexp(CleanText(TableCell('label')), r'\*(.*)\*', default=None))
obj_label = CleanText(TableCell('label'))
obj_balance = CleanDecimal(TableCell('balance'), replace_dots=True)
obj_currency = Currency(TableCell('balance'))
obj__parent = CleanText(TableCell('_parent'))
obj_coming = NotAvailable
obj_iban = NotAvailable
obj__unique = False
def obj_type(self):
for key, type in self.page.TYPES.items():
if key in CleanText(TableCell('label'))(self).lower():
return type
return Account.TYPE_UNKNOWN
def get_token(self):
return Attr('//meta[@name="_csrf"]', 'content')(self.doc)
......@@ -343,18 +378,23 @@ class TokenPage(LoggedPage, HTMLPage):
if id_simple in CleanText(options)(self.doc):
return CleanText(options.xpath('./@value'))(self)
def market_search(self):
marketaccount = []
for account in self.doc.xpath('//div[@class="filterbox-content hide"]//select[@id="numero-compte-titre"]//option'):
account = CleanText(account)(self.doc)
temp = re.search(r'[0-9]+', account)
if temp != None:
marketaccount.append(temp.group(0))
return marketaccount
class InvestPage(LoggedPage, HTMLPage):
@method
class get_unique_market_account(ItemElement):
klass = Account
# Market accounts have no IBAN so we use the account number and specify
# "MARKET" at the end to differentiate it from its parent account.
obj_id = Format('%sMARKET', Regexp(CleanText('//div[@class="head"]/h2'), r'\*(.*)\*', default=None))
obj_label = CleanText('//div[@class="head"]/h2')
obj_balance = CleanDecimal('//div[@id="apercu-val"]/h1', replace_dots=True)
obj_currency = Currency('//div[@id="apercu-val"]/h1')
obj_type = Account.TYPE_MARKET
obj__parent = CleanText('//h3/span[span[@class="info-cheque"]]', children=False)
obj__unique = True
class InvestPage(LoggedPage, HTMLPage):
@method
class iter_investment(TableElement):
item_xpath = '//table[@class="csv-data-container hide"]//tr'
......@@ -367,6 +407,12 @@ class InvestPage(LoggedPage, HTMLPage):
col_valuation = 'Valorisation'
col_diff = '+/- value'
"""
Note: Pagination is not handled yet for investments, if we find a
customer with more than 10 invests we might have to handle clicking
on the button to get 50 invests per page or check if there is a link.
"""
class item(ItemElement):
klass = Investment
......@@ -379,8 +425,5 @@ class InvestPage(LoggedPage, HTMLPage):
obj_code_type = lambda self: Investment.CODE_TYPE_ISIN if Field('code')(self) is not NotAvailable else NotAvailable
def obj_code(self):
chaine = CleanText(TableCell('label'))(self)
return re.search(r'(\w+) - ', chaine).group(0)[:-3]
def get_market_account_label(self):
return CleanText('//h3/span[span]', children=False)(self.doc)
string = CleanText(TableCell('label'))(self)
return re.search(r'(\w+) - ', string).group(0)[:-3]
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