Commit 04970b09 authored by Célande Adrien's avatar Célande Adrien Committed by Vincent A

[bp] repaired transfer

The website layout changed.
The errors are handled by the new and the old code because the layout is
When the execution date corresponds to the week end, the website changes
itself the date.

For now, it does not handle OTP or decoupled.
parent 8b3c198d
......@@ -129,6 +129,11 @@ class BPBrowser(LoginBrowser, StatesMixin):
transfer_choose = URL(r'/voscomptes/canalXHTML/virement/mpiaiguillage/init-saisieComptes.ea', TransferChooseAccounts)
transfer_complete = URL(r'/voscomptes/canalXHTML/virement/mpiaiguillage/soumissionChoixComptes-saisieComptes.ea',
# The two following urls are obtained after a redirection made after a form
# No parameters or data seem to change that the website go back to the evious folder, using ".."
# We can't do much since it is finaly handled by the module requests
transfer_confirm = URL(r'/voscomptes/canalXHTML/virement/virementSafran_pea/validerVirementPea-virementPea.ea',
......@@ -464,9 +469,11 @@ class BPBrowser(LoginBrowser, StatesMixin):
def init_transfer(self, account, recipient, amount, transfer):
self.transfer_choose.stay_or_go(), recipient._value)
assert self.transfer_complete.is_here(), transfer), recipient._value, amount)
assert self.transfer_complete.is_here(), 'An error occured while validating the first part of the transfer.'
return, recipient, amount, transfer.label)
......@@ -19,6 +19,7 @@
from decimal import Decimal
from datetime import timedelta
from import CapBankWealth, CapBankTransferAddRecipient, Account, AccountNotFound, RecipientNotFound
from import CapContact
from weboob.capabilities.base import find_object, strict_find_object, NotAvailable
......@@ -105,6 +106,9 @@ class BPModule(
old = old.encode('latin-1', errors="xmlcharrefreplace").decode('latin-1')
return super(BPModule, self).transfer_check_label(old, new)
def transfer_check_date(self, old_exec_date, new_exec_date):
return old_exec_date <= new_exec_date <= old_exec_date + timedelta(days=2)
def execute_transfer(self, transfer, **params):
return self.browser.execute_transfer(transfer)
......@@ -25,7 +25,7 @@ from import (
TransferBankError, Transfer, TransferStep, NotAvailable, Recipient,
AccountNotFound, AddRecipientBankError
from weboob.capabilities.base import find_object
from weboob.capabilities.base import find_object, empty
from weboob.browser.pages import LoggedPage
from weboob.browser.filters.standard import CleanText, Env, Regexp, Date, CleanDecimal
from weboob.browser.filters.html import Attr, Link
......@@ -114,21 +114,23 @@ class TransferChooseAccounts(LoggedPage, MyHTMLPage):
if self.env['id'] in self.parent.objects: # user add two recipients with same iban...
raise SkipItem()
def init_transfer(self, account_id, recipient_value):
def init_transfer(self, account_id, recipient_value, amount):
matched_values = [Attr('.', 'value')(option) for option in self.doc.xpath('//select[@id="donneesSaisie.idxCompteEmetteur"]/option') \
if account_id in CleanText('.')(option)]
assert len(matched_values) == 1
form = self.get_form(xpath='//form[@class="formvirement"]')
form = self.get_form(xpath='//form[@class="choix-compte"]')
form['donneesSaisie.idxCompteReceveur'] = recipient_value
form['donneesSaisie.idxCompteEmetteur'] = matched_values[0]
form['donneesSaisie.montant'] = amount
class CompleteTransfer(LoggedPage, CheckTransferError):
def complete_transfer(self, amount, transfer):
def complete_transfer(self, transfer):
form = self.get_form(xpath='//form[@method]')
form['montant'] = amount
if 'commentaire' in form and transfer.label:
# for this bank the 'commentaire' is not a real label
# but a reason of transfer
form['commentaire'] = transfer.label
form['dateVirement'] = transfer.exec_date.strftime('%d/%m/%Y')
......@@ -136,7 +138,10 @@ class CompleteTransfer(LoggedPage, CheckTransferError):
class TransferConfirm(LoggedPage, CheckTransferError):
def is_here(self):
return not CleanText('//p[contains(text(), "Vous pouvez le consulter dans le menu")]')(self.doc)
return (
not CleanText('//p[contains(text(), "Vous pouvez le consulter dans le menu")]')(self.doc)
or self.doc.xpath('//input[@title="Confirmer la demande de virement"]')
def double_auth(self, transfer):
code_needed = CleanText('//label[@for="code_securite"]')(self.doc)
......@@ -148,15 +153,21 @@ class TransferConfirm(LoggedPage, CheckTransferError):
def handle_response(self, account, recipient, amount, reason):
account_txt = CleanText('//form//dl/dt[span[contains(text(), "biter")]]/following::dd[1]', replace=[(' ', '')])(self.doc)
recipient_txt = CleanText('//form//dl/dt[span[contains(text(), "diter")]]/following::dd[1]', replace=[(' ', '')])(self.doc)
# handle error
error_msg = CleanText('//div[@id="blocErreur"]')(self.doc)
if error_msg:
raise TransferBankError(message=error_msg)
account_txt = CleanText('//form//h3[contains(text(), "débiter")]//following::span[1]', replace=[(' ', '')])(self.doc)
recipient_txt = CleanText('//form//h3[contains(text(), "créditer")]//following::span[1]', replace=[(' ', '')])(self.doc)
assert in account_txt or ''.join(account.label.split()) == account_txt, 'Something went wrong'
assert in recipient_txt or ''.join(recipient.label.split()) == recipient_txt, 'Something went wrong'
r_amount = CleanDecimal('//form//dl/dt[span[contains(text(), "Montant")]]/following::dd[1]', replace_dots=True)(self.doc)
exec_date = Date(CleanText('//form//dl/dt[span[contains(text(), "Date")]]/following::dd[1]'), dayfirst=True)(self.doc)
currency = FrenchTransaction.Currency('//form//dl/dt[span[contains(text(), "Montant")]]/following::dd[1]')(self.doc)
amount_element = self.doc.xpath('//h3[contains(text(), "Montant du virement")]//following::span[@class="price"]')[0]
r_amount = CleanDecimal.French('.')(amount_element)
exec_date = Date(CleanText('//h3[contains(text(), "virement")]//following::span[@class="date"]'), dayfirst=True)(self.doc)
currency = FrenchTransaction.Currency('.')(amount_element)
transfer = Transfer()
transfer.currency = currency
......@@ -175,20 +186,53 @@ class TransferConfirm(LoggedPage, CheckTransferError):
class TransferSummary(LoggedPage, CheckTransferError):
def handle_response(self, transfer):
# NotAvailable in case of future exec_date not on a working day. = Regexp(CleanText('//div[@class="bloc Tmargin"]'), 'virement N.+ (\d+) ', default=NotAvailable)(self.doc)
if not
summary_filter = CleanText(
'//div[contains(@class, "bloc-recapitulatif")]//p'
# handle error
if "Votre virement n'a pas pu" in summary_filter(self.doc):
raise TransferBankError(message=summary_filter(self.doc))
transfer_id = Regexp(summary_filter, r'référence n° (\d+)', default=None)(self.doc)
# not always available
if transfer_id and not = transfer_id
# TODO handle transfer with sms code.
if 'veuillez saisir votre code de validation' in CleanText('//div[@class="bloc Tmargin"]')(self.doc):
raise NotImplementedError()
# WARNING: At this point, the transfer was made.
# The following code is made to retrieve the transfer execution date,
# so there is no falsy data.
# But the bp website is unstable with changing layout and messages.
# One of the goals here is for the code not to crash to avoid the user thinking
# that the transfer was not made while it was.
old_date = transfer.exec_date
# the date was modified because on a weekend
if 'date correspondant à un week-end' in summary_filter(self.doc):
transfer.exec_date = Date(Regexp(
r'jour ouvré suivant \((\d{2}/\d{2}/\d{4})\)',
), dayfirst=True, default=NotAvailable)(self.doc)
self.logger.warning('The transfer execution date changed from %s to %s' % (old_date.strftime('%Y-%m-%d'), transfer.exec_date.strftime('%Y-%m-%d')))
# made today
elif 'date du jour de ce virement' in summary_filter(self.doc):
# there are several regexp for transfer date:
# Date ([\d\/]+)|le ([\d\/]+)|suivant \(([\d\/]+)\)
# be more passive to avoid impulsive reaction from user
transfer.exec_date = Date(Regexp(
CleanText('//div[@class="bloc Tmargin"]'),
r' (\d{2}/\d{2}/\d{4})'
), dayfirst=True)(self.doc)
r' (\d{2}/\d{2}/\d{4})',
), dayfirst=True, default=NotAvailable)(self.doc)
# else: using the same date because the website does not give one
if empty(transfer.exec_date):
transfer.exec_date = old_date
return transfer
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