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