diff --git a/modules/bnporc/company/__init__.py b/modules/bnporc/company/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/bnporc/company/browser.py b/modules/bnporc/company/browser.py
new file mode 100644
index 00000000..a38ea186
--- /dev/null
+++ b/modules/bnporc/company/browser.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 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 .
+
+from weboob.browser import LoginBrowser, URL, need_login
+from weboob.exceptions import BrowserIncorrectPassword
+
+from .pages import LoginPage, AccountsPage, HistoryPage
+
+
+__all__ = ['BNPCompany']
+
+
+class BNPCompany(LoginBrowser):
+ BASEURL = 'https://secure1.entreprises.bnpparibas.net'
+
+ login = URL('/sommaire/jsp/identification.jsp', LoginPage)
+ accounts = URL('/NCCPresentationWeb/e10_soldes/liste_soldes.do', AccountsPage)
+ history = URL('/NCCPresentationWeb/m04_selectionCompteGroupe/init.do?type=compte&identifiant=', HistoryPage)
+
+ def do_login(self):
+ assert isinstance(self.username, basestring)
+ assert isinstance(self.password, basestring)
+ assert self.password.isdigit()
+ self.login.go()
+ self.login.go()
+ assert self.login.is_here()
+
+ self.page.login(self.username, self.password)
+
+ if self.login.is_here():
+ raise BrowserIncorrectPassword()
+
+ @need_login
+ def get_accounts_list(self):
+ self.accounts.go()
+ return self.page.iter_accounts()
+
+ @need_login
+ def get_account(self, _id):
+ pass
+
+ @need_login
+ def iter_history(self, account):
+ pass
+
+ @need_login
+ def iter_coming_operations(self, account):
+ pass
+
+ def iter_investment(self, account):
+ raise NotImplementedError()
diff --git a/modules/bnporc/company/pages.py b/modules/bnporc/company/pages.py
new file mode 100644
index 00000000..ce4ba828
--- /dev/null
+++ b/modules/bnporc/company/pages.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 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 .
+
+from StringIO import StringIO
+import hashlib
+from decimal import Decimal
+
+from weboob.capabilities.bank import Account
+from weboob.browser.pages import HTMLPage, JsonPage, LoggedPage
+from weboob.tools.captcha.virtkeyboard import MappedVirtKeyboard, VirtKeyboardError
+from weboob.tools.json import json
+
+
+class BNPVirtKeyboard(MappedVirtKeyboard):
+ symbols = {'0': 'ff069462836e30a39c911034048f5bb3',
+ '1': '7969f04e4e82eaefa2ce7a9a23c26178',
+ '2': '1e6020f97ca1c3ce3da4f39ded15d67d',
+ '3': 'f84284b40aea93c24814e23e14e76cc8',
+ '4': '88bab262d4b344c0ef8f06ddd01adbcf',
+ '5': '0a270764fc5d8334bcb55053432b26cb',
+ '6': 'e6a4444a6c752cd3e655f2883e530080',
+ '7': '933d4ca5df6b2b3df2dea00a21a3fed6',
+ '8': ['f28b918777d21a5fde2bffb9899e2138', 'a97e6e27159084d50f8ef00548b70252'],
+ '9': 'be751b77af0d998ab4c2cfd38455b2a6',
+ }
+
+ color=(0,0,0)
+
+ def __init__(self, basepage):
+ img = basepage.doc.xpath('//img[@id="gridpass_img"]')[0]
+ imgdata = basepage.browser.open(img.attrib['src']).content
+ MappedVirtKeyboard.__init__(self, StringIO(imgdata), basepage.doc, img, self.color, convert='RGB')
+ self.check_symbols(self.symbols, basepage.browser.responses_dirname)
+
+ def get_symbol_code(self, md5sum):
+ code = MappedVirtKeyboard.get_symbol_code(self, md5sum)
+ code = code.split("'")[3]
+ assert code.isdigit()
+ return code
+
+ def check_color(self, pixel):
+ for p in pixel:
+ if p >= 200:
+ return False
+ return True
+
+ def checksum(self, coords):
+ """Copy of parent checksum(), but cropping (removes empty lines)"""
+ x1, y1, x2, y2 = coords
+ s = ''
+ for y in range(y1, min(y2 + 1, self.height)):
+ for x in range(x1, min(x2 + 1, self.width)):
+ if self.check_color(self.pixar[x, y]):
+ s += " "
+ else:
+ s += "O"
+ s += "\n"
+ s = '\n'.join([l for l in s.splitlines() if l.strip()])
+ return hashlib.md5(s).hexdigest()
+
+
+class LoginPage(HTMLPage):
+ def login(self, login, password):
+ try:
+ vk = BNPVirtKeyboard(self)
+ except VirtKeyboardError as err:
+ self.logger.error("Error: %s" % err)
+ return False
+
+ form = self.get_form(name='loginPwdForm')
+ form['txtAuthentMode'] = 'PASSWORD'
+ form['txtPwdUserId'] = login
+ form['gridpass_hidden_input'] = vk.get_string_code(password)
+ form.submit()
+
+
+
+class AccountsPage(JsonPage, LoggedPage):
+ def iter_accounts(self):
+ for f in self.path('tableauSoldes.listeGroupes'):
+ for g in f:
+ for a in g.get('listeComptes'):
+ yield Account.from_dict({
+ 'id': a.get('numeroCompte'),
+ 'label': '%s %s' % (a.get('libelleType'), a.get('libelleTitulaire')),
+ 'currency': a.get('deviseTenue'),
+ 'balance': Decimal(a.get('soldeComptable')) / 100,
+ 'coming': Decimal(a.get('soldePrevisionnel')) / 100,
+ })
+
+class HistoryPage(JsonPage, LoggedPage):
+ pass
diff --git a/modules/bnporc/module.py b/modules/bnporc/module.py
index d0cf2e74..f4306e61 100644
--- a/modules/bnporc/module.py
+++ b/modules/bnporc/module.py
@@ -28,6 +28,7 @@ from weboob.tools.value import ValueBackendPassword, Value
from .deprecated.browser import BNPorc
from .enterprise.browser import BNPEnterprise
+from .company.browser import BNPCompany
from .pp.browser import BNPParibasBrowser
@@ -50,7 +51,8 @@ class BNPorcModule(Module, CapBank, CapMessages):
Value('website', label='Type de compte', default='pp',
choices={'pp': 'Particuliers/Professionnels',
'ent': 'Entreprises',
- 'ppold': 'Particuliers/Professionnels (ancien site)'}))
+ 'ppold': 'Particuliers/Professionnels (ancien site)',
+ 'ent2': 'Entreprises et PME (nouveau site)'}))
STORAGE = {'seen': []}
# Store the messages *list* for this duration
@@ -62,7 +64,7 @@ class BNPorcModule(Module, CapBank, CapMessages):
self._threads_age = datetime.utcnow()
def create_default_browser(self):
- b = {'ppold': BNPorc, 'ent': BNPEnterprise, 'pp': BNPParibasBrowser}
+ b = {'ppold': BNPorc, 'ent': BNPEnterprise, 'ent2': BNPCompany, 'pp': BNPParibasBrowser}
self.BROWSER = b[self.config['website'].get()]
return self.create_browser(self.config['login'].get(),
self.config['password'].get())