Compare commits
No commits in common. "3b716d6b35db80b5a61f4da89ce59ed71e50b5e7" and "1b5630c7e93d6bed3f9121953b8ef4fa5103e4ab" have entirely different histories.
3b716d6b35
...
1b5630c7e9
@ -10,12 +10,6 @@ import nested_admin
|
|||||||
|
|
||||||
from . import models
|
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):
|
class MyAdminSite(admin.AdminSite):
|
||||||
site_header = "Fournée 2.0"
|
site_header = "Fournée 2.0"
|
||||||
@ -38,17 +32,9 @@ class PainAdmin(admin.ModelAdmin):
|
|||||||
admin_site.register(models.Pain, PainAdmin)
|
admin_site.register(models.Pain, PainAdmin)
|
||||||
|
|
||||||
|
|
||||||
class DocumentInline(admin.TabularInline):
|
|
||||||
model = models.Document
|
|
||||||
extra = 1
|
|
||||||
|
|
||||||
|
|
||||||
class FournitureAdmin(admin.ModelAdmin):
|
class FournitureAdmin(admin.ModelAdmin):
|
||||||
list_display = ["nom", "ordre", "couleur", "conditionnement", "emballage"]
|
list_display = ["nom", "ordre", "couleur", "conditionnement", "emballage"]
|
||||||
list_editable = ["ordre", "couleur", "conditionnement", "emballage"]
|
list_editable = ["ordre", "couleur", "conditionnement", "emballage"]
|
||||||
inlines = [
|
|
||||||
DocumentInline,
|
|
||||||
]
|
|
||||||
actions = None
|
actions = None
|
||||||
save_as = True
|
save_as = True
|
||||||
|
|
||||||
@ -138,14 +124,13 @@ class RéservationInline(nested_admin.NestedTabularInline):
|
|||||||
classes = ["collapse"]
|
classes = ["collapse"]
|
||||||
|
|
||||||
|
|
||||||
class CommandeInline(nested_admin.NestedTabularInline):
|
class CommandeInline(nested_admin.NestedStackedInline):
|
||||||
model = models.Commande
|
model = models.Commande
|
||||||
inlines = [
|
inlines = [
|
||||||
RéservationInline,
|
RéservationInline,
|
||||||
]
|
]
|
||||||
extra = 1
|
extra = 1
|
||||||
classes = ["collapse"]
|
classes = ["collapse"]
|
||||||
template = "nesting/admin/inlines/réservations_tabular.html"
|
|
||||||
|
|
||||||
|
|
||||||
class CouléeInline(
|
class CouléeInline(
|
||||||
@ -203,14 +188,14 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
self.message_user(
|
self.message_user(
|
||||||
request,
|
request,
|
||||||
f"Création d'une coulée de "
|
f"Création d'une coulée de "
|
||||||
f"{clean(pâte_manquante)} kg "
|
f"{pâte_manquante.normalize()} kg "
|
||||||
f"pour la pâte {pâte_nom}.",
|
f"pour la pâte {pâte_nom}.",
|
||||||
)
|
)
|
||||||
elif pâte_manquante < 0:
|
elif pâte_manquante < 0:
|
||||||
self.message_user(
|
self.message_user(
|
||||||
request,
|
request,
|
||||||
f"Attention la pâte {pâte_nom} est prévue "
|
f"Attention la pâte {pâte_nom} est prévue "
|
||||||
f"en excès ({-clean(pâte_manquante)} kg en trop)",
|
f"en excès ({-pâte_manquante.normalize()} kg en trop)",
|
||||||
messages.WARNING,
|
messages.WARNING,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -249,10 +234,10 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
for i in ingrédients:
|
for i in ingrédients:
|
||||||
quantités.append([
|
quantités.append([
|
||||||
i,
|
i,
|
||||||
clean(c.quantité / c.pâte.poids_de_base * sum(
|
(c.quantité / c.pâte.poids_de_base * sum(
|
||||||
c.pâte.ingrédient_set.filter(pk=i.pk)
|
c.pâte.ingrédient_set.filter(pk=i.pk)
|
||||||
.values_list("quantité", flat=True)
|
.values_list("quantité", flat=True)
|
||||||
))
|
)).quantize(Decimal('1.000')).normalize(),
|
||||||
])
|
])
|
||||||
|
|
||||||
masse_coulée = sum(
|
masse_coulée = sum(
|
||||||
@ -265,12 +250,12 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
problems = ""
|
problems = ""
|
||||||
elif masse_coulée > masse_commandée:
|
elif masse_coulée > masse_commandée:
|
||||||
problems = (
|
problems = (
|
||||||
f"{clean(masse_coulée - masse_commandée)} "
|
f"{(masse_coulée - masse_commandée).normalize()} "
|
||||||
"kg coulés en trop !!"
|
"kg coulés en trop !!"
|
||||||
)
|
)
|
||||||
elif masse_coulée < masse_commandée:
|
elif masse_coulée < masse_commandée:
|
||||||
problems = (
|
problems = (
|
||||||
f"{clean(masse_commandée - masse_coulée)} "
|
f"{(masse_commandée - masse_coulée).normalize()} "
|
||||||
"kg coulés en moins !!"
|
"kg coulés en moins !!"
|
||||||
)
|
)
|
||||||
extra_context["coulées"].append([problems, ingrédients, quantités])
|
extra_context["coulées"].append([problems, ingrédients, quantités])
|
||||||
@ -295,7 +280,7 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
[None, sum(obj.coulée_set.values_list('quantité', flat=True))]
|
[None, sum(obj.coulée_set.values_list('quantité', flat=True))]
|
||||||
]
|
]
|
||||||
for i in ingrédients:
|
for i in ingrédients:
|
||||||
total = clean(sum([
|
total = sum([
|
||||||
qté * qté_unit / unit
|
qté * qté_unit / unit
|
||||||
for qté, qté_unit, unit in
|
for qté, qté_unit, unit in
|
||||||
obj.coulée_set.filter(
|
obj.coulée_set.filter(
|
||||||
@ -306,7 +291,7 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
'pâte__ingrédient__quantité',
|
'pâte__ingrédient__quantité',
|
||||||
'pâte__poids_de_base',
|
'pâte__poids_de_base',
|
||||||
)
|
)
|
||||||
]))
|
]).quantize(Decimal('1.000')).normalize()
|
||||||
totaux.append([i, total])
|
totaux.append([i, total])
|
||||||
extra_context["totaux_coulées"] = totaux
|
extra_context["totaux_coulées"] = totaux
|
||||||
|
|
||||||
@ -322,22 +307,22 @@ class FournéeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
for p in pains:
|
for p in pains:
|
||||||
if c.réservation_set.filter(pain__nom=p).exists():
|
if c.réservation_set.filter(pain__nom=p).exists():
|
||||||
tmp.append(
|
tmp.append(
|
||||||
clean(sum(
|
sum(
|
||||||
c.réservation_set.filter(pain__nom=p).values_list(
|
c.réservation_set.filter(pain__nom=p).values_list(
|
||||||
"quantité", flat=True
|
"quantité", flat=True
|
||||||
)
|
)
|
||||||
))
|
).normalize()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
tmp.append("")
|
tmp.append("")
|
||||||
extra_context["divisions"].append(tmp)
|
extra_context["divisions"].append(tmp)
|
||||||
totaux = [sum(obj.coulée_set.values_list('quantité', flat=True))]
|
totaux = [sum(obj.coulée_set.values_list('quantité', flat=True))]
|
||||||
for p in pains:
|
for p in pains:
|
||||||
total = clean(sum(
|
total = sum(
|
||||||
obj.commande_set.filter(pains__nom=p).values_list(
|
obj.commande_set.filter(pains__nom=p).values_list(
|
||||||
"réservation__quantité", flat=True
|
"réservation__quantité", flat=True
|
||||||
)
|
)
|
||||||
))
|
).normalize()
|
||||||
totaux.append(total)
|
totaux.append(total)
|
||||||
extra_context["totaux_divisions"] = totaux
|
extra_context["totaux_divisions"] = totaux
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
# 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"],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,23 +0,0 @@
|
|||||||
# 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),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,14 +1,8 @@
|
|||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from colorfield.fields import ColorField
|
from colorfield.fields import ColorField
|
||||||
|
|
||||||
|
|
||||||
def upload_to(instance, filename):
|
|
||||||
return "{}/{}".format(uuid4(), filename)
|
|
||||||
|
|
||||||
|
|
||||||
class Fournée(models.Model):
|
class Fournée(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["-date"]
|
ordering = ["-date"]
|
||||||
@ -32,18 +26,6 @@ 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 Fourniture(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["ordre"]
|
ordering = ["ordre"]
|
||||||
@ -51,9 +33,7 @@ class Fourniture(models.Model):
|
|||||||
nom = models.CharField(max_length=64, unique=True)
|
nom = models.CharField(max_length=64, unique=True)
|
||||||
ordre = models.SmallIntegerField(default=0)
|
ordre = models.SmallIntegerField(default=0)
|
||||||
couleur = ColorField(default='#000000')
|
couleur = ColorField(default='#000000')
|
||||||
conditionnement = models.DecimalField(
|
conditionnement = models.DecimalField(max_digits=6, decimal_places=3)
|
||||||
max_digits=6, decimal_places=3, default=0
|
|
||||||
)
|
|
||||||
emballage = models.CharField(max_length=64, blank=True)
|
emballage = models.CharField(max_length=64, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -17,10 +17,6 @@ def submit_row_tag(parser, token):
|
|||||||
def emballe(quantité, fourniture):
|
def emballe(quantité, fourniture):
|
||||||
quotient = quantité // fourniture.conditionnement
|
quotient = quantité // fourniture.conditionnement
|
||||||
reste = 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:
|
if not quotient and reste < fourniture.conditionnement/2:
|
||||||
return None
|
return None
|
||||||
if quotient == 1 and reste < fourniture.conditionnement/2:
|
if quotient == 1 and reste < fourniture.conditionnement/2:
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
{% 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 %} <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 %} #}
|
|
Loading…
Reference in New Issue
Block a user