Esempio n. 1
0
def main():
    config = load_config('config.json')

    timestamp = datetime.today().strftime('%d.%m.%Y - %H:%M')

    for entry in config['accounts_nr'].items():
        #Mit Konto verbinden
        try:
            f = FinTS3PinTanClient(entry[1]['blz'], entry[1]['nr'],
                                   entry[1]['pass'],
                                   'https://fints.ing-diba.de/fints/')

        except:
            #If connection not working wait a bit and try again. Better solution later...
            time.sleep(300)
            f = FinTS3PinTanClient(entry[1]['blz'], entry[1]['nr'],
                                   entry[1]['pass'],
                                   'https://fints.ing-diba.de/fints/')

        #Konten abrufen
        accountList = f.get_sepa_accounts()
        for account in accountList:
            #Wenn das Konto ein Depot ist:
            if account.accountnumber in config['depots_nr']:
                accountholdings = f.get_holdings(account)
                #Für jede Position die aktuellen Werte abrufen
                for accountholding in accountholdings:
                    save_db(timestamp, entry[1]['name'], accountholding.ISIN,
                            accountholding.name, accountholding.market_value,
                            accountholding.value_symbol, accountholding.pieces,
                            accountholding.total_value,
                            accountholding.acquisitionprice)
                    print(timestamp)

    #Add Bitcoin holdings
    lastBTCPrice = 0
    BTCAmount = float(config['BTCAmount'][0])
    try:
        r = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json',
                         timeout=10)
        BTCPrice = json.loads(r.text)['bpi']['EUR']['rate_float']
        lastBTCPrice = BTCPrice
        save_db(timestamp, 'Gemeinschaftskonto', 'BTC', 'BTC', BTCPrice, 'EUR',
                BTCAmount, BTCPrice * BTCAmount, 4400)

    except requests.exceptions.RequestException as e:
        save_db(timestamp, 'Gemeinschaftskonto', 'BTC', 'BTC', lastBTCPrice,
                'EUR', BTCAmount, lastBTCPrice * BTCAmount, 4400)
        print(e)
Esempio n. 2
0
def getBankTransactions(pw_dict):
    logging.basicConfig(level=logging.DEBUG)
    f = FinTS3PinTanClient(
        pw_dict['VB_BLZ'],  # Your bank's BLZ
        pw_dict['VB_ALIAS'],
        pw_dict['VB_PW'],
        pw_dict[
            'VB_ENDP']  # endpoint, e.g.: https://hbci-pintan.gad.de/cgi-bin/hbciservlet
    )

    accounts = f.get_sepa_accounts()
    print(accounts)

    statement = f.get_statement(accounts[0], date(2017, 10, 21), date.today())
    for t in statement:
        amount = t.data['amount'].amount
        vb_date = t.data['date']
        try:
            transaction_details = t.data['transaction_details']
        except:
            print("Hat kein transaction_details")
        parseIban(amount, vb_date, transaction_details)
    # The statement is a list of transaction objects as parsed by the mt940 parser, see
    # https://mt940.readthedocs.io/en/latest/mt940.html#mt940.models.Transaction
    # for documentation. Most information is contained in a dict accessible via their
    # ``data`` property

    # for retrieving the holdings of an account:
    holdings = f.get_holdings(accounts[0])
Esempio n. 3
0
def balance():
    user = request.headers.get('user')
    pin = request.headers.get('pin')
    iban_num = request.args.get('iban')
    bank = request.args.get('bank').lower()
    if iban.is_valid(iban_num) is not True:
        abort(403)
    if not user:
        abort(403)
    if not pin:
        abort(403)
    if not bank:
        abort(403)

    url = api_bank_list[bank]['url']
    blz = api_bank_list[bank]['blz']
    bic = api_bank_list[bank]['bic']
    f = FinTS3PinTanClient(
        blz,  # Your bank's BLZ
        user,
        pin,
        url)
    account_balance = f.get_balance(
        SEPAAccount(iban_num, bic, iban_num[12:], '', blz))
    result = {'balance': str(account_balance.amount.amount)}
    return jsonify(result)
