caissedepargne investment on lifeinsurance and market account

This commit is contained in:
Baptiste Delpey 2015-08-04 16:38:32 +02:00 committed by Romain Bignon
commit d199050366
3 changed files with 138 additions and 8 deletions

View file

@ -21,8 +21,9 @@
from urlparse import urlsplit from urlparse import urlsplit
from weboob.deprecated.browser import Browser, BrowserIncorrectPassword from weboob.deprecated.browser import Browser, BrowserIncorrectPassword
from weboob.capabilities.bank import Account
from .pages import LoginPage, IndexPage, ErrorPage, UnavailablePage from .pages import LoginPage, IndexPage, ErrorPage, UnavailablePage, MarketPage, LifeInsurance
__all__ = ['CaisseEpargne'] __all__ = ['CaisseEpargne']
@ -37,6 +38,11 @@ class CaisseEpargne(Browser):
'https://[^/]+/login.aspx': ErrorPage, 'https://[^/]+/login.aspx': ErrorPage,
'https://[^/]+/Pages/logout.aspx.*': ErrorPage, 'https://[^/]+/Pages/logout.aspx.*': ErrorPage,
'https://[^/]+/page_hs_dei_.*.aspx': UnavailablePage, 'https://[^/]+/page_hs_dei_.*.aspx': UnavailablePage,
'https://[^/]+/Pages/Bourse.*': MarketPage,
'https://www.caisse-epargne.offrebourse.com/ReroutageSJR': MarketPage,
'https://www.caisse-epargne.offrebourse.com/Portefeuille': MarketPage,
'https://[^/]+/Assurance/Pages/Assurance.aspx': LifeInsurance,
'https://www.extranet2.caisse-epargne.fr.*': LifeInsurance,
} }
def __init__(self, nuser, *args, **kwargs): def __init__(self, nuser, *args, **kwargs):
@ -98,6 +104,8 @@ class CaisseEpargne(Browser):
return None return None
def _get_history(self, info): def _get_history(self, info):
if not info['link'].startswith('HISTORIQUE'):
return
if self.is_on_page(IndexPage): if self.is_on_page(IndexPage):
self.page.go_list() self.page.go_list()
else: else:
@ -122,3 +130,21 @@ class CaisseEpargne(Browser):
for tr in self._get_history(info): for tr in self._get_history(info):
tr.type = tr.TYPE_CARD tr.type = tr.TYPE_CARD
yield tr yield tr
def get_investment(self, account):
if account.type is not Account.TYPE_LIFE_INSURANCE and account.type is not Account.TYPE_MARKET:
raise NotImplementedError()
if self.is_on_page(IndexPage):
self.page.go_list()
else:
self.location(self.buildurl('/Portail.aspx'))
self.page.go_history(account._info)
if account.type is Account.TYPE_MARKET:
self.page.submit()
self.location('https://www.caisse-epargne.offrebourse.com/Portefeuille')
elif account.type is Account.TYPE_LIFE_INSURANCE:
self.page.go_life_insurance()
self.page.submit()
self.location('https://www.extranet2.caisse-epargne.fr%s' % self.page.get_cons_repart())
return self.page.iter_investment()

View file

@ -72,3 +72,6 @@ class CaisseEpargneModule(Module, CapBank):
def iter_coming(self, account): def iter_coming(self, account):
with self.browser: with self.browser:
return self.browser.get_coming(account) return self.browser.get_coming(account)
def iter_investment(self, account):
return self.browser.get_investment(account)

View file

