diff --git a/modules/cragr/api/browser.py b/modules/cragr/api/browser.py index 196c771758270c5699553d01d5ac7f5f8a78e50d..66d95934289efa05f8e02fd624f408b4659d9c2b 100644 --- a/modules/cragr/api/browser.py +++ b/modules/cragr/api/browser.py @@ -33,7 +33,7 @@ from .pages import ( LoginPage, LoggedOutPage, KeypadPage, SecurityPage, ContractsPage, AccountsPage, AccountDetailsPage, - IbanPage, CardsPage, ProfilePage, + IbanPage, HistoryPage, CardsPage, CardHistoryPage, ProfilePage, ) @@ -66,6 +66,14 @@ class CragrAPI(LoginBrowser): r'association/operations/moyens-paiement/mes-cartes/jcr:content.listeCartesParCompte.json', r'professionnel/operations/moyens-paiement/mes-cartes/jcr:content.listeCartesParCompte.json', CardsPage) + history = URL(r'particulier/operations/synthese/detail-comptes/jcr:content.n3.operations.json', + r'association/operations/synthese/detail-comptes/jcr:content.n3.operations.json', + r'professionnel/operations/synthese/detail-comptes/jcr:content.n3.operations.json', HistoryPage) + + card_history = URL(r'particulier/operations/synthese/detail-comptes/jcr:content.n3.operations.encours.carte.debit.differe.json', + r'association/operations/synthese/detail-comptes/jcr:content.n3.operations.encours.carte.debit.differe.json', + r'professionnel/operations/synthese/detail-comptes/jcr:content.n3.operations.encours.carte.debit.differe.json', CardHistoryPage) + profile_page = URL(r'particulier/operations/synthese/jcr:content.npc.store.client.json', r'association/operations/synthese/jcr:content.npc.store.client.json', r'professionnel/operations/synthese/jcr:content.npc.store.client.json', ProfilePage) @@ -125,10 +133,6 @@ def get_accounts_list(self): total_spaces = self.page.count_spaces() self.logger.info('The total number of spaces on this connection is %s.' % total_spaces) - # Complete accounts list is required to match card parent accounts - # and to avoid accounts that are present on several spaces - all_accounts = {} - for contract in range(total_spaces): # This request often returns a 500 error so we retry several times. try: @@ -237,8 +241,35 @@ def go_to_account_space(self, contract): assert self.accounts_page.is_here() @need_login - def get_history(self, account): - raise BrowserUnavailable() + def get_history(self, account, coming=False): + # These three parameters are required to get the transactions for non_card accounts + if empty(account._index) or empty(account._category) or empty(account._id_element_contrat): + return + + self.go_to_account_space(account._contract) + params = { + 'compteIdx': int(account._index), + 'grandeFamilleCode': int(account._category), + 'idDevise': str(account.currency), + 'idElementContrat': str(account._id_element_contrat), + } + self.history.go(params=params) + for tr in self.page.iter_history(): + yield tr + + # Get other transactions 100 by 100: + while self.page.has_next_page(): + next_index = self.page.get_next_index() + params = { + 'grandeFamilleCode': int(account._category), + 'compteIdx': int(account._index), + 'idDevise': str(account.currency), + 'startIndex': next_index, + 'count': 100, + } + self.history.go(params=params) + for tr in self.page.iter_history(): + yield tr @need_login def iter_investment(self, account): diff --git a/modules/cragr/api/pages.py b/modules/cragr/api/pages.py index 5edf9cc5d4c7d7248317a187ffb4591bd3515a57..b4839ddccffc86bc906d5c06c538d50012df2585 100644 --- a/modules/cragr/api/pages.py +++ b/modules/cragr/api/pages.py @@ -19,15 +19,17 @@ from __future__ import unicode_literals +from collections import OrderedDict from decimal import Decimal import re import json +import dateutil from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage from weboob.exceptions import BrowserUnavailable from weboob.capabilities import NotAvailable from weboob.capabilities.bank import ( - Account, AccountOwnerType, + Account, AccountOwnerType, Transaction, ) from weboob.browser.elements import DictElement, ItemElement, method @@ -247,7 +249,48 @@ def get_iban(self): class HistoryPage(LoggedPage, JsonPage): - pass + def has_next_page(self): + return Dict('hasNext')(self.doc) + + def get_next_index(self): + return Dict('nextSetStartIndex')(self.doc) + + @method + class iter_history(DictElement): + item_xpath = 'listeOperations' + + class item(ItemElement): + + TRANSACTION_TYPES = OrderedDict(( + ('PAIEMENT PAR CARTE', Transaction.TYPE_CARD), + ('REMISE CARTE', Transaction.TYPE_CARD), + ('PRELEVEMENT CARTE', Transaction.TYPE_CARD_SUMMARY), + ('RETRAIT AU DISTRIBUTEUR', Transaction.TYPE_WITHDRAWAL), + ('RETRAIT MUR D\'ARGENT', Transaction.TYPE_WITHDRAWAL), + ('FRAIS', Transaction.TYPE_BANK), + ('COTISATION', Transaction.TYPE_BANK), + ('VIREMENT', Transaction.TYPE_TRANSFER), + ('CHEQUE EMIS', Transaction.TYPE_CHECK), + ('REMISE DE CHEQUE', Transaction.TYPE_DEPOSIT), + ('PRELEVEMENT', Transaction.TYPE_ORDER), + ('PRELEVT', Transaction.TYPE_ORDER), + ('PRELEVMNT', Transaction.TYPE_ORDER), + ('REMBOURSEMENT DE PRET', Transaction.TYPE_LOAN_PAYMENT), + )) + + klass = Transaction + + obj_label = Format('%s %s', CleanText(Dict('libelleTypeOperation')), CleanText(Dict('libelleOperation'))) + obj_amount = Eval(float_to_decimal, Dict('montant')) + obj_type = Map(CleanText(Dict('libelleTypeOperation')), TRANSACTION_TYPES, Transaction.TYPE_UNKNOWN) + # Needed to fetch deferred card summaries + obj__index = Dict('indexCarte') + + def obj_date(self): + return dateutil.parser.parse(Dict('dateValeur')(self)) + + def obj_rdate(self): + return dateutil.parser.parse(Dict('dateOperation')(self)) class CardsPage(LoggedPage, JsonPage): @@ -281,7 +324,22 @@ def condition(self): class CardHistoryPage(LoggedPage, JsonPage): - pass + @method + class iter_card_history(DictElement): + item_xpath = None + + class item(ItemElement): + klass = Transaction + + obj_label = CleanText(Dict('libelleOperation')) + obj_amount = Eval(float_to_decimal, Dict('montant')) + obj_type = Transaction.TYPE_DEFERRED_CARD + + def obj_date(self): + return dateutil.parser.parse(Dict('datePrelevement')(self)) + + def obj_rdate(self): + return dateutil.parser.parse(Dict('dateOperation')(self)) class InvestmentPage(LoggedPage, JsonPage): diff --git a/modules/cragr/module.py b/modules/cragr/module.py index 959309647fb4e2aec65a499c91cc32ba80d63e27..1363c487e0a6270e41cfb1b1c3e111893a77c4c1 100644 --- a/modules/cragr/module.py +++ b/modules/cragr/module.py @@ -114,15 +114,17 @@ def to_date(obj): return obj.date() return obj - for tr in self.browser.get_history(account): + for tr in self.browser.get_history(account, coming): tr_coming = to_date(tr.date) > today if coming == tr_coming: yield tr + elif coming: + break def iter_history(self, account): if account.type == Account.TYPE_CARD: return self._history_filter(account, False) - return self.browser.get_history(account) + return self.browser.get_history(account, False) def iter_coming(self, account): if account.type == Account.TYPE_CARD: diff --git a/modules/cragr/web/browser.py b/modules/cragr/web/browser.py index 838af343238a5b4cb9313145c21c1e119fc862c2..6d45816985449c371f2a5aa137c885cfa8347761 100644 --- a/modules/cragr/web/browser.py +++ b/modules/cragr/web/browser.py @@ -412,7 +412,7 @@ def market_accounts_matching(self, accounts_list, market_accounts_list): account.balance = self.page.get_pea_balance() @need_login - def get_history(self, account): + def get_history(self, account, coming=False): if account.type in (Account.TYPE_MARKET, Account.TYPE_PEA, Account.TYPE_LIFE_INSURANCE, Account.TYPE_PERP): self.logger.warning('This account is not supported') raise NotImplementedError()