Esempio n. 4
0
def process_account(account, accounts_api, budget_id, earliest):

    fints = FinTS3PinTanClient(
        account.fints_blz,
        account.fints_username,
        account.fints_password,
        account.fints_endpoint,
    )
    existing_ids = []

    if account.account_type == AccountType.HOLDING:
        transactions = retrieve_holdings(account, fints)
    else:
        transactions = retrieve_transactions(account,
                                             fints,
                                             start_date=earliest,
                                             end_date=TODAY)
        try:
            existing_transactions = accounts_api.get_transactions(
                budget_id, since_date=earliest)
            existing_ids = [
                t.import_id for t in existing_transactions.data.transactions
                if t.import_id
            ]
        except Exception:
            logger.warning("Could not retrieve existing transactions")

    return transactions, existing_ids
Esempio n. 5
0
def fetch_bank_data(num: int):
    fints = FinTS3PinTanClient(
        BANK_CODE,
        BANK_USERNAME,
        BANK_PIN,
        BANK_URL,
    )

    accounts_f = fints.get_sepa_accounts()
    for account_f in accounts_f:
        account = db.query(Account).filter(
            Account.iban == account_f.iban).first()
        if not account:
            account = Account(account_f.iban, account_f.bic)
            db.add(account)
            db.flush()

        balance_f = fints.get_balance(account_f)
        balance = Balance(balance_f.amount.amount, balance_f.amount.currency,
                          account)
        db.add(balance)
        db.flush()

        # statements = fints.get_statement(account, date.today() - timedelta(days=30), date.today())
        # pprint.pprint(balance)
        # for statement in statements:
        #     pprint.pprint(statement.data)

    db.commit()
Esempio n. 6
0
def get_transactions(bank_config):
    f = FinTS3PinTanClient(bank_config.blz,
                           bank_config.login,
                           bank_config.pin,
                           bank_config.fints_endpoint,
                           product_id='33D93BB1B017D422A87837C01')

    #    minimal_interactive_cli_bootstrap(f)
    #
    #    with f:
    #        # Since PSD2, a TAN might be needed for dialog initialization. Let's check if there is one required
    #        if f.init_tan_response:
    #            print("A TAN is required", f.init_tan_response.challenge)
    #            tan = input('Please enter TAN:')
    #            f.send_tan(f.init_tan_response, tan)
    #        # Fetch accounts
    accounts = f.get_sepa_accounts()

    # get transactions
    account = next(filter(lambda a: a.iban == bank_config.iban, accounts),
                   None)
    transactions = f.get_transactions(account,
                                      date.today() - timedelta(days=10))

    return list(
        map(
            lambda t: transform_fints_transaction(
                t.data, parse_paypal=bank_config.parse_paypal), transactions))
Esempio n. 7
0
def lambda_handler(event, context):

    logging.basicConfig(level=logging.ERROR)
    f = FinTS3PinTanClient(
        os.environ['BANKING_BLZ'],  # Your bank's BLZ
        os.environ['BANKING_USERNAME'],
        os.environ['BANKING_PIN'],
        os.environ['BANKING_ENDPOINT'])

    accounts = f.get_sepa_accounts()

    balancesAllAccounts = {}
    for account in accounts:
        # conversion to float is okay here because we're not going to do
        # floating point arithmetic with the values.
        balancesAllAccounts[account.iban] = float(
            f.get_balance(account).amount.amount)

    print(
        json.dumps({
            'event': "bookkeeper:balances:read",
            'balances': balancesAllAccounts,
            'created_at': datetime.datetime.utcnow().isoformat() +
            'Z'  # this is ridiculous but Python violates ISO 8601
        }))

    return None
Esempio n. 8
0
def test_consors():
    """
    Ausprobieren, was man von Consors ermitteln kann
    :return:
    """
    #logging.basicConfig(level=logging.DEBUG)
    try:
        f = FinTS3PinTanClient(
            '76030080',  # Your bank's BLZ
            '970715353',  # Your login name
            getpass.getpass('PIN:'),  # Your banking PIN
            "https://brokerage-hbci.consorsbank.de/hbci",
            #'https://hbci-pintan.gad.de/cgi-bin/hbciservlet',
            product_id=None)
        print(f)
    except Exception as ex:
        print(str(ex))

    #minimal_interactive_cli_bootstrap( f )

    #with f:
    # Since PSD2, a TAN might be needed for dialog initialization. Let's check if there is one required
    if f.init_tan_response:
        print("A TAN is required", f.init_tan_response.challenge)
        tan = input('Please enter TAN:')
        f.send_tan(f.init_tan_response, tan)

        # Fetch accounts
    accounts = f.get_sepa_accounts()
    print(accounts)
