diff --git a/modules/bred/bred/browser.py b/modules/bred/bred/browser.py index 5ab4274ef515c9b3c669e9413289a7f2a3df203d..22652f040d5c7c989b625c5761aef49060d99de7 100644 --- a/modules/bred/bred/browser.py +++ b/modules/bred/bred/browser.py @@ -21,10 +21,10 @@ import json import time +import operator from datetime import date -from decimal import Decimal -from weboob.capabilities.bank import Account, Investment +from weboob.capabilities.bank import Account from weboob.browser import LoginBrowser, need_login, URL from weboob.capabilities.base import find_object from weboob.tools.capabilities.bank.transactions import sorted_transactions @@ -117,10 +117,12 @@ def get_accounts_list(self): for universe_key in self.get_universes(): self.move_to_univers(universe_key) accounts.extend(self.get_list()) - accounts.extend(self.get_loans_list()) accounts.extend(self.get_life_insurance_list(accounts)) + accounts.extend(self.get_loans_list()) - return sorted(accounts, key=lambda x: x._univers) + # Life insurances are sometimes in multiple universes, we have to remove duplicates + unique_accounts = {account.id: account for account in accounts} + return sorted(unique_accounts.values(), key=operator.attrgetter('_univers')) @need_login def get_loans_list(self): @@ -138,12 +140,11 @@ def get_list(self): @need_login def get_life_insurance_list(self, accounts): - accounts = self.get_list() self.life_insurances.go() - for ins in self.page.iter_life_insurances(current_univers=self.current_univers): - ins.parent = find_object(accounts, _number=ins._number, type=Account.TYPE_CHECKING) + for ins in self.page.iter_lifeinsurances(univers=self.current_univers): + ins.parent = find_object(accounts, _number=ins._parent_number, type=Account.TYPE_CHECKING) yield ins @need_login @@ -170,7 +171,7 @@ def _make_api_call(self, account, start_date, end_date, offset, max_length=50): @need_login def get_history(self, account, coming=False): - if account.type is Account.TYPE_LOAN or not account._consultable: + if account.type in (Account.TYPE_LOAN, Account.TYPE_LIFE_INSURANCE) or not account._consultable: raise NotImplementedError() if account._univers != self.current_univers: @@ -210,15 +211,8 @@ def get_investment(self, account): if account.type != Account.TYPE_LIFE_INSURANCE: raise NotImplementedError() - if account._univers != self.current_univers: - self.move_to_univers(account._univers) - for invest in account._investments: - inv = Investment() - inv.label = invest['libelle'].strip() - inv.code = invest['code'] - inv.valuation = Decimal(str(invest['montant'])) - yield inv + yield invest @need_login def get_profile(self): diff --git a/modules/bred/bred/pages.py b/modules/bred/bred/pages.py index 19a1e03577442ba8954d9f38a7cd4499705b153f..b71e72214fb064b65a45d06523474f92b56f756d 100644 --- a/modules/bred/bred/pages.py +++ b/modules/bred/bred/pages.py @@ -28,9 +28,12 @@ from weboob.capabilities.base import find_object from weboob.browser.pages import JsonPage, LoggedPage, HTMLPage from weboob.capabilities import NotAvailable -from weboob.capabilities.bank import Account +from weboob.capabilities.bank import Account, Investment +from weboob.tools.capabilities.bank.investments import is_isin_valid from weboob.capabilities.profile import Person -from weboob.browser.filters.standard import CleanText +from weboob.browser.filters.standard import CleanText, CleanDecimal, Env, Eval +from weboob.browser.filters.json import Dict +from weboob.browser.elements import DictElement, ItemElement, method from weboob.tools.capabilities.bank.transactions import FrenchTransaction @@ -187,21 +190,56 @@ def set_iban(self, account): account.iban = iban_response.get('iban', NotAvailable) -class LifeInsurancesPage(MyJsonPage): - def iter_life_insurances(self, current_univers): - for content in self.get_content(): - a = Account() - a.id = str(content['avoirs']['contrats'][0]['numero']) - a._number = content['avoirs']['contrats'][0]['cptRattachement'].rstrip('0') - a.type = Account.TYPE_LIFE_INSURANCE - a.label = ' '.join([content['titulaire'].strip(), content['avoirs']['contrats'][0]['libelleProduit'].strip()]) - a.balance = Decimal(str(content['avoirs']['valeur'])) - a.currency = 'EUR' - a._univers = current_univers - # The investment list for each life insurance is available here: - a._investments = [inv for inv in content['avoirs']['contrats'][0]['allocations']] - a._consultable = False - yield a +class LifeInsurancesPage(LoggedPage, JsonPage): + + @method + class iter_lifeinsurances(DictElement): + item_xpath = 'content' + + class iter_accounts(DictElement): + item_xpath = 'avoirs/contrats' + + def get_owner(self): + return CleanText(Dict('titulaire'))(self) + + class item(ItemElement): + klass = Account + + obj_balance = CleanDecimal(Dict('valorisation')) + obj_type = Account.TYPE_LIFE_INSURANCE + obj_currency = 'EUR' + obj__univers = Env('univers') + + def obj_id(self): + return Eval(str, Dict('numero'))(self) + + def obj_label(self): + return '%s - %s' % (CleanText(Dict('libelleProduit'))(self), self.parent.get_owner()) + + def obj__parent_number(self): + return CleanText(Dict('cptRattachement'))(self).rstrip('0') + + # Investments are already present in this JSON, + # so we fill the lists of Investment objects now + class obj__investments(DictElement): + item_xpath = 'allocations' + + class item(ItemElement): + klass = Investment + + obj_label = CleanText(Dict('libelle')) + obj_valuation = CleanDecimal(Dict('montant')) + + def obj_code_type(self): + if is_isin_valid(CleanText(Dict('code'))(self)): + return Investment.CODE_TYPE_ISIN + return NotAvailable + + def obj_code(self): + code = CleanText(Dict('code'))(self) + if is_isin_valid(code): + return code + return NotAvailable class SearchPage(LoggedPage, JsonPage):