Exemple #1
0
def transactions(request):
    entries = reversed(list(ledger_api.Journal(request.user.ledger_path.path)))

    payee = request.GET.get('payee')
    note = request.GET.get('note')

    rule = Rule(payee=payee, note=note)

    entries = (entry for entry in entries
               if check_rule(entry, rule) is not None)

    count = request.GET.get('count')
    if count is not None:
        try:
            count = int(count)
        except ValueError:
            return JsonResponse(
                {'error': {
                    'count': count,
                }},
                status=422,
            )
        entries = itertools.islice(entries, count)

    return JsonResponse({'entries': list(entries)})
Exemple #2
0
 def get_form_kwargs(self):
     kwargs = super().get_form_kwargs()
     ledger_path = self.request.user.ledger_path.path
     journal = ledger_api.Journal(ledger_path)
     kwargs['accounts'] = journal.accounts()
     kwargs['payees'] = journal.payees()
     kwargs['user'] = self.request.user
     return kwargs
Exemple #3
0
def add_ledger_entry_v1(
    user,
    account_from,
    account_to,
    payee,
    amount,
    currency=None,
    date=None,
    skip_rules=False,
):
    ledger_path = user.ledger_path.path
    if not skip_rules:
        replacement_rules = (Rule.objects.filter(user=user).order_by(
            Length('payee').desc()))
        for rule in replacement_rules:
            try:
                match = re.fullmatch(rule.payee, payee)
            except re.error:
                pass
            else:
                if match:
                    payee = rule.new_payee or payee
                    account_to = rule.account or account_to
                    break

    amount = amount.replace(",", ".").strip()

    if date is None:
        date = datetime.now().strftime("%F")

    entry = ledger_api.Entry(
        payee=payee,
        accounts=[((account_to, amount, currency) if currency is not None else
                   (account_to, amount)), (account_from, )],
        date=date,
    )
    old, new = ledger_api.Journal(ledger_path).append(entry)
    Undo.objects.update_or_create(
        pk=user.id,
        defaults={
            'last_entry': entry,
            'old_position': old,
            'new_position': new,
        },
    )
    return entry
Exemple #4
0
def register(request):
    ledger_path = request.user.ledger_path.path

    csv = ledger_api.Journal(ledger_path).csv()
    df = pd.read_csv(
        csv,
        header=None,
        names=[
            'date',
            'code',
            'payee',
            'account',
            'currency',
            'amount',
            'reconciled',
            'note',
        ],
        usecols=['date', 'payee', 'account', 'currency', 'amount'],
    )

    search = request.GET.get('filter', '')
    if search:
        df = df[df['account'].str.contains(search, case=False)]

    if len(df) == 0:
        return render(
            request,
            'ledger_ui/register.html',
            {
                'transactions': [],
                'filter': search,
            },
        )

    transactions = df
    transactions['total'] = transactions['amount'].cumsum().round(2)

    return render(
        request,
        'ledger_ui/register.html',
        {
            'transactions': transactions,
            'filter': search,
            'currency_count': transactions['currency'].unique().size,
        },
    )
Exemple #5
0
def balance(request):
    ledger_path = request.user.ledger_path.path

    csv = ledger_api.Journal(ledger_path).csv()
    df = pd.read_csv(
        csv,
        header=None,
        names=[
            'date',
            'code',
            'payee',
            'account',
            'currency',
            'amount',
            'reconciled',
            'note',
        ],
        usecols=['account', 'currency', 'amount'],
    )

    search = request.GET.get('filter', '')
    if search:
        df = df[df['account'].str.contains(search, case=False)]

    if len(df) == 0:
        return render(
            request,
            'ledger_ui/balance.html',
            {
                'accounts': [],
                'filter': search,
            },
        )

    balance = df.groupby(['account', 'currency']).sum()
    balance['amount'] = balance['amount'].round(2)

    return render(
        request,
        'ledger_ui/balance.html',
        {
            'accounts': balance.to_dict()['amount'],
            'filter': search,
        },
    )
