support professionnal accounts
This commit is contained in:
parent
a51b63148d
commit
9856c27f8c
2 changed files with 137 additions and 38 deletions
|
|
@ -23,7 +23,7 @@ import urllib
|
|||
|
||||
from weboob.tools.browser import BaseBrowser, BrowserIncorrectPassword
|
||||
|
||||
from .pages import LoginPage, AccountsPage, TransactionsPage
|
||||
from .pages import LoginPage, AccountsPage, ProAccountsPage, TransactionsPage, ProTransactionsPage
|
||||
|
||||
|
||||
__all__ = ['CreditDuNordBrowser']
|
||||
|
|
@ -35,8 +35,11 @@ class CreditDuNordBrowser(BaseBrowser):
|
|||
PAGES = {'https://[^/]+/?': LoginPage,
|
||||
'https://[^/]+/.*\?.*_pageLabel=page_erreur_connexion': LoginPage,
|
||||
'https://[^/]+/vos-comptes/particuliers(\?.*)?': AccountsPage,
|
||||
'https://[^/]+/vos-comptes/.*/transac/.*': TransactionsPage,
|
||||
'https://[^/]+/vos-comptes/.*/transac/particuliers.*': TransactionsPage,
|
||||
'https://[^/]+/vos-comptes/professionnels.*': ProAccountsPage,
|
||||
'https://[^/]+/vos-comptes/.*/transac/professionnels.*': ProTransactionsPage,
|
||||
}
|
||||
account_type = 'particuliers'
|
||||
|
||||
def __init__(self, website, *args, **kwargs):
|
||||
self.DOMAIN = website
|
||||
|
|
@ -47,11 +50,9 @@ class CreditDuNordBrowser(BaseBrowser):
|
|||
|
||||
def home(self):
|
||||
if self.is_logged():
|
||||
self.location(self.buildurl('/vos-comptes/particuliers'))
|
||||
self.location(self.buildurl('/vos-comptes/%s' % self.account_type))
|
||||
else:
|
||||
self.login()
|
||||
return
|
||||
return self.location(self.buildurl('/vos-comptes/particuliers'))
|
||||
|
||||
def login(self):
|
||||
assert isinstance(self.username, basestring)
|
||||
|
|
@ -79,9 +80,13 @@ class CreditDuNordBrowser(BaseBrowser):
|
|||
if not self.is_logged():
|
||||
raise BrowserIncorrectPassword()
|
||||
|
||||
m = re.match('https://[^/]+/vos-comptes/(\w+).*', self.page.url)
|
||||
if m:
|
||||
self.account_type = m.group(1)
|
||||
|
||||
def get_accounts_list(self):
|
||||
if not self.is_on_page(AccountsPage):
|
||||
self.location(self.buildurl('/vos-comptes/particuliers'))
|
||||
self.location(self.buildurl('/vos-comptes/%s' % self.account_type))
|
||||
return self.page.get_list()
|
||||
|
||||
def get_account(self, id):
|
||||
|
|
@ -94,20 +99,12 @@ class CreditDuNordBrowser(BaseBrowser):
|
|||
|
||||
return None
|
||||
|
||||
def iter_transactions(self, link, link_id, execution, is_coming=None):
|
||||
if link_id is None:
|
||||
def iter_transactions(self, link, args, is_coming=None):
|
||||
if args is None:
|
||||
return
|
||||
|
||||
event = 'clicDetailCompte'
|
||||
while 1:
|
||||
data = {'_eventId': event,
|
||||
'_ipc_eventValue': '',
|
||||
'_ipc_fireEvent': '',
|
||||
'deviseAffichee': 'DEVISE',
|
||||
'execution': execution,
|
||||
'idCompteClique': link_id,
|
||||
}
|
||||
self.location(link, urllib.urlencode(data))
|
||||
while args is not None:
|
||||
self.location(link, urllib.urlencode(args))
|
||||
|
||||
assert self.is_on_page(TransactionsPage)
|
||||
|
||||
|
|
@ -116,22 +113,17 @@ class CreditDuNordBrowser(BaseBrowser):
|
|||
for tr in self.page.get_history():
|
||||
yield tr
|
||||
|
||||
is_last = self.page.is_last()
|
||||
if is_last:
|
||||
return
|
||||
|
||||
event = 'clicChangerPageSuivant'
|
||||
execution = self.page.get_execution()
|
||||
is_coming = self.page.is_coming
|
||||
args = self.page.get_next_args(args)
|
||||
|
||||
def get_history(self, account):
|
||||
for tr in self.iter_transactions(account._link, account._link_id, account._execution):
|
||||
for tr in self.iter_transactions(account._link, account._args):
|
||||
yield tr
|
||||
|
||||
for tr in self.get_card_operations(account):
|
||||
yield tr
|
||||
|
||||
def get_card_operations(self, account):
|
||||
for link_id in account._card_ids:
|
||||
for tr in self.iter_transactions(account._link, link_id, account._execution, True):
|
||||
for link_args in account._card_ids:
|
||||
for tr in self.iter_transactions(account._link, link_args, True):
|
||||
yield tr
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from urllib import quote
|
||||
from decimal import Decimal
|
||||
import re
|
||||
from cStringIO import StringIO
|
||||
|
|
@ -97,14 +98,19 @@ class AccountsPage(CDNBasePage):
|
|||
a.label = self.parser.tocleanstring(self.parser.parse(fp, self.browser.ENCODING).xpath('//div[@class="libelleCompteTDB"]')[0])
|
||||
a.balance = Decimal(FrenchTransaction.clean_amount(line[self.COL_BALANCE]))
|
||||
a._link = self.get_history_link()
|
||||
a._execution = self.get_execution()
|
||||
if line[self.COL_HISTORY] == 'true':
|
||||
a._link_id = line[self.COL_ID]
|
||||
a._args = {'_eventId': 'clicDetailCompte',
|
||||
'_ipc_eventValue': '',
|
||||
'_ipc_fireEvent': '',
|
||||
'deviseAffichee': 'DEVISE',
|
||||
'execution': self.get_execution(),
|
||||
'idCompteClique': line[self.COL_ID],
|
||||
}
|
||||
else:
|
||||
a._link_id = None
|
||||
a._args = None
|
||||
|
||||
if a.id.find('_CarteVisa') >= 0:
|
||||
accounts[0]._card_ids.append(a._link_id)
|
||||
accounts[0]._card_ids.append(a._args)
|
||||
if not accounts[0].coming:
|
||||
accounts[0].coming = Decimal('0.0')
|
||||
accounts[0].coming += a.balance
|
||||
|
|
@ -116,6 +122,45 @@ class AccountsPage(CDNBasePage):
|
|||
return iter(accounts)
|
||||
|
||||
|
||||
class ProAccountsPage(AccountsPage):
|
||||
COL_ID = 0
|
||||
COL_BALANCE = 1
|
||||
|
||||
ARGS = ['Banque', 'Agence', 'classement', 'Serie', 'SSCompte', 'Devise', 'CodeDeviseCCB', 'LibelleCompte', 'IntituleCompte', 'Indiceclassement', 'IndiceCompte', 'NomClassement']
|
||||
def params_from_js(self, text):
|
||||
l = []
|
||||
for sub in re.findall("'([^']*)'", text):
|
||||
l.append(sub)
|
||||
|
||||
url = '/vos-comptes/IPT/appmanager/transac/professionnels?_nfpb=true&_windowLabel=portletInstance_18&_pageLabel=page_synthese_v1' + '&_cdnCltUrl=' + "/transacClippe/" + quote(l.pop(0))
|
||||
args = {}
|
||||
|
||||
for i, key in enumerate(self.ARGS):
|
||||
args[key] = l[self.ARGS.index(key)]
|
||||
|
||||
return url, args
|
||||
|
||||
|
||||
def get_list(self):
|
||||
for tr in self.document.xpath('//table[@class="datas"]//tr'):
|
||||
if tr.attrib.get('class', '') == 'entete':
|
||||
continue
|
||||
|
||||
cols = tr.findall('td')
|
||||
|
||||
a = Account()
|
||||
a.id = cols[self.COL_ID].xpath('.//span[@class="right-underline"]')[0].text.strip()
|
||||
a.label = unicode(cols[self.COL_ID].xpath('.//span[@class="left-underline"]')[0].text.strip())
|
||||
balance = self.parser.tocleanstring(cols[self.COL_BALANCE])
|
||||
a.balance = Decimal(FrenchTransaction.clean_amount(balance))
|
||||
a.currency = a.get_currency(balance)
|
||||
a._link, a._args = self.params_from_js(cols[self.COL_ID].find('a').attrib['href'])
|
||||
|
||||
a._card_ids = []
|
||||
|
||||
yield a
|
||||
|
||||
|
||||
class Transaction(FrenchTransaction):
|
||||
PATTERNS = [(re.compile(r'^(?P<text>RET DAB \w+ .*?) LE (?P<dd>\d{2})(?P<mm>\d{2})$'),
|
||||
FrenchTransaction.TYPE_WITHDRAWAL),
|
||||
|
|
@ -143,6 +188,15 @@ class TransactionsPage(CDNBasePage):
|
|||
|
||||
is_coming = None
|
||||
|
||||
def get_next_args(self, args):
|
||||
if self.is_last():
|
||||
return None
|
||||
|
||||
args['_eventId'] = 'clicChangerPageSuivant'
|
||||
args['execution'] = self.get_execution()
|
||||
args.pop('idCompteClique', None)
|
||||
return args
|
||||
|
||||
def is_last(self):
|
||||
for script in self.document.xpath('//script'):
|
||||
txt = script.text
|
||||
|
|
@ -154,11 +208,28 @@ class TransactionsPage(CDNBasePage):
|
|||
|
||||
return True
|
||||
|
||||
def set_coming(self, t):
|
||||
if self.is_coming is not None and t.raw.startswith('TOTAL DES') and t.amount > 0:
|
||||
# ignore card credit and next transactions are already debited
|
||||
self.is_coming = False
|
||||
return True
|
||||
if self.is_coming is None and t.raw.startswith('ACHATS CARTE'):
|
||||
# Ignore card debit
|
||||
return True
|
||||
|
||||
t._is_coming = bool(self.is_coming)
|
||||
return False
|
||||
|
||||
def get_history(self):
|
||||
txt = self.get_from_js('ListeMvts_data = new Array(', ');')
|
||||
|
||||
if txt is None:
|
||||
raise BrokenPageError('Unable to find transactions list in scripts')
|
||||
no_trans = self.get_from_js('js_noMvts = new Ext.Panel(', ')')
|
||||
if no_trans is not None:
|
||||
# there is no transactions for this account, this is normal.
|
||||
return
|
||||
else:
|
||||
raise BrokenPageError('Unable to find transactions list in scripts')
|
||||
|
||||
data = json.loads('[%s]' % txt.replace('"', '\\"').replace("'", '"'))
|
||||
|
||||
|
|
@ -175,13 +246,49 @@ class TransactionsPage(CDNBasePage):
|
|||
t.parse(date, raw)
|
||||
t.set_amount(line[self.COL_VALUE])
|
||||
|
||||
if self.is_coming is not None and raw.startswith('TOTAL DES') and t.amount > 0:
|
||||
# ignore card credit and next transactions are already debited
|
||||
self.is_coming = False
|
||||
continue
|
||||
if self.is_coming is None and raw.startswith('ACHATS CARTE'):
|
||||
# Ignore card debit
|
||||
if self.set_coming(t):
|
||||
continue
|
||||
|
||||
yield t
|
||||
|
||||
class ProTransactionsPage(TransactionsPage):
|
||||
def get_next_args(self, args):
|
||||
txt = self.get_from_js('myPage.setPiedPage(oNavSuivantPrec_1(', ')')
|
||||
|
||||
if txt is None:
|
||||
return None
|
||||
|
||||
l = txt.split(',')
|
||||
if int(l[4]) <= 40:
|
||||
return None
|
||||
|
||||
args['PageDemandee'] = int(args.get('PageDemandee', 1)) + 1
|
||||
return args
|
||||
|
||||
def parse_transactions(self):
|
||||
transactions = {}
|
||||
for script in self.document.xpath('//script'):
|
||||
txt = script.text
|
||||
if txt is None:
|
||||
continue
|
||||
|
||||
for i, key, value in re.findall('listeopecv\[(\d+)\]\[\'(\w+)\'\]="(.*)";', txt):
|
||||
i = int(i)
|
||||
if not i in transactions:
|
||||
transactions[i] = {}
|
||||
transactions[i][key] = value
|
||||
|
||||
return transactions.iteritems()
|
||||
|
||||
def get_history(self):
|
||||
for i, tr in self.parse_transactions():
|
||||
t = Transaction(i)
|
||||
date = tr['date']
|
||||
raw = self.parser.strip('<p>%s</p>' % (' '.join([tr['typeope'], tr['LibComp']])))
|
||||
t.parse(date, raw)
|
||||
t.set_amount(tr['mont'])
|
||||
|
||||
if self.set_coming(t):
|
||||
continue
|
||||
|
||||
t._is_coming = bool(self.is_coming)
|
||||
yield t
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue