prvi kod za live uporabu
Some checks failed
ERP CI Pipeline / test (push) Has been cancelled

This commit is contained in:
mariomitte
2026-07-09 21:21:17 +02:00
parent 857bb65d52
commit 5d39e048ba
239 changed files with 25151 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
# backend/modules/invoicing/tests/conftest.py
import pytest
from django.contrib.auth import get_user_model
from modules.invoicing.models import Invoice
from unittest.mock import MagicMock
from django.conf import settings
from core.celery import app as celery_app
import modules.invoicing.tasks as tasks
import uuid
User = get_user_model()
@pytest.fixture
def user():
# Dodan username
return User.objects.create_user(email="test@test.hr", password="password")
@pytest.fixture
def api_client(user):
from rest_framework.test import APIClient
client = APIClient()
client.force_authenticate(user=user)
return client
@pytest.fixture
def staff_user():
return User.objects.create_superuser(email='admin@erp.hr', password='password', first_name='Admin', last_name='User')
@pytest.fixture
def regular_user():
return User.objects.create_user(email='user@erp.hr', password='password', first_name='User', last_name='User')
@pytest.fixture
def serviser_a():
return User.objects.create_user(email="a@test.hr", password="password", first_name='A', last_name='User')
@pytest.fixture
def serviser_b():
return User.objects.create_user(email="b@test.hr", password="password", first_name='B', last_name='User')
@pytest.fixture
def test_client():
from modules.crm.models import Client
return Client.objects.create(name="Testni Klijent", tax_id="12345678901")
@pytest.fixture
def invoice_a(serviser_a, test_client): # Dodali smo test_client
from modules.invoicing.utils import generate_invoice_number
return Invoice.objects.create(
invoice_number=generate_invoice_number(),
creator=serviser_a,
client=test_client, # OVDJE JE BIO PROBLEM (bio je null)
amount=100.00,
due_date="2026-12-31"
)
@pytest.fixture(autouse=True)
def paperless_settings(settings):
# Postavi privremene vrijednosti za vrijeme trajanja testa
settings.PAPERLESS_API_URL = "http://mock-paperless.local"
settings.PAPERLESS_API_TOKEN = "mock-token"
@pytest.fixture(autouse=True)
def force_celery_eager(settings):
settings.CELERY_TASK_ALWAYS_EAGER = True
settings.CELERY_TASK_EAGER_PROPAGATES = True
# Ovo je ključ: prisilno ažuriraj konfiguraciju Celery aplikacije
celery_app.conf.update(
task_always_eager=True,
task_eager_propagates=True,
)
@pytest.fixture(autouse=True)
def mock_paperless_gateway(monkeypatch):
"""
Koristi monkeypatch za zamjenu PaperlessGateway klase.
"""
mock_client = MagicMock()
mock_client.get_document_metadata.return_value = {
"correspondent": "INV-2026-TEST",
"total_amount": "100.00",
"created": "2026-07-01"
}
# Kreiramo tvornicu koja vraća naš mock
MockGateway = MagicMock(return_value=mock_client)
# Zamijeni klasu unutar tasks modula
monkeypatch.setattr(tasks, "PaperlessGateway", MockGateway)
yield MockGateway
@pytest.fixture
def mock_client(db):
"""Kreira klijenta kojeg koristi PaperlessGateway u testovima."""
from modules.crm.models import Client
return Client.objects.create(name="INV-2026-TEST") # Ime mora odgovarati mocku iz conftest.py
@pytest.fixture
def create_unique_client(db):
"""Factory funkcija za kreiranje unikatnog klijenta."""
from modules.crm.models import Client
def _create_client(name):
return Client.objects.create(
name=name,
tax_id=str(uuid.uuid4()), # Garantira unikatnost
email=f"{uuid.uuid4()}@test.hr" # Garantira unikatnost
)
return _create_client

View File

