feat: add order filtering and default descending sort for preparation and production orders

This commit is contained in:
kdeterme 2026-09-30 08:46:06 +02:00
parent 63a9a080de
commit a08ef0f035
8 changed files with 137 additions and 57 deletions

View file

@ -459,14 +459,22 @@ def stock_preparation_orders_mobile(request):
q_filter |= models.Q(intervention__id=int(search_q)) q_filter |= models.Q(intervention__id=int(search_q))
preparation_orders = preparation_orders.filter(q_filter) preparation_orders = preparation_orders.filter(q_filter)
preparation_orders = preparation_orders.prefetch_related( selected_order = request.GET.get('order', 'desc').strip()
'preparationorderitem_set__product' if selected_order == 'asc':
).order_by('date')[:200] preparation_orders = preparation_orders.prefetch_related(
'preparationorderitem_set__product'
).order_by('date')[:200]
else:
selected_order = 'desc'
preparation_orders = preparation_orders.prefetch_related(
'preparationorderitem_set__product'
).order_by('-date')[:200]
return render(request, "stock/mobile/stock_preparation_orders_mobile.html", { return render(request, "stock/mobile/stock_preparation_orders_mobile.html", {
'preparation_orders': preparation_orders, 'preparation_orders': preparation_orders,
'selected_status': selected_status, 'selected_status': selected_status,
'selected_priority': selected_priority, 'selected_priority': selected_priority,
'search_q': search_q, 'search_q': search_q,
'selected_order': selected_order,
'preparation_order_status_choices': [ 'preparation_order_status_choices': [
('active', _("À traiter & En attente")), ('active', _("À traiter & En attente")),
('to_process', _("A traiter")), ('to_process', _("A traiter")),
@ -544,7 +552,12 @@ def stock_production_orders_mobile(request):
q_filter |= models.Q(id=int(search_q)) | models.Q(preparation_order__intervention__id=int(search_q)) q_filter |= models.Q(id=int(search_q)) | models.Q(preparation_order__intervention__id=int(search_q))
production_orders = production_orders.filter(q_filter).distinct() production_orders = production_orders.filter(q_filter).distinct()
production_orders = production_orders.order_by('created_at')[:200] selected_order = request.GET.get('order', 'desc').strip()
if selected_order == 'asc':
production_orders = production_orders.order_by('created_at')[:200]
else:
selected_order = 'desc'
production_orders = production_orders.order_by('-created_at')[:200]
return render(request, "stock/mobile/stock_production_orders_mobile.html", { return render(request, "stock/mobile/stock_production_orders_mobile.html", {
'production_orders': production_orders, 'production_orders': production_orders,
@ -552,6 +565,7 @@ def stock_production_orders_mobile(request):
'selected_status': selected_status, 'selected_status': selected_status,
'selected_warehouse': selected_warehouse, 'selected_warehouse': selected_warehouse,
'selected_destination': selected_destination, 'selected_destination': selected_destination,
'selected_order': selected_order,
'status_choices': [ 'status_choices': [
('active', _("À traiter & En confection")), ('active', _("À traiter & En confection")),
] + list(ProductionOrder.STATUS_CHOICES), ] + list(ProductionOrder.STATUS_CHOICES),

View file

@ -15,11 +15,11 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='preparationorder', name='preparationorder',
options={'ordering': ('date',), 'verbose_name': 'Bon de préparation', 'verbose_name_plural': 'Bons de préparation'}, options={'ordering': ('-date',), 'verbose_name': 'Bon de préparation', 'verbose_name_plural': 'Bons de préparation'},
), ),
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='productionorder', name='productionorder',
options={'ordering': ('created_at',), 'verbose_name': 'Bon de confection', 'verbose_name_plural': 'Bons de confection'}, options={'ordering': ('-created_at',), 'verbose_name': 'Bon de confection', 'verbose_name_plural': 'Bons de confection'},
), ),
migrations.AddField( migrations.AddField(
model_name='stockmovementbatch', model_name='stockmovementbatch',

View file

@ -1451,7 +1451,7 @@ class PreparationOrder(models.Model):
} }
class Meta: class Meta:
ordering = ("date",) ordering = ("-date",)
verbose_name = _("Bon de préparation") verbose_name = _("Bon de préparation")
verbose_name_plural = _("Bons de préparation") verbose_name_plural = _("Bons de préparation")
@ -2248,7 +2248,7 @@ class ProductionOrder(models.Model):
) )
class Meta: class Meta:
ordering = ("created_at",) ordering = ("-created_at",)
verbose_name = _("Bon de confection") verbose_name = _("Bon de confection")
verbose_name_plural = _("Bons de confection") verbose_name_plural = _("Bons de confection")

View file

@ -47,23 +47,29 @@
<button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button> <button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-4">
<select class="form-select form-select-sm" name="status" onchange="this.form.submit()"> <select class="form-select form-select-sm" name="status" onchange="this.form.submit()">
<option value="">-- {% translate "Tous statuts" %} --</option> <option value="">-- {% translate "Statut" %} --</option>
{% for val, label in preparation_order_status_choices %} {% for val, label in preparation_order_status_choices %}
<option value="{{ val }}" {% if selected_status == val %}selected{% endif %}>{{ label }}</option> <option value="{{ val }}" {% if selected_status == val %}selected{% endif %}>{{ label }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="col-6"> <div class="col-4">
<select class="form-select form-select-sm" name="priority" onchange="this.form.submit()"> <select class="form-select form-select-sm" name="priority" onchange="this.form.submit()">
<option value="">-- {% translate "Toutes priorités" %} --</option> <option value="">-- {% translate "Priorité" %} --</option>
{% for val, label in preparation_order_priority_choices %} {% for val, label in preparation_order_priority_choices %}
<option value="{{ val }}" {% if selected_priority == val %}selected{% endif %}>{{ label }}</option> <option value="{{ val }}" {% if selected_priority == val %}selected{% endif %}>{{ label }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% if selected_status or selected_priority or search_q %} <div class="col-4">
<select class="form-select form-select-sm" name="order" onchange="this.form.submit()">
<option value="desc" {% if selected_order != 'asc' %}selected{% endif %}>{% translate "Plus récents" %}</option>
<option value="asc" {% if selected_order == 'asc' %}selected{% endif %}>{% translate "Plus anciens" %}</option>
</select>
</div>
{% if selected_status or selected_priority or search_q or selected_order == 'asc' %}
<div class="col-12 text-center mt-1"> <div class="col-12 text-center mt-1">
<a href="?" class="btn btn-sm btn-outline-secondary py-0" style="font-size:0.75rem;"> <a href="?" class="btn btn-sm btn-outline-secondary py-0" style="font-size:0.75rem;">
<i class="bi bi-x-circle me-1"></i>{% translate "Effacer les filtres" %} <i class="bi bi-x-circle me-1"></i>{% translate "Effacer les filtres" %}

View file

@ -60,32 +60,38 @@
<button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button> <button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button>
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-4">
<select class="form-select form-select-sm" name="status" onchange="this.form.submit()"> <select class="form-select form-select-sm" name="status" onchange="this.form.submit()">
<option value="">-- {% translate "Tous statuts" %} --</option> <option value="">-- {% translate "Statut" %} --</option>
{% for val, label in status_choices %} {% for val, label in status_choices %}
<option value="{{ val }}" {% if selected_status == val %}selected{% endif %}>{{ label }}</option> <option value="{{ val }}" {% if selected_status == val %}selected{% endif %}>{{ label }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="col-6"> <div class="col-4">
{% if warehouses|length > 1 %} {% if warehouses|length > 1 %}
<select class="form-select form-select-sm" name="warehouse" onchange="this.form.submit()"> <select class="form-select form-select-sm" name="warehouse" onchange="this.form.submit()">
<option value="">-- {% translate "Tous entrepôts" %} --</option> <option value="">-- {% translate "Entrepôt" %} --</option>
{% for wh in warehouses %} {% for wh in warehouses %}
<option value="{{ wh.pk }}" {% if selected_warehouse == wh.pk|stringformat:"s" %}selected{% endif %}>{{ wh.name }}</option> <option value="{{ wh.pk }}" {% if selected_warehouse == wh.pk|stringformat:"s" %}selected{% endif %}>{{ wh.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
{% else %} {% else %}
<select class="form-select form-select-sm" name="destination" onchange="this.form.submit()"> <select class="form-select form-select-sm" name="destination" onchange="this.form.submit()">
<option value="">-- {% translate "Toutes destinations" %} --</option> <option value="">-- {% translate "Dest." %} --</option>
{% for val, label in destination_choices %} {% for val, label in destination_choices %}
<option value="{{ val }}" {% if selected_destination == val %}selected{% endif %}>{{ label }}</option> <option value="{{ val }}" {% if selected_destination == val %}selected{% endif %}>{{ label }}</option>
{% endfor %} {% endfor %}
</select> </select>
{% endif %} {% endif %}
</div> </div>
{% if selected_status or selected_warehouse or selected_destination or search_q %} <div class="col-4">
<select class="form-select form-select-sm" name="order" onchange="this.form.submit()">
<option value="desc" {% if selected_order != 'asc' %}selected{% endif %}>{% translate "Plus récents" %}</option>
<option value="asc" {% if selected_order == 'asc' %}selected{% endif %}>{% translate "Plus anciens" %}</option>
</select>
</div>
{% if selected_status or selected_warehouse or selected_destination or search_q or selected_order == 'asc' %}
<div class="col-12 text-center mt-1"> <div class="col-12 text-center mt-1">
<a href="{% url 'mobile:stock_production_orders_mobile' %}" class="btn btn-sm btn-outline-secondary py-0" style="font-size:0.75rem;"> <a href="{% url 'mobile:stock_production_orders_mobile' %}" class="btn btn-sm btn-outline-secondary py-0" style="font-size:0.75rem;">
<i class="bi bi-x-circle me-1"></i>{% translate "Effacer les filtres" %} <i class="bi bi-x-circle me-1"></i>{% translate "Effacer les filtres" %}
@ -98,34 +104,34 @@
<!-- Filtres rapides par statut (Pills défilantes) --> <!-- Filtres rapides par statut (Pills défilantes) -->
<div class="d-flex gap-1 overflow-auto pb-2 mb-2 status-pills-bar" style="white-space: nowrap;"> <div class="d-flex gap-1 overflow-auto pb-2 mb-2 status-pills-bar" style="white-space: nowrap;">
<a href="?{% if search_q %}q={{ search_q }}&{% endif %}{% if selected_warehouse %}warehouse={{ selected_warehouse }}&{% endif %}{% if selected_destination %}destination={{ selected_destination }}&{% endif %}" <a href="?{% if search_q %}q={{ search_q }}&{% endif %}{% if selected_warehouse %}warehouse={{ selected_warehouse }}&{% endif %}{% if selected_destination %}destination={{ selected_destination }}&{% endif %}{% if selected_order %}order={{ selected_order }}&{% endif %}"
class="badge {% if not selected_status %}bg-primary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if not selected_status %}bg-primary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
{% translate "Tous" %} ({{ prod_counts.total|default:0 }}) {% translate "Tous" %} ({{ prod_counts.total|default:0 }})
</a> </a>
<a href="?status=active{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}" <a href="?status=active{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}{% if selected_order %}&order={{ selected_order }}{% endif %}"
class="badge {% if selected_status == 'active' %}bg-danger text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if selected_status == 'active' %}bg-danger text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
<i class="bi bi-play-circle me-1"></i>{% translate "À traiter & En confection" %} ({{ prod_counts.active|default:0 }}) <i class="bi bi-play-circle me-1"></i>{% translate "À traiter & En confection" %} ({{ prod_counts.active|default:0 }})
</a> </a>
<a href="?status=to_process{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}" <a href="?status=to_process{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}{% if selected_order %}&order={{ selected_order }}{% endif %}"
class="badge {% if selected_status == 'to_process' %}bg-danger text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if selected_status == 'to_process' %}bg-danger text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
<i class="bi bi-hourglass-split me-1"></i>{% translate "À traiter" %} ({{ prod_counts.to_process|default:0 }}) <i class="bi bi-hourglass-split me-1"></i>{% translate "À traiter" %} ({{ prod_counts.to_process|default:0 }})
</a> </a>
<a href="?status=in_production{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}" <a href="?status=in_production{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}{% if selected_order %}&order={{ selected_order }}{% endif %}"
class="badge {% if selected_status == 'in_production' %}bg-primary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if selected_status == 'in_production' %}bg-primary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
<i class="bi bi-gear-wide-connected me-1"></i>{% translate "En confection" %} ({{ prod_counts.in_production|default:0 }}) <i class="bi bi-gear-wide-connected me-1"></i>{% translate "En confection" %} ({{ prod_counts.in_production|default:0 }})
</a> </a>
<a href="?status=completed{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}" <a href="?status=completed{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}{% if selected_order %}&order={{ selected_order }}{% endif %}"
class="badge {% if selected_status == 'completed' %}bg-success text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if selected_status == 'completed' %}bg-success text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
<i class="bi bi-check-circle me-1"></i>{% translate "Terminé" %} ({{ prod_counts.completed|default:0 }}) <i class="bi bi-check-circle me-1"></i>{% translate "Terminé" %} ({{ prod_counts.completed|default:0 }})
</a> </a>
{% if prod_counts.draft %} {% if prod_counts.draft %}
<a href="?status=draft{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}" <a href="?status=draft{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}{% if selected_order %}&order={{ selected_order }}{% endif %}"
class="badge {% if selected_status == 'draft' %}bg-secondary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if selected_status == 'draft' %}bg-secondary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
<i class="bi bi-pencil me-1"></i>{% translate "Brouillon" %} ({{ prod_counts.draft|default:0 }}) <i class="bi bi-pencil me-1"></i>{% translate "Brouillon" %} ({{ prod_counts.draft|default:0 }})
</a> </a>
{% endif %} {% endif %}
{% if prod_counts.cancelled %} {% if prod_counts.cancelled %}
<a href="?status=cancelled{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}" <a href="?status=cancelled{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_warehouse %}&warehouse={{ selected_warehouse }}{% endif %}{% if selected_destination %}&destination={{ selected_destination }}{% endif %}{% if selected_order %}&order={{ selected_order }}{% endif %}"
class="badge {% if selected_status == 'cancelled' %}bg-secondary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2"> class="badge {% if selected_status == 'cancelled' %}bg-secondary text-white{% else %}bg-light text-dark border{% endif %} text-decoration-none py-1 px-2">
<i class="bi bi-x-circle me-1"></i>{% translate "Annulé" %} ({{ prod_counts.cancelled|default:0 }}) <i class="bi bi-x-circle me-1"></i>{% translate "Annulé" %} ({{ prod_counts.cancelled|default:0 }})
</a> </a>

View file

@ -61,7 +61,7 @@
<!-- Préserver l'onglet actif --> <!-- Préserver l'onglet actif -->
<input type="hidden" name="tab" value="preparation-orders"> <input type="hidden" name="tab" value="preparation-orders">
<div class="col-md-4"> <div class="col-md-3">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<span class="input-group-text"><i class="bi bi-search"></i></span> <span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" class="form-control" name="q" value="{{ search_q }}" placeholder="{% translate 'N° bon, code ou titre d\'intervention...' %}"> <input type="text" class="form-control" name="q" value="{{ search_q }}" placeholder="{% translate 'N° bon, code ou titre d\'intervention...' %}">
@ -69,7 +69,7 @@
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-2">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<label class="input-group-text" for="filter-status"><i class="bi bi-funnel me-1"></i>{% translate "Statut" %}</label> <label class="input-group-text" for="filter-status"><i class="bi bi-funnel me-1"></i>{% translate "Statut" %}</label>
<select class="form-select" id="filter-status" name="status" onchange="this.form.submit()"> <select class="form-select" id="filter-status" name="status" onchange="this.form.submit()">
@ -81,7 +81,7 @@
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-2">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<label class="input-group-text" for="filter-priority"><i class="bi bi-exclamation-triangle me-1"></i>{% translate "Priorité" %}</label> <label class="input-group-text" for="filter-priority"><i class="bi bi-exclamation-triangle me-1"></i>{% translate "Priorité" %}</label>
<select class="form-select" id="filter-priority" name="priority" onchange="this.form.submit()"> <select class="form-select" id="filter-priority" name="priority" onchange="this.form.submit()">
@ -93,9 +93,19 @@
</div> </div>
</div> </div>
<div class="col-md-3">
<div class="input-group input-group-sm">
<label class="input-group-text" for="filter-prep-order"><i class="bi bi-arrow-down-up me-1"></i>{% translate "Tri" %}</label>
<select class="form-select" id="filter-prep-order" name="prep_order" onchange="this.form.submit()">
<option value="desc" {% if prep_order != 'asc' %}selected{% endif %}>{% translate "Plus récents d'abord" %}</option>
<option value="asc" {% if prep_order == 'asc' %}selected{% endif %}>{% translate "Plus anciens d'abord (inversé)" %}</option>
</select>
</div>
</div>
<div class="col-md-2 d-flex justify-content-between align-items-center gap-1"> <div class="col-md-2 d-flex justify-content-between align-items-center gap-1">
<div> <div>
{% if selected_status or selected_priority or search_q %} {% if selected_status or selected_priority or search_q or prep_order == 'asc' %}
<a href="?tab=preparation-orders" class="btn btn-sm btn-outline-secondary" title="{% translate 'Effacer les filtres' %}"> <a href="?tab=preparation-orders" class="btn btn-sm btn-outline-secondary" title="{% translate 'Effacer les filtres' %}">
<i class="bi bi-x-circle me-1"></i>{% translate "Effacer" %} <i class="bi bi-x-circle me-1"></i>{% translate "Effacer" %}
</a> </a>
@ -114,23 +124,23 @@
<!-- Filtres rapides par statut (Pills) --> <!-- Filtres rapides par statut (Pills) -->
<div class="d-flex flex-wrap align-items-center gap-2 mb-3"> <div class="d-flex flex-wrap align-items-center gap-2 mb-3">
<span class="small text-muted fw-semibold me-1"><i class="bi bi-funnel me-1"></i>{% translate "Vue rapide :" %}</span> <span class="small text-muted fw-semibold me-1"><i class="bi bi-funnel me-1"></i>{% translate "Vue rapide :" %}</span>
<a href="?tab=preparation-orders{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}" <a href="?tab=preparation-orders{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}{% if prep_order %}&prep_order={{ prep_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if not selected_status %}btn-primary{% else %}btn-outline-secondary{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if not selected_status %}btn-primary{% else %}btn-outline-secondary{% endif %} rounded-pill">
{% translate "Tous" %} <span class="badge {% if not selected_status %}bg-white text-primary{% else %}bg-secondary{% endif %} ms-1">{{ prep_counts.total|default:0 }}</span> {% translate "Tous" %} <span class="badge {% if not selected_status %}bg-white text-primary{% else %}bg-secondary{% endif %} ms-1">{{ prep_counts.total|default:0 }}</span>
</a> </a>
<a href="?tab=preparation-orders&status=active{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}" <a href="?tab=preparation-orders&status=active{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}{% if prep_order %}&prep_order={{ prep_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if selected_status == 'active' %}btn-danger text-white{% else %}btn-outline-danger{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if selected_status == 'active' %}btn-danger text-white{% else %}btn-outline-danger{% endif %} rounded-pill">
<i class="bi bi-play-circle me-1"></i>{% translate "À traiter & En attente" %} <span class="badge {% if selected_status == 'active' %}bg-white text-danger{% else %}bg-danger text-white{% endif %} ms-1">{{ prep_counts.active|default:0 }}</span> <i class="bi bi-play-circle me-1"></i>{% translate "À traiter & En attente" %} <span class="badge {% if selected_status == 'active' %}bg-white text-danger{% else %}bg-danger text-white{% endif %} ms-1">{{ prep_counts.active|default:0 }}</span>
</a> </a>
<a href="?tab=preparation-orders&status=to_process{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}" <a href="?tab=preparation-orders&status=to_process{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}{% if prep_order %}&prep_order={{ prep_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if selected_status == 'to_process' %}btn-warning text-dark{% else %}btn-outline-warning text-dark{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if selected_status == 'to_process' %}btn-warning text-dark{% else %}btn-outline-warning text-dark{% endif %} rounded-pill">
<i class="bi bi-hourglass-split me-1"></i>{% translate "À traiter" %} <span class="badge {% if selected_status == 'to_process' %}bg-dark text-white{% else %}bg-warning text-dark{% endif %} ms-1">{{ prep_counts.to_process|default:0 }}</span> <i class="bi bi-hourglass-split me-1"></i>{% translate "À traiter" %} <span class="badge {% if selected_status == 'to_process' %}bg-dark text-white{% else %}bg-warning text-dark{% endif %} ms-1">{{ prep_counts.to_process|default:0 }}</span>
</a> </a>
<a href="?tab=preparation-orders&status=pending{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}" <a href="?tab=preparation-orders&status=pending{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}{% if prep_order %}&prep_order={{ prep_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if selected_status == 'pending' %}btn-info text-white{% else %}btn-outline-info{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if selected_status == 'pending' %}btn-info text-white{% else %}btn-outline-info{% endif %} rounded-pill">
<i class="bi bi-clock me-1"></i>{% translate "En attente" %} <span class="badge {% if selected_status == 'pending' %}bg-white text-info{% else %}bg-info text-white{% endif %} ms-1">{{ prep_counts.pending|default:0 }}</span> <i class="bi bi-clock me-1"></i>{% translate "En attente" %} <span class="badge {% if selected_status == 'pending' %}bg-white text-info{% else %}bg-info text-white{% endif %} ms-1">{{ prep_counts.pending|default:0 }}</span>
</a> </a>
<a href="?tab=preparation-orders&status=processed{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}" <a href="?tab=preparation-orders&status=processed{% if search_q %}&q={{ search_q }}{% endif %}{% if selected_priority %}&priority={{ selected_priority }}{% endif %}{% if prep_order %}&prep_order={{ prep_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if selected_status == 'processed' %}btn-success{% else %}btn-outline-success{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if selected_status == 'processed' %}btn-success{% else %}btn-outline-success{% endif %} rounded-pill">
<i class="bi bi-check-circle me-1"></i>{% translate "Traité" %} <span class="badge {% if selected_status == 'processed' %}bg-white text-success{% else %}bg-success text-white{% endif %} ms-1">{{ prep_counts.processed|default:0 }}</span> <i class="bi bi-check-circle me-1"></i>{% translate "Traité" %} <span class="badge {% if selected_status == 'processed' %}bg-white text-success{% else %}bg-success text-white{% endif %} ms-1">{{ prep_counts.processed|default:0 }}</span>
</a> </a>
@ -236,10 +246,10 @@
<!-- Préserver l'onglet actif --> <!-- Préserver l'onglet actif -->
<input type="hidden" name="tab" value="production-orders"> <input type="hidden" name="tab" value="production-orders">
<div class="col-md-3"> <div class="col-md-2">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<span class="input-group-text"><i class="bi bi-search"></i></span> <span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" class="form-control" name="prod_q" value="{{ prod_search_q }}" placeholder="{% translate 'Code, produit, bon prép, composant...' %}"> <input type="text" class="form-control" name="prod_q" value="{{ prod_search_q }}" placeholder="{% translate 'Code, produit, bon...' %}">
<button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button> <button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button>
</div> </div>
</div> </div>
@ -270,7 +280,7 @@
<div class="col-md-2"> <div class="col-md-2">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<label class="input-group-text" for="filter-prod-destination"><i class="bi bi-geo-alt me-1"></i>{% translate "Destination" %}</label> <label class="input-group-text" for="filter-prod-destination"><i class="bi bi-geo-alt me-1"></i>{% translate "Dest." %}</label>
<select class="form-select" id="filter-prod-destination" name="prod_destination" onchange="this.form.submit()"> <select class="form-select" id="filter-prod-destination" name="prod_destination" onchange="this.form.submit()">
<option value="">-- {% translate "Toutes" %} --</option> <option value="">-- {% translate "Toutes" %} --</option>
{% for val, label in prod_destination_choices %} {% for val, label in prod_destination_choices %}
@ -280,15 +290,25 @@
</div> </div>
</div> </div>
<div class="col-md-3 d-flex justify-content-end align-items-center gap-1"> <div class="col-md-2">
{% if prod_status or prod_warehouse or prod_destination or prod_search_q %} <div class="input-group input-group-sm">
<label class="input-group-text" for="filter-prod-order"><i class="bi bi-arrow-down-up me-1"></i>{% translate "Tri" %}</label>
<select class="form-select" id="filter-prod-order" name="prod_order" onchange="this.form.submit()">
<option value="desc" {% if prod_order != 'asc' %}selected{% endif %}>{% translate "Plus récents" %}</option>
<option value="asc" {% if prod_order == 'asc' %}selected{% endif %}>{% translate "Plus anciens" %}</option>
</select>
</div>
</div>
<div class="col-md-2 d-flex justify-content-end align-items-center gap-1">
{% if prod_status or prod_warehouse or prod_destination or prod_search_q or prod_order == 'asc' %}
<a href="?tab=production-orders" class="btn btn-sm btn-outline-secondary" title="{% translate 'Effacer les filtres' %}"> <a href="?tab=production-orders" class="btn btn-sm btn-outline-secondary" title="{% translate 'Effacer les filtres' %}">
<i class="bi bi-x-circle me-1"></i>{% translate "Effacer" %} <i class="bi bi-x-circle me-1"></i>{% translate "Effacer" %}
</a> </a>
{% endif %} {% endif %}
{% if can_create_production_order %} {% if can_create_production_order %}
<a href="{% url 'stock:production_order_create' %}" class="btn btn-sm btn-primary text-nowrap"> <a href="{% url 'stock:production_order_create' %}" class="btn btn-sm btn-primary text-nowrap">
<i class="bi bi-plus-circle me-1"></i>{% translate "Nouveau bon" %} <i class="bi bi-plus-circle me-1"></i>{% translate "Nouveau" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
@ -299,34 +319,34 @@
<!-- Filtres rapides par statut (Pills) --> <!-- Filtres rapides par statut (Pills) -->
<div class="d-flex flex-wrap align-items-center gap-2 mb-3"> <div class="d-flex flex-wrap align-items-center gap-2 mb-3">
<span class="small text-muted fw-semibold me-1"><i class="bi bi-funnel me-1"></i>{% translate "Vue rapide :" %}</span> <span class="small text-muted fw-semibold me-1"><i class="bi bi-funnel me-1"></i>{% translate "Vue rapide :" %}</span>
<a href="?tab=production-orders{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if not prod_status %}btn-primary{% else %}btn-outline-secondary{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if not prod_status %}btn-primary{% else %}btn-outline-secondary{% endif %} rounded-pill">
{% translate "Tous" %} <span class="badge {% if not prod_status %}bg-white text-primary{% else %}bg-secondary{% endif %} ms-1">{{ prod_counts.total|default:0 }}</span> {% translate "Tous" %} <span class="badge {% if not prod_status %}bg-white text-primary{% else %}bg-secondary{% endif %} ms-1">{{ prod_counts.total|default:0 }}</span>
</a> </a>
<a href="?tab=production-orders&prod_status=active{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders&prod_status=active{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if prod_status == 'active' %}btn-danger text-white{% else %}btn-outline-danger{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if prod_status == 'active' %}btn-danger text-white{% else %}btn-outline-danger{% endif %} rounded-pill">
<i class="bi bi-play-circle me-1"></i>{% translate "À traiter & En confection" %} <span class="badge {% if prod_status == 'active' %}bg-white text-danger{% else %}bg-danger text-white{% endif %} ms-1">{{ prod_counts.active|default:0 }}</span> <i class="bi bi-play-circle me-1"></i>{% translate "À traiter & En confection" %} <span class="badge {% if prod_status == 'active' %}bg-white text-danger{% else %}bg-danger text-white{% endif %} ms-1">{{ prod_counts.active|default:0 }}</span>
</a> </a>
<a href="?tab=production-orders&prod_status=to_process{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders&prod_status=to_process{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if prod_status == 'to_process' %}btn-danger text-white{% else %}btn-outline-danger{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if prod_status == 'to_process' %}btn-danger text-white{% else %}btn-outline-danger{% endif %} rounded-pill">
<i class="bi bi-hourglass-split me-1"></i>{% translate "À traiter" %} <span class="badge {% if prod_status == 'to_process' %}bg-white text-danger{% else %}bg-danger text-white{% endif %} ms-1">{{ prod_counts.to_process|default:0 }}</span> <i class="bi bi-hourglass-split me-1"></i>{% translate "À traiter" %} <span class="badge {% if prod_status == 'to_process' %}bg-white text-danger{% else %}bg-danger text-white{% endif %} ms-1">{{ prod_counts.to_process|default:0 }}</span>
</a> </a>
<a href="?tab=production-orders&prod_status=in_production{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders&prod_status=in_production{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if prod_status == 'in_production' %}btn-primary{% else %}btn-outline-primary{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if prod_status == 'in_production' %}btn-primary{% else %}btn-outline-primary{% endif %} rounded-pill">
<i class="bi bi-gear-wide-connected me-1"></i>{% translate "En confection" %} <span class="badge {% if prod_status == 'in_production' %}bg-white text-primary{% else %}bg-primary text-white{% endif %} ms-1">{{ prod_counts.in_production|default:0 }}</span> <i class="bi bi-gear-wide-connected me-1"></i>{% translate "En confection" %} <span class="badge {% if prod_status == 'in_production' %}bg-white text-primary{% else %}bg-primary text-white{% endif %} ms-1">{{ prod_counts.in_production|default:0 }}</span>
</a> </a>
<a href="?tab=production-orders&prod_status=completed{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders&prod_status=completed{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if prod_status == 'completed' %}btn-success{% else %}btn-outline-success{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if prod_status == 'completed' %}btn-success{% else %}btn-outline-success{% endif %} rounded-pill">
<i class="bi bi-check-circle me-1"></i>{% translate "Terminé" %} <span class="badge {% if prod_status == 'completed' %}bg-white text-success{% else %}bg-success text-white{% endif %} ms-1">{{ prod_counts.completed|default:0 }}</span> <i class="bi bi-check-circle me-1"></i>{% translate "Terminé" %} <span class="badge {% if prod_status == 'completed' %}bg-white text-success{% else %}bg-success text-white{% endif %} ms-1">{{ prod_counts.completed|default:0 }}</span>
</a> </a>
{% if prod_counts.draft %} {% if prod_counts.draft %}
<a href="?tab=production-orders&prod_status=draft{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders&prod_status=draft{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if prod_status == 'draft' %}btn-secondary{% else %}btn-outline-secondary{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if prod_status == 'draft' %}btn-secondary{% else %}btn-outline-secondary{% endif %} rounded-pill">
<i class="bi bi-pencil me-1"></i>{% translate "Brouillon" %} <span class="badge {% if prod_status == 'draft' %}bg-white text-secondary{% else %}bg-secondary text-white{% endif %} ms-1">{{ prod_counts.draft|default:0 }}</span> <i class="bi bi-pencil me-1"></i>{% translate "Brouillon" %} <span class="badge {% if prod_status == 'draft' %}bg-white text-secondary{% else %}bg-secondary text-white{% endif %} ms-1">{{ prod_counts.draft|default:0 }}</span>
</a> </a>
{% endif %} {% endif %}
{% if prod_counts.cancelled %} {% if prod_counts.cancelled %}
<a href="?tab=production-orders&prod_status=cancelled{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}" <a href="?tab=production-orders&prod_status=cancelled{% if prod_search_q %}&prod_q={{ prod_search_q }}{% endif %}{% if prod_warehouse %}&prod_warehouse={{ prod_warehouse }}{% endif %}{% if prod_destination %}&prod_destination={{ prod_destination }}{% endif %}{% if prod_order %}&prod_order={{ prod_order }}{% endif %}"
class="btn btn-sm py-1 px-2 {% if prod_status == 'cancelled' %}btn-secondary{% else %}btn-outline-secondary{% endif %} rounded-pill"> class="btn btn-sm py-1 px-2 {% if prod_status == 'cancelled' %}btn-secondary{% else %}btn-outline-secondary{% endif %} rounded-pill">
<i class="bi bi-x-circle me-1"></i>{% translate "Annulé" %} <span class="badge {% if prod_status == 'cancelled' %}bg-white text-secondary{% else %}bg-secondary text-white{% endif %} ms-1">{{ prod_counts.cancelled|default:0 }}</span> <i class="bi bi-x-circle me-1"></i>{% translate "Annulé" %} <span class="badge {% if prod_status == 'cancelled' %}bg-white text-secondary{% else %}bg-secondary text-white{% endif %} ms-1">{{ prod_counts.cancelled|default:0 }}</span>
</a> </a>

View file

@ -575,29 +575,51 @@ class PreparationOrderPriorityTests(TestCase):
prod1.refresh_from_db() prod1.refresh_from_db()
prod2.refresh_from_db() prod2.refresh_from_db()
# 1. stock_movements page: check chronological (FIFO) order # 1. stock_movements page: default is most recent first (descending)
resp = self.client.get(reverse("stock:stock_movements") + "?tab=preparation-orders") resp = self.client.get(reverse("stock:stock_movements") + "?tab=preparation-orders")
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
prep_orders = list(resp.context["preparation_orders"]) prep_orders = list(resp.context["preparation_orders"])
self.assertLess(prep_orders.index(po1), prep_orders.index(po2)) self.assertLess(prep_orders.index(po2), prep_orders.index(po1))
resp_prod = self.client.get(reverse("stock:stock_movements") + f"?tab=production-orders&prod_warehouse={wh.id}") resp_prod = self.client.get(reverse("stock:stock_movements") + f"?tab=production-orders&prod_warehouse={wh.id}")
self.assertEqual(resp_prod.status_code, 200) self.assertEqual(resp_prod.status_code, 200)
prod_orders = list(resp_prod.context["production_orders"]) prod_orders = list(resp_prod.context["production_orders"])
self.assertLess(prod_orders.index(prod1), prod_orders.index(prod2)) self.assertLess(prod_orders.index(prod2), prod_orders.index(prod1))
# 2. Mobile pages: check chronological order # Inverted sort option (ascending / oldest first)
resp_inv = self.client.get(reverse("stock:stock_movements") + "?tab=preparation-orders&prep_order=asc")
self.assertEqual(resp_inv.status_code, 200)
prep_orders_inv = list(resp_inv.context["preparation_orders"])
self.assertLess(prep_orders_inv.index(po1), prep_orders_inv.index(po2))
resp_prod_inv = self.client.get(reverse("stock:stock_movements") + f"?tab=production-orders&prod_warehouse={wh.id}&prod_order=asc")
self.assertEqual(resp_prod_inv.status_code, 200)
prod_orders_inv = list(resp_prod_inv.context["production_orders"])
self.assertLess(prod_orders_inv.index(prod1), prod_orders_inv.index(prod2))
# 2. Mobile pages: default is most recent first (descending)
resp_m_prep = self.client.get(reverse("mobile:stock_preparation_orders_mobile")) resp_m_prep = self.client.get(reverse("mobile:stock_preparation_orders_mobile"))
self.assertEqual(resp_m_prep.status_code, 200) self.assertEqual(resp_m_prep.status_code, 200)
m_prep_orders = list(resp_m_prep.context["preparation_orders"]) m_prep_orders = list(resp_m_prep.context["preparation_orders"])
self.assertLess(m_prep_orders.index(po1), m_prep_orders.index(po2)) self.assertLess(m_prep_orders.index(po2), m_prep_orders.index(po1))
resp_m_prod = self.client.get(reverse("mobile:stock_production_orders_mobile")) resp_m_prod = self.client.get(reverse("mobile:stock_production_orders_mobile"))
self.assertEqual(resp_m_prod.status_code, 200) self.assertEqual(resp_m_prod.status_code, 200)
m_prod_orders = list(resp_m_prod.context["production_orders"]) m_prod_orders = list(resp_m_prod.context["production_orders"])
self.assertLess(m_prod_orders.index(prod1), m_prod_orders.index(prod2)) self.assertLess(m_prod_orders.index(prod2), m_prod_orders.index(prod1))
# 3. Home widget # Mobile pages: inverted sort option (ascending / oldest first)
resp_m_prep_inv = self.client.get(reverse("mobile:stock_preparation_orders_mobile") + "?order=asc")
self.assertEqual(resp_m_prep_inv.status_code, 200)
m_prep_orders_inv = list(resp_m_prep_inv.context["preparation_orders"])
self.assertLess(m_prep_orders_inv.index(po1), m_prep_orders_inv.index(po2))
resp_m_prod_inv = self.client.get(reverse("mobile:stock_production_orders_mobile") + "?order=asc")
self.assertEqual(resp_m_prod_inv.status_code, 200)
m_prod_orders_inv = list(resp_m_prod_inv.context["production_orders"])
self.assertLess(m_prod_orders_inv.index(prod1), m_prod_orders_inv.index(prod2))
# 3. Home shortcut widgets: default is oldest first (FIFO)
resp_home = self.client.get(reverse("home:index")) resp_home = self.client.get(reverse("home:index"))
self.assertEqual(resp_home.status_code, 200) self.assertEqual(resp_home.status_code, 200)
home_preps = list(resp_home.context["preparation_order_to_process_list"]) home_preps = list(resp_home.context["preparation_order_to_process_list"])

View file

@ -594,6 +594,7 @@ def stock_movements(request):
selected_status = request.GET.get('prep_status', '').strip() or (request.GET.get('status', '').strip() if tab != 'production-orders' else '') selected_status = request.GET.get('prep_status', '').strip() or (request.GET.get('status', '').strip() if tab != 'production-orders' else '')
selected_priority = request.GET.get('priority', '').strip() selected_priority = request.GET.get('priority', '').strip()
search_q = request.GET.get('prep_q', '').strip() or (request.GET.get('q', '').strip() if tab != 'production-orders' else '') search_q = request.GET.get('prep_q', '').strip() or (request.GET.get('q', '').strip() if tab != 'production-orders' else '')
prep_order = request.GET.get('prep_order', '').strip() or (request.GET.get('order', '').strip() if tab != 'production-orders' else '')
prep_counts = PreparationOrder.objects.aggregate( prep_counts = PreparationOrder.objects.aggregate(
total=models.Count('id'), total=models.Count('id'),
@ -622,7 +623,11 @@ def stock_movements(request):
if search_q.isdigit(): if search_q.isdigit():
q_filter |= models.Q(intervention__id=int(search_q)) q_filter |= models.Q(intervention__id=int(search_q))
preparation_orders = preparation_orders.filter(q_filter) preparation_orders = preparation_orders.filter(q_filter)
preparation_orders = preparation_orders.order_by("date")[:1000] if prep_order == 'asc':
preparation_orders = preparation_orders.order_by("date")[:1000]
else:
prep_order = 'desc'
preparation_orders = preparation_orders.order_by("-date")[:1000]
# Filtrer selon les entrepôts accessibles # Filtrer selon les entrepôts accessibles
accessible_warehouses = get_user_accessible_warehouses(request.user) accessible_warehouses = get_user_accessible_warehouses(request.user)
@ -632,6 +637,7 @@ def stock_movements(request):
prod_search_q = request.GET.get('prod_q', '').strip() or (request.GET.get('q', '').strip() if tab == 'production-orders' else '') prod_search_q = request.GET.get('prod_q', '').strip() or (request.GET.get('q', '').strip() if tab == 'production-orders' else '')
prod_warehouse = request.GET.get('prod_warehouse', '').strip() or (request.GET.get('warehouse', '').strip() if tab == 'production-orders' else '') prod_warehouse = request.GET.get('prod_warehouse', '').strip() or (request.GET.get('warehouse', '').strip() if tab == 'production-orders' else '')
prod_destination = request.GET.get('prod_destination', '').strip() or (request.GET.get('destination', '').strip() if tab == 'production-orders' else '') prod_destination = request.GET.get('prod_destination', '').strip() or (request.GET.get('destination', '').strip() if tab == 'production-orders' else '')
prod_order = request.GET.get('prod_order', '').strip() or (request.GET.get('order', '').strip() if tab == 'production-orders' else '')
prod_base_qs = ProductionOrder.objects.filter(warehouse__in=accessible_warehouses) prod_base_qs = ProductionOrder.objects.filter(warehouse__in=accessible_warehouses)
@ -681,7 +687,11 @@ def stock_movements(request):
prod_q_filter |= models.Q(id=int(prod_search_q)) | models.Q(preparation_order__intervention__id=int(prod_search_q)) prod_q_filter |= models.Q(id=int(prod_search_q)) | models.Q(preparation_order__intervention__id=int(prod_search_q))
production_orders = production_orders.filter(prod_q_filter).distinct() production_orders = production_orders.filter(prod_q_filter).distinct()
production_orders = production_orders.order_by("created_at")[:1000] if prod_order == 'asc':
production_orders = production_orders.order_by("created_at")[:1000]
else:
prod_order = 'desc'
production_orders = production_orders.order_by("-created_at")[:1000]
movements = StockMovement.objects.filter( movements = StockMovement.objects.filter(
models.Q(source_location__warehouse__id__in=accessible_warehouses) | models.Q(source_location__warehouse__id__in=accessible_warehouses) |
@ -771,10 +781,12 @@ def stock_movements(request):
"selected_status": selected_status, "selected_status": selected_status,
"selected_priority": selected_priority, "selected_priority": selected_priority,
"search_q": search_q, "search_q": search_q,
"prep_order": prep_order,
"prod_status": prod_status, "prod_status": prod_status,
"prod_search_q": prod_search_q, "prod_search_q": prod_search_q,
"prod_warehouse": prod_warehouse, "prod_warehouse": prod_warehouse,
"prod_destination": prod_destination, "prod_destination": prod_destination,
"prod_order": prod_order,
"prod_status_choices": [ "prod_status_choices": [
('active', _("À traiter & En confection")), ('active', _("À traiter & En confection")),
] + list(ProductionOrder.STATUS_CHOICES), ] + list(ProductionOrder.STATUS_CHOICES),