Exemplo n.º 1
0
 def create_activity(self, amount):
     return BankAccountActivity(
         bank_account=self.bank_account,
         reference='Reference',
         other_name='John Doe',
         other_account_number='0123456789',
         other_routing_number='01245',
         amount=amount,
         imported_at=datetime.utcnow(),
         posted_on=datetime.utcnow().date(),
         valid_on=datetime.utcnow().date(),
     )
Exemplo n.º 2
0
def process_transactions(bank_account, statement):
    transactions = []  # new transactions which would be imported
    old_transactions = []  # transactions which are already imported
    doubtful_transactions = [] # transactions which may be changed by the bank because they are to new

    for transaction in statement:
        iban = transaction.data.get('applicant_iban', '')
        if iban is None: iban = ''
        bic = transaction.data.get('applicant_bin', '')
        if bic is None: bic = ''
        other_name = transaction.data.get('applicant_name', '')
        if other_name is None: other_name = ''
        purpose = transaction.data.get('purpose', '')
        if purpose is None: purpose = ''
        if 'end_to_end_reference' in transaction.data and \
                transaction.data['end_to_end_reference'] is not None:
            purpose = purpose + ' EREF+' + transaction.data['end_to_end_reference']
        new_activity = BankAccountActivity(
            bank_account_id=bank_account.id,
            amount=transaction.data['amount'].amount,
            reference=purpose,
            other_account_number=iban,
            other_routing_number=bic,
            other_name=other_name,
            imported_at=session.utcnow(),
            posted_on=transaction.data['guessed_entry_date'],
            valid_on=transaction.data['date'],
        )
        if new_activity.posted_on >= date.today():
            doubtful_transactions.append(new_activity)
        elif BankAccountActivity.q.filter(and_(
                BankAccountActivity.bank_account_id ==
                new_activity.bank_account_id,
                BankAccountActivity.amount == new_activity.amount,
                BankAccountActivity.reference == new_activity.reference,
                BankAccountActivity.other_account_number ==
                new_activity.other_account_number,
                BankAccountActivity.other_routing_number ==
                new_activity.other_routing_number,
                BankAccountActivity.other_name == new_activity.other_name,
                BankAccountActivity.posted_on == new_activity.posted_on,
                BankAccountActivity.valid_on == new_activity.valid_on
        )).first() is None:
            transactions.append(new_activity)
        else:
            old_transactions.append(new_activity)

    return (transactions, old_transactions, doubtful_transactions)
Exemplo n.º 3
0
def process_transactions(bank_account, statement):
    transactions = []  # new transactions which would be imported
    old_transactions = []  # transactions which are already imported

    for transaction in statement:
        iban = transaction.data['applicant_iban'] if \
            transaction.data['applicant_iban'] is not None else ''
        bic = transaction.data['applicant_bin'] if \
            transaction.data['applicant_bin'] is not None else ''
        other_name = transaction.data['applicant_name'] if \
            transaction.data['applicant_name'] is not None else ''
        new_activity = BankAccountActivity(
            bank_account_id=bank_account.id,
            amount=int(transaction.data['amount'].amount * 100),
            reference=transaction.data['purpose'],
            original_reference=transaction.data['purpose'],
            other_account_number=iban,
            other_routing_number=bic,
            other_name=other_name,
            imported_at=session.utcnow(),
            posted_on=transaction.data['entry_date'],
            valid_on=transaction.data['date'],
        )
        if BankAccountActivity.q.filter(
                and_(
                    BankAccountActivity.bank_account_id
                    == new_activity.bank_account_id, BankAccountActivity.amount
                    == new_activity.amount, BankAccountActivity.reference
                    == new_activity.reference,
                    BankAccountActivity.other_account_number
                    == new_activity.other_account_number,
                    BankAccountActivity.other_routing_number
                    == new_activity.other_routing_number,
                    BankAccountActivity.other_name == new_activity.other_name,
                    BankAccountActivity.posted_on == new_activity.posted_on,
                    BankAccountActivity.valid_on
                    == new_activity.valid_on)).first() is None:
            transactions.append(new_activity)
        else:
            old_transactions.append(new_activity)

    return (transactions, old_transactions)
