95 lines
3.7 KiB
Python
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
|