diff --git a/modules/paypal/browser.py b/modules/paypal/browser.py index acbf5a15..ca682225 100644 --- a/modules/paypal/browser.py +++ b/modules/paypal/browser.py @@ -20,6 +20,7 @@ from weboob.deprecated.browser import Browser, BrowserIncorrectPassword from .pages import LoginPage, AccountPage, DownloadHistoryPage, LastDownloadHistoryPage, SubmitPage, HistoryParser, UselessPage, HistoryPage, CSVAlreadyAsked +from .newpages import NewHomePage, NewAccountPage, NewHistoryPage import datetime @@ -42,11 +43,22 @@ class Paypal(Browser): '/cgi-bin/webscr\?cmd=_history-download-recent$': LastDownloadHistoryPage, '/cgi-bin/webscr\?dispatch=[a-z0-9]+$': (SubmitPage, HistoryParser()), '/cgi-bin/webscr\?cmd=_history-download-recent-submit&dispatch=[a-z0-9]+$': (SubmitPage, HistoryParser()), + 'https://www.paypal.com/webapps/business/\?nav=0.0': NewHomePage, + 'https://www.paypal.com/businessexp/money': NewAccountPage, + 'https://www.paypal.com/webapps/business/activity\?.*': NewHistoryPage, } DEFAULT_TIMEOUT = 30 # CSV export is slow BEGINNING = datetime.date(1998,6,1) # The day PayPal was founded + website = None + + def find_website_version(self): + self.location('/en/cgi-bin/webscr?cmd=_account&nav=0.0') + if self.is_on_page(AccountPage): + self.website = "old" + else: + self.website = "new" def home(self): self.location('https://' + self.DOMAIN + '/en/cgi-bin/webscr?cmd=_login-run') @@ -68,14 +80,19 @@ class Paypal(Browser): raise BrowserIncorrectPassword() def get_accounts(self): - if not self.is_on_page(AccountPage): + self.find_website_version() + if self.website == "old" and not self.is_on_page(AccountPage): self.location('/en/cgi-bin/webscr?cmd=_account&nav=0.0') + elif not self.is_on_page(NewAccountPage): + self.location('/businessexp/money') return self.page.get_accounts() def get_account(self, _id): - if not self.is_on_page(AccountPage): + if self.website == "old" and not not self.is_on_page(AccountPage): self.location('/en/cgi-bin/webscr?cmd=_account&nav=0.0') + elif not self.is_on_page(NewAccountPage): + self.location('/businessexp/money') return self.page.get_account(_id) @@ -101,9 +118,18 @@ class Paypal(Browser): self.page.filter(start, end) assert self.is_on_page(HistoryPage) - def get_download_history(self, account, step_min=90, step_max=365*2): + def get_download_history(self, account, step_min=None, step_max=None): + if step_min is None and step_max is None: + if self.website == "old": + step_min = 90 + step_max = 365*2 + else: + step_min = 90 + step_max = 180 def fetch_fn(start, end): - if self.download_history(start, end).rows: + if self.website == "old" and self.download_history(start, end).rows: + return self.page.iter_transactions(account) + elif self.download_history(start, end): return self.page.iter_transactions(account) assert step_max <= 365*2 # PayPal limitations as of 2014-06-16 try: @@ -145,11 +171,19 @@ class Paypal(Browser): However, it is not normalized, and sometimes the download is refused and sent later by mail. """ - self.location('/en/cgi-bin/webscr?cmd=_history-download&nav=0.3.1') - assert self.is_on_page(DownloadHistoryPage) - self.page.download(start, end) - assert self.is_on_page(SubmitPage) - return self.page.document + if self.website == "old": + self.location('/en/cgi-bin/webscr?cmd=_history-download&nav=0.3.1') + assert self.is_on_page(DownloadHistoryPage) + self.page.download(start, end) + assert self.is_on_page(SubmitPage) + return self.page.document + else: + s = start.strftime('%d/%m/%Y') + e = end.strftime('%d/%m/%Y') + #Settings a big magic number so we get all transaction for the period + LIMIT = '9999' + self.location('/webapps/business/activity?fromdate=' + s + '&todate=' + e + '&transactiontype=ALL_TRANSACTIONS¤cy=ALL_TRANSACTIONS_CURRENCY&limit=' + LIMIT) + return self.page.transaction_left() def download_last_history(self, account): self.location('/en/cgi-bin/webscr?cmd=_history-download-recent') diff --git a/modules/paypal/newpages.py b/modules/paypal/newpages.py new file mode 100644 index 00000000..bc2ebee5 --- /dev/null +++ b/modules/paypal/newpages.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +# Copyright(C) 2014 Budget Insight +# +# 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 +# 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. +# +# You should have received a copy of the GNU Affero General Public License +# along with weboob. If not, see . + +from decimal import Decimal + +from weboob.deprecated.browser import Page +from weboob.capabilities.bank import Account +from weboob.tools.capabilities.bank.transactions import FrenchTransaction +from weboob.tools.date import parse_french_date + + +class NewHomePage(Page): + pass + +class NewAccountPage(Page): + def get_account(self, _id): + return self.get_accounts().get(_id) + + def get_accounts(self): + accounts = {} + content = self.document.xpath('//div[@id="moneyPage"]')[0] + + # Primary currency account + primary_account = Account() + primary_account.type = Account.TYPE_CHECKING + primary_account.id = unicode(primary_account.currency) + + balance = self.parser.tocleanstring(content.xpath('.//div[@class="col-md-6 available "]')[0]) + primary_account.balance = Decimal(FrenchTransaction.clean_amount(balance)) + primary_account.currency = Account.get_currency(balance) + + primary_account.label = u'%s %s*' % (self.browser.username, balance.split()[-1]) + + accounts[primary_account.id] = primary_account + + return accounts + +class NewHistoryPage(Page): + + def iter_transactions(self, account): + for trans in self.parse(): + if trans._currency == account.currency: + yield trans + + def parse(self): + for i, tr in enumerate(self.document.xpath('//tr')): + t = FrenchTransaction(tr.xpath('./td[@class="transactionId"]/span')[0].text.strip()) + date = parse_french_date(tr.xpath('./td[@class="date"]')[0].text.strip()) + status = tr.xpath('./td[@class="desc"]/ul/li[@class="first"]')[0].text.strip() + #We pass this because it's not transaction + if status == u'Créé' or status == u'Annulé': + continue + raw = tr.xpath('./td[@class="desc"]/strong')[0].text.strip() + t.parse(date=date, raw=raw) + amount = tr.xpath('./td[@class="price"]/span')[0].text.strip() + t.set_amount(amount) + t._currency = Account.get_currency(amount) + yield t + + def transaction_left(self): + return (len(self.document.xpath('//div[@class="no-records"]')) == 0)