+
+
{{ production_order_to_process_count }}
-
{% translate "à traiter" %}
+
{% translate "à traiter · en cours" %}
+
{% for order in production_order_to_process_list %}
@@ -83,12 +103,12 @@
{% empty %}
-
- {% translate "Aucun bon à traiter" %}
+ {% translate "Aucun bon à traiter ou en cours" %}
{% endfor %}
diff --git a/loko/home/tests.py b/loko/home/tests.py
index d859414..891a452 100644
--- a/loko/home/tests.py
+++ b/loko/home/tests.py
@@ -439,6 +439,69 @@ class ControllerShortcutsWidgetTests(TestCase):
self.assertContains(response, 'ITV-CTRL-002')
+class StockSummaryWidgetTests(TestCase):
+ def setUp(self):
+ User = get_user_model()
+ self.user = User.objects.create_user('stockuser', password='pass')
+ self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
+ self.role_admin, _ = Role.objects.get_or_create(name='admin')
+ self.user_config.roles.add(self.role_admin)
+
+ from stock.models import Warehouse, PreparationOrder, ProductionOrder
+ self.warehouse = Warehouse.objects.create(name='Entrepôt Principal')
+ from common.models import UserWarehouseAccess
+ UserWarehouseAccess.objects.create(user_config=self.user_config, warehouse=self.warehouse, can_manage=True)
+
+ # Bons de préparation
+ self.prep_to_process = PreparationOrder.objects.create(order_number='BP-TEST-001', status='to_process')
+ self.prep_pending = PreparationOrder.objects.create(order_number='BP-TEST-002', status='pending')
+ self.prep_processed = PreparationOrder.objects.create(order_number='BP-TEST-003', status='processed')
+
+ # Bons de confection
+ self.prod_to_process = ProductionOrder.objects.create(code='BC-TEST-001', warehouse=self.warehouse, status='to_process', quantity=1)
+ self.prod_in_production = ProductionOrder.objects.create(code='BC-TEST-002', warehouse=self.warehouse, status='in_production', quantity=1)
+ self.prod_completed = ProductionOrder.objects.create(code='BC-TEST-003', warehouse=self.warehouse, status='completed', quantity=1)
+
+ def test_stock_summary_widget_counts_and_context(self):
+ from django.urls import reverse
+ self.client.force_login(self.user)
+ response = self.client.get(reverse('home:index'), HTTP_ACCEPT_LANGUAGE='fr')
+ self.assertEqual(response.status_code, 200)
+
+ # Préparation: active = to_process (1) + pending (1) = 2
+ self.assertEqual(response.context['preparation_order_to_process_count'], 2)
+ self.assertEqual(response.context['preparation_order_to_process_only_count'], 1)
+ self.assertEqual(response.context['preparation_order_pending_count'], 1)
+
+ # Confection: active = to_process (1) + in_production (1) = 2
+ self.assertEqual(response.context['production_order_to_process_count'], 2)
+ self.assertEqual(response.context['production_order_to_process_only_count'], 1)
+ self.assertEqual(response.context['production_order_in_production_count'], 1)
+
+ def test_stock_movements_active_filter(self):
+ from django.urls import reverse
+ self.client.force_login(self.user)
+
+ # Préparation avec status=active
+ resp_prep = self.client.get(reverse('stock:stock_movements') + '?tab=preparation-orders&status=active')
+ self.assertEqual(resp_prep.status_code, 200)
+ prep_orders = list(resp_prep.context['preparation_orders'])
+ self.assertEqual(len(prep_orders), 2)
+ self.assertIn(self.prep_to_process, prep_orders)
+ self.assertIn(self.prep_pending, prep_orders)
+ self.assertNotIn(self.prep_processed, prep_orders)
+
+ # Confection avec prod_status=active
+ resp_prod = self.client.get(reverse('stock:stock_movements') + '?tab=production-orders&prod_status=active')
+ self.assertEqual(resp_prod.status_code, 200)
+ prod_orders = list(resp_prod.context['production_orders'])
+ self.assertEqual(len(prod_orders), 2)
+ self.assertIn(self.prod_to_process, prod_orders)
+ self.assertIn(self.prod_in_production, prod_orders)
+ self.assertNotIn(self.prod_completed, prod_orders)
+
+
+
diff --git a/loko/home/views.py b/loko/home/views.py
index 491bdda..e5926f7 100644
--- a/loko/home/views.py
+++ b/loko/home/views.py
@@ -479,12 +479,29 @@ def index(request):
t = _t('approval_counts', t, _perf_splits)
# ========== Données widget stock_summary ==========
+ _prep_counts = PreparationOrder.objects.aggregate(
+ active=Count('id', filter=Q(status__in=['to_process', 'pending'])),
+ to_process=Count('id', filter=Q(status='to_process')),
+ pending=Count('id', filter=Q(status='pending')),
+ )
+ preparation_order_to_process_count = _prep_counts['active']
+ preparation_order_to_process_only_count = _prep_counts['to_process']
+ preparation_order_pending_count = _prep_counts['pending']
_prep_qs = PreparationOrder.objects.filter(status__in=['to_process', 'pending'])
- preparation_order_to_process_count = _prep_qs.count()
preparation_order_to_process_list = list(_prep_qs.order_by('-date')[:5])
- _prod_qs = ProductionOrder.objects.filter(status__in=['to_process', 'in_production'])
- production_order_to_process_count = _prod_qs.count()
+ from stock.permissions import get_user_accessible_warehouses
+ accessible_warehouses = get_user_accessible_warehouses(request.user)
+ _prod_base_qs = ProductionOrder.objects.filter(warehouse__in=accessible_warehouses)
+ _prod_counts = _prod_base_qs.aggregate(
+ active=Count('id', filter=Q(status__in=['to_process', 'in_production'])),
+ to_process=Count('id', filter=Q(status=ProductionOrder.STATUS_TO_PROCESS)),
+ in_production=Count('id', filter=Q(status=ProductionOrder.STATUS_IN_PRODUCTION)),
+ )
+ production_order_to_process_count = _prod_counts['active']
+ production_order_to_process_only_count = _prod_counts['to_process']
+ production_order_in_production_count = _prod_counts['in_production']
+ _prod_qs = _prod_base_qs.filter(status__in=['to_process', 'in_production'])
production_order_to_process_list = list(
_prod_qs.select_related('warehouse').order_by('-created_at')[:5]
)
@@ -827,8 +844,12 @@ def index(request):
'show_checked_split': show_checked_split,
**split_checked_counts,
'preparation_order_to_process_count': preparation_order_to_process_count,
+ 'preparation_order_to_process_only_count': preparation_order_to_process_only_count,
+ 'preparation_order_pending_count': preparation_order_pending_count,
'preparation_order_to_process_list': preparation_order_to_process_list,
'production_order_to_process_count': production_order_to_process_count,
+ 'production_order_to_process_only_count': production_order_to_process_only_count,
+ 'production_order_in_production_count': production_order_in_production_count,
'production_order_to_process_list': production_order_to_process_list,
'stock_low_alerts_list': stock_low_alerts_list,
'loan_items_total': loan_items_total,
diff --git a/loko/mobile/views.py b/loko/mobile/views.py
index 9fb4ae2..26d5d3a 100644
--- a/loko/mobile/views.py
+++ b/loko/mobile/views.py
@@ -442,7 +442,10 @@ def stock_preparation_orders_mobile(request):
preparation_orders = PreparationOrder.objects.select_related('intervention').all()
if selected_status:
- preparation_orders = preparation_orders.filter(status=selected_status)
+ if selected_status == 'active':
+ preparation_orders = preparation_orders.filter(status__in=['to_process', 'pending'])
+ else:
+ preparation_orders = preparation_orders.filter(status=selected_status)
if selected_priority:
preparation_orders = preparation_orders.filter(priority=selected_priority)
if search_q:
@@ -465,6 +468,7 @@ def stock_preparation_orders_mobile(request):
'selected_priority': selected_priority,
'search_q': search_q,
'preparation_order_status_choices': [
+ ('active', _("À traiter & En attente")),
('to_process', _("A traiter")),
('pending', _("En attente")),
('processed', _("Traité")),
@@ -504,6 +508,7 @@ def stock_production_orders_mobile(request):
draft=models.Count('id', filter=models.Q(status=ProductionOrder.STATUS_DRAFT)),
cancelled=models.Count('id', filter=models.Q(status=ProductionOrder.STATUS_CANCELLED)),
)
+ prod_counts['active'] = prod_counts['to_process'] + prod_counts['in_production']
production_orders = base_qs.select_related(
'warehouse', 'preparation_order', 'output_product', 'output_location',
@@ -513,7 +518,12 @@ def stock_production_orders_mobile(request):
)
if selected_status:
- production_orders = production_orders.filter(status=selected_status)
+ if selected_status == 'active':
+ production_orders = production_orders.filter(
+ status__in=[ProductionOrder.STATUS_TO_PROCESS, ProductionOrder.STATUS_IN_PRODUCTION]
+ )
+ else:
+ production_orders = production_orders.filter(status=selected_status)
if selected_warehouse:
production_orders = production_orders.filter(warehouse_id=selected_warehouse)
if selected_destination:
@@ -542,7 +552,9 @@ def stock_production_orders_mobile(request):
'selected_status': selected_status,
'selected_warehouse': selected_warehouse,
'selected_destination': selected_destination,
- 'status_choices': ProductionOrder.STATUS_CHOICES,
+ 'status_choices': [
+ ('active', _("À traiter & En confection")),
+ ] + list(ProductionOrder.STATUS_CHOICES),
'destination_choices': ProductionOrder.DESTINATION_CHOICES,
'warehouses': accessible_warehouses,
'prod_counts': prod_counts,
diff --git a/loko/stock/locale/en/LC_MESSAGES/django.mo b/loko/stock/locale/en/LC_MESSAGES/django.mo
index 6158c5a..41857b9 100644
Binary files a/loko/stock/locale/en/LC_MESSAGES/django.mo and b/loko/stock/locale/en/LC_MESSAGES/django.mo differ
diff --git a/loko/stock/locale/en/LC_MESSAGES/django.po b/loko/stock/locale/en/LC_MESSAGES/django.po
index a811dc9..ccee12c 100644
--- a/loko/stock/locale/en/LC_MESSAGES/django.po
+++ b/loko/stock/locale/en/LC_MESSAGES/django.po
@@ -5923,3 +5923,16 @@ msgstr "Intervention canceled"
#~ msgid "Rafraîchir la page"
#~ msgstr "Refresh page"
+
+msgid "À traiter & En attente"
+msgstr "To process & Pending"
+
+msgid "À traiter & En confection"
+msgstr "To process & In production"
+
+msgid "À traiter & en attente"
+msgstr "To process & pending"
+
+msgid "À traiter & en confection"
+msgstr "To process & in production"
+
diff --git a/loko/stock/locale/fr/LC_MESSAGES/django.mo b/loko/stock/locale/fr/LC_MESSAGES/django.mo
index 6d748df..bb6e51d 100644
Binary files a/loko/stock/locale/fr/LC_MESSAGES/django.mo and b/loko/stock/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/loko/stock/locale/fr/LC_MESSAGES/django.po b/loko/stock/locale/fr/LC_MESSAGES/django.po
index 4560dfd..d226776 100644
--- a/loko/stock/locale/fr/LC_MESSAGES/django.po
+++ b/loko/stock/locale/fr/LC_MESSAGES/django.po
@@ -5730,3 +5730,16 @@ msgstr "Intervention annulée"
#~ msgid "Rafraîchir la page"
#~ msgstr "Rafraîchir la page"
+
+msgid "À traiter & En attente"
+msgstr "À traiter & En attente"
+
+msgid "À traiter & En confection"
+msgstr "À traiter & En confection"
+
+msgid "À traiter & en attente"
+msgstr "À traiter & en attente"
+
+msgid "À traiter & en confection"
+msgstr "À traiter & en confection"
+
diff --git a/loko/stock/locale/nl/LC_MESSAGES/django.mo b/loko/stock/locale/nl/LC_MESSAGES/django.mo
index 4b56376..f186469 100644
Binary files a/loko/stock/locale/nl/LC_MESSAGES/django.mo and b/loko/stock/locale/nl/LC_MESSAGES/django.mo differ
diff --git a/loko/stock/locale/nl/LC_MESSAGES/django.po b/loko/stock/locale/nl/LC_MESSAGES/django.po
index 87c1a42..817115e 100644
--- a/loko/stock/locale/nl/LC_MESSAGES/django.po
+++ b/loko/stock/locale/nl/LC_MESSAGES/django.po
@@ -5730,3 +5730,16 @@ msgstr "Interventie geannuleerd"
#~ msgid "Rafraîchir la page"
#~ msgstr "Pagina vernieuwen"
+
+msgid "À traiter & En attente"
+msgstr "Te verwerken & In afwachting"
+
+msgid "À traiter & En confection"
+msgstr "Te verwerken & In productie"
+
+msgid "À traiter & en attente"
+msgstr "Te verwerken & in afwachting"
+
+msgid "À traiter & en confection"
+msgstr "Te verwerken & in productie"
+
diff --git a/loko/stock/models.py b/loko/stock/models.py
index e9b2f66..6cf0e48 100644
--- a/loko/stock/models.py
+++ b/loko/stock/models.py
@@ -2246,8 +2246,10 @@ class ProductionOrder(models.Model):
return self.output_product_name
if self.preparation_order_other_item and self.preparation_order_other_item.description:
return self.preparation_order_other_item.description
- if self.notes and 'Issu de' in self.notes and ': ' in self.notes:
- return self.notes.split(': ', 1)[1].strip()
+ if self.notes and ': ' in self.notes:
+ for line in self.notes.splitlines():
+ if ': ' in line:
+ return line.split(': ', 1)[1].strip()
return ""
def __str__(self):
diff --git a/loko/stock/templates/stock/mobile/stock_production_orders_mobile.html b/loko/stock/templates/stock/mobile/stock_production_orders_mobile.html
index 72d7e69..bb49708 100644
--- a/loko/stock/templates/stock/mobile/stock_production_orders_mobile.html
+++ b/loko/stock/templates/stock/mobile/stock_production_orders_mobile.html
@@ -102,6 +102,10 @@
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 }})