Newer
Older
# -*- coding: utf-8 -*-
# Copyright(C) 2016 Baptiste Delpey
#
# 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 <http://www.gnu.org/licenses/>.
from weboob.browser.pages import LoggedPage, JsonPage, pagination
from weboob.browser.elements import ItemElement, method, DictElement
from weboob.browser.filters.standard import (
CleanDecimal, CleanText, Date, Format, BrowserURL, Env,
Field,
)
from weboob.browser.filters.json import Dict
from weboob.capabilities.base import Currency
from weboob.capabilities import NotAvailable
from weboob.capabilities.bank import Account
from weboob.capabilities.bill import Document, Subscription
from weboob.exceptions import (
BrowserUnavailable, NoAccountsException, BrowserIncorrectPassword, BrowserPasswordExpired,
)
from weboob.tools.capabilities.bank.iban import is_iban_valid
from weboob.tools.capabilities.bank.transactions import FrenchTransaction
from weboob.tools.compat import quote_plus
from .pages import Transaction
class AccountsJsonPage(LoggedPage, JsonPage):
TYPES = {u'COMPTE COURANT': Account.TYPE_CHECKING,
u'COMPTE PERSONNEL': Account.TYPE_CHECKING,
u'CPTE PRO': Account.TYPE_CHECKING,
u'CPTE PERSO': Account.TYPE_CHECKING,
u'CODEVI': Account.TYPE_SAVINGS,
u'CEL': Account.TYPE_SAVINGS,
u'Ldd': Account.TYPE_SAVINGS,
u'Livret': Account.TYPE_SAVINGS,
u'PEL': Account.TYPE_SAVINGS,
u'Plan Epargne': Account.TYPE_SAVINGS,
u'PEA': Account.TYPE_PEA,
def on_load(self):
if self.doc['commun']['statut'].lower() == 'nok':
reason = self.doc['commun']['raison']
if reason == 'SYD-COMPTES-UNAUTHORIZED-ACCESS':
raise NoAccountsException("Vous n'avez pas l'autorisation de consulter : {}".format(reason))
elif reason == 'niv_auth_insuff':
raise BrowserIncorrectPassword('Vos identifiants sont incorrects')
elif reason == 'chgt_mdp_oblig':
raise BrowserPasswordExpired('Veuillez renouveler votre mot de passe')
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
raise BrowserUnavailable(reason)
@method
class iter_class_accounts(DictElement):
item_xpath = 'donnees/classeurs'
class iter_accounts(DictElement):
@property
def item_xpath(self):
if 'intradayComptes' in self.el:
return 'intradayComptes'
return 'comptes'
class item(ItemElement):
klass = Account
obj__id = Dict('id')
obj_number = CleanText(Dict('iban'), replace=[(' ', '')])
obj_iban = Field('number')
obj_label = CleanText(Dict('libelle'))
obj__agency = Dict('agenceGestionnaire')
def obj_id(self):
number = Field('number')(self)
if len(number) == 27:
# id based on iban to match ids in database.
return number[4:-2]
return number
def obj_iban(self):
# for some account that don't have Iban the account number is store under this variable in the Json
number = Field('number')(self)
if not is_iban_valid(number):
return NotAvailable
return number
def obj_type(self):
return self.page.acc_type(Field('label')(self))
def acc_type(self, label):
for wording, acc_type in self.TYPES.items():
if wording.lower() in label.lower():
return acc_type
return Account.TYPE_CHECKING
def get_error(self):
if self.doc['commun']['statut'] == 'nok':
# warning: 'nok' is case sensitive, for wrongpass at least it's 'nok'
# for certain other errors (like no accounts), it's 'NOK'
return self.doc['commun']['raison']
return None
class BalancesJsonPage(LoggedPage, JsonPage):
def on_load(self):
if self.doc['commun']['statut'] == 'NOK':
reason = self.doc['commun']['raison']
if reason == 'SYD-COMPTES-UNAUTHORIZED-ACCESS':
raise NoAccountsException("Vous n'avez pas l'autorisation de consulter : {}".format(reason))
def populate_balances(self, accounts):
for account in accounts:
acc_dict = self.doc['donnees']['compteSoldesMap'][account._id]
account.balance = CleanDecimal(replace_dots=True).filter(acc_dict.get('soldeComptable', acc_dict.get('soldeInstantane')))
account.currency = Currency.get_currency(acc_dict.get('deviseSoldeComptable', acc_dict.get('deviseSoldeInstantane')))
account.coming = CleanDecimal(replace_dots=True, default=NotAvailable).filter(acc_dict.get('montantOperationJour'))
yield account
class HistoryJsonPage(LoggedPage, JsonPage):
def get_value(self):
if 'NOK' in self.doc['commun']['statut']:
return 'position'
else:
return 'valeur'
@method
class iter_history(DictElement):
def __init__(self, *args, **kwargs):
super(DictElement, self).__init__(*args, **kwargs)
self.item_xpath = 'donnees/compte/operations' if not 'Prochain' in self.page.url else 'donnees/ecritures'
def condition(self):
return 'donnees' in self.page.doc
def next_page(self):
d = self.page.doc['donnees']['compte'] if not 'Prochain' in self.page.url else self.page.doc['donnees']
if 'ecrituresRestantes' in d:
next_ope = d['ecrituresRestantes']
next_data = d['sceauEcriture']
else:
next_ope = d['operationsRestantes']
next_data = d['sceauOperation']
if next_ope:
data = {}
data['b64e4000_sceauEcriture'] = next_data
if not 'intraday' in self.page.url:
data['cl200_typeReleve'] = Env('value')(self)
return requests.Request("POST", BrowserURL('history_next')(self), data=data)
class item(ItemElement):
klass = Transaction
obj_rdate = Date(Dict('date', default=None), dayfirst=True, default=NotAvailable)
obj_date = Date(Dict('dVl', default=None), dayfirst=True, default=NotAvailable)
# Label is split into l1, l2, l3, l4, l5.
# l5 is needed for transfer label, for example:
# 'l1': "000001 VIR EUROPEEN EMIS NET"
# 'l2': "POUR: XXXXXXXXXXXXX"
# 'l3': "REF: XXXXXXXXXXXXXX"
# 'l4': "REMISE: XXXXXX TRANSFER LABEL"
# 'l5': "MOTIF: TRANSFER LABEL"
obj_raw = Transaction.Raw(Format(
'%s %s %s %s %s',
Dict('l1'),
Dict('l2'),
Dict('l3'),
Dict('l4'),
Dict('l5'),
))
# keep the 3 first rows for transaction label
obj_label = Transaction.Raw(Format(
'%s %s %s',
Dict('l1'),
Dict('l2'),
Dict('l3'),
))
def obj_amount(self):
return CleanDecimal(Dict('c', default=None), replace_dots=True, default=None)(self) or \
CleanDecimal(Dict('d'), replace_dots=True)(self)
def obj_deleted(self):
return self.obj.type == FrenchTransaction.TYPE_CARD_SUMMARY
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
class BankStatementPage(LoggedPage, JsonPage):
def get_min_max_date(self):
criterium = self.doc['donnees']['criteres']
return criterium['dateMin'], criterium['dateMax']
@method
class iter_subscription(DictElement):
item_xpath = 'donnees/comptes'
class item(ItemElement):
klass = Subscription
obj_id = Dict('id')
obj_label = Dict('libelle')
obj_subscriber = Env('subscriber')
def iter_documents(self):
account, = self.doc['donnees']['comptes']
statements = account['releves']
for document in statements:
d = Document()
d.date = datetime.strptime(document['dateEdition'], '%d/%m/%Y')
d.label = '%s %s' % (account['libelle'], document['dateEdition'])
d.type = 'document'
d.format = 'pdf'
d.id = '%s_%s' % (account['id'], document['dateEdition'].replace('/', ''))
d.url = '/icd/syd-front/data/syd-rce-telechargerReleve.html?b64e4000_sceau=%s' % quote_plus(document['sceau'])
yield d