pages.py 7.45 KB
Newer Older
1 2 3 4
# -*- coding: utf-8 -*-

# Copyright(C) 2015      Baptiste Delpey
#
5
# This file is part of a weboob module.
6
#
7
# This weboob module is free software: you can redistribute it and/or modify
8 9 10 11
# 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.
#
12
# This weboob module is distributed in the hope that it will be useful,
13 14 15 16 17
# 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
18
# along with this weboob module. If not, see <http://www.gnu.org/licenses/>.
19 20 21

import re
from datetime import date
22
from decimal import Decimal
23

24
from weboob.exceptions import BrowserPasswordExpired
25 26
from weboob.browser.pages import HTMLPage, LoggedPage, pagination
from weboob.browser.elements import ListElement, ItemElement, method
27
from weboob.browser.filters.standard import CleanText, CleanDecimal, Field, Env, Format
28
from weboob.browser.filters.html import Link, Attr, AbsoluteLink
29 30 31
from weboob.capabilities.bank import Account
from weboob.tools.capabilities.bank.transactions import FrenchTransaction

32 33 34

class HomePage(LoggedPage, HTMLPage):
    def is_corporate(self):
Baptiste Delpey's avatar
Baptiste Delpey committed
35
        return bool(self.doc.xpath('//div[@class="marges5"]/h5/a[contains(text(), "CORPORATE")]'))
36

37
    def is_error(self):
Baptiste Delpey's avatar
Baptiste Delpey committed
38
        return bool(self.doc.xpath('//h1[contains(text(), "Change your password")]'))
39

40 41 42 43 44 45

class LoginPage(HTMLPage):
    def login(self, type, username, password):
        form = self.get_form(name='connecterForm')
        form['type'] = type
        form['login'] = username
46
        form['pwd'] = password[:8]
47 48 49 50
        form.url = '/ce_internet_public/seConnecter.event.do'
        form.submit()


51
class ExpandablePage(LoggedPage, HTMLPage):
52
    def expand(self, account=None, rib=None):
53
        form = self.get_form()
Baptiste Delpey's avatar
Baptiste Delpey committed
54 55
        if rib is not None:
            form['ribSaisi'] = rib
56 57 58 59
        if account is not None:
            form['numCarteSaisi'] = account._nav_num
        # needed if coporate titulaire
        form.url = form.url.replace('Appliquer', 'Afficher')
60 61
        form.submit()

Baptiste Delpey's avatar
Baptiste Delpey committed
62 63 64
    def get_rib_list(self):
        return self.doc.xpath('//select[@name="ribSaisi"]/option/@value')

65 66

class GetableLinksPage(LoggedPage, HTMLPage):
67 68 69 70
    def get_link(self, account):
        # FIXME this will probably crash on 'titulaire' space but all credentials are wrong pass so cannot test
        number, holder = account._completeid.split(':')
        el = self.doc.xpath('.//tr[.//a[text()=$card]][.//td[1][text()=$name]]//a', card=number, name=holder)
71 72 73 74 75 76 77 78 79 80 81 82
        if not el:
            return
        return el[0].get("href")


class PeriodsPage(LoggedPage, HTMLPage):
    def get_periods(self):
        periods = []
        for period in self.doc.xpath('//select[@name="periodeSaisie"]/option/@value'):
            periods.append(period)
        return periods

83
    def expand(self, period, account=None, rib=None):
84
        form = self.get_form(submit='//input[@value="Display"]')
85 86
        if account is not None:
            form['numCarteSaisi'] = account._nav_num
87
        form['periodeSaisie'] = period
Baptiste Delpey's avatar
Baptiste Delpey committed
88 89
        if rib is not None:
            form['ribSaisi'] = rib
90 91
        # needed if coporate titulaire
        form.url = form.url.replace('Appliquer', 'Afficher')
92 93 94 95
        form.submit()


class AccountsPage(ExpandablePage, GetableLinksPage):
96 97 98
    @pagination
    @method
    class iter_accounts(ListElement):
99
        item_xpath = '//table[@id="datas"]/tbody/tr'
100

101
        next_page = Link('//table[@id="datas"]/tfoot//b/following-sibling::a[1]')
102

103 104
        ignore_duplicate = True

105 106 107
        class item(ItemElement):
            klass = Account

108
            obj_id = CleanText('./td[2]')
109 110
            obj_label = CleanText('./td[1]')
            obj_type = Account.TYPE_CARD
