# -*- coding: utf-8 -*- # Copyright(C) 2016 Edouard Lambert # # 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 weboob.browser.pages import HTMLPage, LoggedPage, pagination from weboob.browser.elements import ListElement, ItemElement, method, TableElement from weboob.browser.filters.standard import CleanText, Upper, Date, Regexp, Field, \ CleanDecimal, Env, Async, AsyncLoad from weboob.browser.filters.html import Link, TableCell from weboob.capabilities.bank import Account, Investment from weboob.capabilities.base import NotAvailable from weboob.tools.capabilities.bank.transactions import FrenchTransaction def MyDecimal(*args, **kwargs): kwargs.update(replace_dots=True, default=NotAvailable) return CleanDecimal(*args, **kwargs) class LoginPage(HTMLPage): def login(self, login, password): form = self.get_form(name="bloc_ident") form['_cm_user'] = login form['_cm_pwd'] = password form.submit() class AccountsPage(LoggedPage, HTMLPage): def get_investment_link(self): return Link('//a[contains(text(), "Par fonds")][contains(@href,"GoPositionsParFond")]', default=None)(self.doc) @method class iter_accounts(ListElement): class item(ItemElement): klass = Account obj_id = Regexp(Upper(Field('label')), '[\s]+([^\s]+)[\s]+([^\s]+).*:[\s]+([^\s]+)', '\\1\\2\\3') obj_type = Account.TYPE_PEE obj_label = CleanText('//table[@class="fiche"]//td') obj_balance = MyDecimal('//th[contains(text(), "Montant total")]/em') class InvestmentPage(LoggedPage, HTMLPage): @method class iter_investment(ListElement): item_xpath = '//tr[td[contains(text(), "total")]]' class item(ItemElement): klass = Investment obj_label = CleanText('./preceding-sibling::tr[td[6]][1]/td[1]') obj_quantity = CleanDecimal('./td[last() - 1]') obj_unitvalue = MyDecimal('./preceding-sibling::tr[td[6]][1]/td[3]') obj_valuation = MyDecimal('./td[last()]') obj_vdate = Date(Regexp(CleanText(u'//p[contains(text(), "financière au ")]'), 'au[\s]+(.*)'), dayfirst=True) class Transaction(FrenchTransaction): PATTERNS = [(re.compile(u'^(?PVersement.*)'), FrenchTransaction.TYPE_DEPOSIT), (re.compile(u'^(?P(Arbitrage|Prélèvements.*))'), FrenchTransaction.TYPE_ORDER), (re.compile(u'^(?PRetrait.*)'), FrenchTransaction.TYPE_WITHDRAWAL), (re.compile(u'^(?P.*)'), FrenchTransaction.TYPE_BANK), ] class HistoryPage(LoggedPage, HTMLPage): DEBIT_WORDS = ['sortant', 'paiement', 'retrait', 'frais'] def get_link(self): return Link(u'//a[contains(text(), "Vos opérations traitées")]', default=None)(self.doc) @method class get_investments(TableElement): item_xpath = '//table/tbody/tr' head_xpath = '//table/thead/tr/th' col_label = u'Support' col_unitvalue = re.compile(u'Valeur') col_quantity = u'Nombre de parts' col_valuation = [re.compile(u'Transfert demandé'), re.compile(u'Versement souhaité'), \ re.compile(u'Arbitrage demandé'), re.compile(u'Paiement demandé'), \ re.compile(u'Montant net')] class item(ItemElement): klass = Investment condition = lambda self: len(self.el.xpath('./td')) == 9 obj_label = CleanText(TableCell('label')) obj_quantity = CleanDecimal(TableCell('quantity'), default=NotAvailable) obj_unitvalue = CleanDecimal(TableCell('unitvalue'), default=NotAvailable) obj_valuation = CleanDecimal(TableCell('valuation'), default=NotAvailable) def obj_vdate(self): return Date(Regexp(CleanText(u'//p[contains(text(), " du ")]'), 'du ([\d\/]+)'), dayfirst=True)(self) @pagination @method class iter_history(TableElement): item_xpath = u'//table[@summary="Liste des opérations"]/tbody/tr' head_xpath = u'//table[@summary="Liste des opérations"]/thead/tr/th' col_date = u'Date' col_label = u'Opération' col_amount = re.compile(u'Montant') next_page = Link('//a[contains(@href, "Suiv")]', default=None) class item(ItemElement): klass = Transaction load_details = Link('.//a[1]', default=None) & AsyncLoad obj_date = Date(CleanText(TableCell('date')), dayfirst=True) obj_raw = Transaction.Raw(Env('label')) obj_amount = Env('amount') obj_investments = Env('investments') def parse(self, el): page = Async('details').loaded_page(self) label = CleanText(TableCell('label')(self)[0].xpath('./a[1]'))(self) # Try to get gross amount amount = None for td in page.doc.xpath('//td[em[1][contains(text(), "Total")]]/following-sibling::td'): amount = CleanDecimal('.', default=None)(td) if amount: break amount = amount or MyDecimal(TableCell('amount'))(self) if any(word in label.lower() for word in self.page.DEBIT_WORDS): amount = -amount self.env['label'] = label self.env['amount'] = amount self.env['investments'] = list(page.get_investments())