Esempio n. 9
0
def fints_client(fints_server):
    return FinTS3PinTanClient(
        '12345678',
        'test1',
        '1234',
        fints_server
    )
Esempio n. 10
0
def create_fints_client():
    settings = pyramid.threadlocal.get_current_registry().settings
    return FinTS3PinTanClient(settings.get('banking.blz'),
                              settings.get('banking.kto'),
                              settings.get('banking.pin'),
                              settings.get('banking.url'),
                              mode=FinTSClientMode.INTERACTIVE,
                              product_id=settings.get('banking.product_id'))
Esempio n. 11
0
def setup_platform(hass, config, add_devices, discovery_info=None):
    """Setup the sensor platform."""
    from fints.client import FinTS3PinTanClient
    client = FinTS3PinTanClient(str(config['blz']), str(config['username']),
                                str(config['pin']), str(config['endpoint']))
    add_devices([
        FintsSensor(client, account) for account in client.get_sepa_accounts()
    ])
Esempio n. 12
0
    def fints_client(self, fints_login, form=None):
        fints_user_login, _ignore = fints_login.user_login.get_or_create(user=self.request.user)
        if form:
            fints_user_login.login_name = form.cleaned_data['login_name']
            if form.cleaned_data['pin'] == PIN_CACHED_SENTINEL:
                pin = self.request.securebox[_cache_label(fints_login)]
            else:
                pin = form.cleaned_data['pin']
        else:
            pin = None

        client = FinTS3PinTanClient(
            fints_login.blz,
            fints_user_login.login_name,
            pin,
            fints_login.fints_url,
            product_id="F41CDA6B1F8E0DADA0DDA29FD",
            from_data=fints_user_login.fints_client_data,
            mode=FinTSClientMode.INTERACTIVE,
        )
        client.add_response_callback(self.fints_callback)

        # FIXME HACK HACK HACK The python-fints API with regards to TAN media is not very useful yet
        # Circumvent it here

        if fints_user_login.selected_tan_medium:
            fake_tan_medium = TANMedia5(tan_medium_name = fints_user_login.selected_tan_medium)
            client.set_tan_medium(fake_tan_medium)

        try:
            yield client
            pin_correct = True

        except FinTSClientPINError:
            # PIN wrong, clear cached PIN, indicate error
            self.request.securebox.delete_value(_cache_label(fints_login))
            if form:
                form.add_error(None, _("Can't establish FinTS dialog: Username/PIN wrong?"))
            pin_correct = False

        if pin_correct:
            client.set_tan_medium(None)  # FIXME HACK HACK HACK
            fints_user_login.fints_client_data = client.deconstruct(including_private=True)
            fints_user_login.save(update_fields=['login_name', 'fints_client_data'])

            if form:
                if form.cleaned_data['store_pin'] == '1':
                    storage = Storage.TRANSIENT_ONLY
                elif form.cleaned_data['store_pin'] == '2':
                    storage = Storage.PERMANENT_ONLY
                else:
                    storage = None

                if storage:
                    if form.cleaned_data['pin'] != PIN_CACHED_SENTINEL:
                        self.request.securebox.store_value(_cache_label(fints_login), pin, storage=storage)
                else:
                    self.request.securebox.delete_value(_cache_label(fints_login))
Esempio n. 13
0
 def __init__(self, blz, account_number, pin, endpoint_url):
     self.blz = blz
     self.account_number = account_number
     self.pin = pin
     self.endpoint_url = endpoint_url
     logging.basicConfig(level=logging.ERROR)
     self.f = FinTS3PinTanClient(self.blz, self.account_number, self.pin,
                                 self.endpoint_url)
     self.accounts = self.f.get_sepa_accounts()
Esempio n. 14
0
def get_client(config):
	login = config["fints"]
	client = FinTS3PinTanClient(
		login["blz"],
		login["user"],
		login["pin"],
		login["url"]
	)
	return client
