forked from DGNum/gestioCOF
Merge branch 'master' into 'Production'
Update prod See merge request klub-dev-ens/gestioCOF!517
This commit is contained in:
commit
3178657e33
8 changed files with 85 additions and 21 deletions
|
@ -27,6 +27,15 @@ adhérents ni des cotisations.
|
||||||
|
|
||||||
## Version ??? - ??/??/????
|
## Version ??? - ??/??/????
|
||||||
|
|
||||||
|
## Version 0.13 - 19/02/2023
|
||||||
|
|
||||||
|
### K-Fêt
|
||||||
|
|
||||||
|
- Rajoute la valeur des inventaires
|
||||||
|
- Résout les problèmes de négatif ne disparaissant pas
|
||||||
|
- Affiche son surnom s'il y en a un
|
||||||
|
- Bugfixes
|
||||||
|
|
||||||
## Version 0.12.1 - 03/10/2022
|
## Version 0.12.1 - 03/10/2022
|
||||||
|
|
||||||
### K-Fêt
|
### K-Fêt
|
||||||
|
|
|
@ -19,23 +19,31 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
||||||
|
# Le premier mail est envoyé après 24h de négatif, puis toutes les semaines
|
||||||
first_delay = timedelta(days=1)
|
first_delay = timedelta(days=1)
|
||||||
periodic_delay = timedelta(weeks=1)
|
periodic_delay = timedelta(weeks=1)
|
||||||
accounts_first_mail = (
|
|
||||||
AccountNegative.objects.filter(start__lt=now - first_delay)
|
# On n'envoie des mails qu'aux comptes qui ont un négatif vraiment actif
|
||||||
.filter(last_rappel__isnull=True)
|
# et dont la balance est négative
|
||||||
.all()
|
account_negatives = AccountNegative.objects.filter(
|
||||||
|
account__balance__lt=0
|
||||||
|
).exclude(end__lte=now)
|
||||||
|
|
||||||
|
accounts_first_mail = account_negatives.filter(
|
||||||
|
start__lt=now - first_delay, last_rappel__isnull=True
|
||||||
)
|
)
|
||||||
accounts_periodic_mail = (
|
accounts_periodic_mail = account_negatives.filter(
|
||||||
AccountNegative.objects.filter(last_rappel__isnull=False)
|
last_rappel__lt=now - periodic_delay
|
||||||
.filter(last_rappel__lt=now - periodic_delay)
|
|
||||||
.all()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for neg in accounts_first_mail:
|
for neg in accounts_first_mail:
|
||||||
neg.send_rappel()
|
neg.send_rappel()
|
||||||
self.stdout.write(f"Mail de rappel pour {neg.account} envoyé avec succès.")
|
self.stdout.write(f"Mail de rappel pour {neg.account} envoyé avec succès.")
|
||||||
|
|
||||||
for neg in accounts_periodic_mail:
|
for neg in accounts_periodic_mail:
|
||||||
neg.send_rappel()
|
neg.send_rappel()
|
||||||
self.stdout.write("Mail de rappel pour {neg.account} envoyé avec succès.")
|
self.stdout.write("Mail de rappel pour {neg.account} envoyé avec succès.")
|
||||||
if (not accounts_first_mail) and (not accounts_periodic_mail):
|
|
||||||
|
if not (accounts_first_mail.exists() or accounts_periodic_mail.exists()):
|
||||||
self.stdout.write("Aucun mail à envoyer.")
|
self.stdout.write("Aucun mail à envoyer.")
|
||||||
|
|
|
@ -260,8 +260,10 @@ class Account(models.Model):
|
||||||
elif hasattr(self, "negative"):
|
elif hasattr(self, "negative"):
|
||||||
if self.negative.end is None:
|
if self.negative.end is None:
|
||||||
self.negative.end = timezone.now()
|
self.negative.end = timezone.now()
|
||||||
|
self.negative.save()
|
||||||
elif timezone.now() > self.negative.end + kfet_config.cancel_duration:
|
elif timezone.now() > self.negative.end + kfet_config.cancel_duration:
|
||||||
# Idem: on supprime le négatif après une légère période
|
# Idem: on supprime le négatif après une légère période
|
||||||
|
# Nécessaire pour se souvenir du négatif après une charge annulée
|
||||||
self.negative.delete()
|
self.negative.delete()
|
||||||
|
|
||||||
class UserHasAccount(Exception):
|
class UserHasAccount(Exception):
|
||||||
|
|
|
@ -61,7 +61,7 @@ ul {
|
||||||
}
|
}
|
||||||
|
|
||||||
.table td.no-padding {
|
.table td.no-padding {
|
||||||
padding:0;
|
padding:0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead {
|
.table thead {
|
||||||
|
|
|
@ -248,7 +248,7 @@ function KHistory(options = {}) {
|
||||||
that.cancel_entry(entry);
|
that.cancel_entry(entry);
|
||||||
}
|
}
|
||||||
if (type == "operation") {
|
if (type == "operation") {
|
||||||
for (let opegroup of data["opegroups_to_update"]) {
|
for (let opegroup of (data["opegroups_to_update"] || [])) {
|
||||||
that.update_opegroup(opegroup)
|
that.update_opegroup(opegroup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,12 @@
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
|
<div class="messages">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Les valeurs de stock sont calculées sur la base du prix actuel des articles.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table
|
<table
|
||||||
class="table table-condensed table-hover table-striped sortable"
|
class="table table-condensed table-hover table-striped sortable"
|
||||||
|
@ -50,28 +56,36 @@
|
||||||
<td>Erreur</td>
|
<td>Erreur</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
{% regroup inventoryarts by article.category as category_list %}
|
{% regroup inventoryarts by article.category.name as category_list %}
|
||||||
{% for category in category_list %}
|
{% for category in category_list %}
|
||||||
<tbody class="tablesorter-no-sort">
|
<tbody class="tablesorter-no-sort">
|
||||||
<tr class="section">
|
<tr class="section">
|
||||||
<td colspan="4">{{ category.grouper.name }}</td>
|
<td colspan="4">{{ category.grouper }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for inventoryart in category.list %}
|
{% for inventoryart in category.list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url "kfet.article.read" inventoryart.article.id %}">
|
<a href="{% url "kfet.article.read" inventoryart.article_id %}">
|
||||||
{{ inventoryart.article.name }}
|
{{ inventoryart.article.name }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ inventoryart.stock_old }}</td>
|
<td>{{ inventoryart.stock_old }}</td>
|
||||||
<td>{{ inventoryart.stock_new }}</td>
|
<td>{{ inventoryart.stock_new }}</td>
|
||||||
<td>{{ inventoryart.stock_error }}</td>
|
<td>{{ inventoryart.stock_error }} / {{ inventoryart.amount_error|floatformat:"-2" }} €</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<tbody>
|
||||||
|
<tr class="section">
|
||||||
|
<td>Valeurs totales</td>
|
||||||
|
<td>{{ total_amount_old|floatformat:"-2" }} €</td>
|
||||||
|
<td>{{ total_amount_new|floatformat:"-2" }} €</td>
|
||||||
|
<td>{{ total_amount_error|floatformat:"-2" }} €</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,7 @@
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<h4>{{ account.name|title }}</h4>
|
<h4>{{ account.name|title }}</h4>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
{% if perms.kfet.is_team %}
|
|
||||||
<li>{{ account.nickname }}</li>
|
<li>{{ account.nickname }}</li>
|
||||||
{% endif %}
|
|
||||||
<li>{{ account.email|default:"Pas d'email!" }}</li>
|
<li>{{ account.email|default:"Pas d'email!" }}</li>
|
||||||
<li>
|
<li>
|
||||||
{% if account.promo %}
|
{% if account.promo %}
|
||||||
|
@ -53,7 +51,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if account.negative %}
|
{% if account.negative and account.balance_ukf < 0 %}
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<h4>Négatif</h4>
|
<h4>Négatif</h4>
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
|
|
|
@ -14,7 +14,18 @@ from django.contrib.auth.models import Permission, User
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Count, F, Max, OuterRef, Prefetch, Q, Subquery, Sum
|
from django.db.models import (
|
||||||
|
Count,
|
||||||
|
DecimalField,
|
||||||
|
ExpressionWrapper,
|
||||||
|
F,
|
||||||
|
Max,
|
||||||
|
OuterRef,
|
||||||
|
Prefetch,
|
||||||
|
Q,
|
||||||
|
Subquery,
|
||||||
|
Sum,
|
||||||
|
)
|
||||||
from django.forms import ValidationError, formset_factory
|
from django.forms import ValidationError, formset_factory
|
||||||
from django.http import (
|
from django.http import (
|
||||||
Http404,
|
Http404,
|
||||||
|
@ -2013,12 +2024,34 @@ class InventoryRead(DetailView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
inventoryarticles = (
|
output_field = DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||||
|
inventory_articles = (
|
||||||
InventoryArticle.objects.select_related("article", "article__category")
|
InventoryArticle.objects.select_related("article", "article__category")
|
||||||
.filter(inventory=self.object)
|
.filter(inventory=self.object)
|
||||||
|
.annotate(
|
||||||
|
amount_error=ExpressionWrapper(
|
||||||
|
F("stock_error") * F("article__price"), output_field=output_field
|
||||||
|
)
|
||||||
|
)
|
||||||
.order_by("article__category__name", "article__name")
|
.order_by("article__category__name", "article__name")
|
||||||
)
|
)
|
||||||
context["inventoryarts"] = inventoryarticles
|
context["inventoryarts"] = inventory_articles
|
||||||
|
stats = inventory_articles.aggregate(
|
||||||
|
new=ExpressionWrapper(
|
||||||
|
Sum(F("stock_new") * F("article__price")), output_field=output_field
|
||||||
|
),
|
||||||
|
error=Sum("amount_error"),
|
||||||
|
old=ExpressionWrapper(
|
||||||
|
Sum(F("stock_old") * F("article__price")), output_field=output_field
|
||||||
|
),
|
||||||
|
)
|
||||||
|
context.update(
|
||||||
|
{
|
||||||
|
"total_amount_old": stats["old"],
|
||||||
|
"total_amount_new": stats["new"],
|
||||||
|
"total_amount_error": stats["error"],
|
||||||
|
}
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue