diff --git a/modules/bnporc/browser.py b/modules/bnporc/browser.py index 771d76af..99f1bbb1 100644 --- a/modules/bnporc/browser.py +++ b/modules/bnporc/browser.py @@ -21,12 +21,14 @@ import urllib from datetime import datetime from logging import warning +from random import randint from weboob.deprecated.browser import Browser, BrowserIncorrectPassword, BrowserPasswordExpired -from weboob.capabilities.bank import TransferError, Transfer +from weboob.capabilities.bank import TransferError, Transfer, Account from .perso.accounts_list import AccountsList, AccountPrelevement from .perso.transactions import AccountHistory, AccountComing +from .perso.investment import AccountMarketInvestment, AccountLifeInsuranceInvestment from .perso.transfer import TransferPage, TransferConfirmPage, TransferCompletePage from .perso.login import LoginPage, ConfirmPage, ChangePasswordPage, InfoMessagePage from .perso.messages import MessagePage, MessagesPage @@ -44,6 +46,8 @@ class BNPorc(Browser): PAGES = {'.*pageId=unedescomptes.*': AccountsList, '.*pageId=releveoperations.*': AccountHistory, '.*FicheA': AccountHistory, + '.*SAF_DPF.*': AccountMarketInvestment, + '.*identifiant=Assurance_Vie_Consultation.*': AccountLifeInsuranceInvestment, '.*Action=SAF_CHM.*': ChangePasswordPage, '.*pageId=mouvementsavenir.*': AccountComing, '.*NS_AVEDP.*': AccountPrelevement, @@ -229,6 +233,35 @@ class BNPorc(Browser): return self.page.iter_coming_operations() + def iter_investment(self, account): + if account.type == Account.TYPE_MARKET: + if not account.iban: + return iter([]) + + stp = datetime.strftime(datetime.now(), '%Y%m%d%H%M%S') + self.location('https://www.secure.bnpparibas.net/SAF_DPF?Origine=DSP_DPF&ch4=' + account.iban + 'stp=' + stp) + + data = {'ch4': account.iban, + 'Origine': 'DSP_DPF', + 'chD': 'oui', + 'chT': 'sans', + 'chU': 'alpha', + 'x': randint(0, 99), + 'y': randint(0, 18) + } + self.location('https://www.secure.bnpparibas.net/SAF_DPF', urllib.urlencode(data)) + + elif account.type == Account.TYPE_LIFE_INSURANCE: + if not account._link_id: + return iter([]) + + self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/Fiche?type=folder&identifiant=Assurance_Vie_Consultation_20071002051044&contrat=' + account._link_id) + + else: + raise NotImplementedError() + + return self.page.iter_investment() + @check_expired_password def get_transfer_accounts(self): if not self.is_on_page(TransferPage): diff --git a/modules/bnporc/module.py b/modules/bnporc/module.py index 10965347..bfe4d103 100644 --- a/modules/bnporc/module.py +++ b/modules/bnporc/module.py @@ -101,6 +101,10 @@ class BNPorcModule(Module, CapBank, CapMessages): with self.browser: return self.browser.iter_coming_operations(account) + def iter_investment(self, account): + with self.browser: + return self.browser.iter_investment(account) + def iter_transfer_recipients(self, ignored): if self.config['website'].get() != 'pp': raise NotImplementedError() diff --git a/modules/bnporc/perso/accounts_list.py b/modules/bnporc/perso/accounts_list.py index d49ccbe7..4d3d46db 100644 --- a/modules/bnporc/perso/accounts_list.py +++ b/modules/bnporc/perso/accounts_list.py @@ -32,7 +32,7 @@ class AccountsList(Page): u'Liquidités': Account.TYPE_CHECKING, u'Epargne disponible': Account.TYPE_SAVINGS, u'Titres': Account.TYPE_MARKET, - u'Assurance vie': Account.TYPE_DEPOSIT, + u'Assurance vie': Account.TYPE_LIFE_INSURANCE, u'Crédit immobilier': Account.TYPE_LOAN, } @@ -77,10 +77,15 @@ class AccountsList(Page): if m: account._link_id = m.group(1) else: - # Can't get history for this account. - account._link_id = None - # To prevent multiple-IDs for CIF (for example), add an arbitrary char in ID. - account.id += 'C' + # Find _link_id of life insurances + m = re.match(r'javascript:overviewRedirectionOperation.*contrat=(\d+)', a.get('onclick', '')) + if m: + account._link_id = m.group(1) + else: + # Can't get history for this account. + account._link_id = None + # To prevent multiple-IDs for CIF (for example), add an arbitrary char in ID. + account.id += 'C' account.label = u''+a.text.strip() diff --git a/modules/bnporc/perso/investment.py b/modules/bnporc/perso/investment.py new file mode 100644 index 00000000..e634946b --- /dev/null +++ b/modules/bnporc/perso/investment.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2009-2012 Romain Bignon +# +# This file is part of weboob. +# +# weboob is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# weboob is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + + +import re +from decimal import Decimal +from xml.etree import ElementTree + +from weboob.deprecated.browser import Page +from weboob.capabilities.bank import Investment +from weboob.tools.capabilities.bank.transactions import FrenchTransaction +from weboob.capabilities.base import NotAvailable + + +def clean_text(el): + text = ElementTree.tostring(el, 'utf-8', 'text').decode('utf-8') + return re.sub(ur'[\s\xa0]+', u' ', text).strip() + + +def clean_cells(cells): + return list(map(clean_text, cells)) + + +def clean_amount(amount): + return Decimal(FrenchTransaction.clean_amount(amount)) if amount else NotAvailable + + +def clean_amounts(amounts): + return list(map(clean_amount, amounts)) + + +class AccountMarketInvestment(Page): + def iter_investment(self): + table = self.document.xpath('//table[@align="center"]')[4] + rows = table.xpath('.//tr[@class="hdoc1"]') + for tr in rows: + cells = clean_cells(tr.findall('td')) + cells[2:] = clean_amounts(cells[2:]) + + inv = Investment() + inv.label, _, inv.quantity, inv.unitvalue, inv.valuation = cells + + tr2 = tr.xpath('./following-sibling::tr')[0] + tr2td = tr2.findall('td')[1] + + inv.id = inv.code = clean_text(tr2.xpath('.//a')[0]) + inv.unitprice = clean_amount(tr2td.xpath('.//td[@class="hdotc1nb"]')[0].text) + + inv.description = u'' + inv.diff = inv.quantity * inv.unitprice - inv.valuation + + yield inv + + +class AccountLifeInsuranceInvestment(Page): + def iter_investment(self): + rows = self.document.xpath('//table[@id="mefav_repartition_supports_BPF"]//tr') + for tr in rows: + cells = clean_cells(tr.findall('td')) + cells[3:] = clean_amounts(cells[3:]) + + inv = Investment() + inv.label, _, inv.code, inv.quantity, inv.unitvalue, inv.valuation, _ = cells + + if inv.code: + inv.id = inv.code + if not inv.unitvalue: + # XXX Fonds eu Euros + inv.code = u'XX' + re.sub(ur'[^A-Za-z0-9]', u'', inv.label).upper() + inv.description = u'' + + yield inv