[weboob] [RFC PATCH 1/2] caissedepargne: New feature to download bank statements

Benjamin Cama benoar at dolka.fr
Sun Mar 31 21:40:18 CEST 2019


Some banks offer to directly download an OFX statement: implement that
for caissedepargne. No iteration on accounts for now.

Signed-off-by: Benjamin Cama <benoar at dolka.fr>
---
 modules/caissedepargne/browser.py  | 19 ++++++++++++++++-
 modules/caissedepargne/module.py   |  7 +++++--
 modules/caissedepargne/pages.py    | 33 ++++++++++++++++++++++++++++++
 modules/creditcooperatif/module.py |  4 ++--
 weboob/capabilities/bank.py        | 15 ++++++++++++++
 5 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/modules/caissedepargne/browser.py b/modules/caissedepargne/browser.py
index 228d625e6..25f0e069f 100644
--- a/modules/caissedepargne/browser.py
+++ b/modules/caissedepargne/browser.py
@@ -49,7 +49,7 @@ from .pages import (
     ProTransferSummaryPage, ProAddRecipientOtpPage, ProAddRecipientPage,
     SmsPage, SmsPageOption, SmsRequest, AuthentPage, RecipientPage, CanceledAuth, CaissedepargneKeyboard,
     TransactionsDetailsPage, LoadingPage, ConsLoanPage, MeasurePage, NatixisLIHis, NatixisLIInv, NatixisRedirectPage,
-    SubscriptionPage, CreditCooperatifMarketPage, UnavailablePage,
+    SubscriptionPage, CreditCooperatifMarketPage, UnavailablePage, StatementPage, StatementAckPage,
 )
 
 from .linebourse_browser import LinebourseAPIBrowser
@@ -70,6 +70,8 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
     account_login = URL('/authentification/manage\?step=account&identifiant=(?P<login>.*)&account=(?P<accountType>.*)', LoginPage)
     loading = URL('https://.*/CreditConso/ReroutageCreditConso.aspx', LoadingPage)
     cons_loan = URL('https://www.credit-conso-cr.caisse-epargne.fr/websavcr-web/rest/contrat/getContrat\?datePourIe(?P<datepourie>)', ConsLoanPage)
+    statement_ack = URL('https://.*/Portail.aspx.*', StatementAckPage)
+    statement = URL('https://.*/Portail.aspx.*', StatementPage)
     transaction_detail = URL('https://.*/Portail.aspx.*', TransactionsDetailsPage)
     recipient = URL('https://.*/Portail.aspx.*', RecipientPage)
     transfer = URL('https://.*/Portail.aspx.*', TransferPage)
@@ -861,3 +863,18 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
         self.page.go_document_list(sub_id=sub_id)
 
         return self.page.download_document(document).content
+
+    @need_login
+    def download_statement(self, account):
+        self.home.go()
+        self.home_tache.go(tache='CPTSYNT1')
+        if self.unavailable_page.is_here():
+            # some users don't have checking account
+            self.home_tache.go(tache='EPASYNT0')
+
+        self.page.go_statement()
+        assert self.statement.is_here()
+        self.page.setup_statement(account)
+        assert self.statement_ack.is_here()
+
+        return self.page.download_statement().content
diff --git a/modules/caissedepargne/module.py b/modules/caissedepargne/module.py
index 30a7b6f9f..bceaf5e6c 100644
--- a/modules/caissedepargne/module.py
+++ b/modules/caissedepargne/module.py
@@ -21,7 +21,7 @@ from collections import OrderedDict
 import re
 from decimal import Decimal
 
-from weboob.capabilities.bank import CapBankWealth, CapBankTransferAddRecipient, AccountNotFound, Account, RecipientNotFound
+from weboob.capabilities.bank import CapBankWealth, CapBankTransferAddRecipient, CapBankStatement, AccountNotFound, Account, RecipientNotFound
 from weboob.capabilities.bill import (
     CapDocument, Subscription, SubscriptionNotFound,
     Document, DocumentNotFound, DocumentTypes,
@@ -38,7 +38,7 @@ from .proxy_browser import ProxyBrowser
 __all__ = ['CaisseEpargneModule']
 
 
-class CaisseEpargneModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapDocument, CapContact, CapProfile):
+class CaisseEpargneModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapBankStatement, CapDocument, CapContact, CapProfile):
     NAME = 'caissedepargne'
     MAINTAINER = u'Romain Bignon'
     EMAIL = 'romain at weboob.org'
