Commit 93fe9b90 authored by Sylvie Ye's avatar Sylvie Ye Committed by Romain Bignon

[sgpe] retrieve market accounts and invests

parent 6e50faae
......@@ -26,7 +26,7 @@ from weboob.browser.browsers import LoginBrowser, need_login, StatesMixin
from weboob.browser.url import URL
from weboob.browser.exceptions import ClientError
from weboob.exceptions import BrowserIncorrectPassword, ActionNeeded, NoAccountsException
from weboob.capabilities.base import find_object
from weboob.capabilities.base import find_object, NotAvailable
from import (
AccountNotFound, RecipientNotFound, AddRecipientStep, AddRecipientBankError,
Recipient, TransferBankError, AccountOwnerType,
......@@ -36,7 +36,7 @@ from import Value
from .pages import (
LoginPage, CardsPage, CardHistoryPage, IncorrectLoginPage,
ProfileProPage, ProfileEntPage, ChangePassPage, SubscriptionPage, InscriptionPage,
ErrorPage, UselessPage,
ErrorPage, UselessPage, MainPage, MarketAccountPage, MarketInvestmentPage,
from .json_pages import (
AccountsJsonPage, BalancesJsonPage, HistoryJsonPage, BankStatementPage,
......@@ -107,6 +107,10 @@ class SGPEBrowser(LoginBrowser):
def get_cb_operations(self, account):
if account.type in (account.TYPE_MARKET, ):
# market account transactions are in checking account
self.location('/Pgn/NavigationServlet?PageID=Cartes&MenuID=%sOPF&Classeur=1&NumeroPage=1&Rib=%s&Devise=%s' % (self.MENUID,, account.currency))
if self.inscription_page.is_here():
......@@ -132,6 +136,8 @@ class SGEnterpriseBrowser(SGPEBrowser):
CERTHASH = '2231d5ddb97d2950d5e6fc4d986c23be4cd231c31ad530942343a8fdcc44bb99'
main_page = URL('/icd-web/syd-front/index-comptes.html', MainPage)
accounts = URL('/icd/syd-front/data/syd-comptes-accederDepuisMenu.json', AccountsJsonPage)
intraday_accounts = URL('/icd/syd-front/data/syd-intraday-accederDepuisMenu.json', AccountsJsonPage)
......@@ -142,6 +148,13 @@ class SGEnterpriseBrowser(SGPEBrowser):
'/icd/syd-front/data/syd-intraday-chargerDetail.json', HistoryJsonPage)
history_next = URL('/icd/syd-front/data/syd-comptes-chargerProchainLotEcriture.json', HistoryJsonPage)
market_investment = URL(r'/Pgn/NavigationServlet\?.*PageID=CompteTitreDetailFrame',
market_accounts = URL(r'/Pgn/NavigationServlet\?.*PageID=CompteTitreFrame',
profile = URL('/gae/afficherModificationMesDonnees.html', ProfileEntPage)
subscription = URL(r'/Pgn/NavigationServlet\?MenuID=BANRELRIE&PageID=ReleveRIE&NumeroPage=1&Origine=Menu', SubscriptionPage)
......@@ -177,14 +190,37 @@ class SGEnterpriseBrowser(SGPEBrowser):
acc.owner_type = AccountOwnerType.ORGANIZATION
yield acc
# retrieve market accounts if exist
for market_account in self.iter_market_accounts():
yield market_account
def iter_history(self, account):
if account.type in (account.TYPE_MARKET, ):
# market account transactions are in checking account
value = self.history.go(data={'cl500_compte': account._id, 'cl200_typeReleve': 'valeur'}).get_value()
for tr in self.history.go(data={'cl500_compte': account._id, 'cl200_typeReleve': value}).iter_history(value=value):
yield tr
for tr in self.location('/icd/syd-front/data/syd-intraday-chargerDetail.json', data={'cl500_compte': account._id}).page.iter_history():
yield tr
def iter_market_accounts(self):
# retrieve market accounts if exist
market_accounts_link =
# there are no examples of entreprise space with market accounts yet
assert not market_accounts_link, 'There are market accounts, retrieve them.'
return []
def iter_investment(self, account):
# there are no examples of entreprise space with market accounts yet
return []
def iter_subscription(self):
subscriber = self.get_profile()
......@@ -257,6 +293,39 @@ class SGProfessionalBrowser(SGEnterpriseBrowser, StatesMixin):
self.need_reload_state = None
super(SGProfessionalBrowser, self).load_state(state)
def iter_market_accounts(self):
# retrieve market accounts if exist
market_accounts_link =
if market_accounts_link is NotAvailable:
return []
assert market_accounts_link, 'Market accounts link xpath may have changed'
# need to be on market accounts page to get the accounts iframe
market_accounts_list_link =
if market_accounts_list_link is NotAvailable:
return []
assert market_accounts_link, 'Market accounts iframe link xpath may have changed'
def iter_investment(self, account):
if account.type not in (account.TYPE_MARKET, ):
return []
assert account._url_data, 'This account has no url to retrieve investments'
# need to be on market accounts investment page to get the invetment iframe
self.location('/Pgn/NavigationServlet?%s' % account._url_data)
invests_list_link =
assert invests_list_link, 'It seems that this market account has no investment'
def copy_recipient_obj(self, recipient):
rcpt = Recipient() = recipient.iban
......@@ -24,15 +24,17 @@ import re
from io import BytesIO
from weboob.browser.pages import HTMLPage, LoggedPage
from weboob.browser.elements import ListElement, ItemElement, method
from weboob.browser.elements import ListElement, ItemElement, method, TableElement
from weboob.browser.filters.standard import (
CleanText, CleanDecimal, Date,
Env, Regexp, Field, Format,
Env, Regexp, Field, Format, TableCell,
from weboob.browser.filters.html import Attr
from weboob.browser.filters.html import Attr, Link
from import is_isin_valid
from import FrenchTransaction
from weboob.capabilities.profile import Profile, Person
from weboob.capabilities.bill import Document, Subscription
from import Account, Investment
from weboob.exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable
from import json
......@@ -269,3 +271,83 @@ class InscriptionPage(SGPEPage):
class UselessPage(LoggedPage, SGPEPage):
class MainPage(LoggedPage, SGPEPage):
def get_market_accounts_link(self):
market_accounts_link = Link('//li/a[@title="Comptes titres"]', default=None)(self.doc)
if market_accounts_link:
return market_accounts_link
elif self.doc.xpath('//span[contains(text(), "Comptes titres") and contains(@title, "pas habilité à utiliser ce service")]'):
return NotAvailable
# return None when we don't know if there are market accounts or not
# it will be handled in ``
class MarketAccountPage(LoggedPage, SGPEPage):
def get_table_iframe_link(self):
if self.doc.xpath('//div[contains(text(), "Aucun compte-titres")]'):
return NotAvailable
return Attr('//iframe[@id="frameTableau"]', 'src')(self.doc)
class iter_market_accounts(TableElement):
item_xpath = '//table[@id="tab-corps"]//tr'
head_xpath = '//table[@id="tab-entete"]//td'
col_id = 'COMPTE'
col_label = 'INTITULE'
col_balance = 'EVALUATION'
class item(ItemElement):
def condition(self):
# table with empty row filled by empty `td`
return Field('number')(self)
klass = Account
obj_id = Format('%s_TITRE', CleanText(TableCell('id'), replace=[(' ', '')]))
obj_number = CleanText(TableCell('id'), replace=[(' ', '')])
obj_label = CleanText(TableCell('label'))
obj_balance = CleanDecimal.French(CleanText(TableCell('balance')))
obj_type = Account.TYPE_MARKET
# all `a` balises have same `href`
obj__url_data = Regexp(Link('(.//a)[1]'), r"lienParent\('(.*)'\)", default=NotAvailable)
class MarketInvestmentPage(LoggedPage, SGPEPage):
def get_table_iframe_link(self):
return Attr('//iframe[@id="frameTableau"]', 'src')(self.doc)
class iter_investment(TableElement):
item_xpath = '//table[@id="tab-corps"]//tr'
head_xpath = '//table[@id="tab-entete"]//td'
col_code = 'CODE'
col_label = 'VALEUR'
col_valuation = 'MONTANT'
col_quantity = 'QUANTITE'
col_unitvalue = 'COURS'
class item(ItemElement):
def condition(self):
# table with empty row filled by empty `td`
return Field('valuation')(self)
klass = Investment
obj_code_type = Investment.CODE_TYPE_ISIN
obj_label = CleanText(TableCell('label'))
obj_valuation = CleanDecimal.French(CleanText(TableCell('valuation')))
obj_quantity = CleanDecimal.French(CleanText(TableCell('quantity')))
obj_unitvalue = CleanDecimal.French(CleanText(TableCell('unitvalue')))
def obj_code(self):
code = CleanText(TableCell('code'))(self)
# there is no example of invests without valid ISIN code
# wait for it to retrieve them corretly
assert is_isin_valid(code), 'This code is not a valid ISIN, please check what invest is it.'
return code
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