Newer
Older
# 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 datetime import datetime
from weboob.capabilities.bank import (
TransferError, TransferBankError, Transfer, TransferStep, NotAvailable, Recipient, AccountNotFound, AddRecipientError
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
from weboob.browser.elements import ListElement, ItemElement, method, SkipItem
from weboob.tools.capabilities.bank.transactions import FrenchTransaction
from weboob.tools.capabilities.bank.iban import is_iban_valid
from weboob.tools.value import Value
class CheckTransferError(MyHTMLPage):
def on_load(self):
MyHTMLPage.on_load(self)
error = CleanText(u'//span[@class="app_erreur"] | //p[@class="warning"] | //p[contains(text(), "Votre virement n\'a pas pu être enregistré")]')(self.doc)
if error and not u'Votre demande de virement a été enregistrée le' in error:
raise TransferBankError(message=error)
class TransferChooseAccounts(LoggedPage, MyHTMLPage):
def is_inner(self, text):
for option in self.doc.xpath('//select[@id="donneesSaisie.idxCompteEmetteur"]/option'):
if text == CleanText('.')(option):
return True
return False
@method
class iter_recipients(ListElement):
def condition(self):
return any(self.env['account_id'] in CleanText('.')(option) for option in self.page.doc.xpath('//select[@id="donneesSaisie.idxCompteEmetteur"]/option'))
# You're not dreaming, this is real life
item_xpath = '//select[@id="caca"]/option'
class Item(ItemElement):
klass = Recipient
def condition(self):
return self.el.attrib['value'] != '-1'
def validate(self, obj):
# Some international external recipients show those infos:
# INT - CA - 0815304220511006 - CEGEP CANADA
# Skipping those for the moment.
return not obj.iban or is_iban_valid(obj.iban)
obj_category = Env('category')
obj_label = Env('label')
obj_id = Env('id')
obj_currency = u'EUR'
obj_iban = Env('iban')
obj_bank_name = Env('bank_name')
obj__value = Attr('.', 'value')
def obj_enabled_at(self):
return datetime.now().replace(microsecond=0)
def parse(self, el):
if any(s in CleanText('.')(el) for s in ['Avoir disponible', 'Solde']) or self.page.is_inner(CleanText('.')(el)):
self.env['category'] = u'Interne'
else:
self.env['category'] = u'Externe'
if self.env['category'] == u'Interne':
_id = Regexp(CleanText('.'), '- (.*?) -')(el)
if _id == self.env['account_id']:
raise SkipItem()
account = find_object(self.page.browser.get_accounts_list(), id=_id, error=AccountNotFound)
self.env['id'] = _id
self.env['label'] = account.label
self.env['iban'] = account.iban
except AccountNotFound:
self.env['id'] = Regexp(CleanText('.'), '- (.*?) -')(el).replace(' ', '')
self.env['iban'] = NotAvailable
label = CleanText('.')(el).split('-')
holder = label[-1] if not any(string in label[-1] for string in ['Avoir disponible', 'Solde']) else label[-2]
self.env['label'] = '%s %s' % (label[0].strip(), holder.strip())
self.env['bank_name'] = u'La Banque Postale'
else:
self.env['id'] = self.env['iban'] = Regexp(CleanText('.'), '- (.*?) -')(el).replace(' ', '')
self.env['label'] = Regexp(CleanText('.'), '- (.*?) - (.*)', template='\\2')(el).strip()
first_part = CleanText('.')(el).split('-')[0].strip()
self.env['bank_name'] = u'La Banque Postale' if first_part in ['CCP', 'PEL'] else NotAvailable
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):
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['donneesSaisie.idxCompteReceveur'] = recipient_value
form['donneesSaisie.idxCompteEmetteur'] = matched_values[0]
form.submit()
class CompleteTransfer(LoggedPage, CheckTransferError):
def complete_transfer(self, amount, transfer):
form = self.get_form(xpath='//form[@method]')
form['montant'] = amount
if 'commentaire' in form and transfer.label:
form['commentaire'] = transfer.label
form['dateVirement'] = transfer.exec_date.strftime('%d/%m/%Y')
form.submit()
class TransferConfirm(LoggedPage, CheckTransferError):
def is_here(self):
return not CleanText('//p[contains(text(), "Vous pouvez le consulter dans le menu")]')(self.doc)
def double_auth(self, transfer):
code_needed = CleanText('//label[@for="code_securite"]')(self.doc)
if code_needed:
raise TransferStep(transfer, Value('code', label= code_needed))
form = self.get_form(id='formID')
form.submit()
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)
assert account.id in account_txt or ''.join(account.label.split()) == account_txt
assert recipient.id in recipient_txt or ''.join(recipient.label.split()) == recipient_txt
raise TransferError('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)
transfer = Transfer()
transfer.currency = currency
transfer.amount = r_amount
transfer.account_iban = account.iban
transfer.recipient_iban = recipient.iban
transfer.account_id = account.id
transfer.recipient_id = recipient.id
transfer.exec_date = exec_date
transfer.label = reason
transfer.account_label = account.label
transfer.recipient_label = recipient.label
transfer.account_balance = account.balance
return transfer
class TransferSummary(LoggedPage, CheckTransferError):
def handle_response(self, transfer):
# NotAvailable in case of future exec_date not on a working day.
transfer.id = Regexp(CleanText('//div[@class="bloc Tmargin"]'), 'virement N.+ (\d+) ', default=NotAvailable)(self.doc)
if not transfer.id:
# TODO handle transfer with sms code.
if u'veuillez saisir votre code de validation' in CleanText('//div[@class="bloc Tmargin"]')(self.doc):
raise NotImplementedError()
transfer_date = Date(Regexp(
CleanText('//div[@class="bloc Tmargin"]'),
r'suivant \(([\d\/]+)\)',
default=NotAvailable
), dayfirst=True, default=NotAvailable)(self.doc)
if transfer_date:
transfer.exec_date = transfer_date
else:
transfer.exec_date = Date(Regexp(
CleanText('//div[@class="bloc Tmargin"]'),
r'Date ([\d\/]+)'
), dayfirst=True)(self.doc)
class CreateRecipient(LoggedPage, MyHTMLPage):
def choose_country(self, recipient, is_bp_account):
# if this is present, we can't add recipient currently
Sylvie Ye
committed
more_security_needed = self.doc.xpath(u'//iframe[@title="Gestion de compte par Internet"]')
if more_security_needed:
raise AddRecipientError(message=u"Pour activer le service Certicode, nous vous invitons à vous rapprocher de votre Conseiller en Bureau de Poste.")
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
form = self.get_form(name='SaisiePaysBeneficiaireVirement')
form['compteLBP'] = str(is_bp_account).lower()
form['beneficiaireBean.paysDestination'] = recipient.iban[:2]
form.submit()
class ValidateCountry(LoggedPage, MyHTMLPage):
def populate(self, recipient):
form = self.get_form(name='CaracteristiquesBeneficiaireVirement')
form['beneficiaireBean.nom'] = recipient.label
form['beneficiaireBean.ibans[1].valeur'] = recipient.iban[2:4]
form['beneficiaireBean.ibans[2].valeur'] = recipient.iban[4:8]
form['beneficiaireBean.ibans[3].valeur'] = recipient.iban[8:12]
form['beneficiaireBean.ibans[4].valeur'] = recipient.iban[12:16]
form['beneficiaireBean.ibans[5].valeur'] = recipient.iban[16:20]
form['beneficiaireBean.ibans[6].valeur'] = recipient.iban[20:24]
form['beneficiaireBean.ibans[7].valeur'] = recipient.iban[24:]
form['beneficiaireBean.intituleCompte'] = recipient.label
form.submit()
class ValidateRecipient(LoggedPage, MyHTMLPage):
def is_bp_account(self):
msg = CleanText('//span[has-class("app_erreur")]')(self.doc)
return msg == u'Le n° de compte que vous avez saisi appartient à La Banque Postale, veuillez vérifier votre saisie.'
def get_confirm_link(self):
return Link('//a[@title="confirmer la creation"]')(self.doc)
class ConfirmPage(LoggedPage, MyHTMLPage):
Sylvie Ye
committed
def on_load(self):
error_msg = CleanText('//h2[contains(text(), "Compte rendu")]/following-sibling::p')(self.doc)
if error_msg:
raise AddRecipientError(message=error_msg)
def set_browser_form(self):
form = self.get_form(name='SaisieOTP')
self.browser.recipient_form = dict((k, v) for k, v in form.items() if v)
self.browser.recipient_form['url'] = form.url
class RcptSummary(LoggedPage, MyHTMLPage):
pass