feat: dodaj ishodište i PDF račune za putni nalog
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

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>
This commit is contained in:
mariomitte
2026-07-19 19:54:15 +02:00
parent cbcbf0b3af
commit d35eaeccf4
12 changed files with 175 additions and 14 deletions

View File

@@ -32,6 +32,7 @@ export default function WorkOrderDetailModal({ open, workOrder, onClose, onSubmi
servicer_vehicle_start_mileage: '',
servicer_vehicle_end_mileage: '',
servicer_vehicle_fuel_cost: '',
origin_location: 'Zagreb',
location: '',
travel_start_at: '',
travel_end_at: '',
@@ -92,11 +93,12 @@ export default function WorkOrderDetailModal({ open, workOrder, onClose, onSubmi
if (!open || !workOrder) return;
setIsEditing(false);
setForm({
has_travel_order: !!(workOrder.travel_start_at || workOrder.travel_end_at || workOrder.location || workOrder.start_mileage != null || workOrder.end_mileage != null),
has_travel_order: !!(workOrder.travel_start_at || workOrder.travel_end_at || workOrder.origin_location || workOrder.location || workOrder.start_mileage != null || workOrder.end_mileage != null),
display_code: workOrder.display_code || '',
status: workOrder.status || 'open',
start_mileage: String(workOrder.start_mileage ?? ''),
end_mileage: workOrder.end_mileage == null ? '' : String(workOrder.end_mileage),
origin_location: workOrder.origin_location || 'Zagreb',
location: workOrder.location || '',
travel_start_at: workOrder.travel_start_at ? String(workOrder.travel_start_at).slice(0, 16) : '',
travel_end_at: workOrder.travel_end_at ? String(workOrder.travel_end_at).slice(0, 16) : '',
@@ -332,6 +334,7 @@ export default function WorkOrderDetailModal({ open, workOrder, onClose, onSubmi
servicer_vehicle_start_mileage: form.servicer_vehicle_start_mileage === '' ? null : Number(form.servicer_vehicle_start_mileage),
servicer_vehicle_end_mileage: form.servicer_vehicle_end_mileage === '' ? null : Number(form.servicer_vehicle_end_mileage),
servicer_vehicle_fuel_cost: form.servicer_vehicle_fuel_cost === '' ? null : Number(form.servicer_vehicle_fuel_cost),
origin_location: form.origin_location.trim() || 'Zagreb',
location: form.location.trim(),
travel_start_at: travelStart ? travelStart.toISOString() : null,
travel_end_at: travelEnd ? travelEnd.toISOString() : null,
@@ -599,6 +602,16 @@ export default function WorkOrderDetailModal({ open, workOrder, onClose, onSubmi
<div className="rounded-xl border border-border-hairline bg-canvas-base p-3">
<h4 className="mb-2 text-sm font-semibold text-text-main">Putni podaci</h4>
<div className="grid gap-4 sm:grid-cols-2">
<label className="sm:col-span-2 flex flex-col gap-1 text-sm">
<span className="font-medium text-text-main">Lokacija ishodišta</span>
<input
type="text"
value={form.origin_location}
onInput={(event) => setForm({ ...form, origin_location: event.currentTarget.value })}
className="rounded-lg border border-border-hairline bg-canvas-base px-3 py-2 text-text-main disabled:bg-canvas-deep"
disabled={!isEditing}
/>
</label>
<label className="sm:col-span-2 flex flex-col gap-1 text-sm">
<span className="font-medium text-text-main">Lokacija odredišta {form.has_travel_order ? '*' : ''}</span>
<div className="flex gap-2">
@@ -658,6 +671,7 @@ export default function WorkOrderDetailModal({ open, workOrder, onClose, onSubmi
setForm((prev) => ({
...prev,
has_travel_order: checked,
origin_location: checked ? (prev.origin_location || 'Zagreb') : 'Zagreb',
location: checked ? prev.location : '',
travel_start_at: checked ? prev.travel_start_at : '',
travel_end_at: checked ? prev.travel_end_at : '',
@@ -980,10 +994,10 @@ export default function WorkOrderDetailModal({ open, workOrder, onClose, onSubmi
/>
</label>
<label className="flex flex-col gap-1 text-sm sm:col-span-2">
<span className="font-medium text-text-main">slika_računa</span>
<span className="font-medium text-text-main">slika_računa / pdf_računa</span>
<input
type="file"
accept="image/*"
accept="image/*,.pdf,application/pdf"
onChange={(event) => {
const file = event.currentTarget.files && event.currentTarget.files[0] ? event.currentTarget.files[0] : null;
setInvoiceForm((prev) => ({ ...prev, imageFile: file }));