from decimal import Decimal from django.contrib import messages from django.contrib.auth.decorators import login_required from django.db import models from django.db import transaction from django.http import HttpResponse from django.shortcuts import render from django.utils import timezone from django.utils.translation import gettext_lazy as _ from apps.accounts.forms import AccountBalanceFormSet from apps.accounts.models import Account, Transaction from apps.accounts.services import get_account_balance from apps.common.decorators.htmx import only_htmx @only_htmx @login_required def account_reconciliation(request): initial_data = [ { "account_id": account.id, "account_group": account.group, "account_name": account.name, "decimal_places": account.currency.decimal_places, "suffix": account.currency.suffix, "prefix": account.currency.prefix, "current_balance": get_account_balance(account), } for account in Account.objects.filter(is_archived=False) .select_related("currency", "group") .order_by("group", "name") ] if request.method == "POST": formset = AccountBalanceFormSet(request.POST, initial=initial_data) if formset.is_valid(): with transaction.atomic(): for form in formset: if form.is_valid(): account_id = form.cleaned_data["account_id"] new_balance = form.cleaned_data["new_balance"] account = Account.objects.get(id=account_id) category = form.cleaned_data["category"] tags = form.cleaned_data.get("tags", []) if new_balance is None: continue current_balance = get_account_balance(account) difference = new_balance - current_balance if difference != 0: new_transaction = Transaction.objects.create( account=account, type=( Transaction.Type.INCOME if difference > 0 else Transaction.Type.EXPENSE ), amount=abs(difference), date=timezone.localdate(timezone.now()), reference_date=timezone.localdate( timezone.now() ).replace(day=1), description=_("Balance reconciliation"), is_paid=True, category=category, ) new_transaction.tags.set(tags) messages.success( request, _("Account balances have been reconciled successfully") ) return HttpResponse( status=204, headers={"HX-Trigger": "updated, hide_offcanvas"}, ) else: formset = AccountBalanceFormSet(initial=initial_data) return render( request, "accounts/fragments/account_reconciliation.html", {"form": formset} )