@@ -0,0 +1,46 @@
import uuid
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from django.contrib.auth import get_user_model
from decimal import Decimal
from datetime import date
from modules.crm.models import Client
from modules.invoicing.models import Invoice
class InvoiceAPITests(TestCase):
def setUp(self):
self.api_client = APIClient()
User = get_user_model()
# Kreiraj test korisnika (ako treba autentikacija)
self.user = User.objects.create_user(
username=f"user_{uuid.uuid4().hex[:8]}",
email=f"user+{uuid.uuid4().hex[:8]}@example.test",
password="pass"
)
# Kreiraj jedinstvenog klijenta (email s uuid kako bismo izbjegli duplicate key)
self.client_obj = Client.objects.create(
name="API Test Client",
email=f"client+{uuid.uuid4()}@example.test"
)
# autentikacija ako je endpoint zaštićen
self.api_client.force_authenticate(user=self.user)
def test_api_creates_invoice_with_id(self):
url = reverse('invoice-list') # prilagodi ako je druga ruta
payload = {
"client": self.client_obj.id,
"due_date": date.today().isoformat(),
"items": [
{"description": "Test item", "quantity": "1.00", "unit_price": "10.00"}
]
}
response = self.api_client.post(url, payload, format='json')
assert response.status_code in (200, 201)
# provjeri da se faktura stvorila i da vraća id/kod
created = Invoice.objects.filter(client=self.client_obj).first()
assert created is not None
assert created.amount == Decimal("10.00")

View File

@@ -0,0 +1,18 @@
import pytest
from modules.invoicing.models import Invoice, InvoiceItem
@pytest.mark.django_db
def test_invoice_str_representation(invoice_a):
assert str(invoice_a) == f"{invoice_a.invoice_number} - {invoice_a.client}"
@pytest.mark.django_db
def test_invoice_total_calculation(invoice_a):
InvoiceItem.objects.create(invoice=invoice_a, description="Test", quantity=2, unit_price=50.00)
InvoiceItem.objects.create(invoice=invoice_a, description="Test2", quantity=1, unit_price=25.00)
assert invoice_a.get_total_amount() == 125.00
@pytest.mark.django_db
def test_invoice_number_autogenerate():
# Testiramo da li se automatski generira ako nije zadan
from modules.invoicing.models import Invoice
# (Za ovo ti treba 'creator' i 'client' fixture-i)

View File

@@ -0,0 +1,10 @@
import pytest
from rest_framework import status
@pytest.mark.django_db
def test_invoice_access_restriction(api_client, regular_user, invoice_a):
# invoice_a pripada serviseru_a, a api_client je regular_user
url = f'/api/invoicing/invoices/{invoice_a.id}/'
response = api_client.get(url)
# Trebao bi dobiti 404 ili 403 ovisno o tvojoj logici
assert response.status_code in [status.HTTP_404_NOT_FOUND, status.HTTP_403_FORBIDDEN]

View File

@@ -0,0 +1,22 @@
import pytest
from modules.invoicing.serializers import InvoiceSerializer
from unittest.mock import MagicMock
@pytest.mark.django_db
def test_invoice_serializer_valid(regular_user, test_client):
data = {
"client": test_client.id,
"due_date": "2026-12-31",
"status": "draft",
"items": [
{"description": "Servis", "quantity": 1, "unit_price": 100.00}
]
}
# Context je ključan za pristup useru
request = MagicMock()
request.user = regular_user
serializer = InvoiceSerializer(data=data, context={'request': request})
assert serializer.is_valid(), serializer.errors
invoice = serializer.save()
assert invoice.amount == 100.00

View File

@@ -0,0 +1,40 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from datetime import date
from decimal import Decimal
from modules.crm.models import Client
from modules.invoicing.models import Invoice, InvoiceItem
from modules.invoicing.services import InvoiceService
class InvoiceServiceTests(TestCase):
def setUp(self):
User = get_user_model()
self.creator = User.objects.create_user(username="creator", email="c@example.com", password="pass")
self.client = Client.objects.create(name="Test Client")
def test_create_full_invoice_creates_invoice_and_items(self):
items = [
{"description": "Item A", "quantity": Decimal("2.00"), "unit_price": Decimal("10.00")},
{"description": "Item B", "quantity": Decimal("1.00"), "unit_price": Decimal("5.50")},
]
validated_data = {
"client": self.client,
"due_date": date.today(),
"items": items,
}
invoice = InvoiceService.create_full_invoice(user=self.creator, **validated_data)
# Basic assertions
self.assertIsInstance(invoice, Invoice)
self.assertEqual(invoice.creator, self.creator)
self.assertEqual(invoice.client, self.client)
self.assertEqual(invoice.items.count(), 2)
# Total should be 2*10 + 1*5.5 = 25.5
self.assertEqual(invoice.amount, Decimal("25.50"))
item_descriptions = [i.description for i in invoice.items.all()]
self.assertIn("Item A", item_descriptions)
self.assertIn("Item B", item_descriptions)

