From f062bd816846f7c28bf22741b38f1e87cbf25198 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Tue, 26 May 2026 13:55:27 -0500 Subject: [PATCH] Technician --- public/index.html | 36 +++++++++++++++++++---- public/js/modals/invoice-modal.js | 46 +++++++++++++++++++++++++++-- public/js/utils/api.js | 8 +++++- public/js/views/invoice-view.js | 48 +++++++++++++++++++++++++++++++ public/js/views/settings-view.js | 39 ++++++++++++++++++++++++- src/index.js | 2 +- src/routes/invoices.js | 23 +++++++-------- src/routes/settings.js | 37 ++++++++++++++++++++++++ 8 files changed, 216 insertions(+), 23 deletions(-) diff --git a/public/index.html b/public/index.html index df17ea9..457257b 100644 --- a/public/index.html +++ b/public/index.html @@ -167,6 +167,23 @@
+
+ +

Invoice Workers

+

+ Comma-separated list of workers available in the invoice "Worker" dropdown. + Example: Wallie, Elvin, Andreas +

+
+ +
+ +

@@ -448,11 +465,20 @@ -
- - +
+
+ + +
+
+ + +
diff --git a/public/js/modals/invoice-modal.js b/public/js/modals/invoice-modal.js index 0e00996..6400665 100644 --- a/public/js/modals/invoice-modal.js +++ b/public/js/modals/invoice-modal.js @@ -8,9 +8,35 @@ */ import { addItem, getItems, resetItemCounter } from '../utils/item-editor.js'; import { setDefaultDate, showSpinner, hideSpinner } from '../utils/helpers.js'; +import '../utils/api.js'; let currentInvoiceId = null; let qboLaborRate = null; +let workerList = []; + +export async function loadWorkers() { + try { + const result = await window.API.settings.get('invoice_workers'); + if (result && result.value) { + workerList = result.value.split(',').map(w => w.trim()).filter(Boolean); + } else { + workerList = []; + } + populateWorkerDropdown(); + console.log(`👷 ${workerList.length} Bearbeiter geladen`); + } catch (e) { + console.log('Worker-Liste konnte nicht geladen werden.'); + } +} + +function populateWorkerDropdown() { + const sel = document.getElementById('invoice-worker'); + if (!sel) return; + const current = sel.value; + sel.innerHTML = `` + + workerList.map(w => ``).join(''); + if (current) sel.value = current; +} export async function loadLaborRate() { try { @@ -179,6 +205,11 @@ async function loadInvoiceForEdit(invoiceId) { document.getElementById('invoice-authorization').value = data.invoice.auth_code || ''; document.getElementById('invoice-tax-exempt').checked = data.invoice.tax_exempt; document.getElementById('invoice-bill-to-name').value = data.invoice.bill_to_name || ''; + + // Worker + populateWorkerDropdown(); + const workerEl = document.getElementById('invoice-worker'); + if (workerEl) workerEl.value = data.invoice.worker || ''; const sendDateEl = document.getElementById('invoice-send-date'); if (sendDateEl) { @@ -226,7 +257,12 @@ function prepareNewInvoice() { document.getElementById('invoice-terms').value = 'Net 14'; document.getElementById('invoice-number').value = ''; document.getElementById('invoice-send-date').value = ''; - + + // Worker zurücksetzen + populateWorkerDropdown(); + const workerEl = document.getElementById('invoice-worker'); + if (workerEl) workerEl.value = ''; + // Reset recurring const recurringCb = document.getElementById('invoice-recurring'); const recurringGroup = document.getElementById('invoice-recurring-group'); @@ -278,6 +314,7 @@ export async function handleInvoiceSubmit(e) { tax_exempt: document.getElementById('invoice-tax-exempt').checked, scheduled_send_date: document.getElementById('invoice-send-date')?.value || null, bill_to_name: document.getElementById('invoice-bill-to-name')?.value || null, + worker: document.getElementById('invoice-worker')?.value || null, is_recurring: isRecurring, recurring_interval: recurringInterval, items: getItems('invoice-items') @@ -314,7 +351,9 @@ export async function handleInvoiceSubmit(e) { export function initInvoiceModal() { const form = document.getElementById('invoice-form'); if (form) form.addEventListener('submit', handleInvoiceSubmit); - + + loadWorkers(); // Bearbeiterliste laden + const taxExempt = document.getElementById('invoice-tax-exempt'); if (taxExempt) taxExempt.addEventListener('change', updateInvoiceTotals); @@ -342,4 +381,5 @@ export function initInvoiceModal() { window.openInvoiceModal = openInvoiceModal; window.closeInvoiceModal = closeInvoiceModal; -window.addInvoiceItem = addInvoiceItem; \ No newline at end of file +window.addInvoiceItem = addInvoiceItem; +window.reloadInvoiceWorkers = loadWorkers; \ No newline at end of file diff --git a/public/js/utils/api.js b/public/js/utils/api.js index 311c479..a283702 100644 --- a/public/js/utils/api.js +++ b/public/js/utils/api.js @@ -212,7 +212,13 @@ const API = { method: 'POST', body: formData }).then(r => r.json()); - } + }, + get: (key) => fetch(`/api/settings/${encodeURIComponent(key)}`).then(r => r.json()), + set: (key, value) => fetch(`/api/settings/${encodeURIComponent(key)}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ value }) + }).then(r => r.json()) } }; diff --git a/public/js/views/invoice-view.js b/public/js/views/invoice-view.js index aa6e7f0..d6fe2e8 100644 --- a/public/js/views/invoice-view.js +++ b/public/js/views/invoice-view.js @@ -5,6 +5,7 @@ let invoices = []; let filterCustomer = localStorage.getItem('inv_filterCustomer') || ''; let filterStatus = localStorage.getItem('inv_filterStatus') || 'unpaid'; let groupBy = localStorage.getItem('inv_groupBy') || 'none'; +let filterWorker = localStorage.getItem('inv_filterWorker') || ''; const OVERDUE_DAYS = 30; @@ -194,6 +195,7 @@ function saveSettings() { localStorage.setItem('inv_filterStatus', filterStatus); localStorage.setItem('inv_groupBy', groupBy); localStorage.setItem('inv_filterCustomer', filterCustomer); + localStorage.setItem('inv_filterWorker', filterWorker); } // ============================================================ @@ -237,6 +239,13 @@ function getFilteredInvoices() { const s = filterCustomer.toLowerCase(); f = f.filter(i => (i.customer_name || '').toLowerCase().includes(s)); } + if (filterWorker) { + if (filterWorker === '__none__') { + f = f.filter(i => !i.worker); + } else { + f = f.filter(i => i.worker === filterWorker); + } + } f.sort((a, b) => (parseLocalDate(b.invoice_date) || 0) - (parseLocalDate(a.invoice_date) || 0)); return f; } @@ -563,6 +572,13 @@ export function injectToolbar() { class="px-3 py-1.5 border border-gray-300 rounded-md text-sm w-48 focus:ring-blue-500 focus:border-blue-500">
+
+ + +
+