Root cause was WORKER TIMEOUT, not OOM:
- Gunicorn sends SIGABRT to workers that exceed 60s timeout
- SIGABRT handler calls sys.exit(1) raising SystemExit(BaseException)
- except Exception does NOT catch SystemExit, so the worker crashes
Two fixes:
1. except BaseException — catches SystemExit so the worker survives
and gracefully skips the image (returns None) instead of dying
2. Image.BILINEAR instead of LANCZOS — orders of magnitude faster
for large images, ensures processing completes well within 60s
3. Reduce max_width 1600->800, megapixel limit 20->8 MP, optimize=True removed
(these reduce processing time further)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previous draft()+convert()+thumbnail() ordering still caused OOM for
non-JPEG formats (PNG/HEIC) because draft() is a no-op for those formats,
and convert('RGB') forces a full pixel decode regardless.
Replace with thumbnail()-first ordering:
- thumbnail() internally calls draft() for JPEG before decoding
- thumbnail() performs in-place resize without allocating a second
full-resolution buffer
- convert('RGB') is then called on the already-small image (safe for
any format)
Add _COMPRESS_IMAGE_MAX_MEGAPIXELS guard (20 MP): read image dimensions
from headers only (no pixel decode) and return None for images that
exceed the limit. This prevents OOM even for pathologically large files
where draft() provides no benefit (e.g. PNG, TIFF, HEIC).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PIL img.resize() on high-resolution photos was exhausting worker RAM,
causing Gunicorn to SIGKILL the worker mid-request (production OOM crash).
Replace the explicit resize() path in both _compress_image_for_pdf() and
_compress_image_for_docx() with:
- Image.draft() — hints the JPEG decoder to decode at a lower resolution
- Image.thumbnail() — in-place resize that avoids allocating a second
full-resolution buffer
This keeps peak memory proportional to the output size instead of the
original file size, preventing the worker from being killed when
processing multi-megapixel service-record photos.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevent service-record inference from pulling tasks from other work orders
that share the same vehicle. This keeps task-service-context, service
reports, and related work-order widgets isolated to the selected nalog.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure admin/API deletes and updates of service records, photos, and
attachments invalidate the cached service-records PDF. Also harden cached
PDF lookup/download paths so a missing file is treated as stale cache
instead of a 500.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevent 500 errors on travel-expenses-table when work-hour entries mix
naive and timezone-aware datetimes. Normalize all candidate datetimes to
a consistent timezone before min/max comparisons.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
BaseModel defines id with uuid4 default, help_text, and verbose_name on
created_at/updated_at/is_active. Migration 0036 omitted these, causing
Django to detect pending model changes and refuse to migrate on the server.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce a dedicated travel-expenses table for work orders so Broj sati, Količina dnevnica, and Iznos dnevnice can be reviewed and edited before PDF generation. The PDF now uses the stored travel-expenses values, with default HR rate and cache invalidation on updates.
Aggregate task work-hour rows into travel-cost calculations and monthly servicer reporting, and keep the frontend calendar aligned with the task work-hours source.
Mark stale pending fleet archives as failed after a timeout window and create a fresh request so users receive completion notifications and can download ZIP files again.
Add regression coverage for replacing stale pending service-task archive requests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Group 'Preuzmi mjesečni izvještaj' and 'Preuzmi ZIP' buttons together in the calendar toolbar
- Close the ZIP modal immediately on action click so the user is no longer blocked in the sidebar
- Backend: include generated_archive_id in the 'requested' notification so the frontend can track the specific archive lifecycle
- Frontend: refresh notifications immediately after archive request and start targeted polling (every 10s) until a completed/failed notification arrives for that archive ID
- Add previous/next pagination to the service records table (page mode) when no service context is selected, showing all records 10 per page (matching the work orders table pattern)
- Tests: assert generated_archive_id is present in both requested and completed notifications
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Invalidate cached monthly service-task ZIP archives so exports are rebuilt with the latest task/work-order/vehicle header data. Keep stale detection for work-order archives and add regression tests for per-task header isolation and stale cache replacement.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore the monthly fleet archive task and routes so generated ZIP downloads work again. Also populate service report DOCX headers from the selected task/work-order context and cover the direct and archived export flows with regression tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure /putni-nalozi/racuni reads service-report context from the selected task's own work order instead of reusing the parent page work-order data for every task.
- task-service-context now returns task_work_order metadata per task
- work hours modal reads location and travel data from task_work_order
- tasks without a linked work order fall back to None
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoid HTTP/2 streaming failures on large archive downloads by returning the stored ZIP as a buffered HttpResponse with explicit Content-Length.
Also update archive download tests to support both buffered and streaming response objects.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align service-record PDF/DOCX generation with task-level data and naming.
- Use Task.service_report_note as report note source
- Use Task.scheduled_date for service-record dates
- Rename per-task exports to MT...SN... format
- Add monthly SN/PN ZIP download endpoints with 7-day retention
- Add calendar 'Preuzmi sve' modal with both bulk download actions
- Prefill work-hours day/date from scheduled_date for empty tables
- Remove duplicate note table and clean extra DOCX page breaks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enable generating service-record documents for a single task on /putni-nalozi/racuni while preserving existing full work-order export.
- Backend service-records PDF/DOCX endpoints now accept optional task_id and validate the task belongs to the selected work order.
- Document builders support scoped task lists, and per-task downloads use task-specific filenames.
- Frontend task cards now include per-task PDF/DOCX actions, while top-level buttons are clarified as exporting all service records.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Backend:
- Added service_report_note TextField to Task model (migration 0012)
- Exposed service_report_note in TaskSerializer
- task_service_context: fixed service record filter from
work_order.vehicle_id -> task.vehicle_id (bug: cross-crane work orders
returned wrong records for non-primary crane)
- task_service_context: include service_report_note in each task payload
- _build_work_order_service_records_docx_bytes: use per-task
service_report_note (fallback: work_order.notes); fix service records
query to use task vehicle ids for cross-crane support
- PDF generator: same per-task notes and cross-crane vehicle fix
Frontend:
- WorkOrderInvoicesPdfPage: removed global 'Uredi tablicu radnih sati' /
'Uredi tekst napomene' buttons that always opened tasks[0]; replaced
with per-task buttons inside each task article
- WorkOrderInvoicesPdfPage: separate editingTaskNote state / handler
that calls updateTaskServiceReportNote (PATCH task.service_report_note)
- WorkOrderServiceNotesModal: now receives task prop instead of workOrder;
reads/writes task.service_report_note; shows task title in header
- TaskWorkHoursTableModal: added workOrder prop; renders read-only amber
info block with travel dates, mileage, and servicer vehicle data
- fleetDashboardStore: added updateTaskServiceReportNote() function
- WorkOrderInvoicesPdfPage: show saved service_report_note inline on
each task card for quick reference
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Widget Bilješke sada omogućuje odabir više članova tima (ne samo
servisera), a backend validacija i testovi su usklađeni.
Dodatno, nakon realtime service_note notifikacije sada se osvježava
service-notes store, te create flow više ne briše lokalne bilješke kada
odgovor vrati praznu listu.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Problem: serviser nije mogao odabrati putni nalog kreiran za drugu dizalicu
jer je frontend filtrirao naloge po aktivnom crane kontekstu, a backend
serializer je bacao ValidationError ako WO.vehicle != task.vehicle.
Promjene:
- TaskSerializer: uklonjena cross-crane validacija - WO moze pripadati
drugoj dizalici od one na tasku (cross-crane sharing)
- FleetDashboardShell: serviceRecordWorkOrders i selectedTaskWorkOrders
vise ne filtriraju po vehicle ID-u, prikazuju sve otvorene naloge
- TaskServiceRecordsModal: gumb i modal za izmjenu podataka dizalice
(radni sati, kilometraza) premjesten iz WorkOrderDetailModal
- WorkOrderDetailModal: uklonjen gumb 'Izmijeni podatke dizalice'
- Dodana 3 nova backend testa za cross-crane WO dodjelu (15/15 prolaze)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generirani servisni DOCX preuzimao je document/write zaštitu iz predloška pa je Word datoteka bila neurediva.\n\nDodano je uklanjanje documentProtection/writeProtection/readOnlyRecommended postavki pri kreiranju DOCX dokumenta te regresijski test koji provjerava da zaštita nije prisutna u word/settings.xml.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DOCX izvjestaj servisnih zapisa sada ugradjuje povezane fotografije nakon kompresije slike za manju velicinu dokumenta.
Dodan je regresijski test koji provjerava da generirani DOCX sadrzi embedded media datoteke.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dodaje uredski unos pocetka i kraja rada u sidebaru mjesecnog izvjestaja servisera i automatski racuna redovan rad iz unesenog raspona. Zadrzava postojece predpopunjavanje za rad u uredu i validira neispravan vremenski raspon.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uklanja ovisnost o workOrders.invoices i dohvaća račune direktno kroz API filtriran po mjesecu. Dodaje i display code putnog naloga u serializer kako bi prikaz u izvještaju ostao potpun.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Dodan model MonthlyServicerDayEntry s automatskom popunom prema tipu:
office (08:00-16:00, Zagreb ured), vacation i sick_leave (8h bez vremena)
- Migracija 0034_monthlyservicerdayentry za novu tablicu s UniqueConstraint
- Novi ViewSet + serializer + endpoint fleet/monthly-servicer-day-entries/
(upsert: POST za novi dan ili update za isti dan ako vec postoji)
- Backend DOCX report (_build_monthly_servicer_report_rows) sad generira
redak za SVAKI dan u mjesecu: taskovi > rucni unos > prazan red
- TaskCalendarWidget potpuno prepisan bez mojibake znakova
(zamjena UTF-8 specijalnih znakova ASCII ekvivalentima u string literalima)
- Tablica izvjestaja servisera: klikom na red bez naloga otvara se overlay
s tri opcije (Rad u uredu / Bolovanje / Godisnji)
- TaskCalendarPortal: dodan calendarWorkOrders computed (svi statusi)
kako bi report imao potpune podatke o radnom vremenu
- fetchMonthlyServicerEntries i upsertMonthlyServicerEntry u store-u
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Dva nova Django API endpointa (GET /api/fleet/reports/monthly-servicer/ i
/api/fleet/reports/monthly-costs/) koji generiraju DOCX u A4 landscape formatu
- TaskCalendarWidget prosiren s report panelom: dva gumba iznad kalendara
(Izvjestaj servisera / Izvjestaj troskova) sirenjem drawer-a na 680 px
- Preview tablice s podacima iz postojeceg tasks/workOrders store-a
- downloadMonthlyServiserReport i downloadMonthlyCostsReport u store-u
- TaskCalendarPortal prosljeduje activeWorkOrders prop widgetu
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- task_management/views.py: dodaj ?work_order= filter u get_queryset
(jedini razlog zasto putni nalog nije mogao imati vise taskova bio je
nedostatak backend filtera; model uvijek podrzavao vise taskova)
- fleet/serializers.py: ukloni current_mileage iz read_only_fields
kako bi se moglo azurirati putem PATCH /fleet/cranes/{id}/
- fleetDashboardStore.js: dodaj fetchTasksByWorkOrder(workOrderId)
koji dohvaca GET tasks/tasks/?work_order={id}
- WorkOrderDetailModal.jsx:
* import TaskCreateModal, fetchTasksByWorkOrder, updateVehicle, getStatusLabel
* nova sekcija 'Zadaci putnog naloga' s popisom i gumbom '+ Dodaj zadatak'
* gumb '🔧 Izmijeni podatke dizalice' otvara CraneDataModal
* CraneDataModal: unos superstructure_working_hours, chassis_working_hours,
current_mileage; poziva updateVehicle; prikazuje trenutne vrijednosti
kao placeholder
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- admin.py: thumbnail_preview u WorkOrderInvoiceInline i WorkOrderInvoiceAdmin
prikazuje slike/PDF veze putem optimiziranog image endpointa
- serializers.py: image_url sada uključuje ?w=1280&q=80&fmt=jpeg za ne-PDF slike
- views.py: doda _compress_image_for_pdf() helper, zamijeni ImageReader(photo.image)
s kompresiranom JPEG verzijom na oba mjesta u PDF generiranju
- tasks.py: kompresija invoice slike u JPEG u memoriji prije ImageReader poziva
- WorkOrderInvoicesPdfPage.jsx:
* InvoiceImagePreview prikazuje 'Učitavanje...' dok se blob dohvaća (ne 401 fallback)
* uklonjen resolveMediaUrl fallback koji uzrokuje 401 greške
* svaka invoice kartica je klikabilna i otvara InvoiceDetailModal
* InvoiceDetailModal prikazuje detalje i uvećanu sliku/PDF vezu
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- dodaje DOCX preuzimanje za putni nalog, servisne zapise i racune na /putni-nalozi/racuni\n- uvodi modal za uredjivanje teksta napomene izvjestaja servisera\n- poboljsava update work order toka i upsert lokalnog stanja\n- ukljucuje TaskCalendar migraciju, dev watch stabilizaciju i deploy upute
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- koristi novi Servisni N-R template za service-records DOCX\n- premjesta tablicu radnih sati odmah ispod naslova na prvoj stranici\n- deduplicira ponovljene unose koristenih dijelova u tablici artikala\n- uklanja fallback neautorizirani media URL za zasticene fleet resurse
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Servisni PDF sada koristi work_order.notes za polje Napomene umjesto izvedenog teksta iz task naslova. Dodana je i invalidacija generated PDF cachea pri updateu putnog naloga kako bi se nakon spremanja napomene uvijek generirao svjezi PDF.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uvodi signature polje i default predlozak potpisa za nove korisnike, te data migraciju koja popunjava postojece prazne potpise. Email flow sada dosljedno dodaje korisnicki potpis i sprema finalni body poruke u dispatch log radi audita.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prebaci slanje emaila putnog naloga u pozadinski task bez blokiranja UI-a i dodaj fallback kad queue nije dostupna. Kod greške slanja spremi generirane PDF-ove u cache i pošalji notifikaciju s download linkovima.
Dodaj EmailDispatchLog model i Django admin pregled za praćenje kome je poslano, što je poslano, status i detalje privitaka.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dodaj detalje dizalice (proizvođač, model i SN) te vlasnika u modal radnog zadatka i kalendar zadataka, uz zajednički frontend helper za DRY prikaz.
Proširi Task API serializer dodatnim poljima za prikaz dizalice/vlasnika i ažuriraj testove serializera za nova izlazna polja.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uveden je novi Notes drawer s kalendar integracijom, zajednički DRY
left-slide mehanizam te međusobno isključivo prikazivanje s Kalendar
widgetom.
Backend i frontend su prošireni za servisne bilješke (uključujući slanje
na više servisera), dodan je users/servicers endpoint, te su uvedene
warning notifikacije kada serviser ima više od 2 zadatka isti dan.
U admin su dodane stavke za Bilješke i zaseban Kalendar zadataka (proxy
TaskCalendar) radi lakšeg operativnog pregleda.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dodana je tablica DODATNI TROŠKOVI za putni nalog (backend model + API +
frontend modal) s izračunom ukupnog iznosa za isplatu.
Upload računa sada odmah upisuje redak u dodatne troškove, a asinkroni OCR
(Celery) naknadno pokušava popuniti broj računa i iznos iz slike/PDF-a.
Uvedena je invalidacija PDF cache-a nakon promjena računa/dodatnih troškova
kako bi generirani PDF uvijek prikazivao najnovije podatke.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dodano je origin_location polje kroz backend model, serializer, admin i UI
modal za kreiranje/uređivanje putnog naloga (default: Zagreb).
Računi putnog naloga sada prihvaćaju slike i PDF datoteke, uz backend
validaciju formata, podršku za serving PDF-a i prilagodbu preview prikaza.
Za greške uploada računa dodan je error toast kako bi poruke validacije bile
vidljive korisniku odmah u sučelju.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Dodano polje 'display_code' na WorkOrder model (format: 2 inicijala + ddmmyy)
- Migracije za kreiranje polja i backfill postojećih naloga
- Ažuriran WorkOrderSerializer za validaciju i serijalizaciju display_code
- Backend tasks za auto-generiranje display_code pri kreiranju
- Frontend komponente ažurirane za prikaz display_code umjesto UUID:
* FleetDashboardShell tablica putnih naloga
* WorkOrderDetailModal
* TaskCreateModal
* Sve PDF stranice
- displayIds.js utility za formatiranjeDisplay ID-eva
- Admin interface prikazuje display_code
Dodaje status 'spreman_za_zavrsetak' i backend provjeru koja blokira zatvaranje taska kada je povezani putni nalog otvoren, uz 409 odgovor i opcionalni auto-close flow.\n\nPri zatvaranju putnog naloga taskovi u statusu 'servis' automatski prelaze u 'spreman_za_zavrsetak'. Frontend modal sada podržava potvrdu i retry s auto_close_work_order=true.\n\nUključeni su i ciljani testovi za novi behavior, te ažuriran prikaz statusa u dashboardu.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Backend je vraćao download_url s /api/ prefiksom, a apiClient.js rezolvira URL relativno na BASE_URL koji vec sadrži /api/ - rezultat je bio /api/api/ i 404. Uklonjen /api/ prefiks na svim mjestima (views.py x2, tasks.py x1).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
- Dodan WorkOrderPhoto model (FK na WorkOrder, odvojen od VehicleServicePhoto)
- Migracija 0022_workorderphoto
- WorkOrderViewSet.images action refaktoriran: GET/POST koristi WorkOrderPhoto
- WorkOrderPhotoSerializer dodan u serializers.py
- WorkOrderPhotoInline dodan u admin.py
- Novi WorkOrderPhotoUpload.jsx modal s podrskom za onBack prop i z-[60]
- WorkOrderDetailModal: gumb Dodaj fotografije u editMode, photoUploadOpen state,
ispravljen onSuccess callback (map na URL stringove), reset pri zatvaranju
- FleetDashboardShell: stupac SN u tablici putnih naloga (craneInfo.crane_serial_number)
- fleetDashboardStore: uklonjen dupli toast iz uploadWorkOrderPhoto
- Dodan test test_work_order_images_endpoint.py
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>