Skip to content
Commits on Source (37)
......@@ -175,7 +175,7 @@ def do_login(self):
self.handle_captcha(captcha)
else:
msg = self.page.get_error_message()
assert self.WRONGPASS_MESSAGE in msg, msg
assert any(wrongpass_message in msg for wrongpass_message in self.WRONGPASS_MESSAGES), msg
raise BrowserIncorrectPassword(msg)
def is_login(self):
......
......@@ -451,6 +451,9 @@ def new_recipient(self, recipient, **params):
# Confirm that user want to add recipient
self.page.continue_new_recipient()
if self.recipient_confirmation_page.is_here():
self.page.check_errors()
assert self.add_recipient.is_here()
self.page.set_new_recipient_iban(recipient.iban)
rcpt = self.copy_recipient_obj(recipient)
......
......@@ -148,6 +148,17 @@ def send_code(self, code):
def is_add_recipient_confirmation(self):
return self.doc.xpath('//table[@id="idConfirmation"]//p[contains(., "Votre bénéficiaire est en cours de création automatique")]')
def check_errors(self):
# check if user can add new recipient
errors_id = ('popinClientNonEligible', 'popinClientNonEligibleBis')
for error_id in errors_id:
if self.doc.xpath('//script[contains(text(), "showDivJQInfo(\'%s\')")]' % error_id):
msg = CleanText('//div[@id="%s"]//p' % error_id)(self.doc)
# get the first sentence of information message
# beacause the message is too long and contains unnecessary recommendations
raise AddRecipientBankError(message=msg.split('.')[0])
class AddRecipientPage(LoggedPage, HTMLPage):
is_here = '//table[@id="tab_SaisieBenef"]'
......
urls = ('https://www.becm.fr',)
account_types = ('card', 'checking', 'lifeinsurance', 'loan', 'market', 'pea', 'savings', )
......@@ -610,10 +610,15 @@ def iter_investments(self):
class LifeInsurancesHistoryPage(BNPPage):
IGNORED_STATUSES = (
'En cours',
'Sans suite',
)
def iter_history(self, coming):
for op in self.get('data.listerMouvements.listeMouvements') or []:
#We have not date for this statut so we just skit it
if op.get('statut') == u'En cours':
if op.get('statut') in self.IGNORED_STATUSES:
continue
tr = Transaction.from_dict({
......@@ -622,14 +627,16 @@ def iter_history(self, coming):
'amount': op.get('montantNet'),
})
if op.get('statut') == 'Sans suite':
continue
tr.parse(date=parse_french_date(op.get('dateSaisie')),
vdate = parse_french_date(op.get('dateEffet')) if op.get('dateEffet') else None,
raw='%s %s' % (op.get('libelleMouvement'), op.get('canalSaisie') or ''))
tr._op = op
if not tr.amount:
if op.get('rib', {}).get('codeBanque') == 'null':
self.logger.info('ignoring non-transaction with label %r', tr.raw)
continue
if (op.get('statut') == u'Traité') ^ coming:
yield tr
......@@ -831,6 +838,7 @@ def on_load(self):
raise AddRecipientBankError(message=self.get('message'))
def get_recipient(self, recipient):
# handle polling response
r = Recipient()
r.iban = recipient.iban
r.id = self.get('data.gestionBeneficiaire.identifiantBeneficiaire')
......@@ -840,6 +848,7 @@ def get_recipient(self, recipient):
r.currency = u'EUR'
r.bank_name = NotAvailable
r._id_transaction = self.get('data.gestionBeneficiaire.idTransactionAF') or NotAvailable
r._transfer_id = self.get('data.gestionBeneficiaire.identifiantBeneficiaire') or NotAvailable
return r
......
......@@ -103,6 +103,9 @@ def transfer_check_label(self, old, new):
# Else: inside '<>' chars are deleted
old = re.sub(r'<[^>]*>', '', old).strip()
old = old.split('<')[0]
# replace � by ?, like the bank does
old = old.replace('\ufffd', '?')
return super(BoursoramaModule, self).transfer_check_label(old, new)
def iter_currencies(self):
......
......@@ -31,7 +31,7 @@
from weboob.browser.filters.standard import (
CleanText, CleanDecimal, Field, Format,
Regexp, Date, AsyncLoad, Async, Eval, Env,
Currency as CleanCurrency, Map,
Currency as CleanCurrency, Map, Coalesce,
)
from weboob.browser.filters.json import Dict
from weboob.browser.filters.html import Attr, Link, TableCell
......@@ -214,26 +214,28 @@ def is_here(self):
# This id appears when there are no accounts (pro and pp)
return not self.doc.xpath('//div[contains(@id, "alert-random")]')
ACCOUNT_TYPES = {u'comptes courants': Account.TYPE_CHECKING,
u'cav': Account.TYPE_CHECKING,
'livret': Account.TYPE_SAVINGS,
'pel': Account.TYPE_SAVINGS,
'cel': Account.TYPE_SAVINGS,
u'comptes épargne': Account.TYPE_SAVINGS,
u'mon épargne': Account.TYPE_SAVINGS,
'csljeune': Account.TYPE_SAVINGS, # in url
u'ord': Account.TYPE_MARKET,
u'comptes bourse': Account.TYPE_MARKET,
u'mes placements financiers': Account.TYPE_MARKET,
u'av': Account.TYPE_LIFE_INSURANCE,
u'assurances vie': Account.TYPE_LIFE_INSURANCE,
u'assurance-vie': Account.TYPE_LIFE_INSURANCE,
u'mes crédits': Account.TYPE_LOAN,
u'crédit': Account.TYPE_LOAN,
u'prêt': Account.TYPE_LOAN,
u'pea': Account.TYPE_PEA,
'carte': Account.TYPE_CARD,
}
ACCOUNT_TYPES = {
'comptes courants': Account.TYPE_CHECKING,
'cav': Account.TYPE_CHECKING,
'livret': Account.TYPE_SAVINGS,
'pel': Account.TYPE_SAVINGS,
'cel': Account.TYPE_SAVINGS,
'ldd': Account.TYPE_SAVINGS,
'comptes épargne': Account.TYPE_SAVINGS,
'mon épargne': Account.TYPE_SAVINGS,
'csljeune': Account.TYPE_SAVINGS, # in url
'ord': Account.TYPE_MARKET,
'comptes bourse': Account.TYPE_MARKET,
'mes placements financiers': Account.TYPE_MARKET,
'av': Account.TYPE_LIFE_INSURANCE,
'assurances vie': Account.TYPE_LIFE_INSURANCE,
'assurance-vie': Account.TYPE_LIFE_INSURANCE,
'mes crédits': Account.TYPE_LOAN,
'crédit': Account.TYPE_LOAN,
'prêt': Account.TYPE_LOAN,
'pea': Account.TYPE_PEA,
'carte': Account.TYPE_CARD,
}
@method
class iter_accounts(ListElement):
......@@ -419,12 +421,19 @@ class iter_history(ListElement):
class item(ItemElement):
klass = Transaction
obj_raw = Transaction.Raw(CleanText('.//div[has-class("list__movement__line--label__name")]'))
obj_date = Date(Attr('.//time', 'datetime'))
obj_amount = CleanDecimal('.//div[has-class("list__movement__line--amount")]', replace_dots=True)
obj_category = CleanText('.//span[has-class("category")]')
obj__account_name = CleanText('.//span[contains(@class, "account__name-xs")]', default=None)
# div "label__name" contain two span: one with the short label (hidden in the website) and one with
# the long label. We try to get the long one. If it's empty, we take the content of "label__name" to
# be sure to have a value.
obj_raw = Coalesce(
Transaction.Raw(CleanText('.//span[has-class("list__movement--label-long")]')),
Transaction.Raw(CleanText('.//div[has-class("list__movement__line--label__name")]')),
)
def obj_id(self):
return Attr('.', 'data-id', default=NotAvailable)(self) or Attr('.', 'data-custom-id', default=NotAvailable)(self)
......@@ -505,6 +514,7 @@ class item(ItemElement):
obj_raw = Transaction.Raw(Dict('label'))
obj_date = Date(Dict('dateVal'), dayfirst=True)
obj_bdate = Date(Dict('dateOp'), dayfirst=True)
obj__account_label = Dict('accountLabel')
obj__is_coming = False
......@@ -853,10 +863,10 @@ def obj_label(self):
return label.rstrip('-').rstrip()
def obj_category(self):
text = CleanText('./ancestor::div[has-class("deploy--item")]//a[has-class("deploy__title")]')(self)
if 'Mes comptes Boursorama Banque' in text:
text = CleanText('./ancestor::div[has-class("deploy--item")]//a[has-class("deploy__title")]')(self).lower()
if 'mes comptes boursorama banque' in text:
return 'Interne'
elif any(exp in text for exp in ('Comptes externes', 'Comptes de tiers', 'Mes bénéficiaires')):
elif any(exp in text for exp in ('comptes externes', 'comptes de tiers', 'mes bénéficiaires')):
return 'Externe'
def obj_iban(self):
......
......@@ -54,6 +54,7 @@ class BPBrowser(LoginBrowser, StatesMixin):
# FIXME beware that '.*' in start of URL() won't match all domains but only under BASEURL
login_image = URL(r'.*wsost/OstBrokerWeb/loginform\?imgid=', UselessPage)
login_page = URL(r'.*wsost/OstBrokerWeb/loginform.*', LoginPage)
repositionner_chemin_courant = URL(r'.*authentification/repositionnerCheminCourant-identif.ea', repositionnerCheminCourant)
init_ident = URL(r'.*authentification/initialiser-identif.ea', Initident)
......
......@@ -455,7 +455,7 @@ def on_load(self):
self.get_form(id='autoSubmit').submit()
class UselessPage(LoggedPage, HTMLPage):
class UselessPage(LoggedPage, RawPage):
pass
......
......@@ -185,8 +185,10 @@ def get_history(self, account):
if tr.type is FrenchTransaction.TYPE_CARD_SUMMARY:
if find_object(card_tr_list, label=tr.label, amount=tr.amount, raw=tr.raw, date=tr.date, rdate=tr.rdate):
self.logger.warning('Duplicate transaction: %s' % tr)
self.logger.warning('Duplicated transaction: %s', tr)
items.pop()
continue
card_tr_list.append(tr)
tr.deleted = True
tr_dict = [tr_dict for tr_dict in data_out if tr_dict['Libelle'] == tr.label]
......
......@@ -26,7 +26,7 @@
from weboob.browser.filters.standard import Date, CleanDecimal, CleanText, Format, Field, Env, Regexp, Currency
from weboob.browser.filters.json import Dict
from weboob.capabilities import NotAvailable
from weboob.capabilities.bank import Account, Transaction, Loan
from weboob.capabilities.bank import Account, Loan
from weboob.capabilities.contact import Advisor
from weboob.capabilities.profile import Profile
from weboob.capabilities.bill import DocumentTypes, Subscription, Document
......@@ -34,6 +34,39 @@
from weboob.exceptions import BrowserUnavailable
class Transaction(FrenchTransaction):
PATTERNS = [(re.compile('^CB (?P<text>.*?) FACT (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('^RET(RAIT)? DAB (?P<dd>\d+)-(?P<mm>\d+)-.*', re.IGNORECASE),
FrenchTransaction.TYPE_WITHDRAWAL),
(re.compile('^RET(RAIT)? DAB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2}) (?P<HH>\d{2})H(?P<MM>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_WITHDRAWAL),
(re.compile('^VIR(EMENT)?(\.PERIODIQUE)? (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_TRANSFER),
(re.compile('^PRLV (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_ORDER),
(re.compile('^CHEQUE.*', re.IGNORECASE), FrenchTransaction.TYPE_CHECK),
(re.compile('^(CONVENTION \d+ )?COTIS(ATION)? (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_BANK),
(re.compile(r'^\* (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_BANK),
(re.compile('^REMISE (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_DEPOSIT),
(re.compile('^(?P<text>.*)( \d+)? QUITTANCE .*', re.IGNORECASE),
FrenchTransaction.TYPE_ORDER),
(re.compile('^CB [\d\*]+ TOT DIF .*', re.IGNORECASE),
FrenchTransaction.TYPE_CARD_SUMMARY),
(re.compile('^CB [\d\*]+ (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('^CB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('\*CB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('^FAC CB (?P<text>.*?) (?P<dd>\d{2})/(?P<mm>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
]
class LoginPage(JsonPage):
def get_response(self):
return self.doc
......@@ -187,14 +220,12 @@ class CenetAccountHistoryPage(LoggedPage, CenetJsonPage):
'CHEQUE': Transaction.TYPE_CHECK,
'REMISE CHEQUE': Transaction.TYPE_CASH_DEPOSIT,
'PRLV': Transaction.TYPE_ORDER,
'CB': Transaction.TYPE_CARD
}
TR_TYPES_API = {
'VIR': Transaction.TYPE_TRANSFER,
'PE': Transaction.TYPE_ORDER, # PRLV
'CE': Transaction.TYPE_CHECK, # CHEQUE
'CB': Transaction.TYPE_CARD,
'DE': Transaction.TYPE_CASH_DEPOSIT, # APPRO
'PI': Transaction.TYPE_CASH_DEPOSIT, # REMISE CHEQUE
}
......@@ -291,39 +322,6 @@ def on_load(self):
raise BrowserUnavailable(CleanText('//div[@id="message_error_hs"]')(self.doc))
class Transaction(FrenchTransaction):
PATTERNS = [(re.compile('^CB (?P<text>.*?) FACT (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('^RET(RAIT)? DAB (?P<dd>\d+)-(?P<mm>\d+)-.*', re.IGNORECASE),
FrenchTransaction.TYPE_WITHDRAWAL),
(re.compile('^RET(RAIT)? DAB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2}) (?P<HH>\d{2})H(?P<MM>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_WITHDRAWAL),
(re.compile('^VIR(EMENT)?(\.PERIODIQUE)? (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_TRANSFER),
(re.compile('^PRLV (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_ORDER),
(re.compile('^CHEQUE.*', re.IGNORECASE), FrenchTransaction.TYPE_CHECK),
(re.compile('^(CONVENTION \d+ )?COTIS(ATION)? (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_BANK),
(re.compile(r'^\* (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_BANK),
(re.compile('^REMISE (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_DEPOSIT),
(re.compile('^(?P<text>.*)( \d+)? QUITTANCE .*', re.IGNORECASE),
FrenchTransaction.TYPE_ORDER),
(re.compile('^CB [\d\*]+ TOT DIF .*', re.IGNORECASE),
FrenchTransaction.TYPE_CARD_SUMMARY),
(re.compile('^CB [\d\*]+ (?P<text>.*)', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('^CB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('\*CB (?P<text>.*?) (?P<dd>\d{2})(?P<mm>\d{2})(?P<yy>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
(re.compile('^FAC CB (?P<text>.*?) (?P<dd>\d{2})/(?P<mm>\d{2})', re.IGNORECASE),
FrenchTransaction.TYPE_CARD),
]
class SubscriptionPage(LoggedPage, CenetJsonPage):
@method
class iter_subscription(DictElement):
......
......@@ -591,9 +591,12 @@ def go_loan_list(self):
def is_history_of(self, account_id):
"""
Check whether the displayed history is for the correct account
Check whether the displayed history is for the correct account.
If we do not find the select box we consider we are on the expected account (like it was before this check)
"""
return bool(self.doc.xpath('//option[@value="%s" and @selected]' % account_id))
if self.doc.xpath('//select[@id="MM_HISTORIQUE_COMPTE_m_ExDropDownList"]'):
return bool(self.doc.xpath('//option[@value="%s" and @selected]' % account_id))
return True
def go_history(self, info, is_cbtab=False):
form = self.get_form(id='main')
......
......@@ -296,7 +296,7 @@ class Transaction(FrenchTransaction):
class HistoryPage(LoggedPage, JsonPage):
def has_deferred_cards(self):
return Dict('pendingDeferredDebitCardList/currentMonthCardList', default=None)
return Dict('pendingDeferredDebitCardList/currentMonthCardList', default=None)(self.doc)
def get_keys(self):
if 'exception' in self.doc:
......
......@@ -613,18 +613,18 @@ def iter_transfer_recipients(self, account, transfer_space_info=None):
# can't use 'ignore_duplicate' in DictElement because we need the 'index' to do transfer
seen = set()
seen.add(account.iban)
seen.add(account.id)
for index, internal_rcpt in enumerate(self.page.iter_internal_recipient()):
internal_rcpt._index = index
if internal_rcpt._is_recipient and (internal_rcpt.iban not in seen):
seen.add(internal_rcpt.iban)
if internal_rcpt._is_recipient and (internal_rcpt.id not in seen):
seen.add(internal_rcpt.id)
yield internal_rcpt
for index, external_rcpt in enumerate(self.page.iter_external_recipient()):
external_rcpt._index = index
if external_rcpt.iban not in seen:
seen.add(external_rcpt.iban)
if external_rcpt.id not in seen:
seen.add(external_rcpt.id)
yield external_rcpt
@need_login
......@@ -664,10 +664,16 @@ def init_transfer(self, transfer, **params):
'transferCurrencyCode': account.currency,
'transferDate': transfer.exec_date.strftime('%d/%m/%Y'),
'transferFrequency': 'U',
'transferRef': '',
'transferRef': transfer.label,
'transferType': 'UNIQUE',
'typeCompte': account.label,
}
# update transfer data according to recipient category
if recipient.category == 'Interne':
data['creditAccountNumber'] = recipient.id
data['recipientName'] = recipient._owner_name
# init transfer request
self.transfer.go(
space=space,
......
......@@ -77,6 +77,7 @@ def condition(self):
obj_category = 'Interne'
obj_enabled_at = date.today()
obj__is_recipient = Dict('recipientOfTransfert', default=False)
obj__owner_name = CleanText(Dict('accountHolderLongDesignation'))
@method
class iter_external_recipient(DictElement):
......@@ -90,7 +91,7 @@ def condition(self):
klass = Recipient
obj_id = obj_iban = Dict('ibanCode')
obj_label = Dict('recipientName')
obj_label = CleanText(Dict('recipientName'))
obj_category = 'Externe'
obj_enabled_at = date.today()
......@@ -123,8 +124,12 @@ def handle_response(self, transfer):
t.account_iban = Dict('currentDebitIbanCode')(self.doc)
t.account_label = Dict('typeCompte')(self.doc)
t.recipient_label = CleanText(Dict('currentCreditAccountName'))(self.doc)
t.recipient_id = t.recipient_iban = Dict('currentCreditIbanCode')(self.doc)
t.recipient_label = Dict('currentCreditAccountName')(self.doc)
# Internal transfer
if not Dict('isExternalTransfer')(self.doc):
t.recipient_id = Dict('currentCreditAccountNumber')(self.doc)
return t
......
......@@ -36,6 +36,7 @@ class CreditDuNordBrowser(LoginBrowser):
login = URL('$',
'/.*\?.*_pageLabel=page_erreur_connexion',
'/.*\?.*_pageLabel=reinitialisation_mot_de_passe',
LoginPage)
redirect = URL('/swm/redirectCDN.html', RedirectPage)
entrypage = URL('/icd/zco/#zco', EntryPage)
......@@ -69,21 +70,20 @@ def do_login(self):
if expired_error:
raise BrowserPasswordExpired(expired_error)
if self.login.is_here():
# Force redirection to entry page if the redirect page does not contain an url
if self.redirect.is_here():
self.entrypage.go()
if self.entrypage.is_here() or self.login.is_here():
error = self.page.get_error()
if error:
if 'code confidentiel à la première connexion' in error:
raise BrowserPasswordExpired(error)
raise BrowserIncorrectPassword(error)
else:
# in case we are still on login without error message
# we'll check what's happening.
assert False, "Still on login page."
if not self.logged:
raise BrowserIncorrectPassword()
if self.page.doc.xpath('//head[title="Authentification"]/script[contains(text(), "_pageLabel=reinitialisation_mot_de_passe")]'):
raise BrowserPasswordExpired()
def _iter_accounts(self):
self.loans.go(account_type=self.account_type, loans_page_label=self.loans_page_label)
for a in self.page.get_list():
......
......@@ -97,15 +97,26 @@ def get_string_code(self, string):
return ','.join(res)
class HTMLErrorPage(HTMLPage):
def get_error(self):
# No Coalesce here as both can be empty
return CleanText('//b[has-class("x-attentionErreurLigneHaut")]')(self.doc) or \
CleanText('//div[has-class("x-attentionErreur")]/b')(self.doc)
class RedirectPage(HTMLPage):
pass
def on_load(self):
link = Regexp(CleanText('//script'), 'href="(.*)"', default='')(self.doc)
if link:
self.browser.location(link)
class EntryPage(LoggedPage, HTMLPage):
class EntryPage(LoggedPage, HTMLErrorPage):
pass
class LoginPage(HTMLPage):
class LoginPage(HTMLErrorPage):
VIRTUALKEYBOARD = CDNVirtKeyboard
def login(self, username, password):
......@@ -147,9 +158,6 @@ def classic_login(self, username, password):
}
self.browser.location('/saga/authentification', data=data)
def get_error(self):
return CleanText('//b[has-class("x-attentionErreurLigneHaut")]', default="")(self.doc)
class AccountTypePage(LoggedPage, JsonPage):
def get_account_type(self):
......
......@@ -421,6 +421,7 @@ def get_history(self, account):
history = self.page.get_history(date=self.tr_date)
for tr in history:
# For regrouped transaction, we have to go through each one to get details
if tr._regroup:
self.location(tr._regroup)
for tr2 in self.page.get_tr_merged():
......
......@@ -725,7 +725,7 @@ class item(Transaction.TransactionElement):
obj_original_amount = CleanDecimal(TableCell('original_amount'), default=NotAvailable, replace_dots=True)
obj_original_currency = FrenchTransaction.Currency(TableCell('original_amount'))
obj_type = Transaction.TYPE_DEFERRED_CARD
obj_rdate = Transaction.Date(TableCell('date'))
obj_rdate = obj_bdate = Transaction.Date(TableCell('date'))
obj_date = obj_vdate = Env('date')
obj__is_coming = Env('_is_coming')
......@@ -905,7 +905,7 @@ def condition(self):
return len(self.el.xpath('./td')) >= 4 and not CleanText(TableCell('commerce'))(self).startswith('RETRAIT CB')
obj_raw = Transaction.Raw(Format("%s %s", CleanText(TableCell('commerce')), CleanText(TableCell('ville'))))
obj_rdate = Field('vdate')
obj_rdate = obj_bdate = Field('vdate')
obj_date = Env('date')
def obj_type(self):
......@@ -933,6 +933,8 @@ def obj__is_coming(self):
return True
return False
# Some payment made on the same organization are regrouped,
# we have to get the detail for each one later
def obj__regroup(self):
if "Regroupement" in CleanText('./td')(self):
return Link('./td/span/a')(self)
......@@ -959,6 +961,10 @@ def obj_type(self):
return Transaction.TYPE_DEFERRED_CARD
return Transaction.TYPE_CARD_SUMMARY
def obj_bdate(self):
if Field('type')(self) == Transaction.TYPE_DEFERRED_CARD:
return Transaction.Date(TableCell('date'))(self)
def has_more_operations(self):
xp = CleanText(self.doc.xpath('//div[@class="ei_blocpaginb"]/a'))(self)
if xp == 'Suite des opérations':
......@@ -985,7 +991,7 @@ def condition(self):
return not CleanText(TableCell('commerce'))(self).startswith('RETRAIT CB')
obj_raw = Transaction.Raw(Format("%s %s", CleanText(TableCell('commerce')), CleanText(TableCell('ville'))))
obj_rdate = Field('vdate')
obj_rdate = obj_bdate = Field('vdate')
obj_date = Env('date')
def obj_type(self):
......@@ -1162,9 +1168,11 @@ def obj_code(self):
class PorPage(LoggedPage, HTMLPage):
TYPES = {"PLAN D'EPARGNE EN ACTIONS": Account.TYPE_PEA,
'P.E.A': Account.TYPE_PEA
}
TYPES = {
"PLAN D'EPARGNE EN ACTIONS": Account.TYPE_PEA,
'P.E.A': Account.TYPE_PEA,
'PEA': Account.TYPE_PEA,
}
def get_type(self, label):
for pattern, actype in self.TYPES.items():
......@@ -1539,10 +1547,7 @@ def parse(self, el):
self.env['origin_account']._external_recipients.add(Field('id')(self))
def get_transfer_form(self):
# internal and external transfer form are differents
if self.IS_PRO_PAGE:
return self.get_form(id='P2:F', submit='//input[@type="submit" and contains(@value, "Valider")]')
return self.get_form(id='P1:F', submit='//input[@type="submit" and contains(@value, "Valider")]')
return self.get_form(xpath='//form[@id="P1:F"] | //form[@id="P2:F"]', submit='//input[@type="submit" and contains(@value, "Valider")]')
class VerifCodePage(LoggedPage, HTMLPage):
HASHES = {
......
......@@ -42,7 +42,7 @@ class EdfModule(Module, CapDocument, CapProfile):
ValueBackendPassword('password', label='Mot de passe'),
Value('website', label='Type de compte', default='par',
choices={'par': 'Particulier', 'pro': 'Entreprise'}),
Value('captcha_response', label='Reponse Captcha', required=False, default=''))
Value('otp', label='Entrez le code reçu par SMS', required=False))
accepted_document_types = (DocumentTypes.BILL,)
......