diff --git a/modules/cragr/api/transfer_pages.py b/modules/cragr/api/transfer_pages.py
new file mode 100644
index 0000000000000000000000000000000000000000..8b2f9f9f6bbcd634b40d18c74327d7bab57f5e9f
--- /dev/null
+++ b/modules/cragr/api/transfer_pages.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2019 Sylvie Ye
+#
+# This file is part of a weboob module.
+#
+# This weboob module 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.
+#
+# This weboob module 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 this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+from datetime import date
+
+from weboob.browser.pages import LoggedPage, JsonPage, RawPage
+from weboob.browser.elements import method, ItemElement, DictElement
+from weboob.capabilities.bank import (
+ Account, Recipient, Transfer, TransferBankError,
+)
+from weboob.browser.filters.standard import (
+ CleanDecimal, Env, Date, CleanText,
+)
+from weboob.browser.filters.json import Dict
+
+
+class RecipientsPage(LoggedPage, JsonPage):
+ def is_sender_account(self, account_id):
+ for acc in self.doc:
+ if acc.get('senderOfTransfert') and account_id == acc.get('accountNumber'):
+ return True
+
+ @method
+ class iter_debit_accounts(DictElement):
+ class item(ItemElement):
+ def condition(self):
+ return Dict('accountNumber', default=None)(self)
+
+ klass = Account
+
+ obj_id = obj_number = Dict('accountNumber')
+ obj_label = Dict('accountNatureLongLabel')
+ obj_iban = Dict('ibanCode')
+ obj_currency = Dict('currencyCode')
+
+ def obj_balance(self):
+ balance_value = CleanDecimal(Dict('balanceValue'))(self)
+ if CleanText(Dict('balanceSign'))(self) == '-':
+ return -balance_value
+ return balance_value
+
+ @method
+ class iter_internal_recipient(DictElement):
+ class item(ItemElement):
+ def condition(self):
+ return Dict('recipientOfTransfert', default=None)(self) and \
+ Env('account_id')(self) != Dict('accountNumber', default=None)(self)
+
+ klass = Recipient
+
+ obj_id = Dict('accountNumber')
+ obj_label = Dict('accountNatureLongLabel')
+ obj_iban = Dict('ibanCode')
+ obj_category = 'Interne'
+ obj_enabled_at = date.today()
+
+ @method
+ class iter_external_recipient(DictElement):
+ def store(self, obj):
+ return obj
+
+ class item(ItemElement):
+ def condition(self):
+ return Dict('recipientId', default=None)(self)
+
+ klass = Recipient
+
+ obj_id = obj_iban = Dict('ibanCode')
+ obj_label = Dict('recipientName')
+ obj_category = 'Externe'
+ obj_enabled_at = date.today()
+
+
+class TransferTokenPage(LoggedPage, RawPage):
+ def get_token(self):
+ return self.doc
+
+
+class TransferPage(LoggedPage, JsonPage):
+ def check_transfer(self):
+ error_msg = Dict('messageErreur')(self.doc)
+ if error_msg:
+ raise TransferBankError(message=error_msg)
+ return Dict('page')(self.doc) == '/recap'
+
+ def handle_response(self, transfer):
+ t = Transfer()
+ t._space = transfer._space
+ t._operation = transfer._operation
+ t._token = transfer._token
+ t._connection_id = transfer._connection_id
+
+ t.label = Dict('transferComplementaryInformations1')(self.doc)
+ t.exec_date = Date(Dict('dateVirement'), dayfirst=True)(self.doc)
+ t.amount = CleanDecimal(Dict('amount'))(self.doc)
+ t.currency = Dict('currencyCode')(self.doc)
+
+ t.account_id = Dict('currentDebitAccountNumber')(self.doc)
+ t.account_iban = Dict('currentDebitIbanCode')(self.doc)
+ t.account_label = Dict('typeCompte')(self.doc)
+
+ t.recipient_id = t.recipient_iban = Dict('currentCreditIbanCode')(self.doc)
+ t.recipient_label = Dict('currentCreditAccountName')(self.doc)
+
+ return t
+
+ def check_transfer_exec(self):
+ error_msg = Dict('messageErreur')(self.doc)
+ if error_msg:
+ raise TransferBankError(message=error_msg)
+ return Dict('page')(self.doc)
diff --git a/modules/humanis/__init__.py b/modules/humanis/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..daddbd449f06b1b4fbd9435f2029002c089fb55c
--- /dev/null
+++ b/modules/humanis/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2017 Jean Walrave
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+
+from .module import HumanisModule
+
+
+__all__ = ['HumanisModule']
diff --git a/modules/humanis/browser.py b/modules/humanis/browser.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f52f0f4753a80f379ae110c1fa7abf8efd11680
--- /dev/null
+++ b/modules/humanis/browser.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2016 Jean Walrave
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.browser import AbstractBrowser, URL
+
+from .pages import LoginPage
+
+class HumanisBrowser(AbstractBrowser):
+ PARENT = 'cmes'
+
+ login = URL('humanis/fr/identification/login.cgi', LoginPage)
+
+ def __init__(self, login, password, baseurl, subsite, *args, **kwargs):
+ self.weboob = kwargs['weboob']
+ super(HumanisBrowser, self).__init__(login, password, baseurl, subsite, *args, **kwargs)
diff --git a/modules/humanis/module.py b/modules/humanis/module.py
new file mode 100644
index 0000000000000000000000000000000000000000..85e1e694729ac52a6286cff9c50ae44441f7b076
--- /dev/null
+++ b/modules/humanis/module.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2016 Jean Walrave
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.tools.backend import Module, BackendConfig
+from weboob.tools.value import ValueBackendPassword
+from weboob.capabilities.bank import CapBankPockets, AccountNotFound
+from weboob.capabilities.base import find_object
+
+from .browser import HumanisBrowser
+
+
+__all__ = ['HumanisModule']
+
+
+class HumanisModule(Module, CapBankPockets):
+ NAME = 'humanis'
+ DESCRIPTION = u'Humanis Épargne Salariale'
+ MAINTAINER = u'Jean Walrave'
+ EMAIL = 'jwalrave@budget-insight.com'
+ LICENSE = 'LGPLv3+'
+ VERSION = '1.5'
+ CONFIG = BackendConfig(ValueBackendPassword('login', label=u'Code d\'accès', masked=False),
+ ValueBackendPassword('password', label='Mot de passe'))
+
+ BROWSER = HumanisBrowser
+
+ def create_default_browser(self):
+ return self.create_browser(
+ self.config['login'].get(),
+ self.config['password'].get(),
+ 'https://www.gestion-epargne-salariale.fr',
+ 'humanis/',
+ weboob=self.weboob
+ )
+
+ def get_account(self, _id):
+ return find_object(self.browser.iter_accounts(), id=_id, error=AccountNotFound)
+
+ def iter_accounts(self):
+ return self.browser.iter_accounts()
+
+ def iter_history(self, account):
+ return self.browser.iter_history(account)
+
+ def iter_investment(self, account):
+ return self.browser.iter_investment(account)
+
+ def iter_pocket(self, account):
+ return self.browser.iter_pocket(account)
diff --git a/modules/humanis/pages.py b/modules/humanis/pages.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7d153e27c1eac98dbcc63bc0ae7fd61743f0e13
--- /dev/null
+++ b/modules/humanis/pages.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2016 Jean Walrave
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.browser.pages import AbstractPage
+
+
+class LoginPage(AbstractPage):
+ PARENT = 'cmes'
+ PARENT_URL = 'login'
+ BROWSER_ATTR = 'package.browser.CmesBrowser'
diff --git a/modules/humanis/test.py b/modules/humanis/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..67094555bd7308d6633762c31467f26810318add
--- /dev/null
+++ b/modules/humanis/test.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2017 Jean Walrave
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+
+from weboob.tools.test import BackendTest
+
+
+class HumanisTest(BackendTest):
+ MODULE = 'humanis'
diff --git a/modules/themisbanque/__init__.py b/modules/themisbanque/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a07a1c71f6528d3fd12a8ca15a021a731359be44
--- /dev/null
+++ b/modules/themisbanque/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from .module import ThemisModule
+
+
+__all__ = ['ThemisModule']
diff --git a/modules/themisbanque/browser.py b/modules/themisbanque/browser.py
new file mode 100644
index 0000000000000000000000000000000000000000..2821536689815d4fa3f3a8ac0e979075827d48f7
--- /dev/null
+++ b/modules/themisbanque/browser.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.browser import LoginBrowser, URL, need_login
+from weboob.tools.compat import urljoin
+
+from .pages import LoginPage, LoginConfirmPage, AccountsPage, RibPage, RibPDFPage, HistoryPage
+
+
+class ThemisBrowser(LoginBrowser):
+ BASEURL = 'https://esab.themisbanque.eu/'
+
+ home = URL('/es@b/fr/esab.jsp')
+ login = URL('/es@b/fr/codeident.jsp', LoginPage)
+ login_confirm = URL('/es@b/servlet/internet0.ressourceWeb.servlet.Login', LoginConfirmPage)
+ accounts = URL(r'/es@b/servlet/internet0.ressourceWeb.servlet.PremierePageServlet\?pageToTreatError=fr/Infos.jsp&dummyDate=',
+ r'/es@b/servlet/internet0.ressourceWeb.servlet.PremierePageServlet\?cryptpara=.*',
+ r'/es@b/servlet/internet0.ressourceWeb.servlet.EsabServlet.*',
+ AccountsPage)
+ history = URL('/es@b/servlet/internet0.ressourceWeb.servlet.ListeDesMouvementsServlet.*', HistoryPage)
+ rib = URL(r'/es@b/fr/rib.jsp\?cryptpara=.*', RibPage)
+ rib_pdf = URL(r'/es@b/servlet/internet0.ressourceWeb.servlet.RibPdfDownloadServlet', RibPDFPage)
+
+ def do_login(self):
+ self.home.go()
+ self.login.go()
+ self.page.login(self.username, self.password)
+
+ @need_login
+ def iter_accounts(self):
+ self.accounts.stay_or_go()
+ # sometimes when the user has messages, accounts's page will redirect
+ # to the message page and the user will have to click "ok" to access his accounts
+ # this will happen as long as the messages aren't deleted.
+ # In this case, accounts may be reached through a different link (in the "ok" button)
+ acc_link = self.page.get_acc_link()
+ if acc_link:
+ self.location(urljoin(self.BASEURL, acc_link))
+ return self.page.iter_accounts()
+
+ @need_login
+ def get_history(self, account):
+ self.location(account._link)
+ return self.page.get_operations()
+
+ @need_login
+ def get_profile(self):
+ accounts = list(self.iter_accounts())
+ self.location(accounts[0]._url)
+ return self.page.get_profile()
diff --git a/modules/themisbanque/module.py b/modules/themisbanque/module.py
new file mode 100644
index 0000000000000000000000000000000000000000..389070fd22ed1106a8ec950477c0149aacfdfd02
--- /dev/null
+++ b/modules/themisbanque/module.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.tools.backend import Module, BackendConfig
+from weboob.capabilities.bank import CapBank, AccountNotFound
+from weboob.capabilities.base import find_object
+from weboob.capabilities.profile import CapProfile
+from weboob.tools.value import ValueBackendPassword
+from .browser import ThemisBrowser
+
+
+__all__ = ['ThemisModule']
+
+
+class ThemisModule(Module, CapBank, CapProfile):
+ NAME = 'themisbanque'
+ DESCRIPTION = u'Themis'
+ MAINTAINER = u'Romain Bignon'
+ EMAIL = 'romain@weboob.org'
+ LICENSE = 'LGPLv3+'
+ VERSION = '1.5'
+ CONFIG = BackendConfig(ValueBackendPassword('login', label=u"Numéro d'abonné", masked=False),
+ ValueBackendPassword('password', label='Code secret'),
+ )
+
+ BROWSER = ThemisBrowser
+
+ def create_default_browser(self):
+ return self.create_browser(self.config['login'].get(),
+ self.config['password'].get())
+
+ def get_account(self, _id):
+ return find_object(self.browser.iter_accounts(), id=_id, error=AccountNotFound)
+
+ def iter_accounts(self):
+ return self.browser.iter_accounts()
+
+ def iter_coming(self, account):
+ raise NotImplementedError()
+
+ def iter_history(self, account):
+ return self.browser.get_history(account)
+
+ def iter_investment(self, account):
+ raise NotImplementedError()
+
+ def get_profile(self):
+ return self.browser.get_profile()
diff --git a/modules/themisbanque/pages.py b/modules/themisbanque/pages.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2b11278c929137b91de8cbf6eb7221264c8cb41
--- /dev/null
+++ b/modules/themisbanque/pages.py
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+from __future__ import unicode_literals
+
+import re
+
+from weboob.exceptions import BrowserIncorrectPassword
+from weboob.browser.pages import LoggedPage, HTMLPage, pagination, PDFPage
+from weboob.browser.elements import method, ItemElement, TableElement
+from weboob.capabilities.bank import Account
+from weboob.capabilities.base import NotAvailable
+from weboob.capabilities.profile import Profile
+from weboob.browser.filters.standard import CleanText, CleanDecimal, Async, Regexp, Join, Field
+from weboob.browser.filters.html import Attr, Link, TableCell, ColumnNotFound
+from weboob.tools.capabilities.bank.transactions import FrenchTransaction
+from weboob.tools.capabilities.bank.iban import is_iban_valid
+from weboob.tools.compat import basestring
+from weboob.tools.pdf import extract_text
+
+
+class MyCleanText(CleanText):
+ @classmethod
+ def clean(cls, txt, children=True, newlines=True, normalize='NFC'):
+ if not isinstance(txt, basestring):
+ txt = u'\n'.join([t.strip() for t in txt.itertext()])
+
+ return txt
+
+
+class LoginPage(HTMLPage):
+ def login(self, username, password):
+ form = self.get_form()
+ form['identifiant'] = username
+ form['motpasse'] = password
+ form.submit()
+
+
+class LoginConfirmPage(HTMLPage):
+ def on_load(self):
+ error = CleanText('//td[has-class("ColonneLibelle")]')(self.doc)
+ if len(error) > 0:
+ raise BrowserIncorrectPassword(error)
+
+
+class AccountsPage(LoggedPage, HTMLPage):
+ def get_acc_link(self):
+ msg = CleanText('//body[@class="message"]')(self.doc)
+ if msg:
+ acc_link = Link('//div[@class="Boutons"]/a', 'href')(self.doc)
+ return acc_link
+
+ @method
+ class iter_accounts(TableElement):
+ item_xpath = '//table[has-class("TableBicolore")]//tr[@id and count(td) > 4]'
+ head_xpath = '//table[has-class("TableBicolore")]//tr/td[@id]/@id'
+
+ col_id = 'idCompteLibelle'
+ col_label = 'idCompteIntitule'
+ col_balance = 'idCompteSolde'
+ col_currency = 'idCompteSoldeUM'
+ col_rib = 'idCompteRIB'
+ col_type = 'idCompteNature'
+
+ class item(ItemElement):
+ klass = Account
+
+ def condition(self):
+ return CleanDecimal(TableCell('balance'), replace_dots=True, default=NotAvailable)(self) is not NotAvailable
+
+ TYPE = {
+ 'COMPTE COURANT': Account.TYPE_CHECKING,
+ 'COMPTE TRANSACTION': Account.TYPE_CHECKING,
+ 'COMPTE ORDINAIRE': Account.TYPE_CHECKING,
+ }
+ TYPE_BY_LABELS = {
+ 'CAV': Account.TYPE_CHECKING,
+ }
+
+ obj_id = CleanText(TableCell('id'))
+ obj_label = CleanText(TableCell('label'))
+ obj_currency = FrenchTransaction.Currency(TableCell('currency'))
+ obj_balance = CleanDecimal(TableCell('balance'), replace_dots=True)
+
+ def obj__link(self):
+ return Attr(TableCell('id')(self)[0].xpath('./a'), 'href')(self)
+
+ def obj__url(self):
+ return Link(TableCell('rib')(self)[0].xpath('./a[img[starts-with(@alt, "RIB")]]'), default=None)(self)
+
+ def load_iban(self):
+ link = Link(TableCell('rib')(self)[0].xpath('./a[img[starts-with(@alt, "RIB")]]'), default=None)(self)
+ return self.page.browser.async_open(link)
+
+ def obj_type(self):
+ try:
+ el_to_check = CleanText(TableCell('type'))(self)
+ type_dict = self.TYPE
+ except ColumnNotFound:
+ el_to_check = Field('label')(self)
+ type_dict = self.TYPE_BY_LABELS
+
+ for k, v in type_dict.items():
+ if el_to_check.startswith(k):
+ return v
+ return Account.TYPE_UNKNOWN
+
+ def obj_iban(self):
+ rib_page = Async('iban').loaded_page(self)
+ if 'RibPdf' in rib_page.url:
+ return rib_page.get_iban()
+ return Join('', Regexp(CleanText('//td[has-class("ColonneCode")][contains(text(), "IBAN")]'), r'\b((?!IBAN)[A-Z0-9]+)\b', nth='*'))(rib_page.doc) or NotAvailable
+
+
+class RibPage(LoggedPage, HTMLPage):
+ def get_profile(self):
+ profile = Profile()
+
+ # profile is inside a
separated with a simple without or
+ profile_txt = MyCleanText('//div[@class="TableauAffichage"]/table/tr[3]/td[1]')(self.doc).split('\n')
+ i_name = 0
+ profile.name = u''
+ # name can be on one, two, (more ?) lines, so we stop when line start by a number, we suppose it's the address number
+ while not re.search('^\d', profile_txt[i_name]):
+ profile.name += ' ' + profile_txt[i_name]
+ i_name += 1
+
+ profile.name = profile.name.strip()
+ profile.address = u''
+ # address is not always on two lines, so we consider every lines from here to before last are address, (last one is country)
+ for i in range(i_name, len(profile_txt)-1):
+ profile.address += ' ' + profile_txt[i]
+
+ profile.address = profile.address.strip()
+ profile.country = profile_txt[-1]
+
+ profile.name = profile.name.replace('MONSIEUR ', '').replace('MADAME ', '')
+
+ return profile
+
+
+class RibPDFPage(LoggedPage, PDFPage):
+ def get_iban(self):
+ text = extract_text(self.doc)
+ iban = re.search(r'IBAN([A-Z]{2}\d+)', text).group(1)
+ assert is_iban_valid(iban), 'did not parse IBAN properly'
+ return iban
+
+
+class Transaction(FrenchTransaction):
+ PATTERNS = [(re.compile(r'^VIR(EMENT)?( SEPA)? (?P.*)'), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r'^PRLV (?P.*)'), FrenchTransaction.TYPE_ORDER),
+ (re.compile(r'^(?P.*) CARTE \d+ PAIEMENT CB\s+(?P\d{2})(?P\d{2}) ?(.*)$'),
+ FrenchTransaction.TYPE_CARD),
+ (re.compile(r'^RETRAIT DAB (?P\d{2})(?P\d{2}) (?P.*) CARTE [\*\d]+'),
+ FrenchTransaction.TYPE_WITHDRAWAL),
+ (re.compile(r'^CHEQUE( (?P.*))?$'), FrenchTransaction.TYPE_CHECK),
+ (re.compile(r'^(F )?COTIS\.? (?P.*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^(REMISE|REM.CHQ) (?P.*)'), FrenchTransaction.TYPE_DEPOSIT),
+ (re.compile(r'^(?P.*) CARTE BLEUE'), FrenchTransaction.TYPE_CARD),
+ (re.compile(r'^PRVL SEPA (?P.*)'), FrenchTransaction.TYPE_ORDER),
+ (re.compile(r'^(?P(INT. DEBITEURS).*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^(?P.*(VIR EMIS).*)'), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r'^(?P.*(\bMOUVEMENT\b).*)'), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r'^(?P.*(ARRETE TRIM.).*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^(?P.*(TENUE DE DOSSIE).*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^(?P.*(RELEVE LCR ECH).*)'), FrenchTransaction.TYPE_ORDER),
+ (re.compile(r'^(?P.*(\+ FORT DECOUVERT).*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^(?P.*(EXTRANET @THEMI).*)'), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^(?P.*(REL CPT DEBITEU).*)'), FrenchTransaction.TYPE_ORDER),
+ (re.compile(r"^(?P.*(\bAFFRANCHISSEMENT\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(REMISE VIREMENTS MAGNE).*)"), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r"^(?P.*(\bEFFET\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(\bMANIP\.\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(INTERETS SUR REMISE PTF).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(REMISE ESCOMPTE PTF).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(RETENUE DE GARANTIE).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(RESTITUTION RETENUE GARANTIE).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(\bAMENDES\b).*)"), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r"^(?P.*(\bOA\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^.* COTIS ANN (?P.*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(FORFAIT CENT\.RE).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(ENVOI CB).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(RET\.SDD).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(RETOUR PVL ACD EXPERTISE).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(Annulation PAR REJ\/CHQ).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(REJET CHEQUE).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(CHQ PAYE INFRAC).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P^(CHQ IRREGULIER).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(ERREUR REMISE C).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P^(\bREMCHQ\b).*)"), FrenchTransaction.TYPE_DEPOSIT),
+ (re.compile(r"^(?P^(RETOUR PVL).*)"), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r"^(?P.*(\bTRANSFERT\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(\bCONFIRMATION\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(CAUTION AVEC GAGE).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(\bRAPATRIEMENT\b).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r"^(?P.*(CHANGE REF).*)"), FrenchTransaction.TYPE_BANK),
+ (re.compile(r'^CARTE DU'), FrenchTransaction.TYPE_CARD),
+ (re.compile(r'^(VIR (SEPA)?|Vir|VIR.)(?P.*)'), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r'^VIREMENT DE (?P.*)'), FrenchTransaction.TYPE_TRANSFER),
+ (re.compile(r'^(CHQ|CHEQUE) (?P.*)'), FrenchTransaction.TYPE_CHECK),
+ (re.compile(r'^(PRLV SEPA|PRELEVEMENT) (?P.*)'), FrenchTransaction.TYPE_ORDER),
+ ]
+
+
+class HistoryPage(LoggedPage, HTMLPage):
+ @pagination
+ @method
+ class get_operations(Transaction.TransactionsElement):
+ def next_page(self):
+ for script in self.page.doc.xpath('//script'):
+ m = re.search(r"getCodePagination\('(\d+)','(\d+)','([^']+)'.*", script.text or '', re.MULTILINE)
+ if m:
+ cur_page = int(m.group(1))
+ nb_pages = int(m.group(2))
+ baseurl = m.group(3)
+
+ if cur_page < nb_pages:
+ return baseurl + '&numeroPage=%s&nbrPage=%s' % (cur_page + 1, nb_pages)
+
+ head_xpath = '//div[has-class("TableauBicolore")]/table/tr[not(@id)]/td'
+ item_xpath = '//div[has-class("TableauBicolore")]/table/tr[@id and count(td) > 3]'
+
+ col_date = ['Date comptable', "Date d'opération"]
+ col_vdate = ['Date de valeur']
+ col_raw = ["Libellé de l'opération"]
+
+ class item(Transaction.TransactionElement):
+ pass
diff --git a/modules/themisbanque/test.py b/modules/themisbanque/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..035181218a5e9497c36021c6ffd7b2c6b5797348
--- /dev/null
+++ b/modules/themisbanque/test.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Romain Bignon
+#
+# This file is part of a weboob module.
+#
+# This weboob module is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This weboob module 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this weboob module. If not, see .
+
+
+from weboob.tools.test import BackendTest
+
+
+class ThemisBanqueTest(BackendTest):
+ MODULE = 'themisbanque'
+
+ def test_themisbanque(self):
+ raise NotImplementedError()
|