Commit 9e131dad authored by Benjamin Tampigny's avatar Benjamin Tampigny Committed by Vincent A

[axabanque] implement iter_transfers

parent 961852c7
......@@ -50,7 +50,8 @@ from .pages.wealth import (
)
from .pages.transfer import (
RecipientsPage, AddRecipientPage, ValidateTransferPage, RegisterTransferPage,
ConfirmTransferPage, RecipientConfirmationPage,
ConfirmTransferPage, RecipientConfirmationPage, ScheduledTransfersPage,
ScheduledTransferDetailsPage,
)
from .pages.document import DocumentsPage, DownloadPage
......@@ -176,6 +177,7 @@ class AXABanque(AXABrowser, StatesMixin):
add_recipient = URL(r'/webapp/axabanque/jsp/beneficiaireSepa/saisieBeneficiaireSepaOTP.faces', AddRecipientPage)
recipient_confirmation_page = URL(r'/webapp/axabanque/jsp/beneficiaireSepa/saisieBeneficiaireSepaOTP.faces', RecipientConfirmationPage)
validate_transfer = URL(r'/webapp/axabanque/jsp/virementSepa/saisieVirementSepa.faces', ValidateTransferPage)
# also displays recent single transfers
register_transfer = URL(
r'/transactionnel/client/virement.html',
r'/webapp/axabanque/jsp/virementSepa/saisieVirementSepa.faces',
......@@ -183,6 +185,8 @@ class AXABanque(AXABrowser, StatesMixin):
)
confirm_transfer = URL('/webapp/axabanque/jsp/virementSepa/confirmationVirementSepa.faces', ConfirmTransferPage)
profile_page = URL('/transactionnel/client/coordonnees.html', BankProfilePage)
scheduled_transfers = URL(r'/transactionnel/client/virements-en-cours.html', ScheduledTransfersPage)
scheduled_transfer_details = URL(r'/webapp/axabanque/jsp/virementSepa/virementEnCoursSepa.faces', ScheduledTransferDetailsPage)
reload_state = None
......@@ -618,6 +622,65 @@ class AXABanque(AXABrowser, StatesMixin):
self.register_transfer.go()
return self.page.iter_emitters()
def _get_recipients_by_emitter(self):
recipients_by_emitter = []
for emitter in self.iter_emitters():
self.page.set_account(emitter.id)
recipients = []
for recipient in self.page.get_recipients():
recipients.append(recipient)
recipients_by_emitter.append((emitter, recipients,))
return recipients_by_emitter
@staticmethod
def _find_emitter_and_recipient_from_recipient_name(recipients_by_emitters, recipient_name):
# find more information on non deferred single transfers
# in this case, the only information we have about recipient is a part of its label
matching_recipients = []
for emitter, recipients in recipients_by_emitters:
for recipient in recipients:
# a recipient label is constructed using the following syntax:
# <recipient account name> - <recipient name>
# the transfer list only provides the recipient name
# we will base our matching on it
if recipient.label.endswith(' - {}'.format(recipient_name)):
matching_recipients.append((emitter, recipient,))
# information is valuable only if one recipient from one account matched the recipient name
# otherwise, we won't know which emitter and recipient to use
if len(matching_recipients) == 1:
return matching_recipients[0]
@need_login
def iter_transfers(self, account):
self.scheduled_transfers.go()
for transfer in self.page.iter_transfers():
if not account or account.iban == transfer.account_iban:
transfer_page = self.page.open_transfer_page(transfer._unparsed_js_args)
# will get additional information on the transfer's detailed page
transfer_page.fill_scheduled_transfer(obj=transfer)
yield transfer
# now the awful part, single non deferred transfers displayed with very few information
self.register_transfer.go()
recipients_by_emitter = self._get_recipients_by_emitter()
# Very few information on immediate transfers. In order to retrieve something, a matching is done
# between recipient name given with transfer and the recipient, and since the recipient is linked to an
# emitter account, information about the emitter are found.
for transfer in self.page.iter_transfers():
matched_information = self._find_emitter_and_recipient_from_recipient_name(recipients_by_emitter, transfer._recipient_name)
# keep in mind that if we were not able to find an emitter,
# there is no way to filter with the account parameter
if matched_information:
emitter, recipient = matched_information
# not 100% efficient, because children accounts can have the same beginning of id.
# but we don't have to use the heavy iter_accounts
if account and not account.id.startswith(emitter.id):
continue
transfer.recipient_iban = recipient.iban
transfer.recipient_label = recipient.label
transfer.account_label = emitter.label
yield transfer
class AXAAssurance(AXABrowser):
BASEURL = 'https://espaceclient.axa.fr'
......
......@@ -30,6 +30,7 @@ from weboob.capabilities.wealth import CapBankWealth
from weboob.capabilities.profile import CapProfile
from weboob.capabilities.bill import CapDocument, Subscription, Document, DocumentNotFound, SubscriptionNotFound
from weboob.tools.backend import Module, BackendConfig
from weboob.tools.capabilities.bank.bank_transfer import sorted_transfers
from weboob.tools.value import ValueBackendPassword
from .browser import AXABanque, AXAAssurance
......@@ -173,3 +174,6 @@ class AXABanqueModule(Module, CapBankWealth, CapBankTransferAddRecipient, CapDoc
if self.BROWSER != AXABanque:
raise NotImplementedError()
return self.browser.iter_emitters()
def iter_transfers(self, account=None):
return sorted_transfers(self.browser.iter_transfers(account))
......@@ -22,17 +22,19 @@ from __future__ import unicode_literals
import re
import string
from io import BytesIO
from itertools import chain
from PIL import Image, ImageFilter
from datetime import date
from weboob.browser.pages import HTMLPage, LoggedPage
from weboob.browser.elements import method, TableElement, ItemElement, ListElement
from weboob.browser.filters.html import TableCell
from weboob.browser.filters.html import TableCell, Attr
from weboob.browser.filters.standard import (
CleanText, Date, Regexp, CleanDecimal, Currency, Format, Field,
CleanText, Date, Regexp, CleanDecimal, Currency, Format, Field, Map,
)
from weboob.capabilities.bank import (
Recipient, Transfer, TransferBankError, AddRecipientBankError, RecipientNotFound, Emitter,
Recipient, TransferBankError, AddRecipientBankError, RecipientNotFound, Emitter,
Transfer, TransferDateType, TransferFrequency,
)
from weboob.tools.captcha.virtkeyboard import SimpleVirtualKeyboard
from weboob.capabilities.base import find_object, NotAvailable
......@@ -336,6 +338,35 @@ class RegisterTransferPage(LoggedPage, HTMLPage):
label = raw_label.split('-')
return '%s - %s' % (label[0].strip(), label[2].strip())
@method
class iter_transfers(TableElement):
head_xpath = '//table[@id="idFormSaisieVirement:table-vrtDerniers"]//thead//th'
item_xpath = '//table[@id="idFormSaisieVirement:table-vrtDerniers"]//tbody//tr'
col_amount = 'Montant'
col_exec_date = "Date d'effet"
col__rcpt_name = 'Bénéficiaire'
col__created_date = 'Date de saisie'
class item(ItemElement):
klass = Transfer
def condition(self):
# website don't let you plan deferred for the same day
# so if "date de saisie" is "date d'effet", it is an immediate transfer
# deferred transfers will be handle on another page with more information
return (
Date(CleanText(TableCell('exec_date')), dayfirst=True)(self)
== Date(CleanText(TableCell('_created_date')), dayfirst=True)(self)
)
obj_currency = 'EUR'
obj_exec_date = Date(CleanText(TableCell('exec_date')), dayfirst=True)
obj_amount = CleanDecimal.US(TableCell('amount'))
# will be used after to try to get some information on the recipient and emitter account
obj__recipient_name = CleanText(TableCell('_rcpt_name'))
obj_date_type = TransferDateType.FIRST_OPEN_DAY
class ValidateTransferPage(LoggedPage, HTMLPage):
is_here = '//p[contains(text(), "votre code confidentiel")]'
......@@ -413,3 +444,94 @@ class ConfirmTransferPage(LoggedPage, HTMLPage):
confirm_transfer_xpath = '//h2[contains(text(), "Virement enregistr")]'
assert self.doc.xpath(confirm_transfer_xpath)
class BaseScheduledTransferElement(ItemElement):
klass = Transfer
obj_recipient_label = CleanText('./td[@class="destinataire"]')
obj_amount = CleanDecimal.US('./td[@class="montant"]')
obj_account_id = CleanText('./td[@class="numCompteEmetteur"]')
obj_exec_date = Date(CleanText('./td[@class="dateEffet"]'), dayfirst=True)
obj_label = CleanText('./preceding-sibling::tr[2]//p/text()')
obj_id = Regexp(Attr('./following-sibling::tr[1]//a[1]', 'onclick'), r"'virNumOperation','(\d+)'")
obj_currency = Currency(Regexp(CleanText('./preceding-sibling::tr[1]//td[contains(@class, "montant")]'), r'Montant \((.+)\)'))
obj__unparsed_js_args = Attr('./following-sibling::tr[1]//a[1]', 'onclick')
class ScheduledTransfersPage(LoggedPage, HTMLPage):
def js2args(self, s):
args = {}
for sub in re.findall("\['([^']+)','([^']+)'\]", s):
args[sub[0]] = sub[1]
sub = re.search('oamSubmitForm.+?,\'([^:]+).([^\']+)', s)
args['%s:_idcl' % sub.group(1)] = "%s:%s" % (sub.group(1), sub.group(2))
args['%s_SUBMIT' % sub.group(1)] = 1
args['_form_name'] = sub.group(1)
return args
def iter_transfers(self):
return chain(self.iter_periodic_transfers(), self.iter_deferred_transfers())
@method
class iter_periodic_transfers(ListElement):
item_xpath = '//table[@id="table-virements-en-cours"]//tr[@class="ligne1"]'
class item(BaseScheduledTransferElement):
obj_date_type = TransferDateType.PERIODIC
@method
class iter_deferred_transfers(ListElement):
item_xpath = '//table[@id="table-virements-en-attente"]//tr[@class="ligne1"]'
class item(BaseScheduledTransferElement):
obj_date_type = TransferDateType.DEFERRED
def open_transfer_page(self, unparsed_js_args):
js_args = self.js2args(unparsed_js_args)
form = self.get_form(name=js_args['_form_name'])
form.update(js_args)
# instead of using Form.submit, we just use the form's request to avoid the useless browser.location
# otherwise, after each transfer filling we would have to locate back to the transfers list page
return self.browser.open(form.request).page
class ScheduledTransferDetailsPage(HTMLPage, LoggedPage):
def fill_scheduled_transfer(self, obj):
self._fill_common_scheduled(obj=obj)
if obj.date_type == TransferDateType.PERIODIC:
self._fill_periodic(obj=obj)
# no details specific to deferred transfers
@method
class _fill_common_scheduled(ItemElement):
obj_account_label = CleanText('//table[@id="tableVirt1"]//td[@class="intituleCompte"]')
obj__rcpt_name = CleanText(
'//table[@id="tableVirt2"]//td[text()="Nom du bénéficiaire :"]/following-sibling::td'
)
obj__acc_name = CleanText(
'//table[@id="tableVirt2"]//td[text()="Nom du compte :"]/following-sibling::td'
)
# constructed as in iter_recipients
obj_recipient_label = Format('%s - %s', Field('_acc_name'), Field('_rcpt_name'))
obj_recipient_iban = CleanText('//table[@id="tableVirt2"]//td[text()="IBAN :"]/following-sibling::td')
@method
class _fill_periodic(ItemElement):
FREQ_LABELS = {
'Mensuelle': TransferFrequency.MONTHLY,
'Trimestrielle': TransferFrequency.QUARTERLY,
'Semestrielle': TransferFrequency.BIANNUAL,
'Annuelle': TransferFrequency.YEARLY,
}
obj_last_due_date = Date(CleanText('//table[@id="tableVirt3"]//td[@class="fin"]'), dayfirst=True)
obj_frequency = Map(
CleanText('//table[@id="tableVirt3"]//td[@class="libPeriodicite"]'),
FREQ_LABELS,
TransferFrequency.UNKNOWN
)
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