ComptaClub/comptabilite/forms.py

254 lines
7.8 KiB
Python

import csv
import math
import datetime
from django import forms
from .models import Transaction
from eventCompta.models import Event
from django_select2.forms import Select2MultipleWidget, ModelSelect2Widget
class TransactionForm(forms.ModelForm):
class Meta:
model = Transaction
fields = [
"registrationDate",
"totalAmount",
"description",
"is_done",
"notes",
"counterpart",
"bkAmount",
"bxAmount",
"transaction_type",
"amount",
"otherDescription",
"account_number",
"event",
]
widgets = {
"registrationDate": forms.TextInput(
attrs={"class": "form-control", "placeholder": "Titre de la facture"}
),
"totalAmount": forms.TextInput(
attrs={"class": "form-control", }
),
"description": forms.Textarea(
attrs={"class": "form-control", "placeholder": "Description du contract."}
),
"is_done": forms.CheckboxInput(
attrs={"class": "form-control", }
),
"notes": forms.Textarea(
attrs={"class": "form-control", "placeholder": "Description du contract."}
),
"counterpart": forms.TextInput(
attrs={"class": "form-control", }
),
"bkAmount": forms.TextInput(
attrs={"class": "form-control", }
),
"bxAmount": forms.TextInput(
attrs={"class": "form-control", }
),
"transaction_type": forms.Select(
attrs={"class": "form-control", }
),
"amount": forms.TextInput(
attrs={"class": "form-control", }
),
"otherDescription": forms.Textarea(
attrs={"class": "form-control", "placeholder": "Description du contract."}
),
"account_number": forms.TextInput(
attrs={"class": "form-control", }
),
"event": ModelSelect2Widget(
search_fields=["event_name__icontains"],
max_results=10,
attrs={"data-minimum-input-length": 0, "class": "form-control"},
),
}
class MyDialect(csv.Dialect):
strict = True
skipinitialspace = True
quoting = csv.QUOTE_ALL
delimiter = ";"
quotechar = '"'
lineterminator = "\n"
def import_csv_transaction(file, bank):
"""
Lit un fichier CSV exporté d'un compte et ajoute les lignes
dans la DB. Voici la liste des colonnes, DANS L'ORDRE, qui doivent être
présente dans le fichier (normalement, elles y sont de base) pour que
l'import se passe bien.
Args:
file (path): chemin vers le fichier à lire
bank (str): nom de la banque
Returns:
records_added (int): nombre de lignes insérées dans la base de données
errors (array): liste des erreurs rencontrées
"""
with open(file, "r") as csvfile:
errors = []
records_added = 0
reader = csv.DictReader(csvfile, dialect=MyDialect())
line_number = 0
for row in reader:
line_number += 1
if bank == "crelan":
myDict = map_crelan_dict(row)
else:
myDict = map_keytrade_dict(row)
form = TransactionForm(myDict)
if form.is_valid():
form.save()
records_added += 1
else:
print("Ligne " + str(line_number) + " en erreur.")
print(form.errors)
return records_added
def get_key(current_dict, key, cast_type=None):
if cast_type:
return cast_type(current_dict.get(key)) if current_dict.get(key, None) else None
return current_dict.get(key, None)
def map_crelan_dict(row):
"""
Transforme une ligne du fichier CSV en dictionnaire pour le remplissage
d'un formulaire.
Args:
row (dictionary): transaction financière provenant du fichier CSV
Returns:
current_dict (dictionary): renvoie un dictionnaire pouvant être injecté dans un
transaction form.
"""
amount = get_key(row, "Montant", float)
current_dict = {
"registrationDate": datetime.datetime.strptime(row.get("Date"), "%d/%m/%Y")
if row.get("Date", None)
else None,
"totalAmount": amount,
"bkAmount": amount,
"amount": amount,
"transaction_type": 5,
"description": row.get("Communication")
if row.get("Communication", None)
else None,
"is_done": True,
"is_simulated": False,
"counterpart": row.get("Contrepartie").title()
if row.get("Contrepartie", None)
else None,
"bxAmount": 0,
"otherDescription": row.get("Type d'opération")
if row.get("Type d'opération", None)
else None,
"account_number": row.get("Compte contrepartie")
if row.get("Compte contrepartie", None)
else None,
"notes": row.get("Communication")
if row.get("Communication", None)
else None,
}
current_dict = update_transaction_dict(current_dict)
if not current_dict["counterpart"] and not current_dict["description"]:
current_dict["counterpart"] = "CRELAN"
current_dict["description"] = row.get("Type d'opération")
current_dict["transaction_type"] = 3
current_dict["notes"] = current_dict["description"]
return current_dict
def map_keytrade_dict(row):
"""
Transforme une ligne du fichier CSV en dictionnaire pour le remplissage
d'un formulaire.
Args:
row (dictionary): transaction financière provenant du fichier CSV
Returns:
dictionary: renvoie un dictionnaire pouvant être injecté dans un
transaction form.
"""
amount = get_key(row, "Montant", float)
description = get_key(row, "Description")
account = get_key(row, "Compte")
current_dict = {
"registrationDate": datetime.datetime.strptime(row.get("Date"), "%d.%m.%Y")
if row.get("Date", None)
else None,
"totalAmount": amount,
"bkAmount": amount,
"amount": amount,
"transaction_type": 5,
"description": description,
"is_done": True,
"is_simulated": False,
"counterpart": account,
"bxAmount": 0,
"otherDescription": description,
"account_number": account,
"notes": description,
}
current_dict = update_transaction_dict(current_dict)
if current_dict["counterpart"] == "-":
current_dict["counterpart"] = "KEYTRADE"
current_dict["transaction_type"] = 3
return current_dict
def update_transaction_dict(current_dict):
"""
Par défaut on considère que le montant est positif et est donc une recette. Il faut donc
corriger, si nécessaire, le type de transaction et d'autres informations.
id "Autre dépense" = 4
id "Autre recette" = 5
Args:
current_dic (dictionary): dictionnaire pouvent être donné à un TransactionForm
Returns:
dictionary: renvoie un dictionnaire pouvant être injecté dans un
transaction form.
"""
word = "cotisation"
if current_dict["totalAmount"] is not None and current_dict["totalAmount"] < 0:
current_dict["transaction_type"] = 4
current_dict["totalAmount"] = math.fabs(current_dict["totalAmount"])
current_dict["bkAmount"] = current_dict["totalAmount"]
current_dict["amount"] = current_dict["totalAmount"]
elif (
current_dict["description"] is not None
and word in current_dict["description"].lower()
):
current_dict["transaction_type"] = 6
return current_dict