mirror of
https://github.com/nanoy42/coope
synced 2024-12-23 23:43:47 +00:00
Improvements
This commit is contained in:
parent
192696d026
commit
73e7697a3c
11 changed files with 299 additions and 8 deletions
|
@ -130,3 +130,5 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
INTERNAL_IPS = ["127.0.0.1"]
|
INTERNAL_IPS = ["127.0.0.1"]
|
||||||
|
|
||||||
|
EMAIL_SUBJECT_PREFIX = "[Coope Admin] "
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from simple_history.admin import SimpleHistoryAdmin
|
from simple_history.admin import SimpleHistoryAdmin
|
||||||
from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile
|
from .models import PaymentMethod, GeneralPreferences, Cotisation, DivideHistory, PriceProfile, Improvement
|
||||||
|
|
||||||
class CotisationAdmin(SimpleHistoryAdmin):
|
class CotisationAdmin(SimpleHistoryAdmin):
|
||||||
"""
|
"""
|
||||||
|
@ -40,8 +40,18 @@ class DivideHistoryAdmin(SimpleHistoryAdmin):
|
||||||
list_display = ('date', 'total_cotisations', 'total_cotisations_amount', 'total_ptm_amount', 'coopeman')
|
list_display = ('date', 'total_cotisations', 'total_cotisations_amount', 'total_ptm_amount', 'coopeman')
|
||||||
ordering = ('-date',)
|
ordering = ('-date',)
|
||||||
|
|
||||||
|
class ImprovementAdmin(SimpleHistoryAdmin):
|
||||||
|
"""
|
||||||
|
The admin class for Improvement.
|
||||||
|
"""
|
||||||
|
list_display = ('title', 'mode', 'seen', 'done', 'date')
|
||||||
|
ordering = ('-date',)
|
||||||
|
search_fields = ('title', 'description')
|
||||||
|
list_filter = ('mode', 'seen', 'done')
|
||||||
|
|
||||||
admin.site.register(PaymentMethod, PaymentMethodAdmin)
|
admin.site.register(PaymentMethod, PaymentMethodAdmin)
|
||||||
admin.site.register(GeneralPreferences, GeneralPreferencesAdmin)
|
admin.site.register(GeneralPreferences, GeneralPreferencesAdmin)
|
||||||
admin.site.register(Cotisation, CotisationAdmin)
|
admin.site.register(Cotisation, CotisationAdmin)
|
||||||
admin.site.register(PriceProfile, PriceProfileAdmin)
|
admin.site.register(PriceProfile, PriceProfileAdmin)
|
||||||
admin.site.register(DivideHistory, DivideHistoryAdmin)
|
admin.site.register(DivideHistory, DivideHistoryAdmin)
|
||||||
|
admin.site.register(Improvement, ImprovementAdmin)
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from .models import Cotisation, PaymentMethod, GeneralPreferences, PriceProfile
|
from .models import Cotisation, PaymentMethod, GeneralPreferences, PriceProfile, Improvement
|
||||||
|
|
||||||
class CotisationForm(forms.ModelForm):
|
class CotisationForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
|
@ -50,3 +50,11 @@ class GeneralPreferencesForm(forms.ModelForm):
|
||||||
'home_text': forms.Textarea(attrs={'placeholder': 'Ce message sera affiché sur la page d\'accueil'})
|
'home_text': forms.Textarea(attrs={'placeholder': 'Ce message sera affiché sur la page d\'accueil'})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ImprovementForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
Form to create an improvement
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = Improvement
|
||||||
|
fields = ["title", "mode", "description"]
|
||||||
|
|
31
preferences/migrations/0019_improvement.py
Normal file
31
preferences/migrations/0019_improvement.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Generated by Django 2.1 on 2019-09-08 09:59
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('preferences', '0018_auto_20190627_2302'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Improvement',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=255)),
|
||||||
|
('mode', models.IntegerField(choices=[(0, 'Bug'), (1, 'Amélioration'), (2, 'Nouvelle fonctionnalité')])),
|
||||||
|
('description', models.TextField()),
|
||||||
|
('seen', models.BooleanField(default=False)),
|
||||||
|
('done', models.BooleanField(default=False)),
|
||||||
|
('coopeman', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='improvement_submitted', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Amélioration',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
39
preferences/migrations/0020_auto_20190908_1217.py
Normal file
39
preferences/migrations/0020_auto_20190908_1217.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# Generated by Django 2.1 on 2019-09-08 10:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0019_improvement'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='improvement',
|
||||||
|
name='date',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default='2019-09-08 00:00'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='improvement',
|
||||||
|
name='done',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Fait ?'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='improvement',
|
||||||
|
name='mode',
|
||||||
|
field=models.IntegerField(choices=[(0, 'Bug'), (1, 'Amélioration'), (2, 'Nouvelle fonctionnalité')], verbose_name='Type'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='improvement',
|
||||||
|
name='seen',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Vu ?'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='improvement',
|
||||||
|
name='title',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='Titre'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -202,3 +202,31 @@ class PriceProfile(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Improvement(models.Model):
|
||||||
|
"""
|
||||||
|
Stores bugs and amelioration proposals.
|
||||||
|
"""
|
||||||
|
|
||||||
|
BUG = 0
|
||||||
|
AMELIORATION = 1
|
||||||
|
NEWFEATURE = 2
|
||||||
|
|
||||||
|
MODES = (
|
||||||
|
(BUG, "Bug"),
|
||||||
|
(AMELIORATION, "Amélioration"),
|
||||||
|
(NEWFEATURE, "Nouvelle fonctionnalité")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Amélioration"
|
||||||
|
|
||||||
|
title = models.CharField(max_length=255, verbose_name="Titre")
|
||||||
|
mode = models.IntegerField(choices=MODES, verbose_name="Type")
|
||||||
|
description = models.TextField()
|
||||||
|
seen = models.BooleanField(default=False, verbose_name="Vu ?")
|
||||||
|
done = models.BooleanField(default=False, verbose_name="Fait ?")
|
||||||
|
coopeman = models.ForeignKey(User, on_delete=models.PROTECT, related_name="improvement_submitted")
|
||||||
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
|
21
preferences/templates/preferences/improvement_profile.html
Normal file
21
preferences/templates/preferences/improvement_profile.html
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block entete %}Amélioration {{improvement.title}}{% endblock %}
|
||||||
|
{% block navbar %}
|
||||||
|
<ul>
|
||||||
|
<li><a href="#first">{{improvement.title}}</a></li>
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<section id="first" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>{{improvement.title}}</h2>
|
||||||
|
</header>
|
||||||
|
<a href="{% url 'preferences:improvementsIndex' %}" class="button">Retour à la liste des améliorations</a><br><br>
|
||||||
|
<strong>Titre : </strong> {{improvement.title}}<br>
|
||||||
|
<strong>Type : </strong> {{improvement.get_mode_display}}<br>
|
||||||
|
<strong>Date : </strong> {{improvement.date}}<br>
|
||||||
|
<strong>Fait : </strong> {{improvement.done|yesno:"Oui,Non"}}<br>
|
||||||
|
<strong>Coopeman : </strong> {{improvement.coopeman}}<br>
|
||||||
|
<strong>Description : </strong> {{improvement.description}}<br>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
68
preferences/templates/preferences/improvements_index.html
Normal file
68
preferences/templates/preferences/improvements_index.html
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block entete %}Améliorations{% endblock %}
|
||||||
|
{% block navbar %}
|
||||||
|
<ul>
|
||||||
|
<li><a href="#first">Liste des améliorations à faire</a></li>
|
||||||
|
<li><a href="#seconde">Liste des améliorations faîtes</a></li>
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<section id="first" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Liste des améliorations à faire</h2>
|
||||||
|
</header>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Titre</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Vu ?</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Administration</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for improvement in todo_improvements %}
|
||||||
|
<tr>
|
||||||
|
<td>{{improvement.title}}</td>
|
||||||
|
<td>{{improvement.get_mode_display}}</td>
|
||||||
|
<td>{{improvement.seen|yesno:"Oui,Non"}}</td>
|
||||||
|
<td>{{improvement.date}}</td>
|
||||||
|
<td><a href="{% url 'preferences:improvementProfile' improvement.pk %}" class="button small"><i class="fa fa-eye"></i> Voir</a> <a href="{% url 'preferences:changeImprovementState' improvement.pk %}" class="button small"><i class="fa fa-check"></i> Passer en fait</a> <a href="{% url 'preferences:deleteImprovement' improvement.pk %}" class="button small"><i class="fa fa-trash"></i> Supprimer</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="second" class="main">
|
||||||
|
<header class="major">
|
||||||
|
<h2>Liste des améliorations faîtes</h2>
|
||||||
|
</header>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Titre</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Vu ?</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Administration</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for improvement in done_improvements %}
|
||||||
|
<tr>
|
||||||
|
<td>{{improvement.title}}</td>
|
||||||
|
<td>{{improvement.get_mode_display}}</td>
|
||||||
|
<td>{{improvement.seen|yesno:"Oui,Non"}}</td>
|
||||||
|
<td>{{improvement.date}}</td>
|
||||||
|
<td><a href="{% url 'preferences:improvementProfile' improvement.pk %}" class="button small"><i class="fa fa-eye"></i> Voir</a> <a href="{% url 'preferences:changeImprovementState' improvement.pk %}" class="button small"><i class="fa fa-check"></i> Passer en non fait</a> <a href="{% url 'preferences:deleteImprovement' improvement.pk %}" class="button small"><i class="fa fa-trash"></i> Supprimer</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
|
@ -19,5 +19,10 @@ urlpatterns = [
|
||||||
path('deletePriceProfile/<int:pk>', views.delete_price_profile, name="deletePriceProfile"),
|
path('deletePriceProfile/<int:pk>', views.delete_price_profile, name="deletePriceProfile"),
|
||||||
path('inactive', views.inactive, name="inactive"),
|
path('inactive', views.inactive, name="inactive"),
|
||||||
path('getConfig', views.get_config, name="getConfig"),
|
path('getConfig', views.get_config, name="getConfig"),
|
||||||
path('getCotisation/<int:pk>', views.get_cotisation, name="getCotisation")
|
path('getCotisation/<int:pk>', views.get_cotisation, name="getCotisation"),
|
||||||
,]
|
path('addImprovement', views.add_improvement, name="addImprovement"),
|
||||||
|
path('improvementsIndex', views.improvements_index, name="improvementsIndex"),
|
||||||
|
path('improvementProfile/<int:pk>', views.improvement_profile, name="improvementProfile"),
|
||||||
|
path('deleteImprovement/<int:pk>', views.delete_improvement, name="deleteImprovement"),
|
||||||
|
path('changeImprovementState/<int:pk>', views.change_improvement_state, name="changeImprovementState"),
|
||||||
|
]
|
||||||
|
|
|
@ -7,12 +7,13 @@ from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
from django.core.mail import mail_admins
|
||||||
|
|
||||||
from coopeV3.acl import active_required
|
from coopeV3.acl import active_required
|
||||||
|
|
||||||
from .models import GeneralPreferences, Cotisation, PaymentMethod, PriceProfile
|
from .models import GeneralPreferences, Cotisation, PaymentMethod, PriceProfile, Improvement
|
||||||
|
|
||||||
from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm, PriceProfileForm
|
from .forms import CotisationForm, PaymentMethodForm, GeneralPreferencesForm, PriceProfileForm, ImprovementForm
|
||||||
|
|
||||||
@active_required
|
@active_required
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -245,3 +246,73 @@ def delete_price_profile(request,pk):
|
||||||
price_profile.delete()
|
price_profile.delete()
|
||||||
messages.success(request, message)
|
messages.success(request, message)
|
||||||
return redirect(reverse('preferences:priceProfilesIndex'))
|
return redirect(reverse('preferences:priceProfilesIndex'))
|
||||||
|
|
||||||
|
|
||||||
|
########## Improvements ##########
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
def add_improvement(request):
|
||||||
|
"""
|
||||||
|
Display a form to create an improvement. Any logged user can access it
|
||||||
|
"""
|
||||||
|
form = ImprovementForm(request.POST or None)
|
||||||
|
if form.is_valid():
|
||||||
|
improvement = form.save(commit=False)
|
||||||
|
improvement.coopeman = request.user
|
||||||
|
improvement.save()
|
||||||
|
mail_admins("Nouvelle proposition d'amélioration", "Une nouvelle proposition d'amélioration a été postée (" + improvement.title + ", " + improvement.get_mode_display() + "). Le corps est le suivant : " + improvement.description)
|
||||||
|
messages.success(request, "Votre proposition a bien été envoyée")
|
||||||
|
return redirect(reverse('home'))
|
||||||
|
return render(request, "form.html", {"form": form, "form_title": "Proposition d'amélioration", "form_button": "Envoyer", "form_button_icon": "bug"})
|
||||||
|
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('preferences.view_improvement')
|
||||||
|
def improvements_index(request):
|
||||||
|
"""
|
||||||
|
Display all improvements
|
||||||
|
"""
|
||||||
|
todo_improvements = Improvement.objects.filter(done=False)
|
||||||
|
done_improvements = Improvement.objects.filter(done=True)
|
||||||
|
return render(request, "preferences/improvements_index.html", {"todo_improvements": todo_improvements, "done_improvements": done_improvements})
|
||||||
|
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('preferences.view_improvement')
|
||||||
|
@permission_required('preferences.change_improvement')
|
||||||
|
def improvement_profile(request, pk):
|
||||||
|
"""
|
||||||
|
Display an improvement
|
||||||
|
"""
|
||||||
|
improvement = get_object_or_404(Improvement, pk=pk)
|
||||||
|
improvement.seen = 1
|
||||||
|
improvement.save()
|
||||||
|
return render(request, "preferences/improvement_profile.html", {"improvement": improvement})
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('preferences.change_improvement')
|
||||||
|
def change_improvement_state(request, pk):
|
||||||
|
"""
|
||||||
|
Change done state of an improvement
|
||||||
|
"""
|
||||||
|
improvement = get_object_or_404(Improvement, pk=pk)
|
||||||
|
improvement.done = 1 - improvement.done
|
||||||
|
improvement.save()
|
||||||
|
messages.success(request, "L'état a bien été changé")
|
||||||
|
return redirect(reverse('preferences:improvementsIndex'))
|
||||||
|
|
||||||
|
@active_required
|
||||||
|
@login_required
|
||||||
|
@permission_required('preferences.delete_improvement')
|
||||||
|
def delete_improvement(request, pk):
|
||||||
|
"""
|
||||||
|
Delete an improvement
|
||||||
|
"""
|
||||||
|
improvement = get_object_or_404(Improvement, pk=pk)
|
||||||
|
improvement.delete()
|
||||||
|
messages.success(request, "L'amélioration a bien été supprimée.")
|
||||||
|
return redirect(reverse('preferences:improvementsIndex'))
|
|
@ -70,6 +70,14 @@
|
||||||
<i class="fa fa-search-dollar"></i> <a href="{% url 'gestion:compute-price' %}">Calcul de prix</a>
|
<i class="fa fa-search-dollar"></i> <a href="{% url 'gestion:compute-price' %}">Calcul de prix</a>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<span class="tabulation2">
|
||||||
|
<i class="fa fa-bug"></i> <a href="{% url 'preferences:addImprovement' %}">Proposition d'amélioration</a>
|
||||||
|
</span>
|
||||||
|
{% if perms.preferences.view_improvement %}
|
||||||
|
<span class="tabulation2">
|
||||||
|
<i class="fa fa-bug"></i> <a href="{% url 'preferences:improvementsIndex' %}">Améliorations</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
<span class="tabulation2">
|
<span class="tabulation2">
|
||||||
<i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a>
|
<i class="fa fa-bed"></i> <a href="{% url 'users:logout' %}">Deconnexion</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Reference in a new issue