v004
2
.gitignore
vendored
@@ -19,7 +19,7 @@ dist/
|
|||||||
downloads/
|
downloads/
|
||||||
eggs/
|
eggs/
|
||||||
.eggs/
|
.eggs/
|
||||||
lib/
|
#lib/
|
||||||
lib64/
|
lib64/
|
||||||
parts/
|
parts/
|
||||||
sdist/
|
sdist/
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from fleet.models import Stroj
|
|||||||
from kupci.models import Kupac
|
from kupci.models import Kupac
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Popunjava bazu podataka isključivo Liebherr strojevima koristeći Faker'
|
help = 'Popunjava bazu podataka isključivo Liebherr strojevima sinkronizirano s logistikom flote'
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -18,69 +18,86 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
total = kwargs['total']
|
total = kwargs['total']
|
||||||
fake = Faker(['hr_HR']) # Koristimo hrvatski lokalitet
|
fake = Faker(['hr_HR'])
|
||||||
kupci = list(Kupac.objects.all())
|
kupci = list(Kupac.objects.all())
|
||||||
|
|
||||||
if not kupci:
|
if not kupci:
|
||||||
self.stdout.write(self.style.ERROR("Greška: Nema kupaca u bazi. Prvo pokreni populate_kupci!"))
|
self.stdout.write(self.style.ERROR("Greška: Nema kupaca u bazi. Prvo pokreni populate_kupci!"))
|
||||||
return
|
return
|
||||||
|
|
||||||
# Katalog usklađen s tvojim TIP_STROJA choices
|
# Bogatiji katalog usklađen s tvorničkim Liebherr oznakama i tvojim TIP_STROJA choices
|
||||||
liebherr_katalog = {
|
liebherr_katalog = {
|
||||||
'dizalica_toranj': [
|
'dizalica_toranj': [
|
||||||
'132 EC-H 8 Litronic',
|
'132 EC-H 8 Litronic',
|
||||||
'280 EC-H 12',
|
'280 EC-H 12',
|
||||||
'81 K.1',
|
'81 K.1',
|
||||||
'172 EC-B 8'
|
'172 EC-B 8',
|
||||||
|
'125 K'
|
||||||
],
|
],
|
||||||
'dizalica_auto': [
|
'dizalica_auto': [
|
||||||
'LTM 1030-2.1',
|
'LTM 1030-2.1',
|
||||||
'LTM 1050-3.1',
|
'LTM 1050-3.1',
|
||||||
'LTM 1060-3.1',
|
'LTM 1060-3.1',
|
||||||
'LTM 1120-4.1',
|
'LTM 1120-4.1',
|
||||||
|
'LTM 1350-6.1', # Dodan LTM 1350 (sukladno planiranim modifikacijama ventila)
|
||||||
'LTC 1050-3.1'
|
'LTC 1050-3.1'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Generiram {total} Liebherr strojeva...'))
|
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Generiram {total} Liebherr strojeva u bazu...'))
|
||||||
|
|
||||||
created_count = 0
|
created_count = 0
|
||||||
for _ in range(total):
|
for _ in range(total):
|
||||||
# Odabir tipa (usklađeno s tvojim modelom)
|
|
||||||
tip = random.choice(['dizalica_toranj', 'dizalica_auto'])
|
tip = random.choice(['dizalica_toranj', 'dizalica_auto'])
|
||||||
model_naziv = random.choice(liebherr_katalog[tip])
|
model_naziv = random.choice(liebherr_katalog[tip])
|
||||||
|
|
||||||
# Faker za generiranje serijskog broja i registracije
|
# Odabiremo nasumičnog vlasnika za ovaj stroj
|
||||||
serijski = f"LE-{fake.bothify(text='######')}" # npr. LE-482931
|
vlasnik_stroja = random.choice(kupci)
|
||||||
|
|
||||||
# Registracija samo za autodizalice (Faker format)
|
# Realistični Liebherr tvornički serijski brojevi (npr. LE-054321)
|
||||||
|
serijski = f"LE-{fake.numerify(text='0######')}"
|
||||||
|
|
||||||
|
# USKLAĐENO: Registracija za autodizalice sada ima ispravne razmake i crticu
|
||||||
|
# Koristimo reg. oznaku koja odgovara sjedištu vlasnika ili nasumični HR grad
|
||||||
reg = None
|
reg = None
|
||||||
if tip == 'dizalica_auto':
|
if tip == 'dizalica_auto':
|
||||||
# Generira format sličan ZG-1234-LH
|
gradovi = ['ZG', 'KA', 'RI', 'ST', 'OS', 'KR']
|
||||||
reg = f"ZG-{fake.numerify('####')}-LH"
|
grad = random.choice(gradovi)
|
||||||
|
reg_broj = fake.numerify(text='####')
|
||||||
|
reg_slova = fake.bothify(text='??', letters='ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||||
|
reg = f"{grad} {reg_broj}-{reg_slova}"
|
||||||
|
|
||||||
# Radni sati
|
# Radni sati dizalice
|
||||||
sati = random.uniform(500.0, 12000.0) if tip == 'dizalica_toranj' else random.uniform(100.0, 5000.0)
|
sati = random.uniform(800.0, 15000.0) if tip == 'dizalica_toranj' else random.uniform(200.0, 6000.0)
|
||||||
|
|
||||||
# Faker za datume (unutar zadnjih godinu dana)
|
# Atesti unutar zadnjih godinu dana
|
||||||
datum_atesta = fake.date_between(start_date='-1y', end_date='today')
|
datum_atesta = fake.date_between(start_date='-1y', end_date='today')
|
||||||
|
|
||||||
|
# USKLAĐENO: Generiramo lokaciju. Ako klijent/vlasnik ima definiran grad,
|
||||||
|
# stroj se primarno nalazi na gradilištu u tom gradu (ili okolici), inače koristimo fake.city()
|
||||||
|
lokacija_stroja = vlasnik_stroja.grad if (vlasnik_stroja and hasattr(vlasnik_stroja, 'grad')) else fake.city()
|
||||||
|
|
||||||
|
# Nasumična simulacija aktivnih gradilišta u regiji
|
||||||
|
if random.random() > 0.6:
|
||||||
|
lokacija_stroja = f"Gradilište {lokacija_stroja}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Stroj.objects.create(
|
Stroj.objects.create(
|
||||||
vlasnik=random.choice(kupci),
|
vlasnik=vlasnik_stroja,
|
||||||
naziv=f"Liebherr {model_naziv}",
|
naziv=f"Liebherr {model_naziv}",
|
||||||
serijski_broj=serijski,
|
serijski_broj=serijski,
|
||||||
marka='Liebherr',
|
marka='Liebherr',
|
||||||
model_stroja=model_naziv,
|
model_stroja=model_naziv,
|
||||||
godina_proizvodnje=random.randint(2015, 2024),
|
godina_proizvodnje=random.randint(2012, 2025),
|
||||||
tip=tip,
|
tip=tip,
|
||||||
radni_sati=round(sati, 2),
|
radni_sati=round(sati, 1), # Zaokružujemo na 1 decimalu sukladno formama
|
||||||
registracija=reg,
|
registracija=reg,
|
||||||
datum_zadnjeg_atesta=datum_atesta
|
datum_zadnjeg_atesta=datum_atesta
|
||||||
)
|
)
|
||||||
created_count += 1
|
created_count += 1
|
||||||
|
self.stdout.write(f" ✅ Kreiran: Liebherr {model_naziv} (Vlasnik: {vlasnik_stroja.naziv})")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stderr.write(f"Greška kod S/N {serijski}: {e}")
|
self.stderr.write(f" ❌ Greška kod S/N {serijski}: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} Liebherr strojeva!'))
|
self.stdout.write(self.style.SUCCESS(f'\nGotovo! Uspješno dodano {created_count} Liebherr strojeva u bazu.'))
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import random
|
import random
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import IntegrityError
|
||||||
from faker import Faker
|
from faker import Faker
|
||||||
from fleet.models import Vozilo
|
from fleet.models import Vozilo
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Popunjava bazu podataka s gospodarskim vozilima koristeći Faker'
|
help = 'Popunjava bazu podataka s gospodarskim vozilima sinkronizirano s Astro logistikom'
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -19,7 +20,7 @@ class Command(BaseCommand):
|
|||||||
total = kwargs['total']
|
total = kwargs['total']
|
||||||
fake = Faker(['hr_HR'])
|
fake = Faker(['hr_HR'])
|
||||||
|
|
||||||
# Modeli koji se pojavljuju u tvom radnom nalogu (CSV)
|
# Modeli koji odgovaraju tvom voznom parku za mobilne servisne timove
|
||||||
modeli_podaci = [
|
modeli_podaci = [
|
||||||
'VW Transporter T6',
|
'VW Transporter T6',
|
||||||
'Mercedes-Benz Vito',
|
'Mercedes-Benz Vito',
|
||||||
@@ -30,42 +31,56 @@ class Command(BaseCommand):
|
|||||||
'Opel Vivaro',
|
'Opel Vivaro',
|
||||||
]
|
]
|
||||||
|
|
||||||
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} vozila u flotu...'))
|
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} servisnih vozila u flotu...'))
|
||||||
|
|
||||||
brojac = 0
|
brojac = 0
|
||||||
while brojac < total:
|
maksimalni_pokusaji = total * 5
|
||||||
|
pokusaji = 0
|
||||||
|
|
||||||
|
while brojac < total and pokusaji < maksimalni_pokusaji:
|
||||||
|
pokusaji += 1
|
||||||
model_naziv = random.choice(modeli_podaci)
|
model_naziv = random.choice(modeli_podaci)
|
||||||
|
|
||||||
# Generiranje registracije koristeći Faker numerify/bothify
|
# Generiranje realistične registracije u formatu: ZG 1234-AB
|
||||||
# Format: ZG 1234 AB
|
gradovi = ['ZG', 'KR', 'KA', 'ST', 'RI', 'OS', 'VZ', 'PU']
|
||||||
gradovi = ['ZG', 'ST', 'RI', 'OS', 'VZ', 'PU', 'KA', 'KC']
|
|
||||||
grad = random.choice(gradovi)
|
grad = random.choice(gradovi)
|
||||||
reg_oznaka = fake.numerify(text='####') # npr. 6732
|
|
||||||
reg_slova = fake.bothify(text='??', letters='ABCDEFGHIJKLMNOPQRSTUVWXYZ') # npr. II
|
|
||||||
registracija = f"{grad}{reg_oznaka}{reg_slova}"
|
|
||||||
|
|
||||||
# Kilometri: Koristimo tvoju logiku da trenutni budu veći od početnih
|
reg_broj = fake.numerify(text=random.choice(['###', '####']))
|
||||||
pocetni = random.randint(0, 150000)
|
reg_slova = fake.bothify(text=random.choice(['?', '??']), letters='ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||||
dodatni = random.randint(500, 20000)
|
registracija = f"{grad} {reg_broj}-{reg_slova}"
|
||||||
|
|
||||||
|
# Kilometraža: Trenutni kilometri moraju pratiti povijest korištenja vozila
|
||||||
|
pocetni = random.randint(10000, 220000)
|
||||||
|
dodatni = random.randint(1500, 45000)
|
||||||
trenutni = pocetni + dodatni
|
trenutni = pocetni + dodatni
|
||||||
|
|
||||||
|
# POPRAVLJENO: Koristimo mala slova sukladno tvojim Choice ograničenjima u modelu
|
||||||
|
status_izbor = random.choice(['aktivan', 'aktivan', 'servis', 'neaktivan'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Koristimo registraciju u nazivu radi lakšeg prepoznavanja u Astro frontendu
|
# Provjeravamo postoji li već ova registracija u bazi unaprijed
|
||||||
|
if Vozilo.objects.filter(registracija=registracija).exists():
|
||||||
|
continue
|
||||||
|
|
||||||
vozilo = Vozilo.objects.create(
|
vozilo = Vozilo.objects.create(
|
||||||
naziv=f"{model_naziv} ({grad})",
|
naziv=model_naziv,
|
||||||
registracija=registracija,
|
registracija=registracija,
|
||||||
pocetni_kilometri=pocetni,
|
pocetni_kilometri=pocetni,
|
||||||
trenutni_kilometri=trenutni,
|
trenutni_kilometri=trenutni,
|
||||||
status=random.choice(['aktivan', 'aktivan', 'servis', 'neaktivan'])
|
status=status_izbor
|
||||||
)
|
)
|
||||||
brojac += 1
|
brojac += 1
|
||||||
self.stdout.write(f" ✅ Kreirano: {vozilo.naziv} [{vozilo.registracija}]")
|
self.stdout.write(f" ✅ Kreirano: {vozilo.naziv} [{vozilo.registracija}] | Status: {status_izbor} ({brojac}/{total})")
|
||||||
|
|
||||||
except ValidationError:
|
except IntegrityError:
|
||||||
# Ako Faker pogodi postojeću registraciju, samo nastavi dalje
|
# Ako baza ipak prijavi duplikat registracije unutar transakcije, samo idemo dalje
|
||||||
|
continue
|
||||||
|
except ValidationError as ve:
|
||||||
|
# Ako se pojavi neka druga validacijska greška, ispiši je da znamo o čemu se radi
|
||||||
|
self.stderr.write(f" ⚠️ Validacijska greška za {model_naziv}: {ve}")
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.stderr.write(f" ❌ Greška: {e}")
|
self.stderr.write(f" ❌ Kritična greška pri upisu vozila: {e}")
|
||||||
break
|
break
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(f'\nGotovo! Uspješno dodano {brojac} vozila u flotu.'))
|
self.stdout.write(self.style.SUCCESS(f'\nGotovo! Uspješno dodano {brojac} servisnih vozila u bazu flote.'))
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 6.0.5 on 2026-05-20 20:03
|
# Generated by Django 6.0.5 on 2026-05-22 03:53
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 6.0.5 on 2026-05-20 20:04
|
# Generated by Django 6.0.5 on 2026-05-22 03:53
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Dogadaj
|
from .models import Dogadaj
|
||||||
from operations.serializers import RadniNalogListaSerializer
|
import re
|
||||||
|
|
||||||
class DogadajSerializer(serializers.ModelSerializer):
|
class DogadajSerializer(serializers.ModelSerializer):
|
||||||
# 1. Mapiranje za standardne kalendarske knjižnice (FullCalendar traži title, start, end)
|
# 1. Mapiranje za standardne kalendarske knjižnice
|
||||||
title = serializers.CharField(source='naslov')
|
title = serializers.CharField(source='naslov')
|
||||||
start = serializers.DateTimeField(source='pocetak')
|
start = serializers.DateTimeField(source='pocetak')
|
||||||
end = serializers.DateTimeField(source='kraj')
|
end = serializers.DateTimeField(source='kraj')
|
||||||
@@ -16,23 +16,27 @@ class DogadajSerializer(serializers.ModelSerializer):
|
|||||||
boja = serializers.SerializerMethodField()
|
boja = serializers.SerializerMethodField()
|
||||||
je_radni_nalog = serializers.SerializerMethodField()
|
je_radni_nalog = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
# 4. NOVA RAZDVOJENA POLJA ZA VOCILO I ČISTI OPIS KWARTA
|
||||||
|
vozilo_naziv = serializers.SerializerMethodField()
|
||||||
|
opis_cisti = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Dogadaj
|
model = Dogadaj
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'title', 'start', 'end', 'tip', 'tip_display',
|
'id', 'title', 'start', 'end', 'tip', 'tip_display',
|
||||||
'opis', 'izvrsitelj_ime', 'boja', 'je_radni_nalog',
|
'opis', 'opis_cisti', 'vozilo_naziv', 'izvrsitelj_ime',
|
||||||
'klijent', 'vozilo', 'radni_nalog'
|
'boja', 'je_radni_nalog', 'klijent', 'vozilo', 'radni_nalog'
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_izvrsitelj_ime(self, obj):
|
def get_izvrsitelj_ime(self, obj):
|
||||||
"""Dohvaća ime preko tvog @property izvrsitelj u modelu"""
|
"""Dohvaća ime preko tvog @property izvrsitelj u modelu"""
|
||||||
korisnik = obj.izvrsitelj # Poziva @property iz models.py
|
korisnik = obj.izvrsitelj
|
||||||
if korisnik:
|
if korisnik:
|
||||||
return korisnik.get_full_name() or korisnik.username
|
return korisnik.get_full_name() or korisnik.username
|
||||||
return "Nije dodijeljeno"
|
return "Nije dodijeljeno"
|
||||||
|
|
||||||
def get_boja(self, obj):
|
def get_boja(self, obj):
|
||||||
"""Dodjeljuje boju ovisno o tipu događaja za vizualni kalendar"""
|
"""Dodjeljuje boju ovisno o tipu događaja"""
|
||||||
boje = {
|
boje = {
|
||||||
'servis': '#3498db', # Plava
|
'servis': '#3498db', # Plava
|
||||||
'isporuka': '#2ecc71', # Zelena
|
'isporuka': '#2ecc71', # Zelena
|
||||||
@@ -44,3 +48,30 @@ class DogadajSerializer(serializers.ModelSerializer):
|
|||||||
def get_je_radni_nalog(self, obj):
|
def get_je_radni_nalog(self, obj):
|
||||||
"""Pomaže frontendu da zna treba li prikazati link na radni nalog"""
|
"""Pomaže frontendu da zna treba li prikazati link na radni nalog"""
|
||||||
return obj.radni_nalog is not None
|
return obj.radni_nalog is not None
|
||||||
|
|
||||||
|
def _parsiraj_spojeni_opis(self, obj):
|
||||||
|
"""Pomoćna metoda koja analizira tekst i vraća (vozilo, opis)"""
|
||||||
|
sirovi_tekst = obj.opis or ""
|
||||||
|
|
||||||
|
# Ako tekst sadrži ključne riječi, razdvajamo ga pomoću Regexa na backendu
|
||||||
|
if "Vozilo:" in sirovi_tekst or "Opis kvara:" in sirovi_tekst:
|
||||||
|
vozilo_match = re.search(r"Vozilo:\s*(.*?)(?=\s*Opis kvara:|$)", sirovi_tekst, re.IGNORECASE)
|
||||||
|
opis_match = re.search(r"Opis kvara:\s*(.*)", sirovi_tekst, re.IGNORECASE)
|
||||||
|
|
||||||
|
vozilo = vozilo_match.group(1).strip() if vozilo_match else None
|
||||||
|
opis = opis_match.group(1).strip() if opis_match else sirovi_tekst
|
||||||
|
return vozilo, opis
|
||||||
|
|
||||||
|
# Ako je polje vozilo na modelu već popunjeno kao relacija/tekst, iskoristi ga
|
||||||
|
model_vozilo = str(obj.vozilo) if obj.vozilo else None
|
||||||
|
return model_vozilo, sirovi_tekst
|
||||||
|
|
||||||
|
def get_vozilo_naziv(self, obj):
|
||||||
|
"""Vraća samo čisti naziv vozila"""
|
||||||
|
vozilo, _ = self._parsiraj_spojeni_opis(obj)
|
||||||
|
return vozilo
|
||||||
|
|
||||||
|
def get_opis_cisti(self, obj):
|
||||||
|
"""Vraća samo čisti tekst opisa kvara"""
|
||||||
|
_, opis = self._parsiraj_spojeni_opis(obj)
|
||||||
|
return opis if opis else "Nema dodatnog opisa."
|
||||||
@@ -4,7 +4,7 @@ from faker import Faker
|
|||||||
from kupci.models import Kupac
|
from kupci.models import Kupac
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Popunjava bazu podataka s testnim kupcima'
|
help = 'Popunjava bazu podataka s testnim kupcima spremnim za logistički modul'
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -18,7 +18,21 @@ class Command(BaseCommand):
|
|||||||
total = kwargs['total']
|
total = kwargs['total']
|
||||||
fake = Faker(['hr_HR'])
|
fake = Faker(['hr_HR'])
|
||||||
|
|
||||||
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} kupaca...'))
|
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} testnih kupaca...'))
|
||||||
|
|
||||||
|
# Kontrolirani skup gradova u nominativu za savršen ispis relacija i odredišta u logistici
|
||||||
|
hr_gradovi = [
|
||||||
|
{'grad': 'Zagreb', 'pbr': '10000'},
|
||||||
|
{'grad': 'Karlovac', 'pbr': '47000'},
|
||||||
|
{'grad': 'Ozalj', 'pbr': '47280'},
|
||||||
|
{'grad': 'Split', 'pbr': '21000'},
|
||||||
|
{'grad': 'Rijeka', 'pbr': '51000'},
|
||||||
|
{'grad': 'Osijek', 'pbr': '31000'},
|
||||||
|
{'grad': 'Varaždin', 'pbr': '42000'},
|
||||||
|
{'grad': 'Zadar', 'pbr': '23000'},
|
||||||
|
{'grad': 'Velika Gorica', 'pbr': '10410'},
|
||||||
|
{'grad': 'Sisak', 'pbr': '44000'}
|
||||||
|
]
|
||||||
|
|
||||||
created_count = 0
|
created_count = 0
|
||||||
for _ in range(total):
|
for _ in range(total):
|
||||||
@@ -29,27 +43,33 @@ class Command(BaseCommand):
|
|||||||
else:
|
else:
|
||||||
naziv = fake.name()
|
naziv = fake.name()
|
||||||
|
|
||||||
# Generiramo OIB (nasumičnih 11 znamenki)
|
# Generiramo OIB (točno 11 znamenki u string formatu)
|
||||||
oib = "".join([str(random.randint(0, 9)) for _ in range(11)])
|
oib = "".join([str(random.randint(0, 9)) for _ in range(11)])
|
||||||
|
|
||||||
|
# Odabiremo grad i poštanski broj u paru kako bi podaci imali smisla
|
||||||
|
lokacija = random.choice(hr_gradovi)
|
||||||
|
|
||||||
|
# Generiramo telefonski broj koji je čist i siguran za tel: linkove u Astru
|
||||||
|
pozivni = random.randint(1, 9)
|
||||||
|
telefon = f"+385 {pozivni} {random.randint(100, 999)}-{random.randint(1000, 9999)}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Koristimo get_or_create ili create da izbjegnemo duplikate OIB-a
|
# Upotrebljavamo create, a jedinstvenost OIB-a hvatamo u iznimci
|
||||||
# ako je polje unique=True u modelu
|
|
||||||
Kupac.objects.create(
|
Kupac.objects.create(
|
||||||
naziv=naziv,
|
naziv=naziv,
|
||||||
oib=oib,
|
oib=oib,
|
||||||
email=fake.email(),
|
email=fake.email(),
|
||||||
telefon=fake.phone_number(),
|
telefon=telefon,
|
||||||
adresa=fake.street_address(),
|
adresa=fake.street_address(),
|
||||||
grad=fake.city(),
|
grad=lokacija['grad'], # Usklađeno: čisti nominativ (npr. 'Karlovac')
|
||||||
postanski_broj=fake.postcode(),
|
postanski_broj=lokacija['pbr'], # Usklađeno: odgovarajući poštanski broj
|
||||||
tip=tip,
|
tip=tip,
|
||||||
napomena="Automatski generiran testni kupac.",
|
napomena="Automatski generiran testni kupac usklađen s modulom operativne logistike.",
|
||||||
aktivno=True
|
aktivno=True
|
||||||
)
|
)
|
||||||
created_count += 1
|
created_count += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Ako OIB slučajno bude isti, samo nastavi dalje
|
# Ako OIB slučajno kolidira (unutar unique=True), preskoči i nastavi generirati sljedećeg
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} novih kupaca!'))
|
self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} novih kupaca u bazu!'))
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 6.0.5 on 2026-05-20 20:03
|
# Generated by Django 6.0.5 on 2026-05-22 03:52
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 196 KiB |
BIN
001.BACKEND/media/radni-nalozi/RN-2026-0026/rn_RN-2026-0026.jpg
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 196 KiB |