Exemplo n.º 4
0
def import_bank_account_activities_csv(csv_file, expected_balance,
                                       imported_at=None):
    """
    Import bank account activities from a MT940 CSV file into the database.

    The new activities are merged with the activities that are already saved to
    the database.
    :param csv_file:
    :param expected_balance:
    :param imported_at:
    :return:
    """

    if imported_at is None:
        imported_at = session.utcnow()

    # Convert to MT940Record and enumerate
    reader = csv.reader(csv_file, dialect=MT940Dialect)
    records = enumerate((MT940Record._make(r) for r in reader), 1)
    try:
        # Skip first record (header)
        next(records)
        activities = tuple(
            process_record(index, record, imported_at=imported_at)
            for index, record in records)
    except StopIteration:
        raise CSVImportError(gettext(u"No data present."))
    except csv.Error as e:
        raise CSVImportError(gettext(u"Could not read CSV."), e)
    if not activities:
        raise CSVImportError(gettext(u"No data present."))
    if not is_ordered((a[8] for a in activities), operator.ge):
        raise CSVImportError(gettext(
            u"Transaction are not sorted according to transaction date in "
            u"descending order."))
    first_posted_on = activities[-1][8]
    balance = session.session.query(
        func.coalesce(func.sum(BankAccountActivity.amount), 0)
    ).filter(
        BankAccountActivity.posted_on < first_posted_on
    ).scalar()
    a = tuple(session.session.query(
        BankAccountActivity.amount, BankAccountActivity.bank_account_id,
        BankAccountActivity.reference, BankAccountActivity.reference,
        BankAccountActivity.other_account_number,
        BankAccountActivity.other_routing_number,
        BankAccountActivity.other_name, BankAccountActivity.imported_at,
        BankAccountActivity.posted_on, BankAccountActivity.valid_on
    ).filter(
        BankAccountActivity.posted_on >= first_posted_on)
    )
    b = tuple(reversed(activities))
    matcher = difflib.SequenceMatcher(a=a, b=b)
    for tag, i1, i2, j1, j2 in matcher.get_opcodes():
        if 'equal' == tag:
            continue
        elif 'insert' == tag:
            balance += sum(a[0] for a in islice(activities, j1, j2))
            session.session.add_all(
                BankAccountActivity(
                    amount=e[0], bank_account_id=e[1], reference=e[3],
                    other_account_number=e[4],
                    other_routing_number=e[5], other_name=e[6],
                    imported_at=e[7], posted_on=e[8], valid_on=e[9]
                ) for e in islice(activities, j1, j2)
            )
        elif 'delete' == tag:
            continue
        elif 'replace' == tag:
            raise CSVImportError(
                gettext(u"Import conflict:\n"
                        u"Database bank account activities:\n{0}\n"
                        u"File bank account activities:\n{1}").format(
                    u'\n'.join(str(x) for x in islice(activities, i1, i2)),
                    u'\n'.join(str(x) for x in islice(activities, j1, j2))))
        else:
            raise AssertionError()
    if balance != expected_balance:
        message = gettext(u"Balance after does not equal expected balance: "
                          u"{0} != {1}.")
        raise CSVImportError(message.format(balance, expected_balance))
Exemplo n.º 5
0
def bank_account_activities_edit(activity_id):
    activity = BankAccountActivity.get(activity_id)

    if activity is None:
        flash(f"Bankbewegung mit ID {activity_id} existiert nicht!", 'error')
        abort(404)

    if activity.transaction_id is not None:
        form = BankAccountActivityReadForm(
            obj=activity, bank_account_name=activity.bank_account.name)

        if activity.transaction_id:
            flash(f"Bankbewegung ist bereits zugewiesen!", 'warning')

        form_args = {
            'form': form,
            'show_submit': False,
            'show_cancel': False,
        }

        return render_template('generic_form.html',
                               page_title="Bankbewegung",
                               form_args=form_args,
                               form=form)

    else:
        form = BankAccountActivityEditForm(
            obj=activity,
            bank_account_name=activity.bank_account.name,
            description=activity.reference)

        if form.validate_on_submit():
            debit_account = Account.q.filter(
                Account.id == form.account_id.data).one()
            credit_account = activity.bank_account.account

            transaction = finance.simple_transaction(
                description=form.description.data,
                debit_account=debit_account,
                credit_account=credit_account,
                amount=activity.amount,
                author=current_user,
                valid_on=activity.valid_on,
                confirmed=current_user.member_of(config.treasurer_group))
            activity.split = next(split for split in transaction.splits
                                  if split.account_id == credit_account.id)
            session.add(activity)

            end_payment_in_default_memberships(current_user)

            session.commit()

            flash("Transaktion erfolgreich erstellt.", 'success')

            return redirect(url_for('.bank_accounts_list'))

        form_args = {
            'form': form,
            'cancel_to': url_for('.bank_accounts_list'),
            'submit_text': 'Zuweisen',
        }

        return render_template('generic_form.html',
                               page_title="Bankbewegung zuweisen",
                               form_args=form_args,
                               form=form)