feat: async PDF cache i API media putanje za putne naloge
Some checks failed
ERP CI/CD Pipeline / test (push) Has been cancelled
ERP CI/CD Pipeline / Deploy (server git pull + compose) (push) Has been cancelled

Uvodi background request tok za generiranje PDF-a s 24h cache-om i cleanup taskom preko Celery beat-a. Dodaje notification metadata download_url za ponovno preuzimanje bez regeneracije, API image endpointe za putne naloge i račune, te frontend integraciju za async download i produkcijski prikaz slika.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
mariomitte
2026-07-17 16:42:02 +02:00
parent 5e7ec72a4e
commit e8330473cd
17 changed files with 726 additions and 231 deletions

View File

@@ -7,7 +7,7 @@ import uuid
from io import BytesIO
from PIL import Image
from modules.fleet.models import Vehicle, WorkOrder, VehicleServiceRecord, VehicleServicePhoto
from modules.fleet.models import Vehicle, WorkOrder, WorkOrderInvoice, VehicleServiceRecord, VehicleServicePhoto
def create_test_image(filename='test.jpg', size=(40, 40), color='red'):
@@ -94,3 +94,47 @@ class WorkOrderImagesEndpointTests(TestCase):
response = self.client.get(service_photos_url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 0)
def test_work_order_images_endpoint_returns_api_file_urls(self):
upload_url = reverse('workorder-images', kwargs={'pk': self.work_order.pk})
create_response = self.client.post(
upload_url,
data={
'image': create_test_image('workorder-url.jpg'),
'description': 'URL test',
},
format='multipart',
)
self.assertEqual(create_response.status_code, 201, create_response.data)
list_response = self.client.get(upload_url)
self.assertEqual(list_response.status_code, 200)
images = list_response.data.get('images', [])
self.assertEqual(len(images), 1)
photo_id = images[0]['id']
self.assertIn('/api/fleet/work-orders/', images[0]['original_url'])
self.assertIn(f'/images/{photo_id}/file', images[0]['original_url'])
file_response = self.client.get(images[0]['original_url'])
self.assertEqual(file_response.status_code, 200)
self.assertTrue(file_response['Content-Type'].startswith('image/'))
def test_work_order_invoice_serializer_exposes_image_api_url(self):
invoice = WorkOrderInvoice.objects.create(
work_order=self.work_order,
naziv_racuna='Test račun',
image=create_test_image('invoice.jpg'),
created_by=self.user,
)
list_url = f"{reverse('work-order-invoice-list')}?work_order_id={self.work_order.pk}"
list_response = self.client.get(list_url)
self.assertEqual(list_response.status_code, 200)
self.assertEqual(len(list_response.data), 1)
image_url = list_response.data[0].get('image_url')
self.assertIsNotNone(image_url)
self.assertIn(f'/api/fleet/work-order-invoices/{invoice.pk}/image/', image_url)
image_response = self.client.get(image_url)
self.assertEqual(image_response.status_code, 200)
self.assertTrue(image_response['Content-Type'].startswith('image/'))