Compare commits

...

5 Commits

6 changed files with 259 additions and 14 deletions

View File

@ -10,6 +10,12 @@ import nested_admin
from . import models
def clean(d):
if d == d.to_integral():
return d.quantize(Decimal(1))
else:
return d.quantize(Decimal('1.000')).normalize()
class MyAdminSite(admin.AdminSite):
site_header = "Fournée 2.0"
@ -32,9 +38,17 @@ class PainAdmin(admin.ModelAdmin):
admin_site.register(models.Pain, PainAdmin)
class DocumentInline(admin.TabularInline):
model = models.Document
extra = 1
class FournitureAdmin(admin.ModelAdmin):
list_display = ["nom", "ordre", "couleur", "conditionnement", "emballage"]
list_editable = ["ordre", "couleur", "conditionnement", "emballage"]
inlines = [
DocumentInline,
]
actions = None
save_as = True
@ -124,13 +138,14 @@ class RéservationInline(nested_admin.NestedTabularInline):
classes = ["collapse"]
class CommandeInline(nested_admin.NestedStackedInline):
class CommandeInline(nested_admin.NestedTabularInline):
model = models.Commande
inlines = [
RéservationInline,
]
extra = 1
classes = ["collapse"]
template = "nesting/admin/inlines/réservations_tabular.html"
class CouléeInline(
@ -188,14 +203,14 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
self.message_user(
request,
f"Création d'une coulée de "
f"{pâte_manquante.normalize()} kg "
f"{clean(pâte_manquante)} kg "
f"pour la pâte {pâte_nom}.",
)
elif pâte_manquante < 0:
self.message_user(
request,
f"Attention la pâte {pâte_nom} est prévue "
f"en excès ({-pâte_manquante.normalize()} kg en trop)",
f"en excès ({-clean(pâte_manquante)} kg en trop)",
messages.WARNING,
)
@ -234,10 +249,10 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
for i in ingrédients:
quantités.append([
i,
(c.quantité / c.pâte.poids_de_base * sum(
clean(c.quantité / c.pâte.poids_de_base * sum(
c.pâte.ingrédient_set.filter(pk=i.pk)
.values_list("quantité", flat=True)
)).quantize(Decimal('1.000')).normalize(),
))
])
masse_coulée = sum(
@ -250,12 +265,12 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
problems = ""
elif masse_coulée > masse_commandée:
problems = (
f"{(masse_coulée - masse_commandée).normalize()} "
f"{clean(masse_coulée - masse_commandée)} "
"kg coulés en trop !!"
)
elif masse_coulée < masse_commandée:
problems = (
f"{(masse_commandée - masse_coulée).normalize()} "
f"{clean(masse_commandée - masse_coulée)} "
"kg coulés en moins !!"
)
extra_context["coulées"].append([problems, ingrédients, quantités])
@ -280,7 +295,7 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
[None, sum(obj.coulée_set.values_list('quantité', flat=True))]
]
for i in ingrédients:
total = sum([
total = clean(sum([
qté * qté_unit / unit
for qté, qté_unit, unit in
obj.coulée_set.filter(
@ -291,7 +306,7 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
'pâte__ingrédient__quantité',
'pâte__poids_de_base',
)
]).quantize(Decimal('1.000')).normalize()
]))
totaux.append([i, total])
extra_context["totaux_coulées"] = totaux
@ -307,22 +322,22 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
for p in pains:
if c.réservation_set.filter(pain__nom=p).exists():
tmp.append(
sum(
clean(sum(
c.réservation_set.filter(pain__nom=p).values_list(
"quantité", flat=True
)
).normalize()
))
)
else:
tmp.append("")
extra_context["divisions"].append(tmp)
totaux = [sum(obj.coulée_set.values_list('quantité', flat=True))]
for p in pains:
total = sum(
total = clean(sum(
obj.commande_set.filter(pains__nom=p).values_list(
"réservation__quantité", flat=True
)
).normalize()
))
totaux.append(total)
extra_context["totaux_divisions"] = totaux

View File

@ -0,0 +1,45 @@
# Generated by Django 4.2.13 on 2024-05-15 10:19
from django.db import migrations, models
import django.db.models.deletion
import fournée.core.models
class Migration(migrations.Migration):
dependencies = [
("core", "0014_fourniture_emballage"),
]
operations = [
migrations.CreateModel(
name="Document",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("nom", models.CharField(max_length=64, unique=True)),
(
"fichier",
models.FileField(
upload_to=fournée.core.models.upload_to, verbose_name="Fichier"
),
),
(
"fourniture",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="core.fourniture",
),
),
],
options={
"ordering": ["nom"],
},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.13 on 2024-05-15 10:37
from django.db import migrations, models
import fournée.core.models
class Migration(migrations.Migration):
dependencies = [
("core", "0015_document"),
]
operations = [
migrations.AlterField(
model_name="document",
name="fichier",
field=models.FileField(upload_to=fournée.core.models.upload_to),
),
migrations.AlterField(
model_name="fourniture",
name="conditionnement",
field=models.DecimalField(decimal_places=3, default=0, max_digits=6),
),
]

View File

@ -1,8 +1,14 @@
from uuid import uuid4
from django.db import models
from colorfield.fields import ColorField
def upload_to(instance, filename):
return "{}/{}".format(uuid4(), filename)
class Fournée(models.Model):
class Meta:
ordering = ["-date"]
@ -26,6 +32,18 @@ class Fournée(models.Model):
])
class Document(models.Model):
class Meta:
ordering = ["nom"]
fourniture = models.ForeignKey("Fourniture", on_delete=models.CASCADE)
nom = models.CharField(max_length=64, unique=True)
fichier = models.FileField(upload_to=upload_to)
def __str__(self):
return f"{self.nom}"
class Fourniture(models.Model):
class Meta:
ordering = ["ordre"]
@ -33,7 +51,9 @@ class Fourniture(models.Model):
nom = models.CharField(max_length=64, unique=True)
ordre = models.SmallIntegerField(default=0)
couleur = ColorField(default='#000000')
conditionnement = models.DecimalField(max_digits=6, decimal_places=3)
conditionnement = models.DecimalField(
max_digits=6, decimal_places=3, default=0
)
emballage = models.CharField(max_length=64, blank=True)
def __str__(self):

