Commit c700a2f6 authored by Sylvie Ye's avatar Sylvie Ye Committed by Romain Bignon

[ing] multiple fix

do_login: fix handle login error

CapBank:
* Accont on new website are needed to do transfer.
* Do matching account between old and new website
based on account balance and the 4 last account number characters
* Details of accounts other than checking accounts are not available on new website

CapWealth:
* Invests are not available on new website, use old website to retreive it

CapBankTransfer:
* transfer is only available on new website, do iter recipient on it

CapProfile:
* get profile on new website
parent db85eaab
......@@ -5,19 +5,25 @@
# 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
# it under the terms of the GNU Lesser 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.
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .login import LoginPage
from .accounts_page import AccountsPage, HistoryPage, ComingPage
from .transfer_page import DebitAccountsPage, CreditAccountsPage
from .profile_page import ProfilePage
__all__ = ['LoginPage', ]
__all__ = ['LoginPage', 'AccountsPage',
'HistoryPage', 'ComingPage',
'DebitAccountsPage', 'CreditAccountsPage',
'ProfilePage']
# -*- 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 Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.browser.pages import LoggedPage, JsonPage
from weboob.browser.elements import method, DictElement, ItemElement
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import (
CleanText, CleanDecimal, Date, Eval,
)
from weboob.capabilities.bank import Account, Transaction
class AccountsPage(LoggedPage, JsonPage):
@method
class iter_accounts(DictElement):
item_xpath = 'accounts'
class item(ItemElement):
klass = Account
obj_id = Dict('uid')
obj_label = Dict('type/label')
obj_number = CleanText(Dict('label'), replace=[(' ', '')])
def obj_balance(self):
if not Dict('hasPositiveBalance')(self):
return -CleanDecimal(Dict('ledgerBalance'))(self)
return CleanDecimal(Dict('ledgerBalance'))(self)
class HistoryPage(LoggedPage, JsonPage):
def is_empty_page(self):
return len(self.doc) == 0
@method
class iter_history(DictElement):
class item(ItemElement):
klass = Transaction
obj_id = Eval(str, Dict('id'))
obj_label = CleanText(Dict('detail'))
obj_amount = CleanDecimal(Dict('amount'))
obj_date = Date(Dict('effectiveDate'))
class ComingPage(LoggedPage, JsonPage):
@method
class iter_coming(DictElement):
item_xpath = 'futureOperations'
class item(ItemElement):
klass = Transaction
obj_label = Dict('label')
obj_amount = CleanDecimal(Dict('amount'))
obj_date = Date(Dict('effectiveDate'))
obj_vdate = Date(Dict('operationDate'))
......@@ -5,16 +5,16 @@
# 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
# it under the terms of the GNU Lesser 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.
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from io import BytesIO
......@@ -105,7 +105,3 @@ class LoginPage(JsonPage):
password_radom_coords = vk.password_tiles_coord(password)
# pin positions (website side) start at 1, our positions start at 0
return [password_radom_coords[index-1] for index in pin_position]
def get_error(self):
if 'error' in self.doc:
return (Dict('error/code')(self.doc), Dict('error/message')(self.doc))
# -*- 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 Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.browser.pages import LoggedPage, JsonPage
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import CleanText, Format
from weboob.browser.elements import ItemElement, method
from weboob.capabilities.profile import Profile
from weboob.capabilities.base import NotAvailable
class ProfilePage(LoggedPage, JsonPage):
@method
class get_profile(ItemElement):
klass = Profile
obj_name = Format('%s %s', Dict('name/firstName'), Dict('name/lastName'))
obj_country = Dict('mailingAddress/country')
obj_phone = Dict('phones/0/number', default=NotAvailable)
obj_email = Dict('emailAddress')
obj_address = CleanText(Format(
'%s %s %s %s %s %s %s',
Dict('mailingAddress/address1'),
Dict('mailingAddress/address2'),
Dict('mailingAddress/address3'),
Dict('mailingAddress/address4'),
Dict('mailingAddress/city'),
Dict('mailingAddress/postCode'),
Dict('mailingAddress/country')
))
# -*- 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 Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from datetime import datetime
from weboob.browser.pages import LoggedPage, JsonPage
from weboob.browser.elements import method, DictElement, ItemElement
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import (
Env, Field,
)
from weboob.capabilities.bank import Recipient
class DebitAccountsPage(LoggedPage, JsonPage):
def get_debit_accounts_uid(self):
return [Dict('uid')(recipient) for recipient in self.doc]
class CreditAccountsPage(LoggedPage, JsonPage):
@method
class iter_recipients(DictElement):
class item(ItemElement):
def condition(self):
return Dict('uid')(self) != Env('acc_uid')(self)
klass = Recipient
def obj__is_internal_recipient(self):
return bool(Dict('ledgerBalance', default=None)(self))
obj_id = Dict('uid')
obj_enabled_at = datetime.now().replace(microsecond=0)
def obj_label(self):
if Field('_is_internal_recipient')(self):
return Dict('type/label')(self)
return Dict('owner')(self)
def obj_category(self):
if Field('_is_internal_recipient')(self):
return 'Interne'
return 'Externe'
This diff is collapsed.
......@@ -30,13 +30,13 @@ from weboob.exceptions import BrowserUnavailable
from weboob.browser.exceptions import ServerError
from weboob.capabilities.bank import Account, AccountNotFound
from weboob.capabilities.base import find_object, NotAvailable
from weboob.tools.capabilities.bank.transactions import FrenchTransaction
from .web import (
AccountsList, NetissimaPage, TitrePage,
TitreHistory, TransferPage, BillsPage, StopPage, TitreDetails,
TitreHistory, BillsPage, StopPage, TitreDetails,
TitreValuePage, ASVHistory, ASVInvest, DetailFondsPage, IbanPage,
ActionNeededPage, ReturnPage, ProfilePage, LoanTokenPage, LoanDetailPage,
ApiRedirectionPage,
)
__all__ = ['IngBrowser']
......@@ -86,6 +86,7 @@ class IngBrowser(LoginBrowser):
ibanpage = URL(r'/protected/pages/common/rib/initialRib.jsf', IbanPage)
loantokenpage = URL(r'general\?command=goToConsumerLoanCommand&redirectUrl=account-details', LoanTokenPage)
loandetailpage = URL(r'https://subscribe.ing.fr/consumerloan/consumerloan-v1/consumer/details', LoanDetailPage)
# CapBank-Market
netissima = URL(r'/data/asv/fiches-fonds/fonds-netissima.html', NetissimaPage)
starttitre = URL(r'/general\?command=goToAccount&zone=COMPTE', TitrePage)
......@@ -97,12 +98,16 @@ class IngBrowser(LoginBrowser):
r'https://ingdirectvie.ing.fr/b2b2c/epargne/CoeDetMvt', ASVHistory)
asv_invest = URL(r'https://ingdirectvie.ing.fr/b2b2c/epargne/CoeDetCon', ASVInvest)
detailfonds = URL(r'https://ingdirectvie.ing.fr/b2b2c/fonds/PerDesFac\?codeFonds=(.*)', DetailFondsPage)
# CapDocument
billpage = URL(r'/protected/pages/common/estatement/eStatement.jsf', BillsPage)
# CapProfile
profile = URL(r'/protected/pages/common/profil/(?P<page>\w+).jsf', ProfilePage)
transfer = URL(r'/protected/pages/common/virement/index.jsf', TransferPage)
# New website redirection
api_redirection_url = URL(r'/general\?command=goToSecureUICommand&redirectUrl=transfers', ApiRedirectionPage)
__states__ = ['where']
......@@ -126,6 +131,11 @@ class IngBrowser(LoginBrowser):
def do_login(self):
pass
def redirect_to_api_browser(self):
# get form to be redirected on transfer page
self.api_redirection_url.go()
self.page.go_new_website()
@need_login
def set_multispace(self):
self.where = 'start'
......@@ -151,6 +161,8 @@ class IngBrowser(LoginBrowser):
self.page.change_space(space)
self.current_space = space
else:
self.accountspage.go()
def is_same_space(self, a, b):
return (
......@@ -307,15 +319,16 @@ class IngBrowser(LoginBrowser):
def get_coming(self, account):
self.change_space(account._space)
if account.type != Account.TYPE_CHECKING and\
account.type != Account.TYPE_SAVINGS:
raise NotImplementedError()
# checking accounts are handled on api website
if account.type != Account.TYPE_SAVINGS:
return []
account = self.get_account(account.id, space=account._space)
self.go_account_page(account)
jid = self.page.get_history_jid()
if jid is None:
self.logger.info('There is no history for this account')
return
return []
return self.page.get_coming()
@need_login
......@@ -328,31 +341,23 @@ class IngBrowser(LoginBrowser):
yield result
return
elif account.type != Account.TYPE_CHECKING and\
account.type != Account.TYPE_SAVINGS:
raise NotImplementedError()
# checking accounts are handled on api website
elif account.type != Account.TYPE_SAVINGS:
return
account = self.get_account(account.id, space=account._space)
self.go_account_page(account)
jid = self.page.get_history_jid()
only_deferred_cb = self.only_deferred_cards.get(account._id)
if jid is None:
self.logger.info('There is no history for this account')
return
if account.type == Account.TYPE_CHECKING:
history_function = AccountsList.get_transactions_cc
index = -1 # disable the index. It works without it on CC
else:
history_function = AccountsList.get_transactions_others
index = 0
index = 0
hashlist = set()
while True:
i = index
for transaction in history_function(self.page, index=index):
if only_deferred_cb and transaction.type == FrenchTransaction.TYPE_CARD:
transaction.type = FrenchTransaction.TYPE_DEFERRED_CARD
for transaction in AccountsList.get_transactions_others(self.page, index=index):
transaction.id = hashlib.md5(transaction._hash).hexdigest()
while transaction.id in hashlist:
transaction.id = hashlib.md5((transaction.id + "1").encode('ascii')).hexdigest()
......@@ -373,33 +378,6 @@ class IngBrowser(LoginBrowser):
}
self.accountspage.go(data=data)
@need_login
@start_with_main_site
def iter_recipients(self, account):
self.change_space(account._space)
self.transfer.go()
if not self.page.able_to_transfer(account):
return iter([])
self.page.go_to_recipient_selection(account)
return self.page.get_recipients(origin=account)
@need_login
@start_with_main_site
def init_transfer(self, account, recipient, transfer):
self.change_space(account._space)
self.transfer.go()
self.page.do_transfer(account, recipient, transfer)
return self.page.recap(account, recipient, transfer)
@need_login
@start_with_main_site
def execute_transfer(self, transfer):
self.page.confirm(self.password)
return transfer
def go_on_asv_detail(self, account, link):
try:
if self.page.asv_is_other:
......@@ -484,6 +462,10 @@ class IngBrowser(LoginBrowser):
inv.portfolio_share = shares[inv.label]
yield inv
# return on old ing website
assert self.asv_invest.is_here(), "Should be on ING generali website"
self.lifeback.go()
def get_history_titre(self, account):
self.go_investments(account)
......@@ -571,11 +553,3 @@ class IngBrowser(LoginBrowser):
self._go_to_subscription(self.cache['subscriptions'][subid])
self.page.go_to_year(bill._year)
return self.page.download_document(bill)
############# CapProfile #############
@start_with_main_site
@need_login
def get_profile(self):
profile = self.profile.go(page='coordonnees').get_profile()
self.profile.go(page='infosperso').update_profile(profile)
return profile
......@@ -19,11 +19,7 @@
from __future__ import unicode_literals
import re
from datetime import timedelta
from decimal import Decimal
from weboob.capabilities.bank import CapBankWealth, CapBankTransfer, Account, AccountNotFound, RecipientNotFound
from weboob.capabilities.bank import CapBankWealth, CapBankTransfer, Account, AccountNotFound
from weboob.capabilities.bill import (
CapDocument, Bill, Subscription,
SubscriptionNotFound, DocumentNotFound, DocumentTypes,
......@@ -72,55 +68,42 @@ class INGModule(Module, CapBankWealth, CapBankTransfer, CapDocument, CapProfile)
self._restrict_level(split_path)
return self.iter_subscription()
############# CapBank #############
def iter_accounts(self):
return self.browser.get_accounts_list()
return self.browser.iter_matching_accounts()
def get_account(self, _id):
return self.browser.get_account(_id)
return find_object(self.iter_accounts(), id=_id, error=AccountNotFound)
def iter_history(self, account):
if not isinstance(account, Account):
account = self.get_account(account)
return self.browser.get_history(account)
return self.browser.iter_history(account)
def iter_transfer_recipients(self, account):
def iter_coming(self, account):
if not isinstance(account, Account):
account = self.get_account(account)
return self.browser.iter_recipients(account)
def init_transfer(self, transfer, **params):
self.logger.info('Going to do a new transfer')
transfer.label = ' '.join(w for w in re.sub(r'[^0-9a-zA-Z/\-\?:\(\)\.,\'\+ ]+', '', transfer.label).split()).upper()
if transfer.account_iban:
account = find_object(self.iter_accounts(), iban=transfer.account_iban, error=AccountNotFound)
else:
account = find_object(self.iter_accounts(), id=transfer.account_id, error=AccountNotFound)
if transfer.recipient_iban:
recipient = find_object(self.iter_transfer_recipients(account.id), iban=transfer.recipient_iban, error=RecipientNotFound)
else:
recipient = find_object(self.iter_transfer_recipients(account.id), id=transfer.recipient_id, error=RecipientNotFound)
transfer.amount = Decimal(transfer.amount).quantize(Decimal('.01'))
return self.browser.init_transfer(account, recipient, transfer)
def execute_transfer(self, transfer, **params):
return self.browser.execute_transfer(transfer)
def transfer_check_exec_date(self, old_exec_date, new_exec_date):
return old_exec_date <= new_exec_date <= old_exec_date + timedelta(days=4)
return self.browser.iter_coming(account)
############# CapWealth #############
def iter_investment(self, account):
if not isinstance(account, Account):
account = self.get_account(account)
return self.browser.get_investments(account)
def iter_coming(self, account):
############# CapTransfer #############
def iter_transfer_recipients(self, account):
if not isinstance(account, Account):
account = self.get_account(account)
return self.browser.get_coming(account)
return self.browser.iter_recipients(account)
def init_transfer(self, transfer, **params):
raise NotImplementedError()
def execute_transfer(self, transfer, **params):
raise NotImplementedError()
############# CapDocument #############
def iter_subscription(self):
return self.browser.get_subscriptions()
......@@ -142,5 +125,6 @@ class INGModule(Module, CapBankWealth, CapBankTransfer, CapDocument, CapProfile)
return self.browser.download_document(bill).content
############# CapProfile #############
def get_profile(self):
return self.browser.get_profile()
......@@ -22,8 +22,7 @@ from .accounts_list import (
AccountsList, TitreDetails, ASVInvest, DetailFondsPage, IbanPage,
ProfilePage, LoanTokenPage, LoanDetailPage,
)
from .login import StopPage, ActionNeededPage, ReturnPage
from .transfer import TransferPage
from .login import StopPage, ActionNeededPage, ReturnPage, ApiRedirectionPage
from .bills import BillsPage
from .titre import NetissimaPage, TitrePage, TitreHistory, TitreValuePage, ASVHistory
......@@ -33,8 +32,8 @@ class AccountPrelevement(AccountsList):
__all__ = ['AccountsList', 'NetissimaPage','TitreDetails',
'AccountPrelevement', 'TransferPage',
'AccountPrelevement',
'BillsPage', 'StopPage', 'TitrePage', 'TitreHistory', 'IbanPage',
'TitreValuePage', 'ASVHistory', 'ASVInvest','DetailFondsPage',
'ActionNeededPage', 'ReturnPage', 'ProfilePage', 'LoanTokenPage',
'LoanDetailPage']
'LoanDetailPage', 'ApiRedirectionPage']
......@@ -5,16 +5,16 @@
# 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
# it under the terms of the GNU Lesser 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.
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
......@@ -45,3 +45,10 @@ class StopPage(HTMLPage):
class ReturnPage(LoggedPage, HTMLPage):
def on_load(self):
self.get_form(name='retoursso').submit()
class ApiRedirectionPage(LoggedPage, HTMLPage):
def go_new_website(self):
form = self.get_form(name="module")
form.request.headers['Referer'] = "https://secure.ing.fr"
form.submit()
This diff is collapsed.
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