Esempio n. 15
0
    def client(self):
        """Get the client object.

        As the fints library is stateless, there is not benefit in caching
        the client objects. If that ever changes, consider caching the client
        object and also think about potential concurrency problems.
        """
        from fints.client import FinTS3PinTanClient
        return FinTS3PinTanClient(self._credentials.blz,
                                  self._credentials.login,
                                  self._credentials.pin, self._credentials.url)
Esempio n. 16
0
def GetTransactions(blz, username, password, api, acc):
    # Init fints Class
    fints = FinTS3PinTanClient(
        blz,
        username,
        password,
        api
    )
    accounts = fints.get_sepa_accounts()
    account = next(account for account in accounts if account.accountnumber == acc)
    transactions = fints.get_transactions(account, startDate, endDate)
    return transactions
Esempio n. 17
0
    def test_account(iban, url, login, password):
        client = FinTS3PinTanClient(iban.bank_code, login, password,
                                    url.geturl())

        # find account we are looking for
        sepa_accounts = client.get_sepa_accounts()

        try:
            sepa_account = [a for a in sepa_accounts if a.iban == iban][0]
        except IndexError:
            # remote sepa accounts don't include requested one
            logger.exception("Account with iban %s not found" % iban)
Esempio n. 18
0
    def check(self):
        """
        Fetches the last transactions from the account and checks if they are unknown.
        """

        fints_client = FinTS3PinTanClient(self.blz, self.user, self.password,
                                          self.url)
        accounts = fints_client.get_sepa_accounts()

        # Extract account.
        account = None
        for temp_account in accounts:
            if temp_account.iban.upper() == self.iban:
                account = temp_account
                break
        if account is None:
            raise ValueError("Account '%s' with IBAN '%s' not found." %
                             (self.name, self.iban))

        # Get all transaction from the last 5 days.
        start_date = datetime.date.today() - datetime.timedelta(
            days=self.delta_days)
        transactions = fints_client.get_transactions(account, start_date,
                                                     datetime.date.today())

        for transaction in transactions:
            currency = str(transaction.data["currency"])
            amount = float(transaction.data["amount"].amount)
            date = transaction.data["date"]
            if not isinstance(date, datetime.date):
                raise ValueError("Account '%s' with IBAN '%s' contains " %
                                 (self.name, self.iban) + "illegal data")
            subject = str(transaction.data["purpose"])
            rcpt_name = str(transaction.data["applicant_name"])
            rcpt_iban = str(transaction.data["applicant_iban"])
            obj = SimpleTransaction(rcpt_name, rcpt_iban, amount, currency,
                                    date, subject)

            if obj in self.triggered_transactions:
                continue

            if not self.rules.check_allowed(obj):

                logging.debug(
                    "Transaction '%s' not whitelisted. Triggering event." %
                    str(obj))

                # Trigger events.
                for event in self.events:
                    event.trigger(self, obj)

                self.triggered_transactions.add(obj)
Esempio n. 19
0
def bank_accounts_import():
    form = BankAccountActivitiesImportForm()
    form.account.choices = [(acc.id, acc.name) for acc in BankAccount.q.all()]
    (transactions, old_transactions) = ([], [])
    if form.validate_on_submit():
        # login with fints
        bank_account = BankAccount.q.get(form.account.data)
        process = True
        try:
            fints = FinTS3PinTanClient(bank_account.routing_number,
                                       form.user.data, form.pin.data,
                                       bank_account.fints_endpoint)

            acc = next((a for a in fints.get_sepa_accounts()
                        if a.iban == bank_account.iban), None)
            if acc is None:
                raise KeyError('BankAccount with IBAN {} not found.'.format(
                    bank_account.iban))
            start_date = map_or_default(bank_account.last_updated_at,
                                        datetime.date, date(2018, 1, 1))
            statement = fints.get_statement(acc, start_date, date.today())
            flash("Transaktionen vom {} bis {}.".format(
                start_date, date.today()))
        except FinTSDialogError:
            flash(u"Ungültige FinTS-Logindaten.", 'error')
            process = False
        except KeyError:
            flash(
                u'Das gewünschte Konto kann mit diesem Online-Banking-Zugang\
                    nicht erreicht werden.', 'error')
            process = False

        if process:
            (transactions, old_transactions) = finance.process_transactions(
                bank_account, statement)
        else:
            (transactions, old_transactions) = ([], [])

        if process and form.do_import.data is True:
            # save transactions to database
            session.add_all(transactions)
            session.commit()
            flash(u'Bankkontobewegungen wurden importiert.')
            return redirect(
                url_for(".accounts_show", account_id=bank_account.account_id))

    return render_template('finance/bank_accounts_import.html',
                           form=form,
                           transactions=transactions,
                           old_transactions=old_transactions)
