Commit 7c9742fd authored by Romain Bignon's avatar Romain Bignon

backport devel modules fixes

parent 7c469239
Pipeline #2007 failed with stages
......@@ -21,7 +21,9 @@ from __future__ import unicode_literals
from datetime import date
from weboob.browser import LoginBrowser, URL, need_login, StatesMixin
from weboob.exceptions import BrowserIncorrectPassword, BrowserUnavailable, ImageCaptchaQuestion, BrowserQuestion
from weboob.exceptions import (
BrowserIncorrectPassword, BrowserUnavailable, ImageCaptchaQuestion, BrowserQuestion, ActionNeeded
)
from weboob.tools.value import Value
from weboob.browser.browsers import ClientError
......@@ -85,6 +87,11 @@ class AmazonBrowser(LoginBrowser, StatesMixin):
self.location('/ap/signin', data=res_form, headers=self.otp_headers)
def handle_security(self):
otp_type = self.page.get_otp_type()
if otp_type == '/ap/signin':
# this otp will be always present until user deactivate it
raise ActionNeeded('You have enabled otp in your options, please deactivate it before synchronize')
if self.page.doc.xpath('//span[@class="a-button-text"]'):
self.page.send_code()
......
......@@ -45,9 +45,20 @@ class PanelPage(LoggedPage, HTMLPage):
class SecurityPage(HTMLPage):
def get_otp_type(self):
# amazon send us otp in two cases:
# - if it's the first time we connect to this account for an ip => manage it normally
# - if user has activated otp in his options => raise ActionNeeded, an ask user to deactivate it
form = self.get_form(xpath='//form[.//h1]')
url = form.url.replace(self.browser.BASEURL, '')
# verify: this otp is sent by amazon when we connect to the account for the first time from a new ip or computer
# /ap/signin: this otp is a user activated otp which is always present
assert url in ('verify', '/ap/signin'), url
return url
def get_otp_message(self):
message = self.doc.xpath('//div[@class="a-box-inner"]/p')
return message[0] if message else None
return CleanText('//div[@class="a-box-inner"]/p')(self.doc)
def send_code(self):
form = self.get_form()
......
......@@ -27,7 +27,7 @@ from .pages import ResultsPage, EventPage, EventDatesPage, SearchPage
class BilletreducBrowser(PagesBrowser):
BASEURL = 'http://www.billetreduc.com'
BASEURL = 'https://www.billetreduc.com'
search = URL(r'/recherche.htm', SearchPage)
results = URL(r'/search.htm', ResultsPage)
......
......@@ -166,7 +166,7 @@ class ResultsPage(HTMLPage):
p.do_parse()
return p.res
m = re.match('le \w+ \d+ \w+ \d+ à (\d+)h(\d*)$', txt)
m = re.match('le \w+ \d+ \w+ \d+ à (\d+)h(\d*)$', txt, re.UNICODE)
return [(int(m.group(1)), int(m.group(2) or 0))]
obj_start_date = Env('date')
......
......@@ -61,8 +61,8 @@ class PortfolioPage(LoggedPage, HTMLPage):
obj_label = 'Compte Bolden'
obj_type = Account.TYPE_MARKET
obj_currency = 'EUR'
obj_balance = CleanDecimal('//div[p[has-class("investor-state") and contains(text(),"Total compte Bolden :")]]/p[has-class("investor-status")]', replace_dots=True)
obj_valuation_diff = CleanDecimal('//p[has-class("rent-amount strong dashboard-text")]', replace_dots=True)
obj_balance = CleanDecimal.French('//div[p[has-class("investor-state") and contains(text(),"Total compte Bolden :")]]/p[has-class("investor-status")]')
obj_valuation_diff = CleanDecimal.French('//div[has-class("rent-total")]')
@method
class iter_investments(TableElement):
......@@ -93,7 +93,7 @@ class PortfolioPage(LoggedPage, HTMLPage):
return urljoin(self.page.url, Link('.//a')(TableCell('doc')(self)[0]))
def get_liquidity(self):
return CleanDecimal('//div[p[contains(text(), "Fonds disponibles")]]/p[@class="investor-status strong"]', replace_dots=True)(self.doc)
return CleanDecimal.French('//div[p[contains(text(), "Fonds disponibles")]]/p[has-class("investor-status")]')(self.doc)
class OperationsPage(LoggedPage, HTMLPage):
......
......@@ -41,15 +41,16 @@ class BouyguesBrowser(LoginBrowser):
home = URL(r'https://www.bouyguestelecom.fr/mon-compte', HomePage)
subscriber = URL(r'/personnes/(?P<idUser>\d+)$', SubscriberPage)
subscriptions = URL(r'/personnes/(?P<idUser>\d+)/comptes-facturation', SubscriptionPage)
subscriptions_details = URL(r'/comptes-facturation/(?P<idSub>\d+)/contrats-payes', SubscriptionDetailPage)
document_file = URL(r'/comptes-facturation/(?P<idSub>\d+)/factures/\d+/documents', DocumentFilePage)
documents = URL(r'/comptes-facturation/(?P<idSub>\d+)/factures', DocumentsPage)
sms_page = URL(r'http://www.mobile.service.bbox.bouyguestelecom.fr/services/SMSIHD/sendSMS.phtml',
r'http://www.mobile.service.bbox.bouyguestelecom.fr/services/SMSIHD/confirmSendSMS.phtml',
sms_page = URL(r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/sendSMS.phtml',
r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/confirmSendSMS.phtml',
SendSMSPage)
confirm = URL(r'http://www.mobile.service.bbox.bouyguestelecom.fr/services/SMSIHD/resultSendSMS.phtml', UselessPage)
sms_error_page = URL(r'http://www.mobile.service.bbox.bouyguestelecom.fr/services/SMSIHD/SMS_erreur.phtml',
confirm = URL(r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/resultSendSMS.phtml', UselessPage)
sms_error_page = URL(r'https://www.secure.bbox.bouyguestelecom.fr/services/SMSIHD/SMS_erreur.phtml',
SendSMSErrorPage)
profile = URL(r'/personnes/(?P<idUser>\d+)/coordonnees', ProfilePage)
......
......@@ -74,7 +74,10 @@ class BPBrowser(LoginBrowser, StatesMixin):
'/voscomptes/canalXHTML/pret/encours/detaillerOffrePretConsoListe-encoursPrets.ea',
'/voscomptes/canalXHTML/pret/creditRenouvelable/init-consulterCreditRenouvelable.ea',
'/voscomptes/canalXHTML/pret/encours/rechercherPret-encoursPrets.ea',
'/voscomptes/canalXHTML/sso/commun/init-integration.ea\?partenaire',
'/voscomptes/canalXHTML/sso/lbpf/souscriptionCristalFormAutoPost.jsp',
AccountList)
par_accounts_revolving = URL('https://espaceclientcreditconso.labanquepostale.fr/sav/accueil.do', AccountList)
accounts_rib = URL(r'.*voscomptes/canalXHTML/comptesCommun/imprimerRIB/init-imprimer_rib.ea.*',
'/voscomptes/canalXHTML/comptesCommun/imprimerRIB/init-selection_rib.ea', AccountRIB)
......@@ -103,10 +106,11 @@ class BPBrowser(LoginBrowser, StatesMixin):
par_account_checking_history = URL('/voscomptes/canalXHTML/CCP/releves_ccp/init-releve_ccp.ea\?typeRecherche=10&compte.numero=(?P<accountId>.*)',
'/voscomptes/canalXHTML/CCP/releves_ccp/afficher-releve_ccp.ea', AccountHistory)
deferred_card_history = URL(r'/voscomptes/canalXHTML/CB/releveCB/init-mouvementsCarteDD.ea\?compte.numero=(?P<accountId>\w+)&indexCarte=(?P<cardIndex>\d+)&typeListe=(?P<type>\d+)', AccountHistory)
deferred_card_history_multi = URL(r'/voscomptes/canalXHTML/CB/releveCB/preparerRecherche-mouvementsCarteDD.ea\?compte.numero=(?P<accountId>\w+)&indexCarte=(?P<cardIndex>\d+)&typeListe=(?P<type>\d+)', AccountHistory) # &typeRecherche=10
deferred_card_history = URL(r'/voscomptes/canalXHTML/CB/releveCB/init-mouvementsCarteDD.ea\?compte.numero=(?P<accountId>\w+)&indexCompte=(?P<cardIndex>\d+)&typeListe=(?P<type>\d+)', AccountHistory)
deferred_card_history_multi = URL(r'/voscomptes/canalXHTML/CB/releveCB/preparerRecherche-mouvementsCarteDD.ea\?indexCompte=(?P<accountId>\w+)&indexCarte=(?P<cardIndex>\d+)&typeListe=(?P<type>\d+)', AccountHistory) # &typeRecherche=10
par_account_checking_coming = URL('/voscomptes/canalXHTML/CCP/releves_ccp_encours/preparerRecherche-releve_ccp_encours.ea\?compte.numero=(?P<accountId>.*)&typeRecherche=1',
'/voscomptes/canalXHTML/CB/releveCB/init-mouvementsCarteDD.ea\?compte.numero=(?P<accountId>.*)&typeListe=1&typeRecherche=10', AccountHistory)
'/voscomptes/canalXHTML/CB/releveCB/init-mouvementsCarteDD.ea\?compte.numero=(?P<accountId>.*)&typeListe=1&typeRecherche=10',
'/voscomptes/canalXHTML/CCP/releves_ccp_encours/preparerRecherche-releve_ccp_encours.ea\?indexCompte', AccountHistory)
par_account_savings_and_invests_history = URL('/voscomptes/canalXHTML/CNE/releveCNE/init-releve_cne.ea\?typeRecherche=10&compte.numero=(?P<accountId>.*)',
'/voscomptes/canalXHTML/CNE/releveCNE/releveCNE-releve_cne.ea', AccountHistory)
......@@ -218,6 +222,10 @@ class BPBrowser(LoginBrowser, StatesMixin):
@need_login
def get_accounts_list(self):
if self.session.cookies.get('indicateur'):
# Malformed cookie to delete to reach other spaces
del self.session.cookies['indicateur']
if self.accounts is None:
accounts = []
to_check = []
......@@ -237,7 +245,7 @@ class BPBrowser(LoginBrowser, StatesMixin):
for account in self.page.iter_accounts():
if account.type == Account.TYPE_LOAN:
self.location(account.url)
if 'CreditRenouvelable' not in account.url:
if 'initSSO' not in account.url:
for loan in self.page.iter_loans():
loan.currency = account.currency
accounts.append(loan)
......@@ -248,9 +256,11 @@ class BPBrowser(LoginBrowser, StatesMixin):
student_loan.currency = account.currency
accounts.append(student_loan)
else:
for loan in self.page.iter_revolving_loans():
loan.currency = account.currency
accounts.append(loan)
# The main revolving page is not accessible, we can reach it by this new way
self.location(self.absurl('/voscomptes/canalXHTML/sso/lbpf/souscriptionCristalFormAutoPost.jsp'))
self.page.go_revolving()
revolving_loan = self.page.get_revolving_attributes(account)
accounts.append(revolving_loan)
page.go()
elif account.type == Account.TYPE_PERP:
......@@ -308,7 +318,7 @@ class BPBrowser(LoginBrowser, StatesMixin):
self.go_linebourse(account)
return self.linebourse.iter_history(account.id)
if account.type == Account.TYPE_LOAN:
if account.type in (Account.TYPE_LOAN, Account.TYPE_REVOLVING_CREDIT):
return []
if account.type == Account.TYPE_CARD:
......
......@@ -30,7 +30,7 @@ from weboob.capabilities.profile import Person
from weboob.browser.elements import ListElement, ItemElement, method, TableElement
from weboob.browser.pages import LoggedPage, RawPage, PartialHTMLPage, HTMLPage
from weboob.browser.filters.html import Link, TableCell
from weboob.browser.filters.standard import CleanText, CleanDecimal, Regexp, Env, Field, BrowserURL, Currency, Async, Date, Format
from weboob.browser.filters.standard import CleanText, CleanDecimal, Regexp, Env, Field, Currency, Async, Date, Format
from weboob.exceptions import BrowserUnavailable
from weboob.tools.compat import urljoin, unicode
......@@ -58,6 +58,8 @@ class item_account_generic(ItemElement):
def obj_url(self):
url = Link(u'./a', default=NotAvailable)(self)
if url:
if 'CreditRenouvelable' in url:
url = Link(u'.//a[contains(text(), "espace de gestion crédit renouvelable")]')(self.el)
return urljoin(self.page.url, url)
return url
......@@ -74,9 +76,9 @@ class item_account_generic(ItemElement):
has_coming = False
coming = 0
self.page.browser.open(Field('url')(self))
coming_operations = self.page.browser.open(
BrowserURL('par_account_checking_coming', accountId=Field('id'))(self))
details_page = self.page.browser.open(Field('url')(self))
coming_op_link = Regexp(Link(u'//a[contains(text(), "Opérations à venir")]'), r'../(.*)')(details_page.page.doc)
coming_operations = self.page.browser.open(self.page.browser.BASEURL + '/voscomptes/canalXHTML/CCP/' + coming_op_link)
if CleanText('//span[@id="amount_total"]')(coming_operations.page.doc):
has_coming = True
......@@ -91,10 +93,11 @@ class item_account_generic(ItemElement):
return NotAvailable
def obj_iban(self):
response = self.page.browser.open(
'/voscomptes/canalXHTML/comptesCommun/imprimerRIB/init-imprimer_rib.ea?numeroCompte=%s' % Field('id')(
self))
return response.page.get_iban()
rib_link = Link('//a[abbr[contains(text(), "RIB")]]', default=NotAvailable)(self.el)
if rib_link:
response = self.page.browser.open(rib_link)
return response.page.get_iban()
return NotAvailable
def obj_type(self):
types = {'comptes? bancaires?': Account.TYPE_CHECKING,
......@@ -137,6 +140,10 @@ class AccountList(LoggedPage, MyHTMLPage):
raise BrowserUnavailable()
def go_revolving(self):
form = self.get_form()
form.submit()
@property
def no_accounts(self):
return len(self.doc.xpath('//iframe[contains(@src, "/comptes_contrats/sans_")] |\
......@@ -156,6 +163,22 @@ class AccountList(LoggedPage, MyHTMLPage):
def condition(self):
return item_account_generic.condition(self)
def get_revolving_attributes(self, account):
loan = Loan()
loan.id = account.id
loan.label = '%s - %s' %(account.label, account.id)
loan.currency = account.currency
loan.url = account.url
loan.available_amount = CleanDecimal('//tr[td[contains(text(), "Montant Maximum Autorisé") or contains(text(), "Montant autorisé")]]/td[2]')(self.doc)
loan.used_amount = loan.used_amount = CleanDecimal('//tr[td[contains(text(), "Montant Utilisé") or contains(text(), "Montant utilisé")]]/td[2]')(self.doc)
loan.available_amount = CleanDecimal(Regexp(CleanText('//tr[td[contains(text(), "Montant Disponible") or contains(text(), "Montant disponible")]]/td[2]'), r'(.*) au'))(self.doc)
loan._has_cards = False
loan.type = Account.TYPE_REVOLVING_CREDIT
return loan
@method
class iter_revolving_loans(ListElement):
item_xpath = '//div[@class="bloc Tmargin"]//dl'
......
......@@ -333,7 +333,7 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
days = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
month = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
now = datetime.datetime.today()
d = '%s %s %s %s:%s:%s GMT 0100 (CET)' % (days[now.weekday()], month[now.month - 1], now.year, now.hour, format(now.minute, "02"), now.second)
d = '%s %s %s %s %s:%s:%s GMT+0100 (heure normale d’Europe centrale)' % (days[now.weekday()], now.day, month[now.month - 1], now.year, now.hour, format(now.minute, "02"), now.second)
if self.home.is_here():
msg = self.page.loan_unavailable_msg()
if msg:
......@@ -597,7 +597,7 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
@need_login
def get_investment(self, account):
self.deleteCTX()
if account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET, Account.TYPE_PEA) or 'measure_id' in account._info:
if account.type not in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION, Account.TYPE_MARKET, Account.TYPE_PEA) or 'measure_id' in account._info:
raise NotImplementedError()
if account.type == Account.TYPE_PEA and account.label == 'PEA NUMERAIRE':
......@@ -622,7 +622,7 @@ class CaisseEpargne(LoginBrowser, StatesMixin):
yield investment
return
elif account.type == Account.TYPE_LIFE_INSURANCE:
elif account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_CAPITALISATION):
if "MILLEVIE" in account.label:
self.page.go_life_insurance(account)
label = account.label.split()[-1]
......
This diff is collapsed.
......@@ -19,9 +19,9 @@
from __future__ import unicode_literals
from weboob.capabilities.bank import CapBankWealth, AccountNotFound
from weboob.capabilities.bank import CapBankTransfer, CapBankWealth, Account, AccountNotFound, RecipientNotFound
from weboob.capabilities.contact import CapContact
from weboob.capabilities.base import find_object
from weboob.capabilities.base import find_object, strict_find_object
from weboob.capabilities.profile import CapProfile
from weboob.tools.backend import Module, BackendConfig
from weboob.tools.value import Value, ValueBackendPassword
......@@ -33,7 +33,7 @@ from .pro.browser import CmsoProBrowser
__all__ = ['CmsoModule']
class CmsoModule(Module, CapBankWealth, CapContact, CapProfile):
class CmsoModule(Module, CapBankTransfer, CapBankWealth, CapContact, CapProfile):
NAME = 'cmso'
MAINTAINER = 'Romain Bignon'
EMAIL = 'romain@weboob.org'
......@@ -69,6 +69,39 @@ class CmsoModule(Module, CapBankWealth, CapContact, CapProfile):
def iter_investment(self, account):
return self.browser.iter_investment(account)
def iter_transfer_recipients(self, origin_account):
if self.config['website'].get() != "par":
raise NotImplementedError()
if not isinstance(origin_account, Account):
origin_account = self.get_account(origin_account)
return self.browser.iter_recipients(origin_account)
def init_transfer(self, transfer, **params):
if self.config['website'].get() != "par":
raise NotImplementedError()
self.logger.info('Going to do a new transfer')
account = strict_find_object(
self.iter_accounts(),
error=AccountNotFound,
iban=transfer.account_iban,
id=transfer.account_id
)
recipient = strict_find_object(
self.iter_transfer_recipients(account.id),
error=RecipientNotFound,
iban=transfer.recipient_iban,
id=transfer.recipient_id
)
return self.browser.init_transfer(account, recipient, transfer.amount, transfer.label, transfer.exec_date)
def execute_transfer(self, transfer, **params):
return self.browser.execute_transfer(transfer, **params)
def iter_contacts(self):
if self.config['website'].get() != "par":
raise NotImplementedError()
......
......@@ -22,6 +22,7 @@ from __future__ import unicode_literals
import re
import json
from datetime import date
from functools import wraps
from weboob.browser import LoginBrowser, URL, need_login, StatesMixin
......@@ -33,8 +34,9 @@ from weboob.tools.capabilities.bank.transactions import sorted_transactions
from .pages import (
LogoutPage, InfosPage, AccountsPage, HistoryPage, LifeinsurancePage, MarketPage,
AdvisorPage, LoginPage, RecipientsPage, ProfilePage,
AdvisorPage, LoginPage, ProfilePage,
)
from .transfer_pages import TransferInfoPage, RecipientsListPage, TransferPage
def retry(exc_check, tries=4):
......@@ -92,7 +94,11 @@ class CmsoParBrowser(LoginBrowser, StatesMixin):
'https://www.*/domiweb/prive/particulier', MarketPage)
advisor = URL('/edrapi/v(?P<version>\w+)/oauth/(?P<page>\w+)', AdvisorPage)
recipients = URL(r'/domiapi/oauth/json/transfer/transferinfos', RecipientsPage)
# transfer
transfer_info = URL(r'/domiapi/oauth/json/transfer/transferinfos', TransferInfoPage)
recipients_list = URL(r'/domiapi/oauth/json/transfer/beneficiariesListTransfer', RecipientsListPage)
init_transfer_page = URL(r'/domiapi/oauth/json/transfer/controlTransferOperation', TransferPage)
execute_transfer_page = URL(r'/domiapi/oauth/json/transfer/transferregister', TransferPage)
profile = URL(r'/domiapi/oauth/json/edr/infosPerson', ProfilePage)
......@@ -158,7 +164,7 @@ class CmsoParBrowser(LoginBrowser, StatesMixin):
seen = {}
self.recipients.go(data='{"beneficiaryType":"INTERNATIONAL"}', headers=self.json_headers)
self.transfer_info.go(json={"beneficiaryType":"INTERNATIONAL"})
numbers = self.page.get_numbers()
# First get all checking accounts...
......@@ -305,6 +311,74 @@ class CmsoParBrowser(LoginBrowser, StatesMixin):
return []
raise NotImplementedError()
@retry((ClientError, ServerError))
@need_login
def iter_recipients(self, account):
self.transfer_info.go(json={"beneficiaryType":"INTERNATIONAL"})
if account.type in (Account.TYPE_LOAN, ):
return
if not account._eligible_debit:
return
# internal recipient
for rcpt in self.page.iter_titu_accounts():
if rcpt.id != account.id:
yield rcpt
for rcpt in self.page.iter_manda_accounts():
if rcpt.id != account.id:
yield rcpt
for rcpt in self.page.iter_legal_rep_accounts():
if rcpt.id != account.id:
yield rcpt
# external recipient
for rcpt in self.page.iter_external_recipients():
yield rcpt
@need_login
def init_transfer(self, account, recipient, amount, reason, exec_date):
self.recipients_list.go(json={"beneficiaryType":"INTERNATIONAL"})
transfer_data = {
'beneficiaryIndex': self.page.get_rcpt_index(recipient),
'debitAccountIndex': account._index,
'devise': account.currency,
'deviseReglement': account.currency,
'montant': amount,
'nature': 'externesepa',
'transferToBeneficiary': True,
}
if exec_date and exec_date > date.today():
transfer_data['date'] = int(exec_date.strftime('%s')) * 1000
else:
transfer_data['immediate'] = True
# check if recipient is internal or external
if recipient.id != recipient.iban:
transfer_data['nature'] = 'interne'
transfer_data['transferToBeneficiary'] = False
self.init_transfer_page.go(json=transfer_data)
transfer = self.page.handle_transfer(account, recipient, amount, reason, exec_date)
# transfer_data is used in execute_transfer
transfer._transfer_data = transfer_data
return transfer
@need_login
def execute_transfer(self, transfer, **params):
assert transfer._transfer_data
transfer._transfer_data.update({
'enregistrerNouveauBeneficiaire': False,
'creditLabel': 'de %s' % transfer.account_label if not transfer.label else transfer.label,
'debitLabel': 'vers %s' % transfer.recipient_label,
'typeFrais': 'SHA'
})
self.execute_transfer_page.go(json=transfer._transfer_data)
transfer.id = self.page.get_transfer_confirm_id()
return transfer
@retry((ClientError, ServerError))
@need_login
def get_advisor(self):
......
......@@ -135,6 +135,8 @@ class AccountsPage(LoggedPage, JsonPage):
# Iban is available without last 5 numbers, or by sms
obj_iban = NotAvailable
obj__index = Dict('index')
# to know if we can do transfer on account
obj__eligible_debit = Dict('eligibiliteDebit', default=False)
def obj_balance(self):
balance = CleanDecimal(Dict('soldeEuro', default="0"))(self)
......@@ -194,6 +196,8 @@ class AccountsPage(LoggedPage, JsonPage):
obj_coming = CleanDecimal(Dict('AVenir', default=None), default=NotAvailable)
obj__index = Dict('index')
obj__owner = Dict('nomTitulaire')
# to know if we can do transfer on account
obj__eligible_debit = Dict('eligibiliteDebit', default=False)
def obj_id(self):
type = Field('type')(self)
......@@ -549,35 +553,6 @@ class AdvisorPage(LoggedPage, JsonPage):
obj_address = Format('%s %s', Dict('adresse1'), Dict('adresse3'))
class RecipientsPage(LoggedPage, JsonPage):
def get_numbers(self):
# If account information is not available when asking for the
# recipients (server error for ex.), return an empty dictionary
# that will be filled later after being returned the json of the
# account page (containing the accounts IDs too).
if 'listCompteTitulaireCotitulaire' not in self.doc and 'exception' in self.doc:
return {}
ret = {}
ret.update({
d['index']: d['numeroContratSouscrit']
for d in self.doc['listCompteTitulaireCotitulaire']
})
ret.update({
d['index']: d['numeroContratSouscrit']
for p in self.doc['listCompteMandataire'].values()
for d in p
})
ret.update({
d['index']: d['numeroContratSouscrit']
for p in self.doc['listCompteLegalRep'].values()
for d in p
})
return ret
class ProfilePage(LoggedPage, JsonPage):
# be careful, this page is used in CmsoProBrowser too!
......
# -*- coding: utf-8 -*-
# Copyright(C) 2019 Sylvie Ye
#
# 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 __future__ import unicode_literals
import datetime as dt
from weboob.browser.pages import JsonPage, LoggedPage
from weboob.browser.elements import DictElement, ItemElement, method
from weboob.browser.filters.standard import CleanText, CleanDecimal, Currency
from weboob.browser.filters.json import Dict
from weboob.capabilities.bank import Recipient, Transfer, TransferBankError
from weboob.capabilities.base import NotAvailable
class MyRecipientItemElement(ItemElement):
def condition(self):
return Dict('eligibiliteCredit', default=False)
klass = Recipient
obj_id = Dict('numeroContratSouscrit')
obj_label = Dict('lib')
obj_iban = NotAvailable
obj_enabled_at = dt.date.today()
obj_category = 'Interne'
obj__index = Dict('index')
class RecipientsListPage(LoggedPage, JsonPage):
def get_rcpt_index(self, recipient):
if recipient.category == 'Externe':
for el in self.doc['listBeneficiaries']:
for rcpt in el:
# in this list, recipient iban is like FR111111111111111XXXXX
if rcpt['iban'][:-5] == recipient.iban[:-5] and rcpt['nom'] == recipient.label:
return rcpt['index']
return recipient._index
class TransferInfoPage(LoggedPage, JsonPage):
def get_numbers(self):
# If account information is not available when asking for the
# recipients (server error for ex.), return an empty dictionary
# that will be filled later after being returned the json of the
# account page (containing the accounts IDs too).
if 'listCompteTitulaireCotitulaire' not in self.doc and 'exception' in self.doc:
return {}
ret = {}
ret.update({
d['index']: d['numeroContratSouscrit']
for d in self.doc['listCompteTitulaireCotitulaire']
})
ret.update({
d['index']: d['numeroContratSouscrit']
for p in self.doc['listCompteMandataire'].values()
for d in p
})
ret.update({
d['index']: d['numeroContratSouscrit']
for p in self.doc['listCompteLegalRep'].values()
for d in p
})
return ret
@method
class iter_titu_accounts(DictElement):
item_xpath = 'listCompteTitulaireCotitulaire'
class item(MyRecipientItemElement):
pass
@method
class iter_manda_accounts(DictElement):
item_xpath = 'listCompteMandataire/*'
class item(MyRecipientItemElement):
pass
@method
class iter_legal_rep_accounts(DictElement):
item_xpath = 'listCompteLegalRep/*'
class item(MyRecipientItemElement):
pass
@method
class iter_external_recipients(DictElement):
item_xpath = 'listBeneficiaries'
class item(ItemElement):
klass = Recipient
obj_id = obj_iban = Dict('iban')
obj_label = Dict('nom')
obj_category = 'Externe'
obj_enabled_at = dt.date.today()
obj__index = Dict('index')
def condition(self):
return Dict('actif', default=False)(self)
class TransferPage(LoggedPage, JsonPage):
def on_load(self):
if self.doc.get('exception') and not self.doc.get('debitAccountOwner'):
if Dict('exception/type')(self.doc) == 1:
# technical error
assert False, 'Error with code %s occured during init_transfer: %s' % \
(Dict('exception/code')(self.doc), Dict('exception/message')(self.doc))
elif Dict('exception/type')(self.doc) == 2:
# user error
TransferBankError(message=Dict('exception/message')(self.doc))
def handle_transfer(self, account, recipient, amount, reason, exec_date):
transfer = Transfer()
transfer.amount = CleanDecimal(Dict('amount'))(self.doc)
transfer.currency = Currency(Dict('codeDevise'))(self.doc)
transfer.label = reason
if exec_date:
transfer.exec_date = dt.date.fromtimestamp(int(Dict('date')(self.doc))//1000)
transfer.account_id = account.id
transfer.account_label = CleanText(Dict('debitAccountLabel'))(self.doc)
transfer.account_balance = CleanDecimal(Dict('debitAccountBalance'))(self.doc)
transfer.recipient_id = recipient.id
transfer.recipient_iban = recipient.iban
transfer.recipient_label = CleanText(Dict('creditAccountOwner'))(self.doc)
return transfer
def get_transfer_confirm_id(self):
return self.doc.get('numeroOperation')
This diff is collapsed.
# -*- coding: utf-8 -*-
# Copyright(C) 2012-2019 Budget Insight
from weboob.browser import AbstractBrowser