diff --git a/.pre-commit.sh b/.pre-commit.sh deleted file mode 100755 index 0e0e3c1a..00000000 --- a/.pre-commit.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash -# pre-commit hook for gestioCOF project. -# -# Run formatters first, then checkers. -# Formatters which changed a file must set the flag 'formatter_updated'. - -exit_code=0 -formatter_updated=0 -checker_dirty=0 - -# TODO(AD): We should check only staged changes. -# Working? -> Stash unstaged changes, run it, pop stash -STAGED_PYTHON_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep ".py$") - - -# Formatter: black - -printf "> black ... " - -if type black &>/dev/null; then - if [ -z "$STAGED_PYTHON_FILES" ]; then - printf "OK\n" - else - BLACK_OUTPUT="/tmp/gc-black-output.log" - touch $BLACK_OUTPUT - - if ! echo "$STAGED_PYTHON_FILES" | xargs -d'\n' black --check &>$BLACK_OUTPUT; then - echo "$STAGED_PYTHON_FILES" | xargs -d'\n' black &>$BLACK_OUTPUT - tail -1 $BLACK_OUTPUT - formatter_updated=1 - else - printf "OK\n" - fi - fi -else - printf "SKIP: program not found\n" - printf "HINT: Install black with 'pip3 install black' (black requires Python>=3.6)\n" -fi - -# Formatter: isort - -printf "> isort ... " - -if type isort &>/dev/null; then - if [ -z "$STAGED_PYTHON_FILES" ]; then - printf "OK\n" - else - ISORT_OUTPUT="/tmp/gc-isort-output.log" - touch $ISORT_OUTPUT - - if ! echo "$STAGED_PYTHON_FILES" | xargs -d'\n' isort --check-only &>$ISORT_OUTPUT; then - echo "$STAGED_PYTHON_FILES" | xargs -d'\n' isort &>$ISORT_OUTPUT - printf "Reformatted.\n" - formatter_updated=1 - else - printf "OK\n" - fi - fi -else - printf "SKIP: program not found\n" - printf "HINT: Install isort with 'pip install isort'\n" -fi - -# Checker: flake8 - -printf "> flake8 ... " - -if type flake8 &>/dev/null; then - if [ -z "$STAGED_PYTHON_FILES" ]; then - printf "OK\n" - else - FLAKE8_OUTPUT="/tmp/gc-flake8-output.log" - touch $FLAKE8_OUTPUT - - if ! echo "$STAGED_PYTHON_FILES" | xargs -d'\n' flake8 &>$FLAKE8_OUTPUT; then - printf "FAIL\n" - cat $FLAKE8_OUTPUT - checker_dirty=1 - else - printf "OK\n" - fi - fi -else - printf "SKIP: program not found\n" - printf "HINT: Install flake8 with 'pip install flake8'\n" -fi - -# End - -if [ $checker_dirty -ne 0 ] -then - printf ">>> Checker(s) detect(s) issue(s)\n" - printf " You can still commit and push :)\n" - printf " Be warned that our CI may cause you more trouble.\n" -fi - -if [ $formatter_updated -ne 0 ] -then - printf ">>> Working tree updated by formatter(s)\n" - printf " Add changes to staging area and retry.\n" - exit_code=1 -fi - -printf "\n" - -exit $exit_code diff --git a/cof/settings/common.py b/cof/settings/common.py index ecf464fe..dd80a7b3 100644 --- a/cof/settings/common.py +++ b/cof/settings/common.py @@ -75,6 +75,7 @@ INSTALLED_APPS = [ "django.contrib.admindocs", "bda", "petitscours", + "journaldecaisse.apps.JournaldecaisseConfig", "captcha", "django_cas_ng", "bootstrapform", diff --git a/cof/urls.py b/cof/urls.py index 374c0f1a..9b24f932 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -16,6 +16,8 @@ from wagtail.admin import urls as wagtailadmin_urls from wagtail.core import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls + + from gestioncof import csv_views, views as gestioncof_views from gestioncof.autocomplete import autocomplete from gestioncof.urls import ( @@ -43,6 +45,8 @@ urlpatterns = [ path("event/", include(events_patterns)), # Calendrier path("calendar/", include(calendar_patterns)), + #Journal de Caisse + path('journaldecaisse/', include('journaldecaisse.urls', namespace="journaldecaisse")), # Clubs path("clubs/", include(clubs_patterns)), # Authentification diff --git a/gestioncof/templates/gestioncof/home.html b/gestioncof/templates/gestioncof/home.html index e534f687..599956b9 100644 --- a/gestioncof/templates/gestioncof/home.html +++ b/gestioncof/templates/gestioncof/home.html @@ -91,6 +91,24 @@ {% endfor %} + +

