feat: standardize preparation order numbering format and add recodification command
This commit is contained in:
parent
dc6638c4a5
commit
71154a0fb4
8 changed files with 305 additions and 22 deletions
|
|
@ -47,6 +47,13 @@ class PreparationOrderForm(forms.ModelForm):
|
||||||
else:
|
else:
|
||||||
self.fields[field_name].widget.attrs['class'] = 'form-control'
|
self.fields[field_name].widget.attrs['class'] = 'form-control'
|
||||||
self.fields["requester"].required = False
|
self.fields["requester"].required = False
|
||||||
|
self.fields["order_number"].required = False
|
||||||
|
|
||||||
|
def clean_order_number(self):
|
||||||
|
order_number = self.cleaned_data.get('order_number')
|
||||||
|
if order_number:
|
||||||
|
return order_number.strip()
|
||||||
|
return order_number
|
||||||
|
|
||||||
|
|
||||||
# class PreparationOrderDesignForm(forms.Form):
|
# class PreparationOrderDesignForm(forms.Form):
|
||||||
|
|
|
||||||
102
loko/stock/management/commands/recodify_preparation_orders.py
Normal file
102
loko/stock/management/commands/recodify_preparation_orders.py
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
import re
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import transaction
|
||||||
|
from stock.models import PreparationOrder
|
||||||
|
from interventions.models import Intervention
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = (
|
||||||
|
"Met à jour la numérotation des bons de préparation liés à une intervention "
|
||||||
|
"au nouveau format standard : BP-<code_intervention>-<index:02d> (ex: BP-I113392-01)."
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--dry-run',
|
||||||
|
action='store_true',
|
||||||
|
help="Simule l'exécution sans modifier la base de données.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--intervention',
|
||||||
|
type=str,
|
||||||
|
help="ID ou Code d'une intervention spécifique à traiter (ex: 43047 ou I113392).",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--force',
|
||||||
|
action='store_true',
|
||||||
|
help="Recodifie même si le bon correspond déjà au format standard.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
dry_run = options['dry_run']
|
||||||
|
interv_filter = options['intervention']
|
||||||
|
force = options['force']
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
self.stdout.write(self.style.WARNING("--- MODE SIMULATION (dry-run) : aucune modification en base ---"))
|
||||||
|
|
||||||
|
qs = PreparationOrder.objects.filter(intervention__isnull=False).select_related('intervention')
|
||||||
|
|
||||||
|
if interv_filter:
|
||||||
|
if interv_filter.isdigit():
|
||||||
|
qs = qs.filter(intervention_id=int(interv_filter))
|
||||||
|
else:
|
||||||
|
qs = qs.filter(intervention__code=interv_filter)
|
||||||
|
|
||||||
|
interventions = list(Intervention.objects.filter(
|
||||||
|
id__in=qs.values_list('intervention_id', flat=True).distinct()
|
||||||
|
))
|
||||||
|
|
||||||
|
self.stdout.write(f"Vérification des bons pour {len(interventions)} intervention(s)...")
|
||||||
|
|
||||||
|
total_renamed = 0
|
||||||
|
|
||||||
|
# Pattern strict du format actuel : BP-<interv_code>-<2 chiffres>
|
||||||
|
for interv in interventions:
|
||||||
|
interv_code = interv.code or f"I{interv.id}"
|
||||||
|
interv_orders = list(qs.filter(intervention=interv).order_by('date', 'id'))
|
||||||
|
|
||||||
|
pattern = re.compile(rf"^BP-{re.escape(interv_code)}-\d{{2}}$")
|
||||||
|
|
||||||
|
# Déterminer les bons à renommer
|
||||||
|
orders_to_process = []
|
||||||
|
for order in interv_orders:
|
||||||
|
if force or not pattern.match(order.order_number):
|
||||||
|
orders_to_process.append(order)
|
||||||
|
|
||||||
|
if not orders_to_process and not force:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.stdout.write(f"\nIntervention {interv_code} (#{interv.id}) : {len(interv_orders)} bon(s) associé(s)")
|
||||||
|
|
||||||
|
# Assigner séquentiellement les nouveaux numéros
|
||||||
|
for idx, order in enumerate(interv_orders, start=1):
|
||||||
|
new_number = f"BP-{interv_code}-{idx:02d}"
|
||||||
|
old_number = order.order_number
|
||||||
|
|
||||||
|
if old_number == new_number:
|
||||||
|
self.stdout.write(f" - Bon #{order.id} : déjà conforme ({old_number})")
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.stdout.write(f" - Bon #{order.id} : {old_number} -> {self.style.SUCCESS(new_number)}")
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
with transaction.atomic():
|
||||||
|
order.order_number = new_number
|
||||||
|
order.save(update_fields=['order_number'])
|
||||||
|
order.generate_qr_code()
|
||||||
|
PreparationOrder.objects.filter(pk=order.pk).update(qr_code=order.qr_code.name)
|
||||||
|
order.log_history(
|
||||||
|
action="updated",
|
||||||
|
description=f"Recodification automatique du bon : {old_number} -> {new_number}",
|
||||||
|
user=None,
|
||||||
|
details={'old_order_number': old_number, 'new_order_number': new_number},
|
||||||
|
)
|
||||||
|
|
||||||
|
total_renamed += 1
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
self.stdout.write(self.style.WARNING(f"\n[Simulation terminée] {total_renamed} bon(s) seraient renommés."))
|
||||||
|
else:
|
||||||
|
self.stdout.write(self.style.SUCCESS(f"\n✓ Terminé : {total_renamed} bon(s) renommé(s) avec succès."))
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 6.0.7 on 2026-09-30 08:57
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('stock', '0004_alter_preparationorder_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='preparationorder',
|
||||||
|
name='order_number',
|
||||||
|
field=models.CharField(blank=True, max_length=100, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from django.contrib.gis.db import models
|
from django.contrib.gis.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Sum, F
|
from django.db.models import Sum, F, Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
import uuid
|
import uuid
|
||||||
|
|
@ -1387,7 +1387,7 @@ def update_stock_alerts(product, warehouse):
|
||||||
|
|
||||||
|
|
||||||
class PreparationOrder(models.Model):
|
class PreparationOrder(models.Model):
|
||||||
order_number = models.CharField(max_length=100, unique=True)
|
order_number = models.CharField(max_length=100, unique=True, blank=True)
|
||||||
date = models.DateTimeField(auto_now_add=True)
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
|
|
@ -1510,12 +1510,79 @@ class PreparationOrder(models.Model):
|
||||||
return self.estimated_return_date < timezone.now().date()
|
return self.estimated_return_date < timezone.now().date()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_order_number(cls, intervention=None):
|
||||||
|
"""
|
||||||
|
Génère un numéro de bon de préparation unique.
|
||||||
|
- Si rattaché à une intervention : BP-<code_intervention>-<index:02d>
|
||||||
|
Ex: BP-I113392-01, BP-I113392-02, etc.
|
||||||
|
- Si autonome (sans intervention) : BP-YYYYMMDD-<seq:03d>
|
||||||
|
Ex: BP-20260930-001, BP-20260930-002, etc.
|
||||||
|
"""
|
||||||
|
if intervention:
|
||||||
|
interv_code = (getattr(intervention, 'code', None) or '').strip()
|
||||||
|
if not interv_code:
|
||||||
|
interv_id = getattr(intervention, 'id', intervention)
|
||||||
|
interv_code = f"I{interv_id}"
|
||||||
|
|
||||||
|
prefix = f"BP-{interv_code}-"
|
||||||
|
|
||||||
|
interv_obj = intervention if hasattr(intervention, 'pk') else None
|
||||||
|
q_filter = Q(order_number__startswith=prefix)
|
||||||
|
if interv_obj:
|
||||||
|
q_filter |= Q(intervention=interv_obj)
|
||||||
|
|
||||||
|
existing_orders = list(cls.objects.filter(q_filter).values_list('order_number', flat=True))
|
||||||
|
|
||||||
|
existing_indexes = []
|
||||||
|
for num in existing_orders:
|
||||||
|
if num and num.startswith(prefix):
|
||||||
|
suffix = num[len(prefix):]
|
||||||
|
if suffix.isdigit():
|
||||||
|
existing_indexes.append(int(suffix))
|
||||||
|
|
||||||
|
linked_count = 0
|
||||||
|
if interv_obj:
|
||||||
|
linked_count = cls.objects.filter(intervention=interv_obj).count()
|
||||||
|
|
||||||
|
if existing_indexes:
|
||||||
|
next_index = max(max(existing_indexes), linked_count) + 1
|
||||||
|
elif linked_count > 0:
|
||||||
|
next_index = linked_count + 1
|
||||||
|
else:
|
||||||
|
next_index = 1
|
||||||
|
|
||||||
|
candidate = f"{prefix}{next_index:02d}"
|
||||||
|
while cls.objects.filter(order_number=candidate).exists():
|
||||||
|
next_index += 1
|
||||||
|
candidate = f"{prefix}{next_index:02d}"
|
||||||
|
return candidate
|
||||||
|
else:
|
||||||
|
import datetime
|
||||||
|
today = datetime.date.today()
|
||||||
|
prefix = f"BP-{today.strftime('%Y%m%d')}-"
|
||||||
|
existing = cls.objects.filter(order_number__startswith=prefix).values_list('order_number', flat=True)
|
||||||
|
existing_nums = []
|
||||||
|
for num in existing:
|
||||||
|
suffix = num[len(prefix):]
|
||||||
|
if suffix.isdigit():
|
||||||
|
existing_nums.append(int(suffix))
|
||||||
|
next_num = max(existing_nums, default=0) + 1
|
||||||
|
candidate = f"{prefix}{next_num:03d}"
|
||||||
|
while cls.objects.filter(order_number=candidate).exists():
|
||||||
|
next_num += 1
|
||||||
|
candidate = f"{prefix}{next_num:03d}"
|
||||||
|
return candidate
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
is_new = self.pk is None
|
is_new = self.pk is None
|
||||||
old_status = None
|
old_status = None
|
||||||
if not is_new:
|
if not is_new:
|
||||||
old_status = PreparationOrder.objects.filter(pk=self.pk).values_list('status', flat=True).first()
|
old_status = PreparationOrder.objects.filter(pk=self.pk).values_list('status', flat=True).first()
|
||||||
|
|
||||||
|
if not self.order_number:
|
||||||
|
self.order_number = self.generate_order_number(intervention=self.intervention)
|
||||||
|
|
||||||
# Ne pas re-générer le QR code lors des saves partiels (update_fields)
|
# Ne pas re-générer le QR code lors des saves partiels (update_fields)
|
||||||
update_fields = kwargs.get('update_fields')
|
update_fields = kwargs.get('update_fields')
|
||||||
if update_fields is None and self.order_number:
|
if update_fields is None and self.order_number:
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@
|
||||||
<span class="info-label"><i class="bi bi-hash me-1"></i>{% translate "Numéro" %}</span>
|
<span class="info-label"><i class="bi bi-hash me-1"></i>{% translate "Numéro" %}</span>
|
||||||
<span class="info-value">
|
<span class="info-value">
|
||||||
<a href="{% url 'mobile:intervention_detail_mobile' preparation_order.intervention.id %}">
|
<a href="{% url 'mobile:intervention_detail_mobile' preparation_order.intervention.id %}">
|
||||||
#{{ preparation_order.intervention.id }}
|
{{ preparation_order.intervention.code|default:preparation_order.intervention.id }}
|
||||||
</a>
|
</a>
|
||||||
{% if preparation_order.intervention.status == 'canceled' %}
|
{% if preparation_order.intervention.status == 'canceled' %}
|
||||||
<span class="badge bg-danger ms-1">{% translate "Annulée" %}</span>
|
<span class="badge bg-danger ms-1">{% translate "Annulée" %}</span>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<strong>{% translate "Intervention" %}:</strong>
|
<strong>{% translate "Intervention" %}:</strong>
|
||||||
<a href="{% url 'interventions:intervention_detail' intervention.id %}" target="_blank">
|
<a href="{% url 'interventions:intervention_detail' intervention.id %}" target="_blank">
|
||||||
#{{ intervention.id }} - {{ intervention.title|default:intervention.description|truncatewords:10 }}
|
{{ intervention.code|default:intervention.id }} - {{ intervention.title|default:intervention.description|truncatewords:10 }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
|
||||||
|
|
@ -2542,6 +2542,100 @@ class StockMovementMultiLocationBatchTests(TestCase):
|
||||||
self.assertTrue(loc1_data["is_default"])
|
self.assertTrue(loc1_data["is_default"])
|
||||||
|
|
||||||
|
|
||||||
|
class PreparationOrderNumberCodificationTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
from common.models import UserConfig, Role
|
||||||
|
from interventions.models import Intervention
|
||||||
|
self.user = User.objects.create_superuser(username="admin_po_codif", email="admin_po@test.com", password="password")
|
||||||
|
config, _ = UserConfig.objects.get_or_create(user=self.user)
|
||||||
|
admin_role, _ = Role.objects.get_or_create(name='admin')
|
||||||
|
config.roles.add(admin_role)
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
self.intervention = Intervention.objects.create(
|
||||||
|
title="Intervention Signalisation Molenbeek",
|
||||||
|
code="I113392",
|
||||||
|
priority="2",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_autonomous_order_number_generation(self):
|
||||||
|
"""Les bons autonomes (sans intervention) utilisent le format BP-YYYYMMDD-001, etc."""
|
||||||
|
import datetime
|
||||||
|
today_str = datetime.date.today().strftime('%Y%m%d')
|
||||||
|
num1 = PreparationOrder.generate_order_number(intervention=None)
|
||||||
|
self.assertEqual(num1, f"BP-{today_str}-001")
|
||||||
|
|
||||||
|
po1 = PreparationOrder.objects.create(order_number=num1)
|
||||||
|
num2 = PreparationOrder.generate_order_number(intervention=None)
|
||||||
|
self.assertEqual(num2, f"BP-{today_str}-002")
|
||||||
|
|
||||||
|
def test_intervention_order_number_first_and_sequential(self):
|
||||||
|
"""Le 1er bon lié à une intervention est BP-I113392-01, le 2ème est BP-I113392-02, etc."""
|
||||||
|
num1 = PreparationOrder.generate_order_number(intervention=self.intervention)
|
||||||
|
self.assertEqual(num1, "BP-I113392-01")
|
||||||
|
|
||||||
|
po1 = PreparationOrder.objects.create(order_number=num1, intervention=self.intervention)
|
||||||
|
num2 = PreparationOrder.generate_order_number(intervention=self.intervention)
|
||||||
|
self.assertEqual(num2, "BP-I113392-02")
|
||||||
|
|
||||||
|
po2 = PreparationOrder.objects.create(order_number=num2, intervention=self.intervention)
|
||||||
|
num3 = PreparationOrder.generate_order_number(intervention=self.intervention)
|
||||||
|
self.assertEqual(num3, "BP-I113392-03")
|
||||||
|
|
||||||
|
def test_auto_generate_on_save_if_blank(self):
|
||||||
|
"""La méthode save() génère automatiquement le bon numéro si order_number est vide."""
|
||||||
|
po = PreparationOrder(intervention=self.intervention)
|
||||||
|
po.save()
|
||||||
|
self.assertEqual(po.order_number, "BP-I113392-01")
|
||||||
|
|
||||||
|
po2 = PreparationOrder(intervention=self.intervention)
|
||||||
|
po2.save()
|
||||||
|
self.assertEqual(po2.order_number, "BP-I113392-02")
|
||||||
|
|
||||||
|
def test_handles_gaps_and_existing_legacy_numbers(self):
|
||||||
|
"""Gère correctement les trous d'index et la présence d'anciens bons avec l'ancien format."""
|
||||||
|
# Un bon ancien existe pour cette intervention
|
||||||
|
PreparationOrder.objects.create(
|
||||||
|
order_number="BP-20260930-I43047",
|
||||||
|
intervention=self.intervention
|
||||||
|
)
|
||||||
|
# Le prochain bon détecte qu'il y a déjà 1 bon pour cette intervention et propose 02
|
||||||
|
num = PreparationOrder.generate_order_number(intervention=self.intervention)
|
||||||
|
self.assertEqual(num, "BP-I113392-02")
|
||||||
|
|
||||||
|
def test_new_preparation_order_from_intervention_view_get(self):
|
||||||
|
"""La vue GET pré-remplit le formulaire avec BP-<code_intervention>-01."""
|
||||||
|
url = reverse("stock:new_preparation_order_from_intervention", args=[self.intervention.id])
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "BP-I113392-01")
|
||||||
|
self.assertContains(response, "I113392")
|
||||||
|
|
||||||
|
def test_recodify_command(self):
|
||||||
|
"""La commande management recodify_preparation_orders met à jour les anciens formats."""
|
||||||
|
from django.core.management import call_command
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
po_old = PreparationOrder.objects.create(
|
||||||
|
order_number="BP-20260930-I43047",
|
||||||
|
intervention=self.intervention
|
||||||
|
)
|
||||||
|
|
||||||
|
out = StringIO()
|
||||||
|
# Test dry-run d'abord
|
||||||
|
call_command("recodify_preparation_orders", dry_run=True, stdout=out)
|
||||||
|
self.assertIn("BP-20260930-I43047", out.getvalue())
|
||||||
|
self.assertIn("BP-I113392-01", out.getvalue())
|
||||||
|
po_old.refresh_from_db()
|
||||||
|
self.assertEqual(po_old.order_number, "BP-20260930-I43047")
|
||||||
|
|
||||||
|
# Test exécution réelle
|
||||||
|
out = StringIO()
|
||||||
|
call_command("recodify_preparation_orders", stdout=out)
|
||||||
|
self.assertIn("1 bon(s) renommé(s) avec succès", out.getvalue())
|
||||||
|
po_old.refresh_from_db()
|
||||||
|
self.assertEqual(po_old.order_number, "BP-I113392-01")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3585,7 +3585,10 @@ def new_preparation_order(request):
|
||||||
form = PreparationOrderForm(request.POST)
|
form = PreparationOrderForm(request.POST)
|
||||||
formset = preparation_order_item_formset(request.POST)
|
formset = preparation_order_item_formset(request.POST)
|
||||||
if form.is_valid() and formset.is_valid():
|
if form.is_valid() and formset.is_valid():
|
||||||
preparation_order = form.save()
|
preparation_order = form.save(commit=False)
|
||||||
|
if not preparation_order.order_number:
|
||||||
|
preparation_order.order_number = PreparationOrder.generate_order_number(intervention=None)
|
||||||
|
preparation_order.save()
|
||||||
formset.instance = preparation_order
|
formset.instance = preparation_order
|
||||||
formset.save()
|
formset.save()
|
||||||
|
|
||||||
|
|
@ -3610,17 +3613,7 @@ def new_preparation_order(request):
|
||||||
messages.success(request, _("Commande de préparation enregistrée."))
|
messages.success(request, _("Commande de préparation enregistrée."))
|
||||||
return redirect("stock:stock_movements")
|
return redirect("stock:stock_movements")
|
||||||
else:
|
else:
|
||||||
import datetime
|
order_number = PreparationOrder.generate_order_number(intervention=None)
|
||||||
today = datetime.date.today()
|
|
||||||
prefix = f"BP-{today.strftime('%Y%m%d')}-"
|
|
||||||
existing = PreparationOrder.objects.filter(order_number__startswith=prefix).values_list('order_number', flat=True)
|
|
||||||
existing_nums = []
|
|
||||||
for num in existing:
|
|
||||||
suffix = num[len(prefix):]
|
|
||||||
if suffix.isdigit():
|
|
||||||
existing_nums.append(int(suffix))
|
|
||||||
next_num = max(existing_nums, default=0) + 1
|
|
||||||
order_number = f"{prefix}{next_num:03d}"
|
|
||||||
form = PreparationOrderForm(initial={'order_number': order_number, 'status': 'to_process'})
|
form = PreparationOrderForm(initial={'order_number': order_number, 'status': 'to_process'})
|
||||||
formset = preparation_order_item_formset()
|
formset = preparation_order_item_formset()
|
||||||
categories = list(ProductCategory.objects.order_by('name_fr').values('id', 'code', 'name_fr'))
|
categories = list(ProductCategory.objects.order_by('name_fr').values('id', 'code', 'name_fr'))
|
||||||
|
|
@ -3714,7 +3707,8 @@ def new_preparation_order_from_intervention(request, intervention_id):
|
||||||
return redirect(redirect_target, intervention_id=intervention_id)
|
return redirect(redirect_target, intervention_id=intervention_id)
|
||||||
|
|
||||||
can_create_from_intervention = bool(
|
can_create_from_intervention = bool(
|
||||||
user_config.has_role('admin')
|
request.user.is_superuser
|
||||||
|
or user_config.has_role('admin')
|
||||||
or (
|
or (
|
||||||
can_edit_intervention(request.user, intervention, user_config=user_config)
|
can_edit_intervention(request.user, intervention, user_config=user_config)
|
||||||
and user_config.can_manage_preparation_orders_any()
|
and user_config.can_manage_preparation_orders_any()
|
||||||
|
|
@ -3735,6 +3729,8 @@ def new_preparation_order_from_intervention(request, intervention_id):
|
||||||
if form.is_valid() and formset.is_valid():
|
if form.is_valid() and formset.is_valid():
|
||||||
preparation_order = form.save(commit=False)
|
preparation_order = form.save(commit=False)
|
||||||
preparation_order.intervention = intervention
|
preparation_order.intervention = intervention
|
||||||
|
if not preparation_order.order_number:
|
||||||
|
preparation_order.order_number = PreparationOrder.generate_order_number(intervention=intervention)
|
||||||
preparation_order.save()
|
preparation_order.save()
|
||||||
formset.instance = preparation_order
|
formset.instance = preparation_order
|
||||||
formset.save()
|
formset.save()
|
||||||
|
|
@ -3750,22 +3746,21 @@ def new_preparation_order_from_intervention(request, intervention_id):
|
||||||
name=f.name,
|
name=f.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
interv_ref = intervention.code or f"#{intervention.id}"
|
||||||
preparation_order.log_history(
|
preparation_order.log_history(
|
||||||
action="created",
|
action="created",
|
||||||
description=_("Création du bon de préparation depuis l'intervention #{intervention_id}").format(
|
description=_("Création du bon de préparation depuis l'intervention #{intervention_id}").format(
|
||||||
intervention_id=intervention.id
|
intervention_id=interv_ref
|
||||||
),
|
),
|
||||||
user=request.user,
|
user=request.user,
|
||||||
details={'order_number': preparation_order.order_number, 'intervention_id': intervention.id},
|
details={'order_number': preparation_order.order_number, 'intervention_id': intervention.id, 'intervention_code': intervention.code},
|
||||||
)
|
)
|
||||||
|
|
||||||
messages.success(request, _("Bon de préparation créé avec succès."))
|
messages.success(request, _("Bon de préparation créé avec succès."))
|
||||||
return redirect(redirect_target, intervention_id=intervention_id)
|
return redirect(redirect_target, intervention_id=intervention_id)
|
||||||
else:
|
else:
|
||||||
# Générer un numéro de bon automatique
|
# Générer un numéro de bon automatique
|
||||||
import datetime
|
order_number = PreparationOrder.generate_order_number(intervention=intervention)
|
||||||
today = datetime.date.today()
|
|
||||||
order_number = f"BP-{today.strftime('%Y%m%d')}-I{intervention.id}"
|
|
||||||
form = PreparationOrderForm(initial={
|
form = PreparationOrderForm(initial={
|
||||||
'order_number': order_number,
|
'order_number': order_number,
|
||||||
'status': 'to_process',
|
'status': 'to_process',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue