diff --git a/modules/ing/browser.py b/modules/ing/browser.py
index 68b7f47f..ddee1a55 100644
--- a/modules/ing/browser.py
+++ b/modules/ing/browser.py
@@ -18,13 +18,14 @@
# along with weboob. If not, see .
import re
import hashlib
+import time
from weboob.browser import LoginBrowser, URL, need_login
from weboob.exceptions import BrowserIncorrectPassword, ParseError
from weboob.capabilities.bank import Account, TransferError
from .pages import AccountsList, LoginPage, TitrePage, TitreHistory,\
- TransferPage, TransferConfirmPage, BillsPage, StopPage
+ TransferPage, TransferConfirmPage, BillsPage, StopPage, TitreDetails
__all__ = ['IngBrowser']
@@ -52,6 +53,7 @@ class IngBrowser(LoginBrowser):
transferpage = URL('/protected/pages/cc/transfer/transferManagement.jsf', TransferPage)
dotransferpage = URL('/general\?command=DisplayDoTransferCommand', TransferPage)
valtransferpage = URL('/protected/pages/cc/transfer/create/transferCreateValidation.jsf', TransferConfirmPage)
+ titredetails = URL('/general\?command=display.*', TitreDetails)
# CapBank-Market
starttitre = URL('/general\?command=goToAccount&zone=COMPTE', TitrePage)
titrepage = URL('https://bourse.ingdirect.fr/priv/portefeuille-TR.php', TitrePage)
@@ -226,7 +228,17 @@ class IngBrowser(LoginBrowser):
"javax.faces.ViewState": account._jid,
"cptnbr": account._id
}
- self.accountspage.go(data=data)
+
+ # On ASV pages, data maybe not available.
+ for i in range(4):
+ time.sleep(2**i)
+ self.accountspage.go(data=data)
+
+ if not self.page.has_error():
+ break
+
+ if self.page.is_asv:
+ return
self.starttitre.go()
self.where = u"titre"
@@ -237,12 +249,15 @@ class IngBrowser(LoginBrowser):
raise NotImplementedError()
self.go_investments(account)
- self.titrerealtime.go()
+ if self.where == u'titre':
+ self.titrerealtime.go()
return self.page.iter_investments()
def get_history_titre(self, account):
self.go_investments(account)
- self.titrehistory.go()
+
+ if self.where == u'titre':
+ self.titrehistory.go()
return self.page.iter_history()
############# CapBill #############
diff --git a/modules/ing/pages/__init__.py b/modules/ing/pages/__init__.py
index e246da20..25805a21 100644
--- a/modules/ing/pages/__init__.py
+++ b/modules/ing/pages/__init__.py
@@ -18,7 +18,7 @@
# along with weboob. If not, see .
-from .accounts_list import AccountsList
+from .accounts_list import AccountsList, TitreDetails
from .login import LoginPage, StopPage
from .transfer import TransferPage, TransferConfirmPage
from .bills import BillsPage
@@ -28,6 +28,6 @@ from .titre import TitrePage, TitreHistory
class AccountPrelevement(AccountsList):
pass
-__all__ = ['AccountsList', 'LoginPage',
+__all__ = ['AccountsList', 'LoginPage', 'TitreDetails',
'AccountPrelevement', 'TransferPage', 'TransferConfirmPage',
'BillsPage', 'StopPage', 'TitrePage', 'TitreHistory']
diff --git a/modules/ing/pages/accounts_list.py b/modules/ing/pages/accounts_list.py
index 2831480a..ecdacfa9 100644
--- a/modules/ing/pages/accounts_list.py
+++ b/modules/ing/pages/accounts_list.py
@@ -20,13 +20,15 @@
from datetime import date, timedelta
import datetime
+from decimal import Decimal
import re
-from weboob.capabilities.bank import Account
+from weboob.capabilities.bank import Account, Investment
from weboob.capabilities.base import NotAvailable
from weboob.browser.pages import HTMLPage, LoggedPage
from weboob.browser.elements import ListElement, ItemElement, method
-from weboob.browser.filters.standard import CleanText, CleanDecimal, Filter, Field, MultiFilter, Date, Lower
+from weboob.browser.filters.standard import CleanText, CleanDecimal, Filter, Field, MultiFilter, \
+ Date, Lower, Regexp, Async, AsyncLoad, Slugify
from weboob.browser.filters.html import Attr
from weboob.tools.capabilities.bank.transactions import FrenchTransaction
@@ -57,7 +59,8 @@ class AddPref(MultiFilter):
class AddType(Filter):
types = {u'Courant': Account.TYPE_CHECKING, u'Livret A': Account.TYPE_SAVINGS,
u'Orange': Account.TYPE_SAVINGS, u'Durable': Account.TYPE_SAVINGS,
- u'Titres': Account.TYPE_MARKET, u'PEA': Account.TYPE_MARKET}
+ u'Titres': Account.TYPE_MARKET, u'PEA': Account.TYPE_MARKET,
+ u'Direct Vie': Account.TYPE_MARKET}
def filter(self, label):
for key, acc_type in self.types.items():
@@ -112,9 +115,10 @@ class INGCategory(Filter):
class AccountsList(LoggedPage, HTMLPage):
- def __init__(self, browser, response, *args, **kwargs):
- super(AccountsList, self).__init__(browser, response, *args, **kwargs)
- self.i = 0
+ i = 0
+
+ def has_error(self):
+ return self.doc.xpath('//div[has-class("alert-warning")]') > 0
@method
class get_list(ListElement):
@@ -166,8 +170,7 @@ class AccountsList(LoggedPage, HTMLPage):
item_xpath = '//table'
def get_history_jid(self):
- span = self.doc.xpath('//span[@id="index:panelASV"]')
- if len(span) > 1:
+ if self.is_asv:
# Assurance Vie, we do not support this kind of account.
return None
@@ -182,3 +185,48 @@ class AccountsList(LoggedPage, HTMLPage):
nomore = self.doc.getroot().cssselect('.no-more-transactions')
return (len(nomore) > 0)
+
+ @property
+ def is_asv(self):
+ span = self.doc.xpath('//span[@id="index:panelASV"]')
+ return len(span) > 0
+
+ @method
+ class iter_investments(ListElement):
+ item_xpath = '//div[has-class("asv_fond")]'
+
+ class item(ItemElement):
+ klass = Investment
+
+ # ASV.popup('/general?command=displayAVEuroEpargne')
+ load_details = Attr('.//div[has-class("asv_fond_view")]//a', 'onclick') & Regexp(pattern="'(.*)'") & AsyncLoad
+
+ obj_label = CleanText('.//span[has-class("asv_cat_lbl")]')
+ # XXX I would like to do that... but 1. CleanText doesn't deal with default 2. default values can't be filters yet
+ #obj_code = Async('details') & CleanText('//li[contains(text(), "Code ISIN")]/span') | (Field('label') & Format('XX%s', Slugify))
+ obj_code = Async('details') & CleanText('//li[contains(text(), "Code ISIN")]/span')
+ obj_id = obj_code
+ obj_description = Async('details') & CleanText('//h5')
+ obj_quantity = CleanDecimal('.//dl[contains(dt/text(), "Nombre de parts")]/dd', replace_dots=True)
+ obj_unitvalue = CleanDecimal('.//dl[contains(dt/text(), "Valeur de part")]/dd', replace_dots=True)
+ obj_valuation = CleanDecimal('.//dl[has-class("ligne-montant")]/dd', replace_dots=True)
+
+ def obj_unitprice(self):
+ if 'eurossima' in self.el.get('class'):
+ return self.obj.unitvalue
+
+ percent = CleanDecimal('.//dl[has-class("ligne-pmvalue")]/dd', replace_dots=True)(self)
+ return (self.obj.unitvalue / (1 + percent/Decimal('100.0'))).quantize(Decimal('1.00'))
+
+ def obj_diff(self):
+ return (self.obj.valuation - (self.obj.quantity * self.obj.unitprice)).quantize(Decimal('1.00'))
+
+ def validate(self, obj):
+ if not obj.id:
+ obj.id = obj.code = 'XX' + Slugify(self.obj_label)(self)
+
+ return True
+
+
+class TitreDetails(LoggedPage, HTMLPage):
+ pass