Kaisse

+
+ + +

Gestion tirages BdA

{% if active_tirages %} diff --git a/journaldecaisse/__init__.py b/journaldecaisse/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/journaldecaisse/admin.py b/journaldecaisse/admin.py new file mode 100644 index 00000000..49263e46 --- /dev/null +++ b/journaldecaisse/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from .models import JournalEntry, Produit, ProduitStock, Option + + +class JournalAdmin(admin.ModelAdmin): + list_display = ('entry_text', 'cofeux_id', 'payment_type') + + +admin.site.register(JournalEntry, JournalAdmin) +admin.site.register(Option) +admin.site.register(Produit) +admin.site.register(ProduitStock) \ No newline at end of file diff --git a/journaldecaisse/apps.py b/journaldecaisse/apps.py new file mode 100644 index 00000000..7d4c1ff1 --- /dev/null +++ b/journaldecaisse/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class JournaldecaisseConfig(AppConfig): + + name = "journaldecaisse" diff --git a/journaldecaisse/migrations/0001_initial.py b/journaldecaisse/migrations/0001_initial.py new file mode 100644 index 00000000..76c699a7 --- /dev/null +++ b/journaldecaisse/migrations/0001_initial.py @@ -0,0 +1,54 @@ +# Generated by Django 2.2.10 on 2020-02-18 15:01 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='JournalEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('entry_text', models.CharField(max_length=500, verbose_name='Détail entrée')), + ('entry_date', models.DateTimeField(verbose_name='Date')), + ('cofeux_id', models.CharField(max_length=8, verbose_name='Trigramme')), + ('entry_amount', models.FloatField(verbose_name='Montant')), + ('payment_type', models.CharField(choices=[('Espèces', 'Espèces'), ('CB', 'CB'), ('Chèque', 'Chèque')], max_length=20, verbose_name='Moyen de paiement')), + ], + options={ + 'verbose_name': 'Entrée du JDC', + 'verbose_name_plural': 'Entrées du JDC', + }, + ), + migrations.CreateModel( + name='Option', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Nom')), + ], + ), + migrations.CreateModel( + name='Produit', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Produit', models.CharField(max_length=100, verbose_name='Nom')), + ('option_1_value', models.CharField(max_length=100, verbose_name='Option 1 Valeur')), + ('option_1', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journaldecaisse.Option')), + ], + ), + migrations.CreateModel( + name='ProduitStock', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('Amount', models.IntegerField(verbose_name='Nombre en Stock')), + ('ProduitTaille', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journaldecaisse.Produit')), + ], + ), + ] diff --git a/journaldecaisse/migrations/0002_auto_20200218_1607.py b/journaldecaisse/migrations/0002_auto_20200218_1607.py new file mode 100644 index 00000000..e9893f79 --- /dev/null +++ b/journaldecaisse/migrations/0002_auto_20200218_1607.py @@ -0,0 +1,67 @@ +# Generated by Django 2.2.10 on 2020-02-18 15:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('journaldecaisse', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='produit', + name='option_2', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='option_2', to='journaldecaisse.Option'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_2_value', + field=models.CharField(default=0, max_length=100, verbose_name='Option 2 Valeur'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_3', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='option_3', to='journaldecaisse.Option'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_3_value', + field=models.CharField(default=0, max_length=100, verbose_name='Option 3 Valeur'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_4', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='option_4', to='journaldecaisse.Option'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_4_value', + field=models.CharField(default=0, max_length=100, verbose_name='Option 4 Valeur'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_5', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='option_5', to='journaldecaisse.Option'), + preserve_default=False, + ), + migrations.AddField( + model_name='produit', + name='option_5_value', + field=models.CharField(default=0, max_length=100, verbose_name='Option 5 Valeur'), + preserve_default=False, + ), + migrations.AlterField( + model_name='produit', + name='option_1', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='option_1', to='journaldecaisse.Option'), + ), + ] diff --git a/journaldecaisse/migrations/0003_auto_20200218_1610.py b/journaldecaisse/migrations/0003_auto_20200218_1610.py new file mode 100644 index 00000000..c525ca22 --- /dev/null +++ b/journaldecaisse/migrations/0003_auto_20200218_1610.py @@ -0,0 +1,38 @@ +# Generated by Django 2.2.10 on 2020-02-18 15:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journaldecaisse', '0002_auto_20200218_1607'), + ] + + operations = [ + migrations.AlterField( + model_name='produit', + name='option_1_value', + field=models.CharField(blank=True, max_length=100, verbose_name='Option 1 Valeur'), + ), + migrations.AlterField( + model_name='produit', + name='option_2_value', + field=models.CharField(blank=True, max_length=100, verbose_name='Option 2 Valeur'), + ), + migrations.AlterField( + model_name='produit', + name='option_3_value', + field=models.CharField(blank=True, max_length=100, verbose_name='Option 3 Valeur'), + ), + migrations.AlterField( + model_name='produit', + name='option_4_value', + field=models.CharField(blank=True, max_length=100, verbose_name='Option 4 Valeur'), + ), + migrations.AlterField( + model_name='produit', + name='option_5_value', + field=models.CharField(blank=True, max_length=100, verbose_name='Option 5 Valeur'), + ), + ] diff --git a/journaldecaisse/migrations/0004_produit_price.py b/journaldecaisse/migrations/0004_produit_price.py new file mode 100644 index 00000000..7df8111e --- /dev/null +++ b/journaldecaisse/migrations/0004_produit_price.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.10 on 2020-02-18 15:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('journaldecaisse', '0003_auto_20200218_1610'), + ] + + operations = [ + migrations.AddField( + model_name='produit', + name='price', + field=models.FloatField(default=0, verbose_name='Prix'), + preserve_default=False, + ), + ] diff --git a/journaldecaisse/migrations/__init__.py b/journaldecaisse/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/journaldecaisse/models.py b/journaldecaisse/models.py new file mode 100644 index 00000000..fa8b0f10 --- /dev/null +++ b/journaldecaisse/models.py @@ -0,0 +1,58 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +PAYMENTS_CHOICES = ( + ("Espèces", _("Espèces")), + ("CB", _("CB")), + ("Chèque", _("Chèque")), +) + +class JournalEntry(models.Model): + entry_text = models.CharField("Détail entrée", max_length=500) + entry_date = models.DateTimeField('Date') + cofeux_id = models.CharField("Trigramme", max_length=8) + entry_amount = models.FloatField("Montant") + payment_type = models.CharField( + _("Moyen de paiement"), choices=PAYMENTS_CHOICES, max_length=20 + ) + def __str__(self): + return self.entry_text + + class Meta: + verbose_name = 'Entrée du JDC' + verbose_name_plural = 'Entrées du JDC' + + + + +class Option(models.Model): + name = models.CharField("Nom", max_length=100) + def __str__(self): + return self.name + +class Produit(models.Model): + Produit = models.CharField("Nom", max_length=100) + option_1 = models.ForeignKey(Option, on_delete=models.CASCADE, related_name="option_1") + option_1_value = models.CharField("Option 1 Valeur", max_length=100, blank=True) + option_2 = models.ForeignKey(Option, on_delete=models.CASCADE, related_name="option_2") + option_2_value = models.CharField("Option 2 Valeur", max_length=100, blank=True) + option_3 = models.ForeignKey(Option, on_delete=models.CASCADE, related_name="option_3") + option_3_value = models.CharField("Option 3 Valeur", max_length=100, blank=True) + option_4 = models.ForeignKey(Option, on_delete=models.CASCADE, related_name="option_4") + option_4_value = models.CharField("Option 4 Valeur", max_length=100, blank=True) + option_5 = models.ForeignKey(Option, on_delete=models.CASCADE, related_name="option_5") + option_5_value = models.CharField("Option 5 Valeur", max_length=100, blank=True) + price = models.FloatField("Prix") + + def __str__(self): + name = str(self.Produit) + " " + self.option_1_value + " " + self.option_2_value + " " + self.option_3_value + " " + self.option_4_value + " " + self.option_5_value + return name + + +class ProduitStock(models.Model): + ProduitTaille = models.ForeignKey(Produit, on_delete=models.CASCADE) + Amount = models.IntegerField("Nombre en Stock") + def __str__(self): + return str(self.ProduitTaille) + " : " + str(self.Amount) + + diff --git a/journaldecaisse/templates/journaldecaisse/index.html b/journaldecaisse/templates/journaldecaisse/index.html new file mode 100644 index 00000000..7e2093ab --- /dev/null +++ b/journaldecaisse/templates/journaldecaisse/index.html @@ -0,0 +1,45 @@ +{% extends "petitscours/base_title.html" %} +{% load staticfiles %} + +{% block realcontent %} +

