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

135 lines
5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Commande Django pour importer / mettre à jour les postes d'un contrat
depuis un fichier Excel de type « Inventaire fonteinen » (format SPRB-GOB).
Structure attendue du fichier Excel :
Col A – Nr. Post (entier) → order_number
Col B – Code Fontaine/Article → code
Col C – Beschrijving NL → description_nl
Col D – AO (TVS = à justifier) → is_amount_to_be_justified
Col E – Unité NL → unit (fallback)
Col F – Description FR → description_fr
Col G – NC (ignorée)
Col H – U (unité FR) → unit (prioritaire)
Col I – Quantité présumée (ignorée)
Col J – Prix unitaire → unit_price
Col K – Total par poste (ignorée)
Usage :
python manage.py import_contract_posts \\
--contract E24.143 \\
--excel-path scripts/data/"E24.143 Inventaire_inventaris ingevuld.xlsm"
# simuler sans écrire :
python manage.py import_contract_posts --contract E24.143 \\
--excel-path /tmp/inventaire.xlsm --dry-run
# mettre à jour les postes déjà présents :
python manage.py import_contract_posts --contract E24.143 \\
--excel-path /tmp/inventaire.xlsm --update
"""
from django.core.management.base import BaseCommand, CommandError
from contracts.models import Contract
from contracts.utils_import_posts import import_contract_posts
class Command(BaseCommand):
help = (
"Importe ou met à jour les postes d'un contrat depuis un fichier Excel "
"(format Inventaire fonteinen SPRB-GOB)."
)
def add_arguments(self, parser):
parser.add_argument(
"--contract",
required=True,
metavar="CONTRACT_NUMBER",
help="Numéro du contrat cible (ex : E24.143).",
)
parser.add_argument(
"--excel-path",
required=True,
metavar="PATH",
help="Chemin vers le fichier Excel (.xlsx / .xlsm).",
)
parser.add_argument(
"--dry-run",
action="store_true",
default=False,
help="Simule l'import sans écrire en base de données.",
)
parser.add_argument(
"--update",
action="store_true",
default=False,
help=(
"Met à jour les postes déjà présents (même order_number). "
"Sans ce flag, les postes existants sont ignorés."
),
)
def handle(self, *args, **options):
contract_number = options["contract"]
excel_path = options["excel_path"]
dry_run = options["dry_run"]
update_existing = options["update"]
if dry_run:
self.stdout.write(self.style.WARNING(
"=" * 62 + "\n"
"MODE DRY-RUN – aucune modification ne sera enregistrée\n"
"=" * 62
))
# ------------------------------------------------------------------
# Récupération du contrat
# ------------------------------------------------------------------
try:
contract = Contract.objects.get(contract_number=contract_number)
except Contract.DoesNotExist:
raise CommandError(
f"Contrat « {contract_number} » introuvable en base."
)
self.stdout.write(f"Contrat : {contract}")
self.stdout.write(f"Fichier : {excel_path}")
self.stdout.write(f"Mode update: {'oui' if update_existing else 'non'}\n")
# ------------------------------------------------------------------
# Import
# ------------------------------------------------------------------
stats = import_contract_posts(
contract=contract,
excel_source=excel_path,
dry_run=dry_run,
update_existing=update_existing,
log=lambda msg: self.stdout.write(msg),
)
# ------------------------------------------------------------------
# Résumé
# ------------------------------------------------------------------
marker = " [DRY-RUN]" if dry_run else ""
self.stdout.write("\n" + "=" * 62)
self.stdout.write(self.style.SUCCESS(f"RÉSUMÉ{marker}"))
self.stdout.write("=" * 62)
self.stdout.write(f" Lignes avec numéro de poste : {stats['total_rows']}")
self.stdout.write(self.style.SUCCESS(
f" Postes créés : {stats['created']}"
))
self.stdout.write(self.style.SUCCESS(
f" Postes mis à jour : {stats['updated']}"
))
self.stdout.write(
f" Ignorés (déjà présents) : {stats['skipped_already_exists']}"
)
self.stdout.write(
f" Ignorés (prix manquant) : {stats['skipped_no_price']}"
)
if stats['errors']:
self.stdout.write(self.style.ERROR(
f" Erreurs : {stats['errors']}"
))
self.stdout.write("=" * 62)