import random import os from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model from django.utils import timezone from django.core.files import File from django.db import transaction from operations.models import RadniNalog, RadniNalogSlika, PutniNalog, BrojacSekvence from fleet.models import Vozilo, Stroj from kupci.models import Kupac from kalendar.services import kreiraj_kalendarski_unos User = get_user_model() class Command(BaseCommand): help = 'Popunjava bazu s Putnim nalozima, Radnim nalozima i slikama sinkronizirano sa sekvencama' def add_arguments(self, parser): parser.add_argument('total_putovanja', type=int, nargs='?', default=3, help='Broj putnih naloga') parser.add_argument('--img', type=str, default='test_assets/kvar.jpg') def handle(self, *args, **kwargs): total_putovanja = kwargs['total_putovanja'] test_img_path = kwargs['img'] kupci = Kupac.objects.prefetch_related('strojevi').all() vozila = list(Vozilo.objects.all()) serviseri = list(User.objects.filter(is_staff=True)) if not kupci.exists() or not vozila or not serviseri: self.stdout.write(self.style.ERROR("Baza mora imati kupce, vozila i servisere (is_staff=True).")) return # Provjera slike if not os.path.exists(test_img_path): self.stdout.write(self.style.WARNING(f"Testna slika {test_img_path} nije pronađena. Generiram bez slika.")) test_img_path = None opisi_kvarova = [ "Godišnji periodični servis dizalice.", "Zamjena hidrauličnog crijeva i dopuna ulja.", "Kalibracija senzora opterećenja.", "Popravak daljinskog upravljača.", "Hitna intervencija: Stroj u kvaru." ] self.stdout.write(f'🚀 Generiranje {total_putovanja} putovanja sinkronizirano sa sekvencama...') success_count_rn = 0 trenutna_godina = timezone.now().year # Definiramo ključ i prefikse za putne naloge prefix_pn = f"PN-{trenutna_godina}-" kljuc_sekvence_rn = f"radni_nalog_{trenutna_godina}" for i in range(total_putovanja): serviser = random.choice(serviseri) vozilo = random.choice(vozila) # 1. GENERIRANJE NEUNIŠTIVOG BROJA PUTNOG NALOGA # Da skripta ne bi udarila u unique=True, tražimo stvarni zadnji PN u bazi with transaction.atomic(): zadnji_pn = PutniNalog.objects.select_for_update().filter( broj_naloga__startswith=prefix_pn ).order_by('-broj_naloga').first() if zadnji_pn: try: zadnji_broj_pn = int(zadnji_pn.broj_naloga.split('-')[-1]) novi_broj_pn = zadnji_broj_pn + 1 except (ValueError, IndexError): novi_broj_pn = PutniNalog.objects.count() + 1 else: novi_broj_pn = 1 broj_pn = f"{prefix_pn}{novi_broj_pn:04d}" # Kreiraj PUTNI NALOG putni_nalog = PutniNalog.objects.create( broj_naloga=broj_pn, vozilo=vozilo, korisnik=serviser, relacija="Zagreb - Teren - Zagreb", mjesto_odredista="Teren", vrijeme_polaska=timezone.now() ) self.stdout.write(self.style.MIGRATE_LABEL(f"Otvoren {broj_pn} (Vozilo: {vozilo.registracija})")) # 2. Kreiraj 1 do 3 RADNA NALOGA za ovaj putni nalog za_odraditi = random.randint(1, 3) kreirani_radni_nalozi = [] for j in range(za_odraditi): klijenti_sa_strojima = [k for k in kupci if k.strojevi.exists()] if not klijenti_sa_strojima: continue klijent = random.choice(klijenti_sa_strojima) stroj = random.choice(list(klijent.strojevi.all())) try: # Inicijaliziramo objekt bez broja_naloga kako bi se aktivirala # naša neuništiva sekvencijska logika unutar save() metode modela! nalog = RadniNalog( klijent=klijent, stroj=stroj, putni_nalog=putni_nalog, opis_kvara=random.choice(opisi_kvarova), izvrsitelj=serviser, status=RadniNalog.StatusRadaChoices.PLANIRANO ) # Prvo moramo izvršiti osnovni save da model kroz BrojacSekvence dodijeli čisti broj nalog.save() # Ako slika postoji, dodajemo je NAKON što je broj kreiran (da putanja_slike_naloga ima ispravan folder) if test_img_path: with open(test_img_path, 'rb') as f: nalog.slika_kvara.save(f'rn_{nalog.broj_naloga}.jpg', File(f), save=True) kreirani_radni_nalozi.append(nalog) # Dodatne slike u galeriju if test_img_path: for k in range(random.randint(1, 2)): with open(test_img_path, 'rb') as f: RadniNalogSlika.objects.create( radni_nalog=nalog, slika=File(f, name=f'gal_{nalog.broj_naloga}_{k}.jpg'), opis=f"Detalj kvara {k+1}" ) # 5. Upis u kalendar try: kreiraj_kalendarski_unos(nalog) except Exception as cal_err: self.stdout.write(self.style.WARNING(f" ⚠️ Kalendar unos preskočen: {cal_err}")) success_count_rn += 1 self.stdout.write(f" ✅ {nalog.broj_naloga} uspješno povezan u {broj_pn}") except Exception as e: self.stderr.write(f" ❌ Greška kod kreiranja RN: {e}") # 3. NASUMIČNO ZATVARANJE (Simulacija završenog posla) if random.random() > 0.5 and kreirani_radni_nalozi: try: # Prvo moramo zatvoriti sve radne naloge da bi PN prošao validaciju for rn in kreirani_radni_nalozi: rn.status = RadniNalog.StatusRadaChoices.ZAVRSENO rn.save() # Dodajemo završne kilometre (uvijek više od početnih) putni_nalog.zavrsna_km = putni_nalog.pocetna_km + random.randint(50, 500) putni_nalog.status = PutniNalog.StatusChoices.ZAVRSEN putni_nalog.save() self.stdout.write(self.style.SUCCESS(f" 🏁 {broj_pn} automatski ZATVOREN. Kilometri vozila ažurirani.")) except Exception as e: self.stdout.write(self.style.WARNING(f" ⚠️ PN-{broj_pn} nije zatvoren: {e}")) self.stdout.write(self.style.SUCCESS(f'\nGotovo! Generirano {total_putovanja} PN i {success_count_rn} RN.'))