123 lines
3.7 KiB
Python
123 lines
3.7 KiB
Python
# backend/modules/invoicing/models.py
|
|
|
|
import datetime
|
|
from django.utils import timezone
|
|
from core.base_models import BaseModel
|
|
from django.db import models, transaction
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.conf import settings
|
|
from .utils import generate_invoice_number
|
|
|
|
class Invoice(BaseModel):
|
|
# Identifikacija (UUID i created_at dolaze iz BaseModel)
|
|
invoice_number = models.CharField(max_length=50, unique=True, verbose_name=_("Broj fakture"))
|
|
|
|
# Relacije
|
|
creator = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.PROTECT,
|
|
related_name="invoices"
|
|
)
|
|
client = models.ForeignKey(
|
|
'crm.Client',
|
|
on_delete=models.PROTECT,
|
|
verbose_name=_("Klijent")
|
|
)
|
|
|
|
# Financijski detalji
|
|
amount = models.DecimalField(max_digits=12, decimal_places=2, verbose_name=_("Iznos"))
|
|
currency = models.CharField(max_length=3, default="EUR")
|
|
tax_rate = models.DecimalField(max_digits=4, decimal_places=2, default=25.00)
|
|
|
|
# Statusi
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=[
|
|
('draft', 'Nacrt'),
|
|
('sent', 'Poslano'),
|
|
('paid', 'Plaćeno'),
|
|
('cancelled', 'Stornirano')
|
|
],
|
|
default='draft'
|
|
)
|
|
|
|
due_date = models.DateField(verbose_name=_("Rok plaćanja"))
|
|
|
|
class Meta:
|
|
# Nasljeđujemo ordering iz BaseModel, ali ga možemo override-ati
|
|
verbose_name = _("Faktura")
|
|
verbose_name_plural = _("Fakture")
|
|
|
|
def __str__(self):
|
|
return f"{self.invoice_number} - {self.client}"
|
|
|
|
def get_total_amount(self):
|
|
return self.items.aggregate(total=models.Sum(models.F('quantity') * models.F('unit_price')))['total'] or 0
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.invoice_number:
|
|
self.invoice_number = generate_invoice_number()
|
|
super().save(*args, **kwargs)
|
|
|
|
class InvoiceItem(BaseModel):
|
|
# Relacija prema fakturi
|
|
invoice = models.ForeignKey(
|
|
Invoice,
|
|
on_delete=models.CASCADE,
|
|
related_name='items',
|
|
verbose_name=_("Faktura")
|
|
)
|
|
|
|
# Detalji stavke
|
|
description = models.CharField(
|
|
max_length=255,
|
|
verbose_name=_("Opis stavke")
|
|
)
|
|
quantity = models.DecimalField(
|
|
max_digits=10,
|
|
decimal_places=2,
|
|
default=1,
|
|
verbose_name=_("Količina")
|
|
)
|
|
unit_price = models.DecimalField(
|
|
max_digits=12,
|
|
decimal_places=2,
|
|
verbose_name=_("Cijena po jedinici")
|
|
)
|
|
|
|
# Automatski izračun (property ne zauzima prostor u bazi)
|
|
@property
|
|
def total(self):
|
|
return self.quantity * self.unit_price
|
|
|
|
class Meta:
|
|
verbose_name = _("Stavka fakture")
|
|
verbose_name_plural = _("Stavke fakture")
|
|
|
|
def __str__(self):
|
|
# Koristimo prednost BaseModel __str__ metode
|
|
return f"{self.description} ({self.quantity} x {self.unit_price})"
|
|
|
|
class InvoiceTransaction(BaseModel):
|
|
# Polja specifična za fakturiranje
|
|
invoice = models.ForeignKey(
|
|
Invoice,
|
|
on_delete=models.PROTECT,
|
|
related_name='transactions',
|
|
verbose_name=_("Faktura")
|
|
)
|
|
work_order = models.ForeignKey(
|
|
'fleet.WorkOrder',
|
|
on_delete=models.SET_NULL,
|
|
related_name='invoice_transactions',
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_("Putni nalog"),
|
|
)
|
|
invoice_number = models.CharField(max_length=50)
|
|
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
|
payment_date = models.DateField(default=timezone.now)
|
|
|
|
class Meta:
|
|
verbose_name = "Financijski zapis"
|
|
verbose_name_plural = "Financijski zapisi" |