From bffc5666fed59d036372a830bd0e64938d407447 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Dec 2025 14:39:54 +0100 Subject: [PATCH 01/13] Fix --- pages/banking/index.vue | 4 +++- pages/createDocument/edit/[[id]].vue | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pages/banking/index.vue b/pages/banking/index.vue index 6b7cd38..2f646e6 100644 --- a/pages/banking/index.vue +++ b/pages/banking/index.vue @@ -16,10 +16,13 @@ const route = useRoute() const bankstatements = ref([]) const bankaccounts = ref([]) +const filterAccount = ref([]) + const setupPage = async () => { bankstatements.value = (await useEntities("bankstatements").select("*, statementallocations(*)", "date", false)) bankaccounts.value = await useEntities("bankaccounts").select() + if(bankaccounts.value.length > 0) filterAccount.value = bankaccounts.value } const templateColumns = [ @@ -58,7 +61,6 @@ const clearSearchString = () => { searchString.value = '' } -const filterAccount = ref(bankaccounts.value || []) const displayCurrency = (value, currency = "€") => { return `${Number(value).toFixed(2).replace(".",",")} ${currency}` diff --git a/pages/createDocument/edit/[[id]].vue b/pages/createDocument/edit/[[id]].vue index 23aa8d1..3ba7e88 100644 --- a/pages/createDocument/edit/[[id]].vue +++ b/pages/createDocument/edit/[[id]].vue @@ -1281,6 +1281,7 @@ const saveSerialInvoice = async () => { payment_type: itemInfo.value.payment_type, deliveryDateType: "Leistungszeitraum", createdBy: itemInfo.value.createdBy, + created_by: itemInfo.value.created_by, title: itemInfo.value.title, description: itemInfo.value.description, startText: itemInfo.value.startText, From 3e5c6ca8dab6b2632258376c774416e0e288c6cf Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Dec 2025 14:49:51 +0100 Subject: [PATCH 02/13] Fix --- components/MainNav.vue | 3 +-- pages/accounts/index.vue | 16 ++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/components/MainNav.vue b/components/MainNav.vue index dc00730..0307b41 100644 --- a/components/MainNav.vue +++ b/components/MainNav.vue @@ -339,8 +339,7 @@ const links = computed(() => { },{ label: "Export", to: "/export", - icon: "i-heroicons-clipboard-document-list", - disabled: true + icon: "i-heroicons-clipboard-document-list" } ] } diff --git a/pages/accounts/index.vue b/pages/accounts/index.vue index 3472214..1593e1f 100644 --- a/pages/accounts/index.vue +++ b/pages/accounts/index.vue @@ -26,13 +26,13 @@ const setupPage = async () => { incominginvoices.value = (await useEntities("incominginvoices").select("*, vendor(*)")) items.value = await Promise.all(items.value.map(async (i) => { - let renderedAllocationsTemp = await renderedAllocations(i.id) - let saldo = getSaldo(renderedAllocationsTemp) + // let renderedAllocationsTemp = await renderedAllocations(i.id) + // let saldo = getSaldo(renderedAllocationsTemp) return { ...i, - saldo: saldo, - allocations: renderedAllocationsTemp.length, + // saldo: saldo, + // allocations: renderedAllocationsTemp.length, } })) @@ -103,13 +103,13 @@ const templateColumns = [ },{ key: "label", label: "Name" - },{ + },/*{ key: "allocations", label: "Buchungen" },{ key: "saldo", label: "Saldo" - }, { + },*/ { key: "description", label: "Beschreibung" }, @@ -166,7 +166,7 @@ setupPage() - + --> Date: Thu, 1 Jan 2026 15:30:06 +0100 Subject: [PATCH 03/13] Added Page Leave Guard --- components/PageLeaveGuard.vue | 98 ++++++++++++++++++++++++++ pages/createDocument/edit/[[id]].vue | 1 + pages/incomingInvoices/[mode]/[id].vue | 1 + 3 files changed, 100 insertions(+) create mode 100644 components/PageLeaveGuard.vue diff --git a/components/PageLeaveGuard.vue b/components/PageLeaveGuard.vue new file mode 100644 index 0000000..af9a563 --- /dev/null +++ b/components/PageLeaveGuard.vue @@ -0,0 +1,98 @@ + + + + + \ No newline at end of file diff --git a/pages/createDocument/edit/[[id]].vue b/pages/createDocument/edit/[[id]].vue index 3ba7e88..cb7f612 100644 --- a/pages/createDocument/edit/[[id]].vue +++ b/pages/createDocument/edit/[[id]].vue @@ -3136,6 +3136,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = { + diff --git a/pages/incomingInvoices/[mode]/[id].vue b/pages/incomingInvoices/[mode]/[id].vue index 319fc26..51488ba 100644 --- a/pages/incomingInvoices/[mode]/[id].vue +++ b/pages/incomingInvoices/[mode]/[id].vue @@ -575,6 +575,7 @@ const findIncomingInvoiceErrors = computed(() => { + From 8db4ad22032a9928a348565ce90aa43ae6b6286f Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 15:31:38 +0100 Subject: [PATCH 04/13] Changes --- pages/createDocument/edit/[[id]].vue | 3 +- pages/createDocument/index.vue | 192 +++++----- pages/createDocument/serialInvoice.vue | 477 ++++++++++++++++++++----- pages/incomingInvoices/[mode]/[id].vue | 9 +- stores/data.js | 2 +- 5 files changed, 508 insertions(+), 175 deletions(-) diff --git a/pages/createDocument/edit/[[id]].vue b/pages/createDocument/edit/[[id]].vue index cb7f612..7231861 100644 --- a/pages/createDocument/edit/[[id]].vue +++ b/pages/createDocument/edit/[[id]].vue @@ -1290,6 +1290,7 @@ const saveSerialInvoice = async () => { contactPerson: itemInfo.value.contactPerson, serialConfig: itemInfo.value.serialConfig, letterhead: itemInfo.value.letterhead, + taxType:itemInfo.value.taxType } let data = null @@ -1693,7 +1694,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = { - {{ filteredRows.filter(i => item.key === 'invoices' ? ['invoices', 'advanceInvoices', 'cancellationInvoices'].includes(i.type) : item.key === i.type).length }} + {{ getRowsForTab(item.key).length }} - \ No newline at end of file diff --git a/pages/createDocument/serialInvoice.vue b/pages/createDocument/serialInvoice.vue index 8900d36..cd0c893 100644 --- a/pages/createDocument/serialInvoice.vue +++ b/pages/createDocument/serialInvoice.vue @@ -14,6 +14,23 @@ + + + + + @@ -21,20 +38,9 @@ + + +
+

+ + Laufende Ausführungen +

+
+ +
+
+

Start: {{ dayjs(exec.createdAt).format('DD.MM.YYYY HH:mm') }}

+
+ Läuft +
+ +
+ {{exec.summary}} +
+ +
+ +
+
+
+
+ + + @@ -67,23 +119,6 @@ {{row.customer ? row.customer.name : ""}} - @@ -100,25 +135,231 @@ + + + + +
+ + + + + + +
+ + + + + +
+ +
+ {{ selectedExecutionRows.length }} Vorlage(n) ausgewählt. +
+
+ + +
+
+ + + + + +
+
+ +
+ +
+ Keine abgeschlossenen Ausführungen gefunden. +
+ +
+
+ {{ dayjs(exec.createdAt).format('DD.MM.YYYY HH:mm') }} + + {{ getStatusLabel(exec.status) }} + +
+
+
+ + {{exec.summary}} +
+
+
+
+
+
+ - - \ No newline at end of file + \ No newline at end of file diff --git a/pages/incomingInvoices/[mode]/[id].vue b/pages/incomingInvoices/[mode]/[id].vue index 51488ba..ed07dbf 100644 --- a/pages/incomingInvoices/[mode]/[id].vue +++ b/pages/incomingInvoices/[mode]/[id].vue @@ -43,6 +43,10 @@ const setup = async () => { accounts.value = await useEntities("accounts").selectSpecial() itemInfo.value = await useEntities("incominginvoices").selectSingle(route.params.id, "*, files(*)") + + //TODO: Dirty Fix + itemInfo.value.vendor = itemInfo.value.vendor?.id + await loadFile(itemInfo.value.files[itemInfo.value.files.length-1].id) if(itemInfo.value.date && !itemInfo.value.dueDate) itemInfo.value.dueDate = itemInfo.value.date @@ -412,7 +416,7 @@ const findIncomingInvoiceErrors = computed(() => { value-attribute="id" searchable :disabled="mode === 'show'" - :search-attributes="['label']" + :search-attributes="['label','number']" searchable-placeholder="Suche..." v-model="item.account" :color="(item.account && accounts.find(i => i.id === item.account)) ? 'primary' : 'rose'" @@ -420,6 +424,9 @@ const findIncomingInvoiceErrors = computed(() => { +
diff --git a/stores/data.js b/stores/data.js index dd805a7..a28952b 100644 --- a/stores/data.js +++ b/stores/data.js @@ -2608,7 +2608,7 @@ export const useDataStore = defineStore('data', () => { }, cancellationInvoices: { label: "Stornorechnungen", - labelSingle: "Stornorechnung" + labelSingle: "Storno" }, quotes: { label: "Angebote", From fb746b4d596c505f6a783a1d970ec37de2824853 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Thu, 1 Jan 2026 15:52:05 +0100 Subject: [PATCH 05/13] Statement Sync --- pages/banking/index.vue | 92 +++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/pages/banking/index.vue b/pages/banking/index.vue index 2f646e6..c4f1a40 100644 --- a/pages/banking/index.vue +++ b/pages/banking/index.vue @@ -1,11 +1,12 @@ + + \ No newline at end of file diff --git a/pages/workflows/[token].vue b/pages/workflows/[token].vue new file mode 100644 index 0000000..80b1697 --- /dev/null +++ b/pages/workflows/[token].vue @@ -0,0 +1,121 @@ + + + \ No newline at end of file From 786ac06e091011d52525b5b3b26402837d2afc55 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:57:33 +0100 Subject: [PATCH 11/13] Fix SyncedAt Display --- components/displayBankaccounts.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/displayBankaccounts.vue b/components/displayBankaccounts.vue index cb4ac08..07978a7 100644 --- a/components/displayBankaccounts.vue +++ b/components/displayBankaccounts.vue @@ -37,7 +37,7 @@ const calculateOpenSum = (statement) => { {{ account.name }}: - {{dayjs(account.synced_at).format("DD.MM.YY HH:mm")}} + {{dayjs(account.syncedAt).format("DD.MM.YY HH:mm")}} {{useCurrency(account.balance)}} From c1ed8cd028455d8530add8e44a66f290bf03ee11 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:58:19 +0100 Subject: [PATCH 12/13] Try for SEPA --- pages/export/create/sepa.vue | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 pages/export/create/sepa.vue diff --git a/pages/export/create/sepa.vue b/pages/export/create/sepa.vue new file mode 100644 index 0000000..9e17a7a --- /dev/null +++ b/pages/export/create/sepa.vue @@ -0,0 +1,47 @@ + + + + + \ No newline at end of file From d3fc2e6ad32423cb583ed0ecdf8a916d33515e30 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 6 Jan 2026 11:58:29 +0100 Subject: [PATCH 13/13] LabelPrinter Implementation --- components/LabelPrintModal.vue | 130 ++++++++++++++++++++ components/LabelPrinterButton.vue | 53 +++++++++ components/nimbot.vue | 60 ++++++++++ layouts/default.vue | 9 +- pages/index.vue | 20 ++++ stores/labelPrinter.ts | 190 ++++++++++++++++++++++++++++++ 6 files changed, 460 insertions(+), 2 deletions(-) create mode 100644 components/LabelPrintModal.vue create mode 100644 components/LabelPrinterButton.vue create mode 100644 components/nimbot.vue create mode 100644 stores/labelPrinter.ts diff --git a/components/LabelPrintModal.vue b/components/LabelPrintModal.vue new file mode 100644 index 0000000..e660a6d --- /dev/null +++ b/components/LabelPrintModal.vue @@ -0,0 +1,130 @@ + + + diff --git a/components/LabelPrinterButton.vue b/components/LabelPrinterButton.vue new file mode 100644 index 0000000..2a29fee --- /dev/null +++ b/components/LabelPrinterButton.vue @@ -0,0 +1,53 @@ + + + + + \ No newline at end of file diff --git a/components/nimbot.vue b/components/nimbot.vue new file mode 100644 index 0000000..7ceb36a --- /dev/null +++ b/components/nimbot.vue @@ -0,0 +1,60 @@ + + + diff --git a/layouts/default.vue b/layouts/default.vue index e994ab7..4993db6 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -6,6 +6,7 @@ import dayjs from "dayjs"; import {useCapacitor} from "../composables/useCapacitor.js"; import GlobalMessages from "~/components/GlobalMessages.vue"; import TenantDropdown from "~/components/TenantDropdown.vue"; +import LabelPrinterButton from "~/components/LabelPrinterButton.vue"; const dataStore = useDataStore() const colorMode = useColorMode() @@ -14,6 +15,7 @@ const router = useRouter() const route = useRoute() const auth = useAuthStore() +const labelPrinter = useLabelPrinterStore() const month = dayjs().format("MM") @@ -240,13 +242,16 @@ const footerLinks = [ diff --git a/pages/index.vue b/pages/index.vue index 03bb084..41cb992 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -56,16 +56,36 @@ > + + + Label Drucken + + diff --git a/stores/labelPrinter.ts b/stores/labelPrinter.ts new file mode 100644 index 0000000..60a96a9 --- /dev/null +++ b/stores/labelPrinter.ts @@ -0,0 +1,190 @@ +import { defineStore } from "pinia" +import { + Utils, + RequestCommandId, + ResponseCommandId, + NiimbotBluetoothClient, + NiimbotSerialClient +} from "@mmote/niimbluelib" +import { useToast } from "#imports" + +export const useLabelPrinterStore = defineStore("labelPrinter", { + state: () => ({ + client: null as NiimbotBluetoothClient | NiimbotSerialClient | null, + connected: false, + connectLoading: false, + transportLastUsed: "", + printProgress: 0, + info: {} as any + }), + + actions: { + + /** Logging Helper */ + logger(...args: any[]) { + console.debug("[Printer]", ...args) + }, + + /** --- Client erzeugen --- */ + newClient(transport: "ble" | "serial" = "serial") { + const toast = useToast() + + // alten Client trennen + if (this.client) { + try { this.client.disconnect() } catch {} + } + + // neuen Client erzeugen + this.client = + transport === "ble" + ? new NiimbotBluetoothClient() + : new NiimbotSerialClient() + + /** Events registrieren */ + + this.client.on("printerinfofetched", (e) => { + console.log("printerInfoFetched") + console.log(e.info) + this.info = e.info + }) + + this.client.on("connect", () => { + this.connected = true + toast.add({ title: "Drucker verbunden" }) + this.logger("connected") + }) + + this.client.on("disconnect", () => { + this.connected = false + toast.add({ title: "Drucker getrennt" }) + this.logger("disconnected") + }) + + this.client.on("printprogress", (e) => { + if (e.pagePrintProgress) this.printProgress = e.pagePrintProgress + this.logger( + `Page ${e.page}/${e.pagesTotal}, Page print ${e.pagePrintProgress}%, Page feed ${e.pageFeedProgress}%` + ) + }) + + return this.client + }, + + /** --- Verbinden --- */ + async connect(transport: "ble" | "serial" = "serial") { + const toast = useToast() + + this.connectLoading = true + + this.newClient(transport) + + try { + await this.client!.connect() + this.transportLastUsed = transport + this.connectLoading = false + } catch (err) { + console.error("[Printer] Connect failed:", err) + toast.add({ title: "Verbindung fehlgeschlagen", color: "red" }) + this.connectLoading = false + } + }, + + /** --- Trennen --- */ + async disconnect({ forget = false } = {}) { + const toast = useToast() + this.logger("Disconnect requested…") + + if (!this.client) return + + try { + // Timer stoppen + try { + if (this.client.heartbeatTimer) { + clearInterval(this.client.heartbeatTimer) + this.client.heartbeatTimer = null + } + if (this.client.abstraction?.statusPollTimer) { + clearInterval(this.client.abstraction.statusPollTimer) + this.client.abstraction.statusPollTimer = null + } + } catch {} + + await this.client.disconnect?.() + + // Serial-Port schließen + const port = (this.client as any).port + if (port) { + try { + if (port.readable) port.readable.cancel?.() + if (port.writable) await port.writable.abort?.() + await port.close?.() + + if (forget && navigator.serial?.forgetPort) { + await navigator.serial.forgetPort(port) + } + } catch (err) { + this.logger("Error closing port:", err) + } + } + + // BLE GATT + if ( + this.client instanceof NiimbotBluetoothClient && + this.client.device?.gatt?.connected + ) { + try { + this.client.device.gatt.disconnect() + } catch {} + } + + this.connected = false + this.client = null + toast.add({ title: "Drucker getrennt" }) + } catch (err) { + console.error("[Printer] Disconnect error", err) + toast.add({ title: "Fehler beim Trennen", color: "red" }) + } + }, + + /** Hilfsfunktion: EncodedImage reparieren */ + reviveEncodedImage(encoded: any) { + if (!encoded?.rowsData) return encoded + for (const row of encoded.rowsData) { + if (row.rowData && !(row.rowData instanceof Uint8Array)) { + row.rowData = new Uint8Array(Object.values(row.rowData)) + } + } + return encoded + }, + + /** --- Drucken --- */ + async print(encoded: any, options?: { density?: number; pages?: number }) { + const toast = useToast() + + if (!this.client) throw new Error("Kein Drucker verbunden") + + const fixed = this.reviveEncodedImage(encoded) + const taskName = this.client.getPrintTaskType() ?? "B1" + + const task = this.client.abstraction.newPrintTask(taskName, { + totalPages: options?.pages ?? 1, + statusPollIntervalMs: 100, + statusTimeoutMs: 8000, + density: options?.density ?? 5 + }) + + try { + this.printProgress = 0 + await task.printInit() + await task.printPage(fixed, options?.pages ?? 1) + await task.waitForFinished() + toast.add({ title: "Druck abgeschlossen" }) + } catch (e) { + console.error("[Printer] print error", e) + toast.add({ title: "Druckfehler", color: "red" }) + } finally { + await task.printEnd() + } + } + } +})