330 lines
11 KiB
Python
330 lines
11 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"
|
|
|
|
|
|
# class KeytradeForm(TransactionForm):
|
|
# def __init__(self, item):
|
|
# current_dict = {
|
|
# 'registrationDate': datetime.datetime.strptime(row.get('Date'), '%d.%m.%Y'),
|
|
# 'totalAmount': float(row.get('Montant')),
|
|
# 'bkAmount': float(row.get('Montant')),
|
|
# 'amount': float(row.get('Montant')),
|
|
# 'transaction_type': 5,
|
|
# 'description': row.get('Description'),
|
|
# 'is_done': True,
|
|
# 'is_simulated': False,
|
|
# 'counterpart': row.get('Compte'),
|
|
# 'bxAmount': 0,
|
|
# 'otherDescription': row.get('Description'),
|
|
# 'account_number': row.get('Compte'),
|
|
# 'notes': row.get('Description'),
|
|
# }
|
|
|
|
# # Par défaut on considère que le montant est positif et
|
|
# # est donc une recette. Il faut donc corrigé si nécessaire.
|
|
# # id "Autre dépense" = 4 | id "Autre recette" = 5
|
|
# word = "cotisation"
|
|
# if 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 word in current_dict['description'].lower():
|
|
# current_dict['transaction_type'] = 6
|
|
|
|
# if current_dict['counterpart'] == "-":
|
|
# current_dict['counterpart'] = 'KEYTRADE'
|
|
# current_dict['transaction_type'] = 3
|
|
|
|
# super().init(current_dict)
|
|
|
|
|
|
# class CrelanForm(TransactionForm):
|
|
# def __init__(self, item):
|
|
# current_dict = {
|
|
# 'registrationDate': datetime.datetime.strptime(row.get('Date'), '%d/%m/%Y'),
|
|
# 'totalAmount': float(row.get('Montant')),
|
|
# 'bkAmount': float(row.get('Montant')),
|
|
# 'amount': float(row.get('Montant')),
|
|
# 'transaction_type': 5,
|
|
# 'description': row.get('Communication'),
|
|
# 'is_done': True,
|
|
# 'is_simulated': False,
|
|
# 'counterpart': row.get('Contrepartie'),
|
|
# 'bxAmount': 0,
|
|
# 'otherDescription': row.get("Type d'opération"),
|
|
# 'account_number': row.get('Compte contrepartie'),
|
|
# }
|
|
|
|
# # Par défaut on considère que le montant est positif et
|
|
# # est donc une recette. Il faut donc corrigé si nécessaire.
|
|
# # id "Autre dépense" = 4 | id "Autre recette" = 5
|
|
# word = "cotisation"
|
|
# if 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 word in current_dict['description'].lower():
|
|
# current_dict['transaction_type'] = 6
|
|
|
|
# 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']
|
|
|
|
# super().init(current_dict)
|
|
|
|
|
|
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())
|
|
|
|
for row in reader:
|
|
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 en erreur…")
|
|
for key, value in form.errors.items():
|
|
print(key, "->", value)
|
|
|
|
errors.append(form.errors)
|
|
|
|
return records_added, errors
|
|
|
|
|
|
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
|