From 902670d28b66e89a85c9484fb4c2ba2348cc9238 Mon Sep 17 00:00:00 2001 From: Sylvie Ye Date: Tue, 15 Jan 2019 18:42:32 +0100 Subject: [PATCH] [cmb] add new feature: transfer --- modules/cmso/module.py | 29 +++++++++++++-- modules/cmso/par/browser.py | 57 ++++++++++++++++++++++++++---- modules/cmso/par/transfer_pages.py | 48 ++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 9 deletions(-) diff --git a/modules/cmso/module.py b/modules/cmso/module.py index 7a0b899018..dcfe94cfa1 100644 --- a/modules/cmso/module.py +++ b/modules/cmso/module.py @@ -19,9 +19,9 @@ from __future__ import unicode_literals -from weboob.capabilities.bank import CapBankTransfer, CapBankWealth, Account, 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 @@ -77,6 +77,31 @@ def iter_transfer_recipients(self, origin_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() diff --git a/modules/cmso/par/browser.py b/modules/cmso/par/browser.py index afaead6f8e..7b6a921bf0 100644 --- a/modules/cmso/par/browser.py +++ b/modules/cmso/par/browser.py @@ -22,6 +22,7 @@ import re import json +from datetime import date from functools import wraps from weboob.browser import LoginBrowser, URL, need_login, StatesMixin @@ -35,7 +36,7 @@ LogoutPage, InfosPage, AccountsPage, HistoryPage, LifeinsurancePage, MarketPage, AdvisorPage, LoginPage, ProfilePage, ) -from .transfer_pages import TransferInfoPage +from .transfer_pages import TransferInfoPage, RecipientsListPage, TransferPage def retry(exc_check, tries=4): @@ -95,6 +96,9 @@ class CmsoParBrowser(LoginBrowser, StatesMixin): # 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) @@ -160,7 +164,7 @@ def iter_accounts(self): seen = {} - self.transfer_info.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... @@ -310,10 +314,7 @@ def iter_investment(self, account): @retry((ClientError, ServerError)) @need_login def iter_recipients(self, account): - self.transfer_info.go( - data='{"beneficiaryType":"INTERNATIONAL"}', - headers=self.json_headers - ) + self.transfer_info.go(json={"beneficiaryType":"INTERNATIONAL"}) if account.type in (Account.TYPE_LOAN, ): return @@ -334,6 +335,50 @@ def iter_recipients(self, account): 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): diff --git a/modules/cmso/par/transfer_pages.py b/modules/cmso/par/transfer_pages.py index 4dd54bb44a..96eed8e9d4 100644 --- a/modules/cmso/par/transfer_pages.py +++ b/modules/cmso/par/transfer_pages.py @@ -23,8 +23,9 @@ 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 +from weboob.capabilities.bank import Recipient, Transfer, TransferBankError from weboob.capabilities.base import NotAvailable @@ -42,6 +43,17 @@ def condition(self): 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 @@ -106,3 +118,37 @@ class item(ItemElement): 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') -- GitLab