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 @@
+ Comma-separated list of workers available in the invoice "Worker" dropdown.
+ Example: Wallie, Elvin, Andreas
+
+
-
-
+
+
+
+
+
+
+
+
+
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">
+
+
+
+
+