This commit is contained in:
2026-05-22 06:08:51 +00:00
parent c078a257e9
commit bafec754d1
131 changed files with 2346 additions and 893 deletions

View File

@@ -7,16 +7,31 @@ from django.utils import timezone
from django.core.files.base import ContentFile
from django.core.exceptions import ValidationError
class BrojacSekvence(models.Model):
"""
Tablica koja pamti isključivo zadnji iskorišteni broj za bilo koji entitet.
Broj se ovdje samo povećava i nikada se ne briše.
"""
naziv_sekvence = models.CharField(max_length=50, unique=True) # npr. 'radni_nalog_2026'
zadnji_broj = models.PositiveIntegerField(default=0)
class Meta:
verbose_name = "Brojač sekvence"
verbose_name_plural = "Brojači sekvenci"
def __str__(self):
return f"{self.naziv_sekvence}: {self.zadnji_broj}"
# --- FUNKCIJE ZA DINAMIČKE PUTANJE ---
def putanja_slike_naloga(instance, filename):
# Kreira putanju: radni-nalozi/broj_naloga/originalni_naziv.ekstenzija
return f'radni-nalozi/{instance.broj_naloga}/{filename}'
# Čistimo broj naloga od duplih oznaka za putanju direktorija
broj = instance.broj_naloga.replace('RN-', '') if instance.broj_naloga else 'nepoznato'
return f'radni-nalozi/RN-{broj}/{filename}'
def putanja_galerije_naloga(instance, filename):
# instance je objekt RadniNalogSlika koji ima ForeignKey na radni_nalog
# Sprema u isti folder gdje je i glavna slika kvara
return f'radni-nalozi/{instance.radni_nalog.broj_naloga}/galerija/{filename}'
broj = instance.radni_nalog.broj_naloga.replace('RN-', '') if instance.radni_nalog.broj_naloga else 'nepoznato'
return f'radni-nalozi/RN-{broj}/galerija/{filename}'
class PutniNalog(models.Model):
@@ -93,7 +108,7 @@ class RadniNalog(models.Model):
class StatusRadaChoices(models.TextChoices):
PLANIRANO = 'PLANIRANO', 'Planirano'
U_RADU = 'U_RADU', 'U radu'
ZAVRSENO = 'ZAVRSENO', 'Završeno'
ZAVRSENO = 'ZAVRSENO', 'Završen'
broj_naloga = models.CharField(max_length=20, unique=True, blank=True)
@@ -137,29 +152,41 @@ class RadniNalog(models.Model):
potpis_klijenta = models.ImageField(upload_to='potpisi/%Y/%m/', null=True, blank=True)
def save(self, *args, **kwargs):
# 1. AUTOMATSKO GENERIRANJE BROJA NALOGA
if self.broj_naloga == "":
self.broj_naloga = None
# 1. AUTOMATSKO GENERIRANJE BROJA NALOGA KROZ SEKVENCIJSKU TABLICU
if not self.broj_naloga:
godina = timezone.now().year
kljuc_sekvence = f"radni_nalog_{godina}"
prefix = f"RN-{godina}-"
# Koristimo atomsku transakciju i select_for_update da spriječimo
# da dva korisnika dobiju isti broj u istoj milisekundi
# Atomska transakcija zaključava samo brojač, sprječavajući dupliranje
with transaction.atomic():
zadnji_nalog = RadniNalog.objects.select_for_update().filter(
broj_naloga__startswith=prefix
).order_by('-broj_naloga').first()
# Dohvati ili kreiraj brojač za tekuću godinu i zaključaj redak za ažuriranje
brojac, created = BrojacSekvence.objects.select_for_update().get_or_create(
naziv_sekvence=kljuc_sekvence
)
# Ako je brojač tek kreiran, a u bazi već imaš povijest (npr. stigao si do 25),
# radimo automatsku sinkronizaciju da ne krene od 1 ako tablica nije prazna
if created:
zadnji_u_bazi = RadniNalog.objects.filter(broj_naloga__startswith=prefix).order_by('-broj_naloga').first()
if zadnji_u_bazi:
try:
brojac.zadnji_broj = int(zadnji_u_bazi.broj_naloga.split('-')[-1])
except (ValueError, IndexError):
brojac.zadnji_broj = 0
if zadnji_nalog:
try:
# RN-2026-0001 -> uzimamo "0001"
zadnji_broj_str = zadnji_nalog.broj_naloga.split('-')[-1]
novi_broj = int(zadnji_broj_str) + 1
except (ValueError, IndexError):
novi_broj = 1
else:
novi_broj = 1
# Uvećaj sekvencu za 1 (Čak i ako se nalozi obrišu, ovaj broj samo raste!)
brojac.zadnji_broj += 1
brojac.save()
self.broj_naloga = f"{prefix}{novi_broj:04d}"
# Dodijeli savršeno formatiran broj
self.broj_naloga = f"{prefix}{brojac.zadnji_broj:04d}"
else:
if self.broj_naloga.startswith("RN-RN-"):
self.broj_naloga = self.broj_naloga.replace("RN-RN-", "RN-")
# 2. OBRADA SLIKE
if self.slika_kvara:
@@ -174,9 +201,8 @@ class RadniNalog(models.Model):
# 4. LOGIKA ZA PUTNI NALOG
if self.putni_nalog and self.status == self.StatusRadaChoices.ZAVRSENO:
pn = self.putni_nalog
# Provjeravamo ima li nalog još uvijek otvorenih radnih naloga
ostali_otvoreni = pn.radni_nalozi.exclude(
id=self.id, # Isključujemo trenutni nalog jer je on upravo završen
id=self.id,
status=self.StatusRadaChoices.ZAVRSENO
).exists()
@@ -200,7 +226,10 @@ class RadniNalog(models.Model):
verbose_name_plural = "Radni nalozi"
def __str__(self):
return f"RN-{self.broj_naloga} | {self.stroj.naziv}"
# POPRAVLJENO: Budući da polje broj_naloga već u bazi sadrži "RN-2026-0004",
# ovdje izbacujemo fiksni prefiks "RN-" kako se ne bi duplirao pri pretvaranju objekta u string!
return f"{self.broj_naloga} | {self.stroj.naziv}"
class RadniNalogSlika(models.Model):
radni_nalog = models.ForeignKey(
@@ -208,7 +237,6 @@ class RadniNalogSlika(models.Model):
related_name='slike',
on_delete=models.CASCADE
)
# POPRAVLJENO: uklonjeni navodnici oko funkcije
slika = models.ImageField(upload_to=putanja_galerije_naloga, verbose_name="Slika")
opis = models.CharField(max_length=100, blank=True, verbose_name="Kratki opis slike")