Esempio n. 20
0
def test_fints(config):
    click.echo('Creating FinTS client...')
    f = FinTS3PinTanClient(
        config['fints']['blz'],
        config['fints']['username'],
        get_pin(config),
        config['fints']['endpoint'],
    )

    click.echo('Fetching SEPA account list...')
    accounts = f.get_sepa_accounts()
    click.echo('Looking for correct SEPA account...')
    accounts_matching = [
        a for a in accounts if a.iban == config['fints']['iban']
    ]
    if not accounts_matching:
        click.echo(
            click.style('The specified SEPA account %s could not be found.' %
                        config['fints']['iban'],
                        fg='red'))
        click.echo('Only the following SEPA accounts were detected:')
        click.echo(', '.join([a.iban for a in accounts]))
        sys.exit(1)
    elif len(accounts_matching) > 1:
        click.echo(
            click.style(
                'Multiple SEPA accounts match the given IBAN. We currently can not handle this '
                'situation.',
                fg='red'))
        click.echo('Only the following SEPA accounts were detected:')
        click.echo(', '.join([a.iban for a in accounts]))
        sys.exit(1)

    account = accounts_matching[0]
    click.echo(click.style('Found matching SEPA account.', fg='green'))

    click.echo('Fetching statement of the last 30 days...')
    statement = f.get_statement(account,
                                date.today() - timedelta(days=30),
                                date.today())
    if statement:
        click.echo(
            click.style('Found %d transactions. The last one is:' %
                        len(statement),
                        fg='green'))
        pprint.pprint(statement[-1].data)
    else:
        click.echo(
            'No recent transaction found. Please check if this is correct.')
Esempio n. 21
0
def main():
    print("Start BalanceFetcher ", datetime.datetime.now())
    print("==============================================")
    logging.basicConfig()
    get_conf()
    loginAccounts = get_accounts()
    for login in loginAccounts:
        f = FinTS3PinTanClient(login['bankcode'], login['banklogin'],
                               login['bankpin'], login['bankurl'])
        userid = get_userid(f)
        accounts = f.get_sepa_accounts()
        for account in accounts:
            get_transactions(account, f, userid)
            balance = get_balance(account, f)
            save_balance(balance, account, userid)
    print("==============================================")
Esempio n. 22
0
    def client(self):
        """Get the client object.

        As the fints library is stateless, there is not benefit in caching
        the client objects. If that ever changes, consider caching the client
        object and also think about potential concurrency problems.

        Note: As of version 2, the fints library is not stateless anymore.
        This should be considered when reworking this integration.
        """

        return FinTS3PinTanClient(
            self._credentials.blz,
            self._credentials.login,
            self._credentials.pin,
            self._credentials.url,
        )
Esempio n. 23
0
def test_pin_locked(fints_server):
    client = FinTS3PinTanClient(
        '12345678',
        'test1',
        '3938',
        fints_server,
    )
    with pytest.raises(FinTSClientTemporaryAuthError):
        with client:
            pass

    assert client.pin.blocked

    with pytest.raises(Exception):
        with client:
            pass

    with pytest.raises(Exception, match="Refusing"):
        str(client.pin)
Esempio n. 24
0
def test_pin_wrong(fints_server):
    client = FinTS3PinTanClient(
        '12345678',
        'test1',
        '99999',
        fints_server,
    )
    with pytest.raises(FinTSClientPINError):
        with client:
            pass

    assert client.pin.blocked

    with pytest.raises(Exception):
        with client:
            pass

    with pytest.raises(Exception, match="Refusing"):
        str(client.pin)