Exemple #6
0
def submit_as_json(request):
    params = json.loads(request.body)
    ledger_data = {
        'payee': params['payee'],
        'date': params.get('date',
                           datetime.now().strftime("%F")),
        'accounts': params['accounts'],
        'note': params.get('note', ''),
    }

    if not params.get('skip_rules', False):
        apply_rules(ledger_data, request.user)
    normalize_data(ledger_data)

    entry = ledger_api.Entry(**ledger_data)

    old, new = ledger_api.Journal(request.user.ledger_path.path).append(entry)
    Undo.objects.update_or_create(
        pk=request.user.id,
        defaults={
            'last_entry': entry,
            'old_position': old,
            'new_position': new,
        },
    )

    response_data = {
        'payee':
        entry.payee,
        'date':
        entry.date,
        'accounts':
        [list(account._asdict().values()) for account in entry.accounts],
    }
    optionals = {}
    if entry.note:
        optionals['note'] = entry.note
    response_data.update(optionals)

    return JsonResponse(response_data, status=201)
Exemple #7
0
def charts(request):
    ledger_path = request.user.ledger_path.path

    csv = ledger_api.Journal(ledger_path).csv(
        '--monthly',
        '-X', settings.LEDGER_DEFAULT_CURRENCY,
    )
    df = pd.read_csv(
        csv,
        header=None,
        names=[
            'date', 'code', 'payee', 'account', 'currency', 'amount',
            'reconciled', 'note',
        ],
        usecols=['date', 'payee', 'account', 'amount'],
        parse_dates=['date'],
    )
    if len(df) == 0:
        return render(
            request,
            'ledger_ui/charts.html',
        )

    assets = df[df['account'].str.contains("^Assets:|^Liabilities:")]
    income = df[df['account'].str.contains("^Income:")]

    account_filter = request.GET.get('account_filter', '')
    if account_filter:
        expenses = df[df['account'].str.contains(
            account_filter,
            case=False,
        )].copy()
    else:
        expenses = df[df['account'].str.contains("^Expenses:")].copy()

    date_grouped_assets = assets[['date', 'amount']].groupby('date').sum()
    date_grouped_expenses = expenses[['date', 'amount']].groupby('date').sum()
    date_grouped_income = income[['date', 'amount']].groupby('date').sum()

    date_range = pd.date_range(df['date'].min(), df['date'].max(), freq='MS')
    date_grouped_assets = date_grouped_assets.reindex(date_range, fill_value=0)
    date_grouped_expenses = date_grouped_expenses.reindex(date_range, fill_value=0)
    date_grouped_income = date_grouped_income.reindex(date_range, fill_value=0)

    date_grouped_assets['amount'] = date_grouped_assets['amount'].cumsum()
    expenses['date'] = expenses['date'].dt.strftime("%Y-%m")

    return render(
        request,
        'ledger_ui/charts.html',
        {
            'dates': {'data': date_range.strftime('%Y-%m').to_series().to_list()},
            'expenses_totals': date_grouped_expenses['amount'].round(2).to_json(),
            'income_totals': (-date_grouped_income['amount']).round(2).to_json(),
            'expenses': (
                expenses[['date', 'account', 'amount']].to_json(
                    orient='table', index=False)),
            'assets': date_grouped_assets['amount'].round(2).to_json(),
            'account_filter': account_filter,
        },
    )
