From daf4ee53ef00497f325aa23e383c36ed07494fb4 Mon Sep 17 00:00:00 2001 From: Christophe Francois Date: Fri, 29 May 2020 09:44:08 +0200 Subject: [PATCH] [creditmutuel] Fetch history of portfolio accounts --- modules/creditmutuel/browser.py | 48 ++++++++++++++++++++----- modules/creditmutuel/pages.py | 62 +++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/modules/creditmutuel/browser.py b/modules/creditmutuel/browser.py index 7f797ba528..9de6a896a3 100644 --- a/modules/creditmutuel/browser.py +++ b/modules/creditmutuel/browser.py @@ -57,7 +57,7 @@ ErrorPage, SubscriptionPage, NewCardsListPage, CardPage2, FiscalityConfirmationPage, ConditionsPage, MobileConfirmationPage, UselessPage, DecoupledStatePage, CancelDecoupled, OtpValidationPage, OtpBlockedErrorPage, TwoFAUnabledPage, - LoansOperationsPage, OutagePage, InvestmentDetailsPage, + LoansOperationsPage, OutagePage, PorInvestmentsPage, PorHistoryPage, PorHistoryDetailsPage, ) @@ -132,7 +132,17 @@ class CreditMutuelBrowser(TwoFactorBrowser): r'/(?P.*)fr/banque/PORT_Synthese.aspx', PorPage ) - investment_details = URL(r'/(?P.*)fr/banque/PORT_Valo.aspx', InvestmentDetailsPage) + por_investments = URL( + r'/(?P.*)fr/banque/PORT_Valo.aspx', + r'/(?P.*)fr/banque/PORT_Valo.aspx\?&ddp=(?P.*)', + PorInvestmentsPage + ) + por_history = URL( + r'/(?P.*)fr/banque/PORT_OperationsLst.aspx', + r'/(?P.*)fr/banque/PORT_OperationsLst.aspx\?&ddp=(?P.*)', + PorHistoryPage + ) + por_history_details = URL(r'/(?P.*)fr/banque/PORT_OperationsDet.aspx', PorHistoryDetailsPage) por_action_needed = URL(r'/(?P.*)fr/banque/ORDR_InfosGenerales.aspx', EmptyPage) li = URL(r'/(?P.*)fr/assurances/profilass.aspx\?domaine=epargne', @@ -643,11 +653,33 @@ def get_monthly_transactions(self, trs): def get_history(self, account): transactions = [] - if account.type == Account.TYPE_LIFE_INSURANCE: - self.location(account._link_inv) - self.li_history.go(subbank=self.currentSubBank) - for tr in self.page.iter_history(): - yield tr + if account._is_inv: + if account.type in (Account.TYPE_MARKET, Account.TYPE_PEA): + self.go_por_accounts() + self.por_history.go(subbank=self.currentSubBank, ddp=account._link_id) + self.page.submit_date_range_form() + if self.page.has_no_transaction(): + return + page_index = 0 + # We stop at a maximum of 100 pages to avoid an infinite loop. + while page_index < 100: + page_index += 1 + for tr in self.page.iter_history(): + transactions.append(tr) + if not self.page.has_next_page(): + break + self.page.submit_next_page_form() + for tr in transactions: + if tr._details_link: + self.location(tr._details_link) + self.page.fill_transaction(obj=tr) + yield tr + elif account.type == Account.TYPE_LIFE_INSURANCE: + if account._link_inv: + self.location(account._link_inv) + self.li_history.go(subbank=self.currentSubBank) + for tr in self.page.iter_history(): + yield tr return if not account._link_id: @@ -778,7 +810,7 @@ def get_investment(self, account): if account._is_inv: if account.type in (Account.TYPE_MARKET, Account.TYPE_PEA): self.go_por_accounts() - self.location(account._link_inv) + self.por_investments.go(subbank=self.currentSubBank, ddp=account._link_id) elif account.type == Account.TYPE_LIFE_INSURANCE: if not account._link_inv: return [] diff --git a/modules/creditmutuel/pages.py b/modules/creditmutuel/pages.py index 7c5f467fdd..c7b47a6c30 100644 --- a/modules/creditmutuel/pages.py +++ b/modules/creditmutuel/pages.py @@ -1555,7 +1555,7 @@ def condition(self): ) # These values are defined for other types of accounts - obj__card_number = obj__link_id = None + obj__card_number = None obj__is_inv = True @@ -1572,7 +1572,7 @@ def obj_label(self): obj_valuation_diff = CleanDecimal.French(TableCell('valuation_diff'), default=NotAvailable) - obj__link_inv = Link('.//a', default=NotAvailable) + obj__link_id = Regexp(Link('.//a', default=NotAvailable), r'&ddp=([^&]*)', default=NotAvailable) def obj_type(self): return self.page.get_type(Field('label')(self)) @@ -1710,7 +1710,7 @@ def fill_iban(self, accounts): a.iban = CleanText('.//em[2]', replace=[(' ', '')])(ele) -class InvestmentDetailsPage(LoggedPage, HTMLPage): +class PorInvestmentsPage(LoggedPage, HTMLPage): @method class iter_investment(TableElement): item_xpath = '//table[@id="tabValorisation"]/tbody/tr[td]' @@ -1769,6 +1769,62 @@ def obj_diff_ratio(self): return diff_ratio_percent / 100 return NotAvailable + +class PorHistoryPage(LoggedPage, HTMLPage): + def submit_date_range_form(self): + form = self.get_form(id='frmMere') + form['txtDateSaisie'] = (datetime.today() - relativedelta(years=1)).strftime('%d/%m/%Y') + form.submit() + + def has_next_page(self): + return bool(self.doc.xpath('//input[@id="NEXT"]')) + + def submit_next_page_form(self): + form = self.get_form(id='frmMere') + form['NEXT.x'] = 0 + form['NEXT.y'] = 0 + form.submit() + + def has_no_transaction(self): + return bool(self.doc.xpath('//td[@id="bwebTdPasOperation"]')) + + @method + class iter_history(TableElement): + item_xpath = '//table[@class="liste bourse"]/tbody/tr[td]' + head_xpath = '//table[@class="liste bourse"]/thead//th' + + col_date = 'Exécution' + col_label = 'Opération' + col_investment_label = 'Valeur' + col_investment_quantity = re.compile(r'Quantité') + col_amount = 'Montant net' + + class item(ItemElement): + klass = Transaction + + obj_date = Date(CleanText(TableCell('date')), dayfirst=True) + obj_label = CleanText(TableCell('label')) + obj_amount = CleanDecimal.French(TableCell('amount'), default=NotAvailable) + obj_type = Transaction.TYPE_BANK + obj__details_link = Base(TableCell('label'), Link('.//a', default=NotAvailable)) + + +class PorHistoryDetailsPage(LoggedPage, HTMLPage): + @method + class fill_transaction(ItemElement): + obj_amount = CleanDecimal.French('//td[@class="tot"]/following-sibling::td[1]') + + def obj_investments(self): + investment = Investment() + investment.label = Regexp(CleanText('//td[@id="esdtdLibelleValeur"]'), r'(.*) \(')(self) + investment.unitprice = CleanDecimal.French('//td[@id="esdtdCrsValeur"]')(self) + investment.valuation = Field('amount')(self) + investment.quantity = CleanDecimal.French('//td[@id="esdtdQteValeur"]')(self) + investment.code = IsinCode(Regexp(CleanText('//td[@id="esdtdLibelleValeur"]'), r'\((.*)\)'), default=NotAvailable)(self) + investment.code_type = IsinType(Regexp(CleanText('//td[@id="esdtdLibelleValeur"]'), r'\((.*)\)'), default=NotAvailable)(self) + return [investment] + + class MyRecipient(ItemElement): klass = Recipient -- GitLab