Esempio n. 25
0
def test_resume(fints_client, fints_server):
    with fints_client:
        system_id = fints_client.system_id
        dialog_id = fints_client._standing_dialog.dialog_id
        assert fints_client.bpd_version == 78

        d_data = fints_client.pause_dialog()

    c_data = fints_client.deconstruct(including_private=True)

    FinTS3PinTanClient('12345678',
                       'test1',
                       '1234',
                       fints_server,
                       from_data=c_data)
    assert system_id == fints_client.system_id

    with fints_client.resume_dialog(d_data):
        assert dialog_id == fints_client._standing_dialog.dialog_id
        assert fints_client.bpd_version == 78
Esempio n. 26
0
    def __init_fints_connection(self):
        """Private: Initialise new fints connection.

        :return: None
        """
        self.interactive.show_progress_realtime(_("Initialise connection"),
                                                10,
                                                reload=False)
        try:
            if not hasattr(self, 'fints_connection'):
                password = self.fints_login.get_password('fints_password')
                self.fints_connection = FinTS3PinTanClient(
                    self.fints_login.blz,
                    self.fints_login.fints_login,
                    password,
                    self.fints_login.fints_url,
                    mode=FinTSClientMode.INTERACTIVE)
        except Exception as e:
            frappe.throw(
                _("Could not conntect to fints server with error<br>{0}").
                format(e))
Esempio n. 27
0
    def retrieveAndSave(self):
        client = FinTS3PinTanClient(
            self.config["fints"]["blz"],  # Your bank's BLZ
            self.config["fints"]["account"],  # your account number
            self.config["fints"]["password"],
            # e.g. 'https://fints.ing-diba.de/fints/'
            self.config["fints"]["endpoint"])

        retriever = TRetriever(client, self.config["fints"]["selectedAccount"])
        converter = CsvConverter(self.config["fints"]["csv_separator"])

        csv_output = "\n".join(
            map(
                lambda transaction: converter.convert(transaction),
                retriever.get_hbci_transactions(self.config["fints"]["start"],
                                                Date.today())))

        with open(self.config["files"]["csv_file"], 'w') as f:
            f.write(converter.get_headline())
            f.write("\n")
            f.write(csv_output)
Esempio n. 28
0
def api_transactions():
    user = request.headers.get('user')
    pin = request.headers.get('pin')
    iban_num = request.args.get('iban')
    bank = request.args.get('bank').lower()
    if iban.is_valid(iban_num) is not True:
        abort(403)
    if not user:
        abort(403)
    if not pin:
        abort(403)
    if not bank:
        abort(403)
    url = api_bank_list[bank]['url']
    blz = api_bank_list[bank]['blz']
    bic = api_bank_list[bank]['bic']
    f = FinTS3PinTanClient(
        blz,  # Your bank's BLZ
        user,
        pin,
        url)
    today = date.today()
    first_day = today.replace(day=1)
    transaction = f.get_transactions(
        SEPAAccount(iban_num, bic, iban_num[12:], '', blz), first_day, today)
    result = []
    for transaction in reversed(transaction):
        result.append({
            'date': transaction.data['date'],
            'amount': str(transaction.data['amount'].amount),
            'applicant_name': transaction.data['applicant_name'],
            'purpose': transaction.data['purpose'],
            'posting_text': transaction.data['posting_text']
        })
    f.deconstruct()
    return jsonify(result)
Esempio n. 29
0
def import_fin_ts():
    logging.basicConfig(level=logging.WARN)
    f = FinTS3PinTanClient(os.environ.get('CSA_ACCOUNT_BLZ'),
                           os.environ.get('CSA_ACCOUNT_USERNAME'),
                           os.environ.get('CSA_ACCOUNT_PASSWORD'),
                           'https://hbci-pintan.gad.de/cgi-bin/hbciservlet',
                           mode=FinTSClientMode.INTERACTIVE,
                           product_id=os.environ.get('CSA_HBCI_PRODUCT_ID',
                                                     None))
    f.fetch_tan_mechanisms()

    try:
        with f:
            m = f.get_tan_media()
        f.set_tan_medium(m[1][0])
    except FinTSUnsupportedOperation:
        print("TAN Mediums not supported.")

    with f:
        if f.init_tan_response:
            print(f.init_tan_response.challenge)
            tan = input('Please enter TAN:')
            f.send_tan(f.init_tan_response, tan)

        res = get_new_transactions(f)

        while isinstance(res, NeedTANResponse):
            print(res.challenge)

            tan = input('Please enter TAN:')
            res = f.send_tan(res, tan)

        print("No more challenges to complete")

        for transaction in res:
            save_transaction(transaction)