@@ -151,3 +151,6 @@ class CaisseEpargneModule(Module, CapBankWealth, CapBankTransferAddRecipient, Ca
             return
 
         return self.browser.download_document(document)
+
+    def download_statement(self, account):
+        return self.browser.download_statement(account)
diff --git a/modules/caissedepargne/pages.py b/modules/caissedepargne/pages.py
index 5d13b6ecd..2a0719cfe 100644
--- a/modules/caissedepargne/pages.py
+++ b/modules/caissedepargne/pages.py
@@ -749,6 +749,14 @@ class IndexPage(LoggedPage, HTMLPage):
     def is_transfer_allowed(self):
         return not self.doc.xpath('//ul/li[contains(text(), "Aucun compte tiers n\'est disponible")]')
 
+    def go_statement(self):
+        form = self.get_form(id='main')
+        form['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$Menu_Ajax'
+        form['__EVENTTARGET'] = 'MM$Menu_Ajax'
+        link = Link('//a[contains(@title, "Télécharger une opération")]')(self.doc)
+        form['__EVENTARGUMENT'] = re.search(r'Ajax", "(.*)", true', link).group(1)
+        form.submit()
+
 
 class ConsLoanPage(JsonPage):
     def get_conso(self):
@@ -1422,6 +1430,31 @@ class SubscriptionPage(LoggedPage, HTMLPage):
         form['MM$COMPTE_EDOCUMENTS$ctrlEDocumentsConsultationDocument$eventId'] = document._event_id
         return form.submit()
 
+class StatementPage(LoggedPage, HTMLPage):
+    def is_here(self):
+        return bool(CleanText(u'//h2[contains(text(), "Télécharger des opérations")]')(self.doc))
+
+    def setup_statement(self, account):
+        form = self.get_form(id='main')
+        form['__EVENTTARGET'] = 'MM$TELECHARGE_OPERATIONS$m_ChoiceBar$lnkRight'
+        form['__EVENTARGUMENT'] = ''
+        form['MM$TELECHARGE_OPERATIONS$m_ExDDLListeComptes'] = 'C#' + unicode(account.id) + '##' + unicode(account.currency)
+        # groupDate for a period is 'fourchette' with m_DateDebut$txtDate and
+        # m_DateFin$txtDate parameters; not implemented.
+        # 'permanent' is for “since last download”, but actually download
+        # everything up to the earlist available transaction.
+        form['MM$TELECHARGE_OPERATIONS$groupeDate'] = 'permanent'
+        # 0 is for OFX
+        form['MM$TELECHARGE_OPERATIONS$ddlChoixLogiciel'] = '0'
+        form['m_ScriptManager'] = 'MM$m_UpdatePanel|MM$TELECHARGE_OPERATIONS$m_ChoiceBar$lnkRight'
+        form.submit()
+
+class StatementAckPage(LoggedPage, HTMLPage):
+    def is_here(self):
+        return bool(CleanText(u'//h2[contains(text(), "Accusé de réception")]')(self.doc))
+
+    def download_statement(self):
+        return self.browser.location(self.browser.BASEURL + '/Pages/telechargement.aspx')
 
 class UnavailablePage(LoggedPage, HTMLPage):
     # This page seems to not be a 'LoggedPage'
diff --git a/modules/creditcooperatif/module.py b/modules/creditcooperatif/module.py
index c84d3de80..c574c86a9 100644
--- a/modules/creditcooperatif/module.py
+++ b/modules/creditcooperatif/module.py
@@ -17,7 +17,7 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this weboob module. If not, see <http://www.gnu.org/licenses/>.
 
-from weboob.capabilities.bank import CapBankTransferAddRecipient
+from weboob.capabilities.bank import CapBankTransferAddRecipient, CapBankStatement
 from weboob.capabilities.profile import CapProfile
 from weboob.tools.backend import AbstractModule, BackendConfig
 from weboob.tools.value import ValueBackendPassword, Value
@@ -28,7 +28,7 @@ from .proxy_browser import ProxyBrowser
 __all__ = ['CreditCooperatifModule']
 
 
-class CreditCooperatifModule(AbstractModule, CapBankTransferAddRecipient, CapProfile):
+class CreditCooperatifModule(AbstractModule, CapBankTransferAddRecipient, CapBankStatement, CapProfile):
     NAME = 'creditcooperatif'
     MAINTAINER = u'Kevin Pouget'
     EMAIL = 'weboob at kevin.pouget.me'
diff --git a/weboob/capabilities/bank.py b/weboob/capabilities/bank.py
index 03123e198..fe45b8764 100644
--- a/weboob/capabilities/bank.py
+++ b/weboob/capabilities/bank.py
@@ -47,6 +47,7 @@ __all__ = [
     'RecipientNotFound', 'AddRecipientError', 'AddRecipientBankError', 'AddRecipientTimeout',
     'AddRecipientStep', 'RecipientInvalidIban', 'RecipientInvalidLabel',
     'Rate', 'CapCurrencyRate',
+    'CapBankStatement',
 ]
 
 
@@ -794,3 +795,17 @@ class CapCurrencyRate(CapBank):
         :rtype: :class:`Rate`
         """
         raise NotImplementedError()
+
+class CapBankStatement(CapBank):
+    """
+    Capability of bank websites to directly get an OFX statement.
+    """
+
+    def download_statement(self, account):
+        """
+        Download an OFX statement for an account.
+        :param account: account to get the statement for
+        """
+        raise NotImplementedError()
+
+    # TODO iter_statement(self)
-- 
2.20.1



More information about the weboob mailing list