Commit 5b367292 authored by Dorian Roly's avatar Dorian Roly Committed by ntome

[BNP] Add documents to personnal pages

Now bnp personnal, bnp pro and bnp privee can get documents
parent e417e2f8
......@@ -79,6 +79,14 @@ class BNPCompany(LoginBrowser):
( - timedelta(days=90)).strftime('%Y%m%d'),'%Y%m%d'))
def iter_documents(self, subscription):
raise NotImplementedError()
def iter_subscription(self):
raise NotImplementedError()
def iter_coming_operations(self, account):
return self.get_transactions(,
......@@ -33,7 +33,7 @@ from import sorted_transactions
from .pages import (
LoginPage, AuthPage, AccountsPage, AccountHistoryViewPage, AccountHistoryPage,
ActionNeededPage, TransactionPage, MarketPage, InvestPage
ActionNeededPage, TransactionPage, MarketPage, InvestPage,
......@@ -124,6 +124,14 @@ class BNPEnterprise(LoginBrowser):
return []
return self._iter_history_base(account)
def iter_documents(self, subscription):
raise NotImplementedError()
def iter_subscription(self):
raise NotImplementedError()
def _iter_history_base(self, account):
dformat = "%Y%m%d"
......@@ -33,6 +33,10 @@ from weboob.capabilities.profile import CapProfile
from weboob.capabilities.base import find_object, strict_find_object
from import Module, BackendConfig
from import ValueBackendPassword, Value, ValueBool
from weboob.capabilities.bill import (
Subscription, CapDocument, SubscriptionNotFound, DocumentNotFound, Document,
from .enterprise.browser import BNPEnterprise
from .company.browser import BNPCompany
......@@ -42,7 +46,7 @@ from .pp.browser import BNPPartPro, HelloBank
__all__ = ['BNPorcModule']
class BNPorcModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapMessages, CapContact, CapProfile):
class BNPorcModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapMessages, CapContact, CapProfile, CapDocument):
NAME = 'bnporc'
MAINTAINER = u'Romain Bignon'
EMAIL = ''
......@@ -61,6 +65,13 @@ class BNPorcModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapMessag
'ent2': 'Entreprises et PME (nouveau site)'}))
STORAGE = {'seen': []}
accepted_document_types = (
# Store the messages *list* for this duration
CACHE_THREADS = timedelta(seconds=3 * 60 * 60)
......@@ -74,6 +85,14 @@ class BNPorcModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapMessag
self.BROWSER = b[self.config['website'].get()]
return self.create_browser(self.config)
def iter_resources(self, objs, split_path):
if Account in objs:
return self.iter_accounts()
if Subscription in objs:
return self.iter_subscription()
def iter_accounts(self):
return self.browser.iter_accounts()
......@@ -205,4 +224,27 @@ class BNPorcModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapMessag'seen', default=[]).append(
def get_subscription(self, _id):
return find_object(self.iter_subscription(), id=_id, error=SubscriptionNotFound)
def iter_documents(self, subscription):
if not isinstance(subscription, Subscription):
subscription = self.get_subscription(subscription)
return self.browser.iter_documents(subscription)
def iter_subscription(self):
return self.browser.iter_subscription()
def get_document(self, _id):
subscription_id = _id.split('_')[0]
subscription = self.get_subscription(subscription_id)
return find_object(self.iter_documents(subscription), id=_id, error=DocumentNotFound)
def download_document(self, document):
if not isinstance(document, Document):
document = self.get_document(document)
OBJECTS = {Thread: fill_thread}
......@@ -30,6 +30,7 @@ from import (
AccountNotFound, Account, AddRecipientStep, AddRecipientTimeout,
TransferInvalidRecipient, Loan,
from weboob.capabilities.bill import Subscription
from weboob.capabilities.profile import ProfileMissing
from import retry
from import sorted_transactions
......@@ -50,6 +51,7 @@ from .pages import (
UselessPage, TransferAssertionError, LoanDetailsPage,
from .document_pages import DocumentsPage, TitulairePage
__all__ = ['BNPPartPro', 'HelloBank']
......@@ -121,6 +123,9 @@ class BNPParibasBrowser(JsonBrowserMixin, LoginBrowser):
advisor = URL(r'/conseiller-wspl/rest/monConseiller', AdvisorPage)
titulaire = URL(r'/demat-wspl/rest/listerTitulairesDemat', TitulairePage)
document = URL(r'/demat-wspl/rest/rechercheCriteresDemat', DocumentsPage)
profile = URL(r'/kyc-wspl/rest/informationsClient', ProfilePage)
list_detail_card = URL(r'/udcarte-wspl/rest/listeDetailCartes', ListDetailCardPage)
......@@ -510,6 +515,40 @@ class BNPParibasBrowser(JsonBrowserMixin, LoginBrowser):
def get_thread(self, thread):
raise NotImplementedError()
def iter_documents(self, subscription):
titulaires = self.titulaire.go().get_titulaires()
# Calling '/demat-wspl/rest/listerDocuments' before the request on 'document'
# is necessary when you specify an ikpi, otherwise no documents are returned
# When we only have one titulaire, no need to use the ikpi parameter in the request,
# all document are provided with this simple request
data = {
'dateDebut': ( - relativedelta(years=3)).strftime('%d/%m/%Y'),
# Ikpi is necessary for multi titulaires accounts to get each document of each titulaires
if len(titulaires) > 1:
data['ikpiPersonne'] = subscription._iduser
return, sub_number=subscription._number, baseurl=self.BASEURL)
def iter_subscription(self):
acc_list = self.iter_accounts()
for acc in acc_list:
sub = Subscription()
sub.label = acc.label
sub.subscriber = acc._subscriber =
# number is the hidden number of an account like "****1234"
# and it's used in the parsing of the docs in iter_documents
sub._number = acc.number
# iduser is the ikpi affiliate to the account,
# usefull for multi titulaires connexions
sub._iduser = acc._iduser
yield sub
class BNPPartPro(BNPParibasBrowser):
# -*- coding: utf-8 -*-
# Copyright(C) 2009-2019 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
# 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.browser.elements import DictElement, ItemElement, method
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import Format, Date, Env
from weboob.browser.pages import JsonPage, LoggedPage
from weboob.capabilities.bill import Document, DocumentTypes
patterns = {
r'Relevé': DocumentTypes.STATEMENT,
r'Livret(s) A': DocumentTypes.STATEMENT,
r'développement durable': DocumentTypes.STATEMENT,
r'Synthèse': DocumentTypes.STATEMENT,
r'Echelles/Décomptes': DocumentTypes.STATEMENT,
r'épargne logement': DocumentTypes.STATEMENT,
r'Livret(s) jeune': DocumentTypes.STATEMENT,
r'Compte(s) sur Livret': DocumentTypes.STATEMENT,
r'Récapitulatifs annuels': DocumentTypes.REPORT,
r"Avis d'exécution": DocumentTypes.REPORT,
r'Factures': DocumentTypes.BILL,
def get_document_type(family):
for patt, type in patterns.items():
if, family):
return type
return DocumentTypes.OTHER
class TitulairePage(LoggedPage, JsonPage):
def get_titulaires(self):
return set([t['idKpiTitulaire'] for t in self.doc['data']['listeTitulairesDemat']['listeTitulaires']])
class DocumentsPage(LoggedPage, JsonPage):
class iter_documents(DictElement):
item_xpath = 'data/rechercheCriteresDemat/*/*/listeDocument'
ignore_duplicate = True
class item(ItemElement):
klass = Document
def condition(self):
if 'ibanCrypte' in self.el:
return Env('sub_id')(self) in Dict('ibanCrypte')(self)
return Env('sub_number')(self) in Dict('idContrat')(self)
obj_date = Date(Dict('dateDoc'), dayfirst=True)
obj_format = 'pdf'
obj_id = Format('%s_%s', Env('sub_id'), Dict('idDoc'))
def obj_label(self):
if 'ibanCrypte' in self.el:
return '%s %s N° %s' % (Dict('dateDoc')(self), Dict('libelleSousFamille')(self), Dict('numeroCompteAnonymise')(self))
return '%s %s N° %s' % (Dict('dateDoc')(self), Dict('libelleSousFamille')(self), Dict('idContrat')(self))
def obj_url(self):
# For most of the cases on the json
if 'ibanCrypte' in self.el:
ibanCrypte = Dict('ibanCrypte')(self)
idDoc = Dict('idDoc')(self)
typeCompte = Dict('typeCompte')(self)
typeDoc = Dict('typeDoc')(self)
typeFamille = 'R001' # add typeFamille correctly for professional and private pages
idLocalisation = Dict('idLocalisation')(self)
viDocDocument = Dict('viDocDocument')(self)
famDoc = Dict('famDoc')(self)
consulted = Dict('consulted')(self)
dateDoc = Dict('dateDoc')(self)
return '%sdemat-wspl/rest/consultationDocumentDemat?'\
'&viDocDocument=%s&familleDoc=%s&consulted=%s&dateDocument=%s&ikpiPersonne=' % (Env('baseurl')(self), ibanCrypte, idDoc, typeCompte,
typeDoc, typeFamille, idLocalisation, viDocDocument, famDoc, consulted, dateDoc)
# For the cases present on privee.mabanque where sometimes the doc url is the different
idDoc = Dict('idDoc')(self)
numClient = Dict('numClient')(self)
heureDoc = Dict('heureDoc')(self)
idLocalisation = Dict('idLocalisation')(self)
viDocDocument = Dict('viDocDocument')(self)
typeReport = Dict('typeReport')(self)
dateDoc = Dict('dateDoc')(self)
return '%sdemat-wspl/rest/consultationDocumentSpecialBpfDemat?'\
'&idLocalisation=%s&typeReport=%s&viDocDocument=%s&dateDocument=%s' % (Env('baseurl')(self), idDoc, numClient,
heureDoc, idLocalisation, typeReport, viDocDocument, dateDoc)
def obj_type(self):
return get_document_type(Dict('libelleSousFamille')(self))
......@@ -30,7 +30,8 @@ import lxml.html as html
from weboob.browser.elements import DictElement, ListElement, TableElement, ItemElement, method
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import (
Format, Eval, Regexp, CleanText, Date, CleanDecimal, Field, Coalesce, Map, Env, Currency,
Format, Eval, Regexp, CleanText, Date, CleanDecimal, Field, Coalesce, Map, Env,
from weboob.browser.filters.html import TableCell
from weboob.browser.pages import JsonPage, LoggedPage, HTMLPage
......@@ -306,7 +307,6 @@ class ProfilePage(LoggedPage, JsonPage):
class AccountsPage(BNPPage):
class iter_accounts(DictElement):
item_xpath = 'data/infoUdc/familleCompte'
......@@ -354,6 +354,8 @@ class AccountsPage(BNPPage):
obj_balance = Dict('soldeDispo')
obj_coming = Dict('soldeAVenir')
obj_number = Dict('value')
obj__subscriber = Format('%s %s', Dict('titulaire/nom'), Dict('titulaire/prenom'))
obj__iduser = Dict('titulaire/ikpi')
def obj_iban(self):
iban = Map(Dict('key'), Env('ibans')(self), default=NotAvailable)(self)
......@@ -381,6 +383,8 @@ class LoanDetailsPage(BNPPage):
obj_rate = Dict('data/tauxRemboursement')
obj_nb_payments_left = Dict('data/nbRemboursementRestant')
obj_next_payment_date = Date(Dict('data/dateProchainAmortissement'), dayfirst=True)
obj__subscriber = Format('%s %s', Dict('data/titulaire/nom'), Dict('data/titulaire/prenom'))
obj__iduser = None
class fill_revolving_details(ItemElement):
......@@ -745,6 +749,8 @@ class CapitalisationPage(LoggedPage, HTMLPage):
obj_balance = CleanDecimal(TableCell('balance'), replace_dots=True)
obj_coming = None
obj_iban = None
obj__subscriber = None
obj__iduser = None
def obj_type(self):
for k, v in
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment