diff --git a/modules/creditdunord/browser.py b/modules/creditdunord/browser.py index c893504a849d2f74b7d99e7f3557b887dfbefc8d..740d1c7fb8d81bb7374859db319f9d7a1bbb89ea 100644 --- a/modules/creditdunord/browser.py +++ b/modules/creditdunord/browser.py @@ -21,8 +21,9 @@ from weboob.browser import LoginBrowser, URL, need_login from weboob.exceptions import BrowserIncorrectPassword, BrowserPasswordExpired -from weboob.capabilities.bank import Account, Investment +from weboob.capabilities.bank import Account from weboob.capabilities.base import find_object +from weboob.tools.capabilities.bank.investments import create_french_liquidity from .pages import ( LoginPage, ProfilePage, AccountTypePage, AccountsPage, ProAccountsPage, TransactionsPage, IbanPage, RedirectPage, EntryPage, AVPage, ProIbanPage, @@ -152,20 +153,16 @@ def get_history(self, account, coming=False): @need_login def get_investment(self, account): if 'LIQUIDIT' in account.label: - inv = Investment() - inv.code = 'XX-Liquidity' - inv.label = 'Liquidité' - inv.valuation = account.balance - return [inv] + return [create_french_liquidity(account.balance)] if not account._inv: return [] if account.type in (Account.TYPE_MARKET, Account.TYPE_PEA): self.location(account._link, data=account._args) - if self.page.can_iter_investments(): + if self.page.can_iter_investments() and self.page.not_restrained(): return self.page.get_market_investment() - elif (account.type == Account.TYPE_LIFE_INSURANCE): + elif account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION): self.location(account._link, data=account._args) self.location(account._link.replace("_attente", "_detail_contrat_rep"), data=account._args) if self.page.can_iter_investments(): diff --git a/modules/creditdunord/pages.py b/modules/creditdunord/pages.py index bbfbdb210f16b7346e1e8c54b6da36c17463d88f..236a1206fce1c8f3806fe6d542f41c90ee0a950b 100755 --- a/modules/creditdunord/pages.py +++ b/modules/creditdunord/pages.py @@ -29,7 +29,7 @@ from weboob.browser.pages import HTMLPage, LoggedPage, JsonPage from weboob.browser.elements import method, ItemElement, TableElement -from weboob.browser.filters.standard import CleanText, Date, CleanDecimal, Regexp, Format, Field +from weboob.browser.filters.standard import CleanText, Date, CleanDecimal, Regexp, Format, Field, Eval from weboob.browser.filters.json import Dict from weboob.browser.filters.html import Attr, TableCell from weboob.exceptions import ActionNeeded, BrowserIncorrectPassword, BrowserUnavailable, BrowserPasswordExpired @@ -38,6 +38,7 @@ from weboob.capabilities.base import Currency, find_object from weboob.capabilities import NotAvailable from weboob.tools.capabilities.bank.transactions import FrenchTransaction +from weboob.tools.capabilities.bank.investments import is_isin_valid from weboob.tools.captcha.virtkeyboard import GridVirtKeyboard from weboob.tools.compat import quote, unicode from weboob.tools.json import json @@ -243,12 +244,16 @@ class AccountsPage(LoggedPage, CDNBasePage): u"PLAN D'EPARGNE": Account.TYPE_SAVINGS, u'PLAN ÉPARGNE': Account.TYPE_SAVINGS, u'ASS.VIE': Account.TYPE_LIFE_INSURANCE, + u'BONS CAPI': Account.TYPE_CAPITALISATION, u'ÉTOILE AVANCE': Account.TYPE_LOAN, u'ETOILE AVANCE': Account.TYPE_LOAN, u'PRÊT': Account.TYPE_LOAN, u'CREDIT': Account.TYPE_LOAN, u'FACILINVEST': Account.TYPE_LOAN, - u'TIT': Account.TYPE_MARKET, + u'TITRES': Account.TYPE_MARKET, + u'COMPTE TIT': Account.TYPE_MARKET, + u'PRDTS BLOQ. TIT': Account.TYPE_MARKET, + u'PRODUIT BLOQUE TIT': Account.TYPE_MARKET, u'COMPTE A TERME': Account.TYPE_DEPOSIT, } @@ -494,7 +499,7 @@ def get_list(self): else: self.logger.warning('The card account %s has no parent account' % a.id) - a._inv = False + a._inv = True if a.type == Account.TYPE_CHECKING: previous_checking_account = a @@ -640,36 +645,42 @@ def get_history(self, acc_type): def can_iter_investments(self): return 'Vous ne pouvez pas utiliser les fonctions de bourse.' not in CleanText('//div[@id="contenusavoir"]')(self.doc) - def get_market_investment(self): - if CleanText('//div[contains(text(), "restreint aux fonctions de bourse")]')(self.doc): - return + def not_restrained(self): + return not CleanText('//div[contains(text(), "restreint aux fonctions de bourse")]')(self.doc) - COL_LABEL = 0 - COL_QUANTITY = 1 - COL_UNITPRICE = 2 - COL_UNITVALUE = 3 - COL_VALUATION = 4 - COL_PERF = 5 + @method + class get_market_investment(TableElement): + # Fetch the tables with at least 5 head columns (browser adds a missing a ) + item_xpath = '//div[not(@id="PortefeuilleCV")]/table[@class="datas"][tr[@class="entete"][count(td)>4]]//tr[position()>1]' + head_xpath = '//div[not(@id="PortefeuilleCV")]/table[@class="datas"][tr[@class="entete"][count(td)>4]]//tr[@class="entete"]/td' - for table in self.doc.xpath('//div[not(@id="PortefeuilleCV")]/table[@class="datas"]'): - for tr in table.xpath('.//tr[not(@class="entete")]'): - cols = tr.findall('td') - if len(cols) < 7: - continue - delta = 0 - if len(cols) == 9: - delta = 1 - - inv = Investment() - inv.code = CleanText('.')(cols[COL_LABEL + delta].xpath('.//span')[1]).split(' ')[0].split(u'\xa0')[0] - inv.label = CleanText('.')(cols[COL_LABEL + delta].xpath('.//span')[0]) - inv.quantity = MyDecimal('.')(cols[COL_QUANTITY + delta]) - inv.unitprice = MyDecimal('.')(cols[COL_UNITPRICE + delta]) - inv.unitvalue = MyDecimal('.')(cols[COL_UNITVALUE + delta]) - inv.valuation = MyDecimal('.')(cols[COL_VALUATION + delta]) - inv.diff = MyDecimal('.')(cols[COL_PERF + delta]) - - yield inv + col_label = 'Valeur' + col_quantity = 'Quantité' + col_unitvalue = 'Cours' + col_valuation = 'Estimation' + col_portfolio_share = '%' + + class item(ItemElement): + klass = Investment + + obj_label = CleanText(TableCell('label', colspan=True)) + obj_valuation = MyDecimal(TableCell('valuation', colspan=True)) + obj_quantity = MyDecimal(TableCell('quantity', colspan=True)) + obj_unitvalue = MyDecimal(TableCell('unitvalue', colspan=True)) + obj_portfolio_share = Eval(lambda x: x / 100, MyDecimal(TableCell('portfolio_share'))) + + def obj_code(self): + code = Regexp(Field('label'), '([0-9A-Z]{12})')(self) + if is_isin_valid(code): + return code + + def obj_code_type(self): + if is_isin_valid(Field('code')(self)): + return Investment.CODE_TYPE_ISIN + return NotAvailable + + def condition(self): + return "Sous-total" not in Field('label')(self) @method class get_deposit_investment(TableElement):