loko/streetup/stock/management/commands/associate_stock_locations.py
2026-07-22 14:48:40 +02:00

95 lines
3.7 KiB
Python

"""
Commande Django pour associer par défaut à chaque produit les emplacements
dans lesquels il est actuellement renseigné dans le stock.
"""
from django.core.management.base import BaseCommand
from django.db import transaction
from stock.models import Product
class Command(BaseCommand):
help = "Associe les emplacements de stock existants aux emplacements autorisés des produits."
def add_arguments(self, parser):
parser.add_argument(
'--positive-only',
action='store_true',
help="Associe uniquement les emplacements où la quantité en stock est strictement supérieure à 0.",
)
parser.add_argument(
'--dry-run',
action='store_true',
help="Simule l'exécution en affichant ce qui serait modifié sans enregistrer en base.",
)
def handle(self, *args, **options):
positive_only = options['positive_only']
dry_run = options['dry_run']
if dry_run:
self.stdout.write(self.style.WARNING("--- MODE SIMULATION (DRY-RUN) ---"))
# Optimisation : prefetch_related pour éviter les requêtes N+1
products = Product.objects.all().prefetch_related('authorized_locations', 'stocks__location')
updated_products_count = 0
total_associations_count = 0
self.stdout.write(f"Analyse de {products.count()} produits...")
# Si dry_run, pas besoin de transaction atomique
if dry_run:
updated_products_count, total_associations_count = self.process_associations(
products, positive_only, dry_run=True
)
else:
with transaction.atomic():
updated_products_count, total_associations_count = self.process_associations(
products, positive_only, dry_run=False
)
if dry_run:
self.stdout.write(
self.style.SUCCESS(
f"✓ Simulation terminée. {updated_products_count} produits auraient été mis à jour "
f"avec {total_associations_count} nouvelles associations d'emplacements."
)
)
else:
self.stdout.write(
self.style.SUCCESS(
f"✓ Mise à jour terminée ! {updated_products_count} produits mis à jour "
f"avec {total_associations_count} nouvelles associations d'emplacements."
)
)
def process_associations(self, products, positive_only, dry_run):
updated_products_count = 0
total_associations_count = 0
for product in products:
product_stocks = list(product.stocks.all())
if positive_only:
product_stocks = [s for s in product_stocks if s.quantity > 0]
if not product_stocks:
continue
current_authorized = set(product.authorized_locations.all())
locations_to_add = []
for stock in product_stocks:
if stock.location not in current_authorized:
locations_to_add.append(stock.location)
if locations_to_add:
loc_names = ", ".join(f"'{loc.name}'" for loc in locations_to_add)
self.stdout.write(
f"Produit '{product.name}' (code: {product.code}) : "
f"Ajout des emplacements : {loc_names}"
)
if not dry_run:
product.authorized_locations.add(*locations_to_add)
updated_products_count += 1
total_associations_count += len(locations_to_add)
return updated_products_count, total_associations_count