add from v002

This commit is contained in:
2026-05-20 21:24:00 +02:00
parent 1a6ba86d0b
commit 83fb8c69bb
98 changed files with 10878 additions and 0 deletions

View File

View File

@@ -0,0 +1,41 @@
from django.contrib import admin
from .models import Kupac
@admin.register(Kupac)
class KupacAdmin(admin.ModelAdmin):
# Polja koja se vide u glavnoj tablici
list_display = ('naziv', 'oib', 'grad', 'telefon', 'prikaz_broja_strojeva', 'aktivno')
# Brzo uređivanje "aktivno" statusa direktno iz tablice
list_editable = ('aktivno',)
# Filtriranje s desne strane
list_filter = ('tip', 'aktivno', 'grad')
# Pretraga po ključnim poljima
# OBAVEZNO dodaj search_fields kako bi autocomplete radio u fleet i nalozi
search_fields = ('naziv', 'oib', 'email', 'grad')
# Organizacija forme za unos
fieldsets = (
('Osnovni podaci', {
'fields': (('naziv', 'tip'), 'oib')
}),
('Kontakt i lokacija', {
'fields': ('email', 'telefon', 'adresa', ('grad', 'postanski_broj'))
}),
('Dodatno', {
'fields': ('napomena', 'aktivno'),
'classes': ('collapse',)
}),
)
# Čitanje sistemskih podataka bez mogućnosti izmjene
readonly_fields = ('datum_kreiranja',)
def prikaz_broja_strojeva(self, obj):
"""Prikazuje broj strojeva direktno u listi kupaca"""
# Koristimo property koji si već definirao u modelu
return obj.broj_strojeva
prikaz_broja_strojeva.short_description = 'Strojevi'

View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class KupciConfig(AppConfig):
name = 'kupci'

View File

View File

@@ -0,0 +1,8 @@
from typing import Any
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args: Any, **options: Any):
print("hello world")

View File

@@ -0,0 +1,55 @@
import random
from django.core.management.base import BaseCommand
from faker import Faker
from kupci.models import Kupac
class Command(BaseCommand):
help = 'Popunjava bazu podataka s testnim kupcima'
def add_arguments(self, parser):
parser.add_argument(
'total',
type=int,
help='Broj kupaca koje želiš kreirati',
default=10
)
def handle(self, *args, **kwargs):
total = kwargs['total']
fake = Faker(['hr_HR'])
self.stdout.write(self.style.MIGRATE_HEADING(f'🚀 Kreiram {total} kupaca...'))
created_count = 0
for _ in range(total):
tip = random.choice(['pravno', 'fizicko'])
if tip == 'pravno':
naziv = fake.company()
else:
naziv = fake.name()
# Generiramo OIB (nasumičnih 11 znamenki)
oib = "".join([str(random.randint(0, 9)) for _ in range(11)])
try:
# Koristimo get_or_create ili create da izbjegnemo duplikate OIB-a
# ako je polje unique=True u modelu
Kupac.objects.create(
naziv=naziv,
oib=oib,
email=fake.email(),
telefon=fake.phone_number(),
adresa=fake.street_address(),
grad=fake.city(),
postanski_broj=fake.postcode(),
tip=tip,
napomena="Automatski generiran testni kupac.",
aktivno=True
)
created_count += 1
except Exception as e:
# Ako OIB slučajno bude isti, samo nastavi dalje
continue
self.stdout.write(self.style.SUCCESS(f'✅ Uspješno dodano {created_count} novih kupaca!'))

View File

@@ -0,0 +1,49 @@
from django.db import models
class Kupac(models.Model):
TIP_KUPCA = [
('pravno', 'Pravna osoba (Tvrtka)'),
('fizicko', 'Fizička osoba'),
]
naziv = models.CharField(
max_length=255,
help_text="Ime i prezime ili puni naziv tvrtke"
)
oib = models.CharField(
max_length=11,
unique=True,
null=True,
blank=True,
verbose_name="OIB"
)
email = models.EmailField(blank=True)
telefon = models.CharField(max_length=50, blank=True)
adresa = models.CharField(max_length=255, blank=True)
grad = models.CharField(max_length=100, blank=True)
postanski_broj = models.CharField(max_length=10, blank=True)
tip = models.CharField(
max_length=10,
choices=TIP_KUPCA,
default='pravno'
)
napomena = models.TextField(blank=True, help_text="Interni podaci o kupcu")
# Sistemski podaci
datum_kreiranja = models.DateTimeField(auto_now_add=True)
aktivno = models.BooleanField(default=True)
class Meta:
verbose_name = "Kupac"
verbose_name_plural = "Kupci"
ordering = ['naziv']
def __str__(self):
return self.naziv
@property
def broj_strojeva(self):
"""Vraća broj strojeva povezanih s ovim kupcem"""
return self.strojevi.count()

