from itertools import zip_longest from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.db.models import Q from django.urls import reverse from collections import OrderedDict from django.views.decorators.http import require_http_methods from django.contrib.auth.decorators import login_required from .models import ( Transaction, TransactionType, EvaluationRules, EvaluationRulesAdaptation, Annuality, ComplementaryInformations, ) from .forms import TransactionForm from .tools import ( get_transactions_and_sums_for_year_and_type, get_transactiontypes_and_total_amount_transactions, get_transactiontype_and_sum_for_spf_export, get_right_engagement_and_sump_spf, get_asset_liability_and_sump_spf, ) from tools.pdf_generator import ( SpfDocument, BillPaper, ) import locale EXTENSES = 0 RECETTES = 1 @login_required @require_http_methods(["GET"]) def comptability_export(request, accounting_year, export_type): """ Définit une fonction d'export. Args: request: La requête HTTP accounting_year (int): L'année à exporter. export_type (string): à choisir parmi `sxs` (side by side) ou `ooo` (one over other). Returns: render_to_response sur la page HTML correspondante. """ if export_type == "sxs": return get_transaction_list_for_accountingyear_sxs(request, accounting_year) else: return get_transaction_list_for_accountingyear_ooo(request, accounting_year) @login_required @require_http_methods(["GET"]) def get_transaction_list_for_accountingyear_ooo(request, accounting_year): """ Affichage et calcul des `Recettes` et `Dépenses` pour une année donnée dans deux tableaux l'un au dessus de l'autre (one over other - ooo). Args: accounting_year (int): année comptable """ transactions_list_expenses = get_transactions_and_sums_for_year_and_type( accounting_year, EXTENSES ) transactions_list_recettes = get_transactions_and_sums_for_year_and_type( accounting_year, RECETTES ) context = { "transactions_list_expenses": transactions_list_expenses, "accounting_year": accounting_year, "transactions_list_recettes": transactions_list_recettes, } return render(request, "comptability/year_transaction_export_ooo.html", context) @login_required @require_http_methods(["GET"]) def get_transaction_list_for_accountingyear_sxs(request, accounting_year): """ Calcule et affiche la comptabilité d'une année passée en parametre dans un unique tableau (side by side). Args: accounting_year (int): année comptable """ expenses_transactiontypes_list = get_transactiontypes_and_total_amount_transactions( accounting_year, EXTENSES ) recettes_transactiontypes_list = get_transactiontypes_and_total_amount_transactions( accounting_year, RECETTES ) transactiontypes_list = zip_longest( recettes_transactiontypes_list["transactiontypes_info_list"], expenses_transactiontypes_list["transactiontypes_info_list"], ) context = { "transactiontypes_list": transactiontypes_list, "accounting_year": accounting_year, "total_expenses": expenses_transactiontypes_list["total"], "total_recettes": recettes_transactiontypes_list["total"], } return render(request, "comptability/year_transaction_export_sxs.html", context) @login_required @require_http_methods(["GET"]) def export_year_spf_finance(request, accounting_year): """ Calcule et affiche la comptabilité d'une année passée en parametre dans un unique tableau (side by side). Args: accounting_year (int): année comptable """ annuality = Annuality.objects.filter(year__year=accounting_year) transactiontypes_list_expenses = get_transactiontype_and_sum_for_spf_export( accounting_year, EXTENSES ) transactiontypes_list_recettes = get_transactiontype_and_sum_for_spf_export( accounting_year, RECETTES ) transactiontypes_list = [ x for x in zip_longest( transactiontypes_list_recettes["transaction_type_info"], transactiontypes_list_expenses["transaction_type_info"], ) ] # règle d'évaluation rules_list = EvaluationRules.objects.filter( Q(stop_date__year__lte=accounting_year) | Q(stop_date__isnull=True) ).exclude(start_date__year__gt=accounting_year) rules_adaptation_list = EvaluationRulesAdaptation.objects.filter( start_date__year=accounting_year ) complementary_informations = ComplementaryInformations.objects.filter( annuality=annuality[0] ) liquidity = 0 assets_list = get_asset_liability_and_sump_spf(accounting_year, category=0) for item in assets_list: if item[0] == "Liquidité": item[1] += ( annuality[0].opening_balance + transactiontypes_list_recettes["sum_total_transaction"] - transactiontypes_list_expenses["sum_total_transaction"] ) liquidity = item[1] asset_liability_list = get_asset_liability_and_sump_spf(accounting_year, category=1) asset_liability_sum = [x for x in zip_longest(assets_list, asset_liability_list)] right_list = get_right_engagement_and_sump_spf(accounting_year, category=0) engagement_list = get_right_engagement_and_sump_spf(accounting_year, category=1) right_engagement_sum = [x for x in zip_longest(right_list, engagement_list)] if len(right_engagement_sum) == 0: right_engagement_sum = None annuality[0].closing_balance = liquidity annuality[0].save() context = { "transactiontypes_list": transactiontypes_list, "accounting_year": accounting_year, "total_expenses": transactiontypes_list_expenses["sum_total_transaction"], "total_recette": transactiontypes_list_recettes["sum_total_transaction"], "rules_list": rules_list, "rules_adaptation_list": rules_adaptation_list, "complementary_informations": complementary_informations, "asset_liability_sum": asset_liability_sum, "right_engagement_sum": right_engagement_sum, } return render(request, "comptability/year_transaction_export_spf.html", context) @login_required @require_http_methods(["GET"]) def generate_pdf_for_spf(request, accounting_year): """ Génère un fichier PDF suivant les contraintes du SPF Finances Args: accounting_year (int): année Returns: ??? """ response = HttpResponse(content_type="application/pdf") response["Content-Disposition"] = ( 'attachment; filename="comptes_simplifies_annuels_' + str(accounting_year) + '.pdf"' ) document = SpfDocument(response) document.generate(str(accounting_year)) document.download() return response def __get_transactions_for_accounting_year_and_kind( accounting_year, transaction_kind=None ): """ """ transactions_list = Transaction.objects.by_year(accounting_year) if transaction_kind == "bank": transaction_kind = "Banque" transactions_list = transactions_list.filter(bkAmount__isnull=False).exclude( bkAmount=0 ) elif transaction_kind == "box": transaction_kind = "Caisse" transactions_list = transactions_list.filter(bxAmount__isnull=False).exclude( bxAmount=0 ) else: transaction_kind = "Tous" transactions_list = transactions_list.order_by("registrationDate") return transactions_list, transaction_kind @login_required @require_http_methods(["GET"]) def transaction_by_year_listing(request, accounting_year, transaction_kind=None): """ Liste toutes les lignes de comptabilité pour une année recue en paramètre Args: accounting_year (int): année comptable transaction_kind (string): """ ( transactions_list, transaction_kind, ) = __get_transactions_for_accounting_year_and_kind( accounting_year, transaction_kind ) # pour mon dictionnaire date/total previous_date = None date_sum = OrderedDict() tmp_Total = 0 tmp_Amount = 0 sum_extense = 0 sum_extense_simulated = 0 sumrecette = 0 sumrecette_simulated = 0 transaction_list_and_sum = [] for transaction in transactions_list: # transaction type : # 0 : dépense # 1 : recette # Suivant le type de la transaction, on ajoute son montant aux recettes ou au dépenses et # on ajoute le signe ("+" ou "-") afin de pouvoir l'utiliser dans des calculs après. tmp_Amount = transaction.totalAmount if transaction.transaction_type.transaction_type == EXTENSES: sum_extense_simulated += tmp_Amount if transaction.is_simulated is not None and transaction.is_simulated == 0: sum_extense += tmp_Amount tmp_Amount = -tmp_Amount else: sumrecette_simulated += tmp_Amount if transaction.is_simulated is not None and transaction.is_simulated == 0: sumrecette += tmp_Amount # Si la transaction n'est pas simulée on ajoute la transaction au total temporaire. if ( transaction.is_simulated is not None and transaction.is_simulated == 0 ) and (transaction.is_done is not None and transaction.is_done == 1): tmp_Total += tmp_Amount transaction_list_and_sum.append((transaction, tmp_Total)) if transaction.registrationDate.date() in date_sum: date_sum[transaction.registrationDate.date()] += tmp_Amount else: if previous_date is not None: date_sum[transaction.registrationDate.date()] = ( date_sum[previous_date] + tmp_Amount ) else: date_sum[transaction.registrationDate.date()] = tmp_Amount previous_date = transaction.registrationDate.date() total = sumrecette - sum_extense total_simulated = sumrecette_simulated - sum_extense_simulated nb_transaction = transactions_list.count() context = { "transaction_list": transaction_list_and_sum, "accounting_year": accounting_year, "totaldepense": sum_extense, "totalrecette": sumrecette, "totaldepense_simulated": sum_extense_simulated, "totalrecette_simulated": sumrecette_simulated, "total": total, "total_simulated": total_simulated, "date_sum": date_sum, "nb_transaction": nb_transaction, "t_type": transaction_kind, } return render(request, "comptability/transactions/listing.html", context) @login_required @require_http_methods(["GET"]) def year_listing(request): """ Liste toutes les années pour lesquelles il y a des transactions. """ years = Transaction.objects.dates("registrationDate", "year") year_list = [] for year in years: year_list.append((year.year, Transaction.objects.by_year(year.year).count(),)) context = {"year_list": year_list} return render(request, "year_listing.html", context) @login_required @require_http_methods(["GET"]) def transaction_listing_for_year_and_type( request, accounting_year, transaction_type_id ): """ Liste toutes les transactions d'un `type de transaction` et pour une année passés en paramètre. Args: request (???): accounting_year (int): année transaction_type_id (int): id d'un type de transaction Returns: (???) """ transaction_type = TransactionType.objects.get(pk=transaction_type_id) # by_year_condition = Q(registrationDate__year=accounting_year) by_type_condition = Q(transaction_type=transaction_type_id) by_parent_type_condition = Q(transaction_type__parent=transaction_type_id) transaction_list = ( Transaction.objects.by_year(accounting_year) .filter((by_type_condition | by_parent_type_condition)) .order_by("registrationDate") ) total = 0 total_simulated = 0 transaction_list_and_sum = [] for transaction in transaction_list: total_simulated += transaction.totalAmount if transaction.is_simulated is not None and transaction.is_simulated == 0: total += transaction.totalAmount transaction_list_and_sum.append((transaction, total)) context = { "transaction_list": transaction_list_and_sum, "transaction_type": str(transaction_type), "accounting_year": accounting_year, "total_simulated": total_simulated, "total": total, } return render(request, "transactions/listing.html", context) @login_required @require_http_methods(["GET"]) def transaction_details(request, transaction_id): """ Renvoie les détails d'une ligne de comptabilité. """ transaction = Transaction.objects.get(pk=transaction_id) context = {"event": transaction.event, "transaction": transaction} # changed template return render(request, "transactions/details.html", context) @login_required @require_http_methods(["GET", "POST"]) def transaction_create_or_update(request, transaction_id=None): """ Création ou modificatin d'une transaction. Args: transaction_id (int): identifiant d'une transaction. """ if transaction_id: transaction = get_object_or_404(Transaction, pk=transaction_id) else: transaction = None if request.method == "POST": form = TransactionForm(request.POST, instance=transaction) if form.is_valid(): transaction = form.save() return HttpResponseRedirect(reverse("transaction_details", args=(transaction.pk, ))) else: form = TransactionForm(instance=transaction) context = {"form": form, "transaction_id": transaction_id} return render(request, "transactions/create.html", context)