diff --git a/modules/caissedepargne/browser.py b/modules/caissedepargne/browser.py index 34d48ef1fbfc7b51390ae35e301dd686b21f9a44..08ed25a1aa29e48bff4d14aa298617a22d02717e 100644 --- a/modules/caissedepargne/browser.py +++ b/modules/caissedepargne/browser.py @@ -52,6 +52,7 @@ SmsPage, SmsPageOption, SmsRequest, AuthentPage, RecipientPage, CanceledAuth, CaissedepargneKeyboard, TransactionsDetailsPage, LoadingPage, ConsLoanPage, MeasurePage, NatixisLIHis, NatixisLIInv, NatixisRedirectPage, SubscriptionPage, CreditCooperatifMarketPage, UnavailablePage, CardsPage, CardsComingPage, CardsOldWebsitePage, TransactionPopupPage, + OldLeviesPage, NewLeviesPage, ) from .linebourse_browser import LinebourseAPIBrowser @@ -86,6 +87,8 @@ class CaisseEpargne(LoginBrowser, StatesMixin): cards_old = URL('https://.*/Portail.aspx.*', CardsOldWebsitePage) cards = URL('https://.*/Portail.aspx.*', CardsPage) cards_coming = URL('https://.*/Portail.aspx.*', CardsComingPage) + old_checkings_levies = URL(r'https://.*/Portail.aspx.*', OldLeviesPage) + new_checkings_levies = URL(r'https://.*/Portail.aspx.*', NewLeviesPage) authent = URL('https://.*/Portail.aspx.*', AuthentPage) subscription = URL('https://.*/Portail.aspx\?tache=(?P).*', SubscriptionPage) transaction_popup = URL(r'https://.*/Portail.aspx.*', TransactionPopupPage) @@ -458,7 +461,7 @@ def get_accounts_list(self): - In CardsPage, there are cards (with "Business" in the label) without checking account on the website (neither history nor coming), so we skip them. - Some card on the CardsPage that have a checking account parent, but if we follow the link to - reach it with CardsComingPage, we find an other card that not in CardsPage. + reach it with CardsComingPage, we find an other card that is not in CardsPage. """ if self.new_website: for account in self.accounts: @@ -736,33 +739,51 @@ def match_cb(tr): @need_login def get_coming(self, account): - if account.type != account.TYPE_CARD: - return [] + if account.type == account.TYPE_CHECKING: + return self.get_coming_checking(account) + elif account.type == account.TYPE_CARD: + return self.get_coming_card(account) + return [] + def get_coming_checking(self, account): + # The accounts list or account history page does not contain comings for checking accounts + # We need to go to a specific levies page where we can find past and coming levies (such as recurring ones) + trs = [] + self.home.go() + self.page.go_cards() # need to go to cards page to have access to the nav bar where we can choose LeviesPage from + if not self.page.levies_page_enabled(): + return trs + self.page.go_levies() # need to go to a general page where we find levies for all accounts before requesting a specific account + if not self.page.comings_enabled(account.id): + return trs + self.page.go_levies(account.id) + if self.new_checkings_levies.is_here() or self.old_checkings_levies.is_here(): + today = datetime.datetime.today().date() + # Today transactions are in this page but also in history page, we need to ignore it as a coming + for tr in self.page.iter_coming(): + if tr.date > today: + trs.append(tr) + return trs + + def get_coming_card(self, account): trs = [] - if not hasattr(account.parent, '_info'): raise NotImplementedError() - # We are on the old website if hasattr(account, '_coming_eventargument'): - if not self.cards_old.is_here(): self.home.go() self.page.go_list() self.page.go_cards() self.page.go_card_coming(account._coming_eventargument) - return sorted_transactions(self.page.iter_coming()) - # We are on the new website. info = account.parent._card_links - # if info is empty, that mean there are no coming yet + # if info is empty, that means there are no comings yet if info: for tr in self._get_history(info.copy(), account): tr.type = tr.TYPE_DEFERRED_CARD trs.append(tr) - return sorted_transactions(trs) @need_login diff --git a/modules/caissedepargne/pages.py b/modules/caissedepargne/pages.py index c057df6ab04295ee7f5c74f6ccc0a8a038988d62..758d3452af5a73d075201f28ddc5dc6549d775b2 100644 --- a/modules/caissedepargne/pages.py +++ b/modules/caissedepargne/pages.py @@ -538,6 +538,36 @@ def submit_form(self, form, eventargument, eventtarget, scriptmanager): fix_form(form) form.submit() + def go_levies(self, account_id=None): + form = self.get_form(id='main') + if account_id: + # Go to an account specific levies page + eventargument = "" + if "MM$m_CH$IsMsgInit" in form: + # Old website + form['MM$SYNTHESE_SDD_RECUS$m_ExDropDownList'] = account_id + eventtarget = "MM$SYNTHESE_SDD_RECUS$m_ExDropDownList" + scriptmanager = "MM$m_UpdatePanel|MM$SYNTHESE_SDD_RECUS$m_ExDropDownList" + else: + # New website + form['MM$SYNTHESE_SDD_RECUS$ddlCompte'] = account_id + eventtarget = "MM$SYNTHESE_SDD_RECUS$ddlCompte" + scriptmanager = "MM$m_UpdatePanel|MM$SYNTHESE_SDD_RECUS$ddlCompte" + self.submit_form(form, eventargument, eventtarget, scriptmanager,) + else: + # Go to an general levies page page where all levies are found + if "MM$m_CH$IsMsgInit" in form: + # Old website + eventargument = "SDDRSYN0" + eventtarget = "Menu_AJAX" + scriptmanager = "m_ScriptManager|Menu_AJAX" + else: + # New website + eventargument = "SDDRSYN0&codeMenu=WPS1" + eventtarget = "MM$Menu_Ajax" + scriptmanager = "MM$m_UpdatePanel|MM$Menu_Ajax" + self.submit_form(form, eventargument, eventtarget, scriptmanager,) + def go_list(self): form = self.get_form(id='main') @@ -842,6 +872,13 @@ def go_pro_transfer_availability(self): def is_transfer_allowed(self): return not self.doc.xpath('//ul/li[contains(text(), "Aucun compte tiers n\'est disponible")]') + def levies_page_enabled(self): + """ Levies page does not exist in the nav bar for every connections """ + return ( + CleanText('//a/span[contains(text(), "Suivre mes prélèvements reçus")]')(self.doc) or # new website + CleanText('//a[contains(text(), "Suivre les prélèvements reçus")]')(self.doc) # old website + ) + class TransactionPopupPage(LoggedPage, HTMLPage): def is_here(self): @@ -851,6 +888,74 @@ def complete_label(self): return CleanText('''//div[@class="scrollPane"]/table[//caption[contains(text(), "Détail de l'opération")]]//tr[2]''')(self.doc) +class NewLeviesPage(IndexPage): + """ Scrape new website 'Prélèvements' page for comings for checking accounts """ + + def is_here(self): + return CleanText('//h2[contains(text(), "Suivez vos prélèvements reçus")]')(self.doc) + + def comings_enabled(self, account_id): + """ Check if a specific account can be selected on the general levies page """ + return account_id in CleanText('//span[@id="MM_SYNTHESE_SDD_RECUS"]//select/option/@value')(self.doc) + + @method + class iter_coming(TableElement): + head_xpath = '//div[contains(@id, "ListePrelevement_0")]/table[contains(@summary, "Liste des prélèvements en attente")]//tr/th' + item_xpath = '//div[contains(@id, "ListePrelevement_0")]/table[contains(@summary, "Liste des prélèvements en attente")]//tr[contains(@id, "trRowDetail")]' + + col_label = 'Libellé/Référence' + col_coming = 'Montant' + col_date = 'Date' + + class item(ItemElement): + klass = Transaction + + # Transaction typing will mostly not work since transaction as comings will only display the debiting organism in the label + # Labels will bear recognizable patterns only when they move from future to past, where they will be typed by iter_history + # when transactions change state from coming to history 'Prlv' is append to their label, this will help the backend for the matching + obj_raw = Transaction.Raw(Format('Prlv %s', Field('label'))) + obj_label = CleanText(TableCell('label')) + obj_amount = CleanDecimal.French(TableCell('coming'), sign=lambda x: -1) + obj_date = Date(CleanText(TableCell('date')), dayfirst=True) + + def condition(self): + return not CleanText('''//p[contains(text(), "Vous n'avez pas de prélèvement en attente d'exécution.")]''')(self) + + +class OldLeviesPage(IndexPage): + """ Scrape old website 'Prélèvements' page for comings for checking accounts """ + + def is_here(self): + return CleanText('//span[contains(text(), "Suivez vos prélèvements reçus")]')(self.doc) + + def comings_enabled(self, account_id): + """ Check if a specific account can be selected on the general levies page """ + return account_id in CleanText('//span[@id="MM_SYNTHESE_SDD_RECUS"]//select/option/@value')(self.doc) + + @method + class iter_coming(TableElement): + head_xpath = '''//span[contains(text(), "Prélèvements en attente d'exécution")]/ancestor::table[1]/following-sibling::table[1]//tr[contains(@class, "DataGridHeader")]//td''' + item_xpath = '''//span[contains(text(), "Prélèvements en attente d'exécution")]/ancestor::table[1]/following-sibling::table[1]//tr[contains(@class, "DataGridHeader")]//following-sibling::tr''' + + col_label = 'Libellé/Référence' + col_coming = 'Montant' + col_date = 'Date' + + class item(ItemElement): + klass = Transaction + + # Transaction typing will mostly not work since transaction as comings will only display the debiting organism in the label + # Labels will bear recognizable patterns only when they move from future to past, where they will be typed by iter_history + # when transactions change state from coming to history 'Prlv' is append to their label, this will help the backend for the matching + obj_raw = Transaction.Raw(Format('Prlv %s', Field('label'))) + obj_label = CleanText(TableCell('label')) + obj_amount = CleanDecimal.French(TableCell('coming'), sign=lambda x: -1) + obj_date = Date(CleanText(TableCell('date')), dayfirst=True) + + def condition(self): + return not CleanText('''//table[@id="MM_SYNTHESE_SDD_RECUS_rpt_dgList_0"]//td[contains(text(), "Vous n'avez pas de prélèvements")]''')(self) + + class CardsPage(IndexPage): def is_here(self): return CleanText('//h3[normalize-space(text())="Mes cartes (cartes dont je suis le titulaire)"]')(self.doc)