View File

@@ -0,0 +1,73 @@
from rest_framework import serializers
from .models import Kupac
from fleet.models import Stroj
from fleet.serializers import StrojSerializer
class KupacSerializer(serializers.ModelSerializer):
"""
Serializer za listu kupaca.
Fokus je na brzini i osnovnim kontaktnim podacima.
"""
# Izračunato polje iz modela (@property broj_strojeva)
broj_strojeva = serializers.IntegerField(read_only=True)
class Meta:
model = Kupac
fields = [
'id',
'naziv',
'grad',
'telefon',
'email',
'tip',
'aktivno',
'broj_strojeva' # Korisno za prikaz u tablici (npr. "5 strojeva")
]
class KupacDetaljiSerializer(serializers.ModelSerializer):
# Ugniježđeni strojevi - prikazujemo puni objekt, ne samo ID
strojevi = StrojSerializer(many=True, read_only=True)
# Dodatna polja koja nisu u bazi, ali su korisna za Astro frontend
ukupan_broj_strojeva = serializers.IntegerField(source='broj_strojeva', read_only=True)
class Meta:
model = Kupac
fields = [
'id',
'naziv',
'oib',
'email',
'telefon',
'adresa',
'grad',
'postanski_broj',
'tip',
'napomena',
'aktivno',
'datum_kreiranja',
'ukupan_broj_strojeva',
'strojevi' # Lista svih strojeva s punim detaljima
]
class KupacListaSerializer(serializers.ModelSerializer):
"""
Optimizirani serializer za prikaz u tablici (listi).
Vraća samo osnovne podatke potrebne za pregled svih klijenata.
"""
# Dohvaćamo property iz modela koji broji strojeve
broj_strojeva = serializers.IntegerField(read_only=True)
class Meta:
model = Kupac
fields = [
'id',
'naziv',
'grad',
'oib',
'telefon',
'tip',
'aktivno',
'broj_strojeva'
]

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

14
001.BACKEND/kupci/urls.py Normal file
View File

@@ -0,0 +1,14 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import KupacViewSet
# Kreiramo router i registriramo naš ViewSet
# 'r' ispred stringa označava raw string (dobra praksa za regex/putanje)
router = DefaultRouter()
router.register(r'svi', KupacViewSet, basename='kupac')
# Definiramo URL obrasce
urlpatterns = [
# Sve rute koje router generira (npr. /api/kupci/ i /api/kupci/{id}/)
path('', include(router.urls)),
]

View File

@@ -0,0 +1,35 @@
from rest_framework import viewsets, filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import Kupac
from .serializers import KupacSerializer, KupacDetaljiSerializer, KupacListaSerializer
class KupacViewSet(viewsets.ModelViewSet):
"""
API endpoint koji omogućuje pregled, pretragu i uređivanje kupaca.
"""
# Optimizacija: prefetch_related smanjuje broj upita jer odmah vuče strojeve
queryset = Kupac.objects.all().prefetch_related('strojevi')
serializer_class = KupacSerializer
# Dodajemo filter backende
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter
]
# 1. Točno filtriranje (npr. ?tip=pravno)
filterset_fields = ['tip', 'aktivno', 'grad']
# 2. Pretraga (npr. ?search=gradnja)
# Pretražuje naziv ili OIB
search_fields = ['naziv', 'oib']
# 3. Sortiranje (npr. ?ordering=-datum_kreiranja)
ordering_fields = ['naziv', 'datum_kreiranja']
ordering = ['naziv'] # Defaultno sortiranje
def get_serializer_class(self):
if self.action == 'retrieve': # Ako se gleda pojedinačni kupac
return KupacDetaljiSerializer # Onaj s ugniježđenim strojevima
return KupacListaSerializer # Obični bez strojeva