@ -28,7 +28,7 @@ from weboob.deprecated.mech import ClientForm
from weboob.tools.ordereddict import OrderedDict from weboob.tools.ordereddict import OrderedDict
from weboob.deprecated.browser import Page, BrokenPageError, BrowserUnavailable, BrowserIncorrectPassword from weboob.deprecated.browser import Page, BrokenPageError, BrowserUnavailable, BrowserIncorrectPassword
from weboob.capabilities import NotAvailable from weboob.capabilities import NotAvailable
from weboob.capabilities.bank import Account from weboob.capabilities.bank import Account, Investment
from weboob.tools.capabilities.bank.transactions import FrenchTransaction from weboob.tools.capabilities.bank.transactions import FrenchTransaction
@ -126,7 +126,7 @@ class IndexPage(Page):
} }
def _get_account_info(self, a): def _get_account_info(self, a):
m = re.search("PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"]HISTORIQUE_([\d\w&]+)['\"]", a.attrib.get('href', '')) m = re.search("PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"]([HISTORIQUE_\w|SYNTHESE_ASSURANCE_CNP|BOURSE][\d\w&]+)?['\"]", a.attrib.get('href', ''))
if m is None: if m is None:
return None return None
else: else:
@ -134,7 +134,21 @@ class IndexPage(Page):
# and is necessary for navigation. # and is necessary for navigation.
link = m.group(2) link = m.group(2)
parts = link.split('&') parts = link.split('&')
return {'type': parts[0], 'id': parts[1], 'link': link} info = {}
if len(parts) > 1:
info['type'] = parts[0]
info['id'] = parts[1]
else:
id = re.search("([\d]+)", a.attrib.get('title'))
info['type'] = link
info['id'] = id.group(1)
if info['type'] == 'SYNTHESE_ASSURANCE_CNP':
info['acc_type'] = Account.TYPE_LIFE_INSURANCE
if info['type'] == 'BOURSE':
info['acc_type'] = Account.TYPE_MARKET
info['link'] = link
return info
def _add_account(self, accounts, link, label, account_type, balance): def _add_account(self, accounts, link, label, account_type, balance):
info = self._get_account_info(link) info = self._get_account_info(link)
@ -146,12 +160,12 @@ class IndexPage(Page):
account.id = info['id'] account.id = info['id']
account._info = info account._info = info
account.label = label account.label = label
account.type = account_type account.type = info['acc_type'] if 'acc_type' in info else account_type
account.balance = Decimal(FrenchTransaction.clean_amount(balance)) account.balance = Decimal(FrenchTransaction.clean_amount(balance)) if balance else NotAvailable
account.currency = account.get_currency(balance) account.currency = account.get_currency(balance)
account._card_links = [] account._card_links = []
if account._info['type'] == 'CB' and account.id in accounts: if account._info['type'] == 'HISTORIQUE_CB' and account.id in accounts:
a = accounts[account.id] a = accounts[account.id]
if not a.coming: if not a.coming:
a.coming = Decimal('0.0') a.coming = Decimal('0.0')
@ -233,7 +247,7 @@ class IndexPage(Page):
self.browser.select_form(name='main') self.browser.select_form(name='main')
self.browser.set_all_readonly(False) self.browser.set_all_readonly(False)
self.browser['__EVENTTARGET'] = 'MM$SYNTHESE' self.browser['__EVENTTARGET'] = 'MM$SYNTHESE'
self.browser['__EVENTARGUMENT'] = 'HISTORIQUE_%s' % info['link'] self.browser['__EVENTARGUMENT'] = info['link']
try: try:
self.browser['MM$m_CH$IsMsgInit'] = '0' self.browser['MM$m_CH$IsMsgInit'] = '0'
except ControlNotFoundError: except ControlNotFoundError:
@ -323,3 +337,90 @@ class IndexPage(Page):
self.browser.submit() self.browser.submit()
return True return True
def go_life_insurance(self):
link = self.document.xpath('//table[@summary="Mes contrats d\'assurance vie"]/tbody/tr//a')[0]
m = re.search("PostBack(Options)?\([\"'][^\"']+[\"'],\s*['\"](REDIR_ASS_VIE[\d\w&]+)?['\"]", link.attrib.get('href', ''))
if m is not None:
self.browser.select_form(name='main')
self.browser.set_all_readonly(False)
self.browser['__EVENTTARGET'] = 'MM$SYNTHESE_ASSURANCE_CNP'
self.browser['__EVENTARGUMENT'] = m.group(2)
try:
self.browser['MM$m_CH$IsMsgInit'] = '0'
except ControlNotFoundError:
# Not available on new website.
pass
self.browser.controls.append(ClientForm.TextControl('text', 'm_ScriptManager', {'value': ''}))
self.browser['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$SYNTHESE'
try:
self.browser.controls.remove(self.browser.find_control(name='Cartridge$imgbtnMessagerie', type='image'))
self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageFondMessagerie', type='image'))
self.browser.controls.remove(self.browser.find_control(name='MM$m_CH$ButtonImageMessagerie', type='image'))
except ControlNotFoundError:
pass
self.browser.submit()
class MarketPage(Page):
def parse_decimal(self, td):
value = self.parser.tocleanstring(td)
if value and value != '-':
return Decimal(FrenchTransaction.clean_amount(value))
else:
return NotAvailable
def submit(self):
self.browser.select_form(nr=1)
self.browser.submit()
def iter_investment(self):
for tbody in self.document.xpath(u'//table[@summary="Contenu du portefeuille valorisé"]/tbody'):
inv = Investment()
inv.label = self.parser.tocleanstring(tbody.xpath('./tr[1]/td[1]/a/span')[0])
inv.code = self.parser.tocleanstring(tbody.xpath('./tr[1]/td[1]/a')[0]).split(' - ')[1]
inv.quantity = self.parse_decimal(tbody.xpath('./tr[2]/td[2]')[0])
inv.unitvalue = self.parse_decimal(tbody.xpath('./tr[2]/td[3]')[0])
inv.unitprice = self.parse_decimal(tbody.xpath('./tr[2]/td[5]')[0])
inv.valuation = self.parse_decimal(tbody.xpath('./tr[2]/td[4]')[0])
inv.diff = self.parse_decimal(tbody.xpath('./tr[2]/td[7]')[0])
yield inv
class LifeInsurance(MarketPage):
def get_cons_repart(self):
return self.document.xpath('//tr[@id="sousMenuConsultation3"]/td/div/a')[0].attrib['href']
def iter_investment(self):
for tr in self.document.xpath(u'//table[@class="boursedetail"]/tr[@class and not(@class="total")]'):
inv = Investment()
libelle = self.parser.tocleanstring(tr.xpath('./td[1]')[0]).split(' ')
inv.label, inv.code = self.split_label_code(libelle)
diff = self.parse_decimal(tr.xpath('./td[6]')[0])
inv.quantity = self.parse_decimal(tr.xpath('./td[2]')[0])
inv.unitvalue = self.parse_decimal(tr.xpath('./td[3]')[0])
inv.unitprice = self.calc(inv.unitvalue, diff)
inv.valuation = self.parse_decimal(tr.xpath('./td[5]')[0])
inv.diff = self.get_diff(inv.valuation, self.calc(inv.valuation, diff))
yield inv
def calc(self, value, diff):
if value is NotAvailable or diff is NotAvailable:
return NotAvailable
return Decimal(value) / (1 + Decimal(diff)/100)
def get_diff(self, valuation, calc):
if valuation is NotAvailable or calc is NotAvailable:
return NotAvailable
return valuation - calc
def split_label_code(self, libelle):
m = re.search('FR\d+', libelle[-1])
if m:
return ' '.join(libelle[:-1]), libelle[-1]
else:
return ' '.join(libelle), NotAvailable