diff --git a/modules/bnporc/backend.py b/modules/bnporc/backend.py index 8a8725ee..41d322ec 100644 --- a/modules/bnporc/backend.py +++ b/modules/bnporc/backend.py @@ -89,11 +89,11 @@ class BNPorcBackend(BaseBackend, ICapBank, ICapMessages): def iter_history(self, account): with self.browser: - return self.browser.iter_history(account._link_id) + return self.browser.iter_history(account) def iter_coming(self, account): with self.browser: - return self.browser.iter_coming_operations(account._link_id) + return self.browser.iter_coming_operations(account) def iter_transfer_recipients(self, ignored): for account in self.browser.get_transfer_accounts().itervalues(): diff --git a/modules/bnporc/browser.py b/modules/bnporc/browser.py index 2974e137..818880ee 100644 --- a/modules/bnporc/browser.py +++ b/modules/bnporc/browser.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright(C) 2009-2012 Romain Bignon +# Copyright(C) 2009-2013 Romain Bignon # # This file is part of weboob. # @@ -24,12 +24,14 @@ from logging import warning from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword, BrowserPasswordExpired from weboob.capabilities.bank import TransferError, Transfer -from .pages import AccountsList, AccountHistory, ChangePasswordPage, \ - AccountComing, AccountPrelevement, TransferPage, \ - TransferConfirmPage, TransferCompletePage, \ - LoginPage, ConfirmPage, InfoMessagePage, \ - MessagePage, MessagesPage +from .perso.accounts_list import AccountsList, AccountPrelevement +from .perso.transactions import AccountHistory, AccountComing +from .perso.transfer import TransferPage, TransferConfirmPage, TransferCompletePage +from .perso.login import LoginPage, ConfirmPage, ChangePasswordPage, InfoMessagePage +from .perso.messages import MessagePage, MessagesPage + +from .pro import ProAccountsList, ProAccountHistory __all__ = ['BNPorc'] @@ -54,6 +56,11 @@ class BNPorc(BaseBrowser): '.*Action=DSP_MSG.*': InfoMessagePage, '.*MessagesRecus.*': MessagesPage, '.*BmmFicheLireMessage.*': MessagePage, + + # Pro + 'https?://www.secure.bnpparibas.net/banque/portail/entrepros/Fiche\?.*identifiant=PRO_Une_Comptes.*': ProAccountsList, + 'https?://www.secure.bnpparibas.net/SAF_ROP\?Origine=DSP_HISTOCPT.*': ProAccountHistory, + 'https?://www.secure.bnpparibas.net/SAF_ROP\?Origine=DSP_ET.*': ProAccountHistory, } def __init__(self, *args, **kwargs): @@ -140,75 +147,85 @@ class BNPorc(BaseBrowser): return None - def iter_history(self, id): - if id is None: + def iter_history(self, account): + if account._link_id is None: return iter([]) - if not self.is_on_page(AccountsList): - self.location('/NSFR?Action=DSP_VGLOBALE') + if account._stp is not None: + # Pro + self.location(self.buildurl('/SAF_ROP', Origine='DSP_HISTOCPT', ch4=account._link_id, stp=account._stp)) + else: + # Perso + if not self.is_on_page(AccountsList): + self.location('/NSFR?Action=DSP_VGLOBALE') - execution = self.page.document.xpath('//form[@name="goToApplication"]/input[@name="execution"]')[0].attrib['value'] - data = {'gt': 'homepage:basic-theme', - 'externalIAId': 'IAStatements', - 'cboFlowName': 'flow/iastatement', - 'contractId': id, - 'groupId': '-2', - 'pastOrPendingOperations': 1, - 'groupSelected':'-2', - 'step': 'STAMENTS', - 'pageId': 'releveoperations', - #'operationsPerPage': 100, - #'_eventId': 'changeOperationsPerPage', - 'sendEUD': 'true', - 'execution': execution, - } + execution = self.page.document.xpath('//form[@name="goToApplication"]/input[@name="execution"]')[0].attrib['value'] + data = {'gt': 'homepage:basic-theme', + 'externalIAId': 'IAStatements', + 'cboFlowName': 'flow/iastatement', + 'contractId': account._link_id, + 'groupId': '-2', + 'pastOrPendingOperations': 1, + 'groupSelected':'-2', + 'step': 'STAMENTS', + 'pageId': 'releveoperations', + #'operationsPerPage': 100, + #'_eventId': 'changeOperationsPerPage', + 'sendEUD': 'true', + 'execution': execution, + } - self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/FicheA', urllib.urlencode(data)) + self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/FicheA', urllib.urlencode(data)) - execution = self.page.document.xpath('//form[@name="displayStatementForm"]/input[@name="_flowExecutionKey"]')[0].attrib['value'] - data = {'_eventId': 'changeOperationsPerPage', - 'newCategoryId': '', - 'categorisationInProgress': '', - 'contractId': id, - '_flowExecutionKey': execution, - 'groupId': '-2', - 'operations.objectsPerPage': 100, - 'operations.pageNumber': 1, - 'pageId': 'releveoperations', - } + execution = self.page.document.xpath('//form[@name="displayStatementForm"]/input[@name="_flowExecutionKey"]')[0].attrib['value'] + data = {'_eventId': 'changeOperationsPerPage', + 'newCategoryId': '', + 'categorisationInProgress': '', + 'contractId': account._link_id, + '_flowExecutionKey': execution, + 'groupId': '-2', + 'operations.objectsPerPage': 100, + 'operations.pageNumber': 1, + 'pageId': 'releveoperations', + } - # it's not a joke, BNP guys are really crappy. - for i in xrange(30): - data['_operations.list[%d].checkedOff' % i] = 'on' - data['_operations.list[%d].selectedForCategorization' % i] = 'on' - self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/FicheA', urllib.urlencode(data)) + # it's not a joke, BNP guys are really crappy. + for i in xrange(30): + data['_operations.list[%d].checkedOff' % i] = 'on' + data['_operations.list[%d].selectedForCategorization' % i] = 'on' + self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/FicheA', urllib.urlencode(data)) return self.page.iter_operations() - def iter_coming_operations(self, id): - if id is None: + def iter_coming_operations(self, account): + if account._link_id is None: return iter([]) - if not self.is_on_page(AccountsList): - self.location('/NSFR?Action=DSP_VGLOBALE') + if account._stp is not None: + # Pro + self.location(self.buildurl('/SAF_ROP', Origine='DSP_ET', ch4=account._link_id, stp=account._stp)) + else: + # Persô + if not self.is_on_page(AccountsList): + self.location('/NSFR?Action=DSP_VGLOBALE') - execution = self.page.document.xpath('//form[@name="goToApplication"]/input[@name="execution"]')[0].attrib['value'] - data = {'gt': 'homepage:basic-theme', - 'externalIAId': 'IAStatements', - 'cboFlowName': 'flow/iastatement', - 'contractId': id, - 'groupId': '-2', - 'pastOrPendingOperations': 2, - 'groupSelected':'-2', - 'step': 'STAMENTS', - 'pageId': 'mouvementsavenir', - #'operationsPerPage': 100, - #'_eventId': 'changeOperationsPerPage', - 'sendEUD': 'true', - 'execution': execution, - } + execution = self.page.document.xpath('//form[@name="goToApplication"]/input[@name="execution"]')[0].attrib['value'] + data = {'gt': 'homepage:basic-theme', + 'externalIAId': 'IAStatements', + 'cboFlowName': 'flow/iastatement', + 'contractId': account._link_id, + 'groupId': '-2', + 'pastOrPendingOperations': 2, + 'groupSelected':'-2', + 'step': 'STAMENTS', + 'pageId': 'mouvementsavenir', + #'operationsPerPage': 100, + #'_eventId': 'changeOperationsPerPage', + 'sendEUD': 'true', + 'execution': execution, + } - self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/FicheA', urllib.urlencode(data)) + self.location('https://www.secure.bnpparibas.net/banque/portail/particulier/FicheA', urllib.urlencode(data)) return self.page.iter_coming_operations() diff --git a/modules/bnporc/pages/__init__.py b/modules/bnporc/pages/__init__.py deleted file mode 100644 index 26840954..00000000 --- a/modules/bnporc/pages/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- 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 . - - -from .accounts_list import AccountsList -from .transactions import AccountHistory, AccountComing -from .transfer import TransferPage, TransferConfirmPage, TransferCompletePage -from .login import LoginPage, ConfirmPage, ChangePasswordPage, InfoMessagePage -from .messages import MessagePage, MessagesPage - -class AccountPrelevement(AccountsList): pass - -__all__ = ['AccountsList', 'AccountComing', 'AccountHistory', 'LoginPage', - 'ConfirmPage', 'InfoMessagePage', 'AccountPrelevement', 'ChangePasswordPage', - 'TransferPage', 'TransferConfirmPage', 'TransferCompletePage', - 'MessagePage', 'MessagesPage'] diff --git a/modules/bnporc/perso/__init__.py b/modules/bnporc/perso/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/bnporc/pages/accounts_list.py b/modules/bnporc/perso/accounts_list.py similarity index 96% rename from modules/bnporc/pages/accounts_list.py rename to modules/bnporc/perso/accounts_list.py index 3141754e..295bf4bc 100644 --- a/modules/bnporc/pages/accounts_list.py +++ b/modules/bnporc/perso/accounts_list.py @@ -26,7 +26,7 @@ from weboob.capabilities.bank import Account from weboob.capabilities.base import NotAvailable from weboob.tools.browser import BasePage, BrokenPageError, BrowserPasswordExpired -__all__ = ['AccountsList'] +__all__ = ['AccountsList', 'AccountPrelevement'] class AccountsList(BasePage): @@ -53,6 +53,9 @@ class AccountsList(BasePage): def _parse_account(self, tr): account = Account() + # for pro usage + account._stp = None + account.id = tr.xpath('.//td[@class="libelleCompte"]/input')[0].attrib['id'][len('libelleCompte'):] if len(str(account.id)) == 23: account.id = str(account.id)[5:21] @@ -107,3 +110,7 @@ class AccountsList(BasePage): if 'MessagesRecus' in link.attrib.get('href', ''): return link.attrib['href'] raise BrokenPageError('Unable to find the link to the messages page') + + +class AccountPrelevement(AccountsList): + pass diff --git a/modules/bnporc/pages/login.py b/modules/bnporc/perso/login.py similarity index 100% rename from modules/bnporc/pages/login.py rename to modules/bnporc/perso/login.py diff --git a/modules/bnporc/pages/messages.py b/modules/bnporc/perso/messages.py similarity index 100% rename from modules/bnporc/pages/messages.py rename to modules/bnporc/perso/messages.py diff --git a/modules/bnporc/pages/transactions.py b/modules/bnporc/perso/transactions.py similarity index 100% rename from modules/bnporc/pages/transactions.py rename to modules/bnporc/perso/transactions.py diff --git a/modules/bnporc/pages/transfer.py b/modules/bnporc/perso/transfer.py similarity index 100% rename from modules/bnporc/pages/transfer.py rename to modules/bnporc/perso/transfer.py diff --git a/modules/bnporc/pro.py b/modules/bnporc/pro.py new file mode 100644 index 00000000..023bef6c --- /dev/null +++ b/modules/bnporc/pro.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2009-2013 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 urlparse import urlparse, parse_qsl +from decimal import Decimal + +from weboob.capabilities.bank import Account +from weboob.tools.browser import BasePage + +from .perso.transactions import Transaction + + +__all__ = ['ProAccountsList', 'ProAccountHistory'] + + +class ProAccountsList(BasePage): + COL_LABEL = 1 + COL_ID = 2 + COL_BALANCE = 3 + COL_COMING = 5 + def get_list(self): + for tr in self.document.xpath('//tr[@class="comptes"]'): + cols = tr.findall('td') + + account = Account() + account.id = self.parser.tocleanstring(cols[self.COL_ID]) + account.label = self.parser.tocleanstring(cols[self.COL_LABEL]) + account.balance = Decimal(self.parser.tocleanstring(cols[self.COL_BALANCE])) + account.coming = Decimal(self.parser.tocleanstring(cols[self.COL_COMING])) + account._link_id = None + account._stp = None + + a = cols[self.COL_LABEL].find('a') + if a is not None: + url = urlparse(a.attrib['href']) + p = dict(parse_qsl(url.query)) + account._link_id = p.get('ch4', None) + account._stp = p.get('stp', None) + + yield account + +class ProAccountHistory(BasePage): + COL_DATE = 0 + COL_LABEL = 1 + COL_DEBIT = -2 + COL_CREDIT = -1 + + def iter_operations(self): + for i, tr in enumerate(self.document.xpath('//tr[@class="hdoc1" or @class="hdotc1"]')): + if 'bgcolor' not in tr.attrib: + continue + cols = tr.findall('td') + + op = Transaction(i) + + date = self.parser.tocleanstring(cols[self.COL_DATE]) + raw = self.parser.tocleanstring(cols[self.COL_LABEL]) + raw = re.sub(r'[ \xa0]+', ' ', raw).strip() + op.parse(date=date, raw=raw) + + debit = self.parser.tocleanstring(cols[self.COL_DEBIT]) + credit = self.parser.tocleanstring(cols[self.COL_CREDIT]) + op.set_amount(credit, debit) + + yield op + + def iter_coming_operations(self): + raise NotImplementedError()