Exemple #8
0
def journal(request):
    if request.method == 'POST':
        if request.POST.get('revert'):
            undo = get_object_or_404(Undo, pk=request.user)

            ledger_path = request.user.ledger_path.path
            journal = ledger_api.Journal(ledger_path, undo)

            try:
                journal.revert()
            except journal.CannotRevert:
                return render(
                    request,
                    'ledger_ui/error/cannot_revert.html',
                    status=409,
                )

    entries = list(ledger_api.Journal(request.user.ledger_path.path))


    entry_filter = request.GET.get('filter', '')
    if entry_filter:
        entries = [
            entry
            for entry in entries
            if entry_filter.lower() in entry['body'].lower()
        ]

    count = request.GET.get('count', settings.LEDGER_ENTRY_COUNT)
    try:
        count = int(count)
    except ValueError:
        if count is not None and count != 'all':
            return HttpResponse(
                '<h1>Unprocessable Entity</h1> Bad count.',
                status=422,
            )
    if count != 'all' and len(entries) <= count:
        count = 'all'
    if count != 'all':
        entries = entries[-count:]

    reversed_sort = request.GET.get('reverse', 'true').lower() not in ['false', '0']
    if reversed_sort:
        entries = reversed(entries)

    ledger_path = request.user.ledger_path.path
    journal = ledger_api.Journal(ledger_path)
    try:
        undo = Undo.objects.get(pk=request.user)
    except Undo.DoesNotExist:
        pass
    else:
        journal.last_data = undo

    return render(
        request,
        'ledger_ui/journal.html',
        {
            'entries': entries,
            'reverse': reversed_sort,
            'count': count,
            'filter': entry_filter,
            'count_step': settings.LEDGER_ENTRY_COUNT,
            'can_revert': journal.can_revert(),
        },
    )
Exemple #9
0
def submit(request):
    ledger_path = request.user.ledger_path.path
    journal = ledger_api.Journal(ledger_path)
    ledger_errors = False

    try:
        accounts = journal.accounts()
        currencies = journal.currencies()
        payees = journal.payees()
    except journal.LedgerCliError as e:
        accounts = currencies = payees = []
        ledger_errors = e.__cause__

    if request.method == 'POST':
        form = SubmitForm(
            request.POST,
            payees=payees,
        )
        formset = AccountFormSet(
            request.POST,
        )
        if form.is_valid() and formset.is_valid():
            validated = form.cleaned_data
            entry = ledger_api.Entry(
                date=validated['date'],
                payee=validated['payee'],
                note=validated['note'],
                accounts=[
                    (account['name'], account['amount'], account['currency'])
                    for account in formset.cleaned_data
                    if account.get('name')
                ],
            )

            if validated['amend']:
                undo = get_object_or_404(Undo, pk=request.user)
                journal.last_data = undo

                try:
                    journal.revert()
                except journal.CannotRevert:
                    return render(
                        request,
                        'ledger_ui/error/cannot_revert.html',
                        status=409,
                    )

            old, new = journal.append(entry)
            Undo.objects.update_or_create(
                pk=request.user.id,
                defaults={
                    'last_entry': entry,
                    'old_position': old,
                    'new_position': new,
                },
            )
            return redirect('ledger_ui:journal')

    else:
        amend = request.GET.get('amend', 'false').lower() not in ['false', '0']
        if amend:
            last_entry = get_object_or_404(Undo, pk=request.user).last_entry
            form = SubmitForm(
                {
                    'date': last_entry.date,
                    'payee': last_entry.payee,
                    'note': last_entry.note,
                    'amend': True,
                },
                payees=payees,
            )
            default_currency = settings.LEDGER_DEFAULT_CURRENCY
            for account in last_entry.accounts:
                if account.amount is not None:
                    default_currency = account.currency
                    break
            formset = AccountFormSet(
                form_kwargs={
                    'default_currency': default_currency,
                },
                initial=[
                    account._asdict()
                    for account in last_entry.accounts
                ],
            )
        else:
            form = SubmitForm(payees=payees)
            formset = AccountFormSet(
                initial=[
                    {'name': settings.LEDGER_DEFAULT_TO},
                    {'name': settings.LEDGER_DEFAULT_FROM},
                ]
            )

    return render(
        request,
        'ledger_ui/submit.html',
        {
            'form': form,
            'formset': formset,
            'accounts': accounts,
            'currencies': currencies,
            'ledger_errors': ledger_errors,
        },
    )