View File

@@ -0,0 +1,40 @@
import uuid
from django.test import TestCase
from unittest.mock import patch
from datetime import datetime
from django.contrib.auth import get_user_model
from modules.crm.models import Client
from modules.invoicing import tasks
class TasksTests(TestCase):
def setUp(self):
# Kreiramo klijenta koji vraća PaperlessGateway; email jedinstven radi izbjegavanja duplikata
self.client = Client.objects.create(
name="Paperless Corp",
email=f"paperless+{uuid.uuid4()}@example.test"
)
User = get_user_model()
# Kreiramo sistemskog korisnika koji već postoji (trebao bi se koristiti)
self.system_user = User.objects.create_user(
username="bot@erp.hr",
email="bot@erp.hr",
password="pass"
)
@patch("modules.invoicing.tasks.PaperlessGateway.get_document_metadata")
@patch("modules.invoicing.tasks.InvoiceService.create_full_invoice")
def test_process_paperless_document_calls_service(self, mock_create_full_invoice, mock_get_metadata):
# simuliramo odgovor infrastrukture
mock_get_metadata.return_value = {
"created": "2026-01-01T00:00:00Z",
"correspondent": "Paperless Corp",
"total_amount": "100.00"
}
# Pozovemo task sinkrono pomoću .apply (ispravno mapira argumente)
tasks.process_paperless_document_task.apply(args=(123,))
# Očekujemo da je pozvan servis za izradu fakture
mock_create_full_invoice.assert_called()

View File

@@ -0,0 +1,28 @@
from django.test import TestCase
from datetime import date
from decimal import Decimal
from django.contrib.auth import get_user_model
from modules.invoicing.models import Invoice
from modules.invoicing.utils import generate_invoice_number
from modules.crm.models import Client
class UtilsTests(TestCase):
def setUp(self):
User = get_user_model()
self.user = User.objects.create_user(username="creator", email="c@example.com", password="pass")
self.client = Client.objects.create(name="Client Utils")
def test_generate_invoice_number_increments(self):
year = date.today().year
Invoice.objects.create(
invoice_number=f"INV-{year}-0001",
creator=self.user,
client=self.client,
amount=Decimal("1.00"),
due_date=date.today()
)
next_num = generate_invoice_number()
# očekujemo INV-<year>-0002
self.assertTrue(next_num.endswith("-0002"))

View File

@@ -0,0 +1,46 @@
from django.test import TestCase, RequestFactory
from django.contrib.auth import get_user_model
from unittest.mock import patch
from modules.invoicing.views import InvoiceTransactionEntryViewSet
from modules.invoicing.serializers import InvoiceTransactionSerializer
from modules.invoicing.models import InvoiceTransaction
from modules.invoicing.models import Invoice
from modules.crm.models import Client
from datetime import date
from decimal import Decimal
class ViewsTests(TestCase):
def setUp(self):
User = get_user_model()
self.user = User.objects.create_user(username="u1", email="u1@example.com", password="pass")
self.client = Client.objects.create(name="Client Views")
self.invoice = Invoice.objects.create(
invoice_number="INV-TEST-0001",
creator=self.user,
client=self.client,
amount=Decimal("10.00"),
due_date=date.today()
)
self.factory = RequestFactory()
self.viewset = InvoiceTransactionEntryViewSet()
@patch("modules.invoicing.services.InvoiceService.create_invoice_entry")
def test_perform_create_calls_service(self, mock_create_invoice_entry):
# simuliramo serializer.validated_data i poziv perform_create
serializer = InvoiceTransactionSerializer(data={
"invoice": self.invoice.id,
"invoice_number": self.invoice.invoice_number,
"amount": "5.00",
"is_active": True
}, context={"request": self.factory.get("/")})
serializer.is_valid(raise_exception=True)
# Poziv perform_create koji bi trebao delegirati na servis
self.viewset.request = self.factory.post("/")
self.viewset.request.user = self.user
# Ako kod poziva InvoiceService.create_invoice_entry, mock se aktivira.
self.viewset.perform_create(serializer)
mock_create_invoice_entry.assert_called()