Baptiste Delpey's avatar
Baptiste Delpey committed
111
            obj__rib = Env('rib')
Vincent Paredes's avatar
Vincent Paredes committed
112
            obj_currency = u'EUR'
113 114 115 116 117 118 119
            obj_number = CleanText('./td[2]', replace=[(' ', '')])
            obj_url = AbsoluteLink('./td[2]/a')

            obj__completeid = Format('%s:%s', obj_id, obj_label)

        def store(self, obj):
            return obj
120

121 122

class ComingPage(ExpandablePage):
123 124 125 126
    def get_link(self, account):
        # FIXME this will probably crash on 'titulaire' space but all credentials are wrong pass so cannot test
        card, holder = account._completeid.split(':')
        el = self.doc.xpath('.//tr[.//a[text()=$card]][.//td[1][text()=$name]]//a', card=card, name=holder)
127 128 129 130 131
        if not el:
            return
        link = re.search(r",'(.*)'\);", el[0].get("href"))
        if link:
            return link.group(1)
132

133 134 135 136 137 138 139 140
    def get_balance(self, account):
        # TODO find how pagination works on this page and find account with pagination
        card, holder = account._completeid.split(':')
        el = self.doc.xpath('.//tr[.//a[text()=$card]][.//td[1][text()=$name]]/td[4]', card=card, name=holder)
        if not el:
            return
        return CleanDecimal('.', replace_dots=(',', '.'))(el[0])

141 142 143

class HistoPage(GetableLinksPage, PeriodsPage):
    pass
144 145 146 147 148 149


class TransactionsPage(LoggedPage, HTMLPage):
    @pagination
    @method
    class get_history(ListElement):
150 151
        item_xpath = '(//table[contains(@id, "datas")]/tbody/tr | //table[contains(@id, "datas")]//tr[@class])'
        next_page = Link('(//table[@id="tgDecorationTableFoot"] | //table[@id="datas"]/tfoot)//b/following-sibling::a[1]')
152 153 154 155 156 157 158

        class item(ItemElement):
            klass = FrenchTransaction

            obj_rdate = FrenchTransaction.Date(CleanText('./td[1]'))
            obj_date = FrenchTransaction.Date(CleanText('./td[3]'))
            obj_raw = FrenchTransaction.Raw(CleanText('./td[2]'))
159
            _obj_amnt = FrenchTransaction.Amount(CleanText('./td[5]'), replace_dots=False)
160 161 162 163
            obj_original_amount = FrenchTransaction.Amount(CleanText('./td[4]'), replace_dots=False)
            obj_original_currency = FrenchTransaction.Currency(CleanText('./td[4]'))
            obj_commission = FrenchTransaction.Amount(CleanText('./td[6]'), replace_dots=False)

164 165 166
            def obj__coming(self):
                if Field('date')(self) >= date.today():
                    return True
167

168 169 170 171 172 173
            def obj_amount(self):
                if not Field('obj_commission'):
                    return Field('_obj_amnt')
                else:
                    return CleanDecimal(replace_dots=False).filter(self.el.xpath('./td[5]')) - CleanDecimal(replace_dots=False).filter(self.el.xpath('./td[6]'))

174

175
class ErrorPage(HTMLPage):
176 177 178 179
    def on_load(self):
        msg = CleanText('//div[@id="errors"]')(self.doc)
        if msg == 'Your password has expired: you must change it.':
            raise BrowserPasswordExpired(msg)
180 181


182
class TiCardPage(ExpandablePage, TransactionsPage):
183 184
    @method
    class iter_accounts(ListElement):
185 186
        item_xpath = '//table[@class="params"]/tr//option'

187 188
        class item(ItemElement):
            klass = Account
189 190
            obj_id = CleanText('.', replace=[(' ', '')])
            obj_label = Format('%s %s', CleanText('//table[@class="params"]/tr/td[1]/b[2]'), Field('id'))
191
            obj_type = Account.TYPE_CARD
192
            obj__nav_num = Attr('.', 'value')
193
            obj_currency = u'EUR'
194

195 196 197 198 199
    def get_balance(self):
        if self.doc.xpath('//div[@class="messageaucunedonnee"]'):
            return Decimal(0)
        return CleanDecimal('//div[@class="titre-datas"][1]/b')(self.doc)

200

201 202
class TiHistoPage(PeriodsPage, TransactionsPage):
    pass