From ae6ae08fb14993539066c546429f47e39be6597c Mon Sep 17 00:00:00 2001 From: mariomitte Date: Thu, 23 Jul 2026 13:34:53 +0200 Subject: [PATCH] feat: dodaj DOCX akcije i napomene na racunima - 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> --- README.md | 24 ++++- ...707771-6a5e43a2c43d689a89515bbe-ZmsCD3.csv | 10 ++ .../migrations/0011_taskcalendar.py | 26 ++++++ backend/requirements.txt | 1 + docker-compose.dev.yml | 6 +- frontend/astro.config.mjs | 7 +- .../dashboard/WorkOrderInvoicesPdfPage.jsx | 70 ++++++++++++++ .../dashboard/WorkOrderServiceNotesModal.jsx | 93 +++++++++++++++++++ frontend/src/stores/fleetDashboardStore.js | 64 ++++++++++++- 9 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 backend/modules/crm/import_templates/11707771-6a5e43a2c43d689a89515bbe-ZmsCD3.csv create mode 100644 backend/modules/task_management/migrations/0011_taskcalendar.py create mode 100644 frontend/src/components/dashboard/WorkOrderServiceNotesModal.jsx diff --git a/README.md b/README.md index a2fe4e7..c52603b 100644 --- a/README.md +++ b/README.md @@ -1 +1,23 @@ -Frontend + Backend + Database + Deployment \ No newline at end of file +Frontend + Backend + Database + Deployment + +### Deploy na serveru + +Nakon što je gitea webhook i testirana skripta za automatsko povlačenje promjena iz repozitorija + +```bash +cd /opt/erp/app +git fetch origin +git reset --hard origin/main + +# rebuild backend + worker + beat + frontend +docker compose -f docker-compose.yml -f docker-compose.prod.yml build backend worker beat frontend + +# digni servise s novim image-ima +docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --force-recreate backend worker beat frontend + +# OBAVEZNO migracije (scheduled_date) +docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backend python manage.py migrate + +# provjera +curl -i https://api-004.captain.mitteworkspace.cloud/api/health/ +``` \ No newline at end of file diff --git a/backend/modules/crm/import_templates/11707771-6a5e43a2c43d689a89515bbe-ZmsCD3.csv b/backend/modules/crm/import_templates/11707771-6a5e43a2c43d689a89515bbe-ZmsCD3.csv new file mode 100644 index 0000000..99f969b --- /dev/null +++ b/backend/modules/crm/import_templates/11707771-6a5e43a2c43d689a89515bbe-ZmsCD3.csv @@ -0,0 +1,10 @@ +CONTACT ID,EMAIL,sib_contact_owner,LASTNAME,FIRSTNAME,_DETECTED_LANGUAGE,DOUBLE_OPT-IN,OPT_IN,CONTACT_TIMEZONE,EXT_ID,LINKEDIN,JOB_TITLE,WHATSAPP,LANDLINE_NUMBER,SMS,ADDED_TIME,MODIFIED_TIME +1,mtkalac.mt@gmail.com,,TKALAC,MARIO,,,,,,,,,,385955002483,20-07-2026,20-07-2026 +2,domagoj@knezljubo.hr,,Knez,Domagoj,Croatian,No,NO,(GMT+02:00) Zagreb,domagojknezljubo,,direktor,385913228904,,385913228904,20-07-2026,20-07-2026 +3,ljubo@knezljubo.hr,,Ljubo,Knez,Croatian,No,NO,(GMT+02:00) Zagreb,ljuboknezljubo,,direktor,385913228902,,385913228902,20-07-2026,20-07-2026 +4,mario.tkalac@knezljubo.hr,,Tkalac,Mario,Croatian,No,NO,(GMT+02:00) Zagreb,mariotkalacknezljubo,,serviser,385916303466,,385916303466,20-07-2026,20-07-2026 +5,fran@knezljubo.hr,,Mati,Fran,Croatian,No,NO,(GMT+02:00) Zagreb,franmaticknezljubo,,prodaja,385916004943,,385916004943,20-07-2026,20-07-2026 +6,iva.markic@knezljubo.hr,,Marki,Iva,Croatian,No,NO,(GMT+02:00) Zagreb,ivamarkicknezljubo,,knjigovoa,385919423525,,385919423525,20-07-2026,20-07-2026 +7,ramo@knezljubo.hr,,Husanovi,Ramo,Croatian,No,NO,(GMT+02:00) Zagreb,ramohusanovicknezljubo,,serviser,385913228910,,385913228910,20-07-2026,20-07-2026 +8,office@knezljubo.hr,,Cepi,Dario,Croatian,No,NO,(GMT+02:00) Zagreb,dariocepicknezljubo,logistika,385994067776,,385994067776,20-07-2026,20-07-2026 +9,josip@knezljubo.hr,,Pintari,Josip,Croatian,No,NO,(GMT+02:00) Zagreb,josippintaricknezljubo,serviser,385913228906,,385913228906,20-07-2026,20-07-2026 diff --git a/backend/modules/task_management/migrations/0011_taskcalendar.py b/backend/modules/task_management/migrations/0011_taskcalendar.py new file mode 100644 index 0000000..4dc32a2 --- /dev/null +++ b/backend/modules/task_management/migrations/0011_taskcalendar.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2.15 on 2026-07-21 11:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('task_management', '0010_alter_tasktemplate_status'), + ] + + operations = [ + migrations.CreateModel( + name='TaskCalendar', + fields=[ + ], + options={ + 'verbose_name': 'Kalendar zadatka', + 'verbose_name_plural': 'Kalendar zadataka', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('task_management.task',), + ), + ] diff --git a/backend/requirements.txt b/backend/requirements.txt index dac90f6..0950884 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -50,6 +50,7 @@ pytesseract==0.3.13 pytest>=9.1.1 pytest-django>=4.12.0 python-dateutil==2.9.0.post0 +python-docx==1.2.0 python-dotenv==1.2.2 pusher>=3.3.4 redis==8.0.0 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7bcfc9e..78fcbf2 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -22,7 +22,8 @@ services: build: context: ./frontend dockerfile: Dockerfile - command: npm run dev -- --host 0.0.0.0 --force + working_dir: /app + command: sh -c "rm -rf /app/.astro /app/node_modules/.vite && npm run dev -- --host 0.0.0.0 --force" ports: - "4321:4321" volumes: @@ -30,6 +31,9 @@ services: - /app/node_modules environment: - NODE_ENV=development + - CHOKIDAR_USEPOLLING=true + - CHOKIDAR_INTERVAL=1000 + - WATCHPACK_POLLING=true - PUBLIC_ALLOWED_HOSTS=localhost,127.0.0.1 - PUBLIC_HMR_HOST=localhost - PUBLIC_HMR_PROTOCOL=ws diff --git a/frontend/astro.config.mjs b/frontend/astro.config.mjs index 3a35729..3cf1001 100644 --- a/frontend/astro.config.mjs +++ b/frontend/astro.config.mjs @@ -68,7 +68,12 @@ export default defineConfig({ }, watch: { usePolling: true, - interval: 100, + interval: 1000, + awaitWriteFinish: { + stabilityThreshold: 1200, + pollInterval: 200, + }, + ignored: ['**/.git/**', '**/node_modules/**', '**/.astro/**', '**/dist/**'], }, } : { diff --git a/frontend/src/components/dashboard/WorkOrderInvoicesPdfPage.jsx b/frontend/src/components/dashboard/WorkOrderInvoicesPdfPage.jsx index a0c946e..fda1ab2 100644 --- a/frontend/src/components/dashboard/WorkOrderInvoicesPdfPage.jsx +++ b/frontend/src/components/dashboard/WorkOrderInvoicesPdfPage.jsx @@ -1,22 +1,28 @@ import { useEffect, useMemo, useState } from 'preact/hooks'; import { useStore } from '@nanostores/preact'; import { + downloadWorkOrderDocx, downloadWorkOrderPdf, + downloadWorkOrderInvoicesDocx, downloadWorkOrderInvoicesPdf, + downloadWorkOrderServiceRecordsDocx, downloadWorkOrderServiceRecordsPdf, fetchWorkOrderAdditionalCostsTable, fetchWorkOrderById, fetchWorkOrderInvoices, fetchWorkOrderTaskServiceContext, updateTaskWorkHoursTable, + updateWorkOrder, updateWorkOrderAdditionalCostsTable, } from '../../stores/fleetDashboardStore'; import { $accessToken, $authReady, hydrateAuthFromStorage } from '../../stores/authStore'; import { fetchNotifications, connectNotifications } from '../../stores/notificationStore'; import { formatWorkOrderDisplayCode } from '../../lib/displayIds'; +import { showToast } from '../../stores/toastStore'; import { resolveMediaUrl, useAuthenticatedMediaSources } from './WorkOrderImageCarousel'; import TaskWorkHoursTableModal from './TaskWorkHoursTableModal'; import WorkOrderAdditionalCostsModal from './WorkOrderAdditionalCostsModal'; +import WorkOrderServiceNotesModal from './WorkOrderServiceNotesModal'; function readWorkOrderIdFromQuery() { if (typeof window === 'undefined') { @@ -77,6 +83,9 @@ export default function WorkOrderInvoicesPdfPage() { const [additionalCostsTable, setAdditionalCostsTable] = useState({ data: { rows: [] }, total_for_payout: '0.00' }); const [editingAdditionalCosts, setEditingAdditionalCosts] = useState(false); const [savingAdditionalCosts, setSavingAdditionalCosts] = useState(false); + const [editingServiceReportNote, setEditingServiceReportNote] = useState(false); + const [savingServiceReportNote, setSavingServiceReportNote] = useState(false); + const [serviceReportNoteError, setServiceReportNoteError] = useState(''); useEffect(() => { hydrateAuthFromStorage(); @@ -207,6 +216,24 @@ export default function WorkOrderInvoicesPdfPage() { } }; + const handleSaveServiceReportNote = async (notes) => { + if (!workOrderId) return; + setSavingServiceReportNote(true); + setServiceReportNoteError(''); + try { + const updated = await updateWorkOrder(workOrderId, { notes: String(notes || '') }); + setWorkOrder(updated); + setEditingServiceReportNote(false); + showToast('Napomena za izvještaj servisera je spremljena.', 'success'); + } catch (err) { + const message = err?.message || 'Spremanje napomene nije uspjelo.'; + setServiceReportNoteError(message); + showToast(message, 'error'); + } finally { + setSavingServiceReportNote(false); + } + }; + return (
@@ -225,6 +252,14 @@ export default function WorkOrderInvoicesPdfPage() { > Preuzmi PDF putnog naloga +
@@ -244,6 +279,14 @@ export default function WorkOrderInvoicesPdfPage() { > Uredi tablicu radnih sati + + {loading &&

Učitavanje task konteksta...

} @@ -331,6 +382,14 @@ export default function WorkOrderInvoicesPdfPage() { > Preuzmi račune putnog naloga + {!loading && !error && ( @@ -385,6 +444,17 @@ export default function WorkOrderInvoicesPdfPage() { onClose={() => setEditingAdditionalCosts(false)} onSave={handleSaveAdditionalCostsTable} /> + { + setEditingServiceReportNote(false); + setServiceReportNoteError(''); + }} + onSave={handleSaveServiceReportNote} + />
); } diff --git a/frontend/src/components/dashboard/WorkOrderServiceNotesModal.jsx b/frontend/src/components/dashboard/WorkOrderServiceNotesModal.jsx new file mode 100644 index 0000000..420db2e --- /dev/null +++ b/frontend/src/components/dashboard/WorkOrderServiceNotesModal.jsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from 'preact/hooks'; +import ModalShell from '../ui/ModalShell'; + +export default function WorkOrderServiceNotesModal({ + open, + workOrder, + saving = false, + error = '', + onClose, + onSave, +}) { + const [notes, setNotes] = useState(''); + + useEffect(() => { + if (!open) { + setNotes(''); + return; + } + setNotes(String(workOrder?.notes || '')); + }, [open, workOrder?.id, workOrder?.notes]); + + if (!open) return null; + + const handleSave = async () => { + await onSave?.(notes); + }; + + return ( + +
+
+
+

UREDI TEKST NAPOMENE

+

+ Tekst se prikazuje u PDF-u "Izvještaj servisera" na prvoj stranici. +

+
+ +
+ +
+ {error ? ( +
+ {error} +
+ ) : null} + +