View File

@ -17,6 +17,10 @@ def submit_row_tag(parser, token):
def emballe(quantité, fourniture):
quotient = quantité // fourniture.conditionnement
reste = quantité % fourniture.conditionnement
if not reste and quotient == 1:
return f"{quotient} {fourniture.emballage}"
if not reste and quotient > 1:
return f"{quotient} {fourniture.emballage}s"
if not quotient and reste < fourniture.conditionnement/2:
return None
if quotient == 1 and reste < fourniture.conditionnement/2:

View File

@ -0,0 +1,138 @@
{% load admin_urlname admin_urlquote from admin_urls %}
{% load i18n nested_admin static %}
{% with inline_admin_formset.formset.is_nested as is_nested %}
{% with inline_admin_formset.opts as inline_opts %}
<div class="inline-group group djn-group djn-tabular{% if is_nested %} djn-group-nested{% else %} djn-group-root{% endif %}"
id="{{ inline_admin_formset.formset.prefix }}-group"
data-inline-type="stacked"
data-inline-formset="{{ inline_admin_formset.inline_formset_data }}"
data-inline-model="{{ inline_admin_formset.inline_model_id }}">
<div class="tabular inline-related {% if forloop.last and inline_admin_formset.has_add_permission %}last-related{% endif %}">
<fieldset class="module djn-fieldset {{ inline_admin_formset.classes }}">
<h2>
{% if inline_admin_formset.opts.title %}{{ inline_admin_formset.opts.title }}{% else %}{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}
</h2>
{{ inline_admin_formset.formset.management_form }}
{{ inline_admin_formset.formset.non_form_errors }}
<table class="djn-items inline-related djn-table">
<thead class="djn-module djn-thead">
<tr>
<th class="original"></th>
{% for field in inline_admin_formset.fields %}
{% if not field.widget.is_hidden %}
<th class="djn-th {{ field.label|lower|slugify }}{% if field.required %} required{% endif %}" style="width:270px;">{{ field.label|capfirst }}
{% if field.help_text %}&nbsp;<img src="{% static "admin/img/icon-unknown.svg" %}" class="help help-tooltip" width="10" height="10" alt="({{ field.help_text|striptags }})" title="{{ field.help_text|striptags }}" />{% endif %}
</th>
{% endif %}
{% endfor %}
<th class="djn-th">Réservations</th>
{% if inline_admin_formset.formset.can_delete %}<th class="djn-th" style="width:70px;">{% trans "Delete?" %}</th>{% endif %}
</tr>
</thead>
{% with inline_admin_formset.opts.sortable_field_name|default:"" as sortable_field_name %}
{% for inline_admin_form in inline_admin_formset|formsetsort:sortable_field_name %}
<tbody class="djn-tbody{% if not forloop.last or not inline_admin_formset.has_add_permission %} djn-item{% endif %} djn-inline-form{% if inline_admin_formset.opts.classes %} {{ inline_admin_formset.opts.classes|join:" " }}{% endif %}{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} djn-empty-form empty-form{% endif %}{% if inline_admin_form.form.inlines %} djn-has-inlines{% endif %}"
data-inline-model="{{ inline_admin_form.model_admin.opts.app_label }}-{{ inline_admin_form.model_admin.opts.model_name }}"
{% if inline_admin_form.pk_field.field %}
data-is-initial="{% if inline_admin_form.pk_field.field.value %}true{% else %}false{% endif %}"
{% endif %}
id="{{ inline_admin_formset.formset.prefix }}-{% if forloop.last and inline_admin_formset.has_add_permission %}empty{% else %}{%if is_nested %}{% endif %}{{ inline_admin_form.form|form_index }}{% endif %}">
{% if inline_admin_form.form.non_field_errors %}
<tr><td class="djn-td" colspan="{{ inline_admin_form|cell_count }}">
<ul class="errorlist">
<li>{{ inline_admin_form.form.non_field_errors }}</li>
</ul>
</td></tr>
{% endif %}
<tr class="djn-tr form-row{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}">
<td class="original{% if inline_admin_formset.opts.sortable_field_name %} is-sortable{% endif %}">
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
{% if inline_admin_form.original %}
{{ inline_admin_form.original }}
{% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}<a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="{% if inline_admin_formset.has_change_permission %}inlinechangelink{% else %}inlineviewlink{% endif %}">{% if inline_admin_formset.has_change_permission %}{% trans "Change" %}{% else %}{% trans "View" %}{% endif %}</a>{% endif %}
{% endif %}
{% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% trans "View on site" %}</a>{% endif %}
</p>{% endif %}
{% if inline_admin_formset.opts.sortable_field_name %}
<span class="djn-drag-handler"></span>
{% endif %}
{% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{% if inline_admin_form.fk_field %}{{ inline_admin_form.fk_field.field }}{% endif %}
{% spaceless %}
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if field.field.is_hidden %} {{ field.field }} {% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endspaceless %}
</td>
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if not field.field.is_hidden %}
<td class="djn-td field-{{ field.field.name }}">
{% if field.is_readonly %}
<p>{{ field.contents }}</p>
{% else %}
{% if field.field.errors %}
{{ field.field.errors.as_ul }}
{% endif %}
{{ field.field }}
{% endif %}
</td>
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% if inline_admin_form.form.inlines %}
<td class="djn-td">
{% for nested in inline_admin_form.form.inlines %}
{% include nested.opts.template with inline_admin_formset=nested %}
{% endfor %}
</td>
{% endif %}
{% if inline_admin_formset.formset.can_delete %}
{% if inline_admin_form.original %}
<td class="delete djn-delete-handler {{ inline_admin_formset.handler_classes|join:" " }}">{{ inline_admin_form.deletion_field.field }}</td>
{% else %}
<td class="delete">
<div><a class="inline-deletelink djn-remove-handler {{ inline_admin_formset.handler_classes|join:" " }}" href="javascript:void(0)">Remove</a></div>
</td>
{% endif %}
{% endif %}
</tr>
</tbody>
{% if forloop.last and inline_admin_formset.has_add_permission %}
<tbody>
<tr class="add-row">
<td colspan="{{ inline_admin_form|cell_count }}" class="djn-add-item">
<a href="javascript://" class="add-handler djn-add-handler {{ inline_admin_formset.handler_classes|join:" " }}">{% blocktrans with inline_admin_formset.opts.verbose_name|strip_parent_name:inline_opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}</a>
</td>
</tr>
</tbody>
{% endif %}
{% endfor %}
{% endwith %}
</table>
</fieldset>
</div>
</div>
{% endwith %}{# ends with inline_admin_formset.opts as inline_opts #}
{% endwith %}{# ends {% with inline_admin_formset.formset.is_nested as is_nested %} #}