Journal de Kaisse

+{% if entry_list %} + + + + + + + + + + {% for entry in entry_list %} + + + + + + + + {% endfor %} + +
DateCofeux.seObjetMoyen de PaiementMontant
{{ entry.entry_date|date:"M d, Y" }}{{ entry.cofeux_id }}{{ entry.entry_text }} {{ entry.payment_type}} {{ entry.entry_amount }}€
+ {% if is_paginated %} + + {% endif %} +{% else %} +

Aucune demande :(

+{% endif %} +{% endblock %} diff --git a/journaldecaisse/templates/journaldecaisse/vente.html b/journaldecaisse/templates/journaldecaisse/vente.html new file mode 100644 index 00000000..b9d11b7a --- /dev/null +++ b/journaldecaisse/templates/journaldecaisse/vente.html @@ -0,0 +1,124 @@ +{% extends "petitscours/base_title.html" %} +{% load staticfiles %} + +{% block realcontent %} +

Vente

+ +
+ {% for product in product_list %} + + {% endfor %} +
+
+ + + +{% endblock %} + diff --git a/journaldecaisse/tests.py b/journaldecaisse/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/journaldecaisse/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/journaldecaisse/urls.py b/journaldecaisse/urls.py new file mode 100644 index 00000000..1afa9e7b --- /dev/null +++ b/journaldecaisse/urls.py @@ -0,0 +1,15 @@ +from django.urls import path +from django.conf.urls import url + +from . import views + +app_name = 'journaldecaisse' +urlpatterns = [ + path('', views.index, name='index'), + path('vente.html', views.vente, name='vente'), + url(r'^ajax/iterate_options/$', views.iterate_options, name='iterate_options'), + url(r'^ajax/submit_entry/$', views.submit_entry, name='submit_entry'), + +] + + diff --git a/journaldecaisse/views.py b/journaldecaisse/views.py new file mode 100644 index 00000000..3240f12a --- /dev/null +++ b/journaldecaisse/views.py @@ -0,0 +1,75 @@ +from django.http import HttpResponse +from .models import JournalEntry, Produit, ProduitStock +from django.shortcuts import render +from django.http import JsonResponse +import datetime + +global_filter = None + +def index(request): + entry_list = JournalEntry.objects.order_by('entry_date') + context = {'entry_list': entry_list} + return render(request, 'journaldecaisse/index.html', context) + +def vente(request): + print(request.user.first_name) + global global_filter + product_list = Produit.objects.all() + global_filter = product_list + product_list = product_list.values_list() + products = [] + for elem in product_list: + products.append(elem[1]) + products = list(set(products)) + + + context = {"product_list" : products} + return render(request, 'journaldecaisse/vente.html', context) + + + + +def iterate_options(request): + + global global_filter + elem_selected = None + elem_price = None + elem_stock = None + options_selected = request.GET.get('option', None) + options_selected = options_selected.split(":") + field = str(Produit._meta.get_fields()[int(options_selected[2])+3]).split(".")[-1] + elems = global_filter.filter(**{options_selected[0]: options_selected[1]}) + global_filter = elems + elems = elems.values_list() + options = [] + for elem in elems: + options.append(elem[int(options_selected[2])+2]) + options = list(set(options)) + if global_filter.count() == 1: + elem_selected = str(global_filter.first()) + elem_price = global_filter.first().price + elem_stock = ProduitStock.objects.get(ProduitTaille=global_filter.first()).Amount + + + + + data = { + 'options': options, + 'options_int': int(options_selected[2])+2, + 'field': field, + 'elem_selected': elem_selected, + 'price': elem_price, + 'stock': elem_stock, + 'name': request.user.first_name + } + return JsonResponse(data) + + + +def submit_entry(request): + product = global_filter.first() + buyer = request.GET.get('buyer', None) + payment = request.GET.get('payment', None) + author = request.user.first_name + JournalEntry.objects.create(entry_date=datetime.datetime.now(), entry_text=str(product) + " " + buyer, cofeux_id=author, entry_amount=product.price, payment_type=payment) + return JsonResponse({"success":"True"})