Esempio n. 30
0
def upload_transactions(config, days=30, ignore=None):
    ignore = ignore or []
    ignore_patterns = []
    for i in ignore:
        try:
            ignore_patterns.append(re.compile(i))
        except re.error as e:
            click.echo(
                click.style('Not a valid regular expression: %s' % i,
                            fg='red'))
            click.echo(
                click.style('"%s" at position %d' % (e.msg, e.pos), fg='red'))

    click.echo('Creating FinTS client...')
    f = FinTS3PinTanClient(
        config['fints']['blz'],
        config['fints']['username'],
        get_pin(config),
        config['fints']['endpoint'],
    )

    click.echo('Fetching SEPA account list...')
    accounts = f.get_sepa_accounts()
    click.echo('Looking for correct SEPA account...')
    accounts_matching = [
        a for a in accounts if a.iban == config['fints']['iban']
    ]
    if not accounts_matching:
        click.echo(
            click.style('The specified SEPA account %s could not be found.' %
                        config['fints']['iban'],
                        fg='red'))
        click.echo('Only the following SEPA accounts were detected:')
        click.echo(', '.join([a.iban for a in accounts]))
        sys.exit(1)
    elif len(accounts_matching) > 1:
        click.echo(
            click.style(
                'Multiple SEPA accounts match the given IBAN. We currently can not handle this '
                'situation.',
                fg='red'))
        click.echo('Only the following SEPA accounts were detected:')
        click.echo(', '.join([a.iban for a in accounts]))
        sys.exit(1)

    account = accounts_matching[0]
    click.echo(click.style('Found matching SEPA account.', fg='green'))

    click.echo('Fetching statement of the last %d days...' % days)
    statement = f.get_statement(account,
                                date.today() - timedelta(days=days),
                                date.today())
    if statement:
        click.echo(
            click.style('Found %d transactions.' % len(statement), fg='green'))
        click.echo('Parsing...')

        transactions = []
        ignored = 0
        for transaction in statement:
            reference = ' '.join(
                transaction.data.get(t)
                for t in ('posting_text', 'purpose', 'bank_reference',
                          'customer_reference') if transaction.data.get(t))
            payer = {
                'name': transaction.data.get('applicant_name', ''),
                'iban': transaction.data.get('applicant_iban', ''),
            }
            eref = transaction.data.get('end_to_end_reference', '')

            ignore = False
            for i in ignore_patterns:
                if i.search(reference):
                    ignore = True
                    ignored += 1
                    break

            if not ignore:
                transactions.append({
                    'amount':
                    str(transaction.data['amount'].amount),
                    'reference':
                    reference + (' EREF: {}'.format(eref) if eref else ''),
                    'payer': ((payer.get('name') or '') + ' - ' +
                              (payer.get('iban') or '')).strip(),
                    'date':
                    transaction.data['date'].isoformat(),
                })

        if ignored > 0:
            click.echo(
                click.style('Ignored %d transactions.' % ignored, fg='blue'))
        payload = {'event': None, 'transactions': transactions}

        click.echo('Uploading...')
        try:
            r = requests.post(get_endpoint(config),
                              headers={
                                  'Authorization':
                                  'Token {}'.format(config['pretix']['key'])
                              },
                              json=payload)
            if r.status_code == 201:
                click.echo(click.style('Job uploaded.', fg='green'))
            else:
                click.echo(
                    click.style('Invalid response code: %d' % r.status_code,
                                fg='red'))
                click.echo(r.text)
                sys.exit(2)
        except (RequestException, OSError) as e:
            click.echo(click.style('Connection error: %s' % str(e), fg='red'))
            sys.exit(2)
        except ValueError as e:
            click.echo(
                click.style('Could not read response: %s' % str(e), fg='red'))
            sys.exit(2)
    else:
        click.echo('No recent transaction found.')