31 Commits

Author SHA1 Message Date
d4c4a2b9c1 Merge pull request 'dev' (#255) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m4s
Build and Release FEDEO / build-and-release (push) Successful in 6m36s
Reviewed-on: #255
2026-10-29 17:20:58 +00:00
1b617c7910 Merge pull request 'dev' (#252) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m1s
Build and Release FEDEO / build-and-release (push) Successful in 6m34s
Reviewed-on: #252
2026-10-27 20:39:01 +00:00
9676a270da Merge pull request 'dev' (#251) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m7s
Build and Release FEDEO / build-and-release (push) Successful in 6m36s
Reviewed-on: #251
2026-10-25 13:17:14 +00:00
f3b4fac4c0 Merge pull request 'dev' (#247) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m2s
Build and Release FEDEO / build-and-release (push) Successful in 6m25s
Reviewed-on: #247
2026-10-15 16:47:32 +00:00
a8c6c2625c Merge pull request 'dev' (#242) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m6s
Build and Release FEDEO / build-and-release (push) Successful in 6m31s
Reviewed-on: #242
2026-10-13 16:18:53 +00:00
a2a1151ccb Merge pull request 'dev' (#241) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m5s
Build and Release FEDEO / build-and-release (push) Successful in 6m29s
Reviewed-on: #241
2026-10-07 17:11:45 +00:00
daaed01d02 Merge pull request 'dev' (#240) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 59s
Build and Release FEDEO / build-and-release (push) Successful in 6m16s
Reviewed-on: #240
2026-10-02 16:30:02 +00:00
e7d6c482ad Merge pull request 'dev' (#239) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 58s
Build and Release FEDEO / build-and-release (push) Successful in 6m13s
Reviewed-on: #239
2026-10-02 15:17:16 +00:00
ed305f656c Merge pull request 'dev' (#238) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 57s
Build and Release FEDEO / build-and-release (push) Successful in 6m14s
Reviewed-on: #238
2026-10-02 14:18:01 +00:00
30e932a874 Merge pull request 'dev' (#237) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 58s
Build and Release FEDEO / build-and-release (push) Successful in 6m16s
Reviewed-on: #237
2026-10-02 12:51:46 +00:00
0a74e07288 Merge pull request 'fix(scanner): accept Epson empty write probes' (#234) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 58s
Build and Release FEDEO / build-and-release (push) Successful in 6m8s
Reviewed-on: #234
2026-10-01 15:36:56 +00:00
4c3de8c942 Merge pull request 'dev' (#233) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 58s
Build and Release FEDEO / build-and-release (push) Successful in 6m11s
Reviewed-on: #233
2026-09-30 17:23:15 +00:00
49d44d9701 Merge pull request 'dev' (#232) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 1m1s
Build and Release FEDEO / build-and-release (push) Successful in 6m18s
Reviewed-on: #232
2026-09-30 15:43:45 +00:00
96a7acff33 Merge pull request 'feat(selfhost): show sanitized update logs in admin UI' (#231) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 47s
Build and Release FEDEO / build-and-release (push) Successful in 6m7s
Reviewed-on: #231
2026-09-25 17:22:30 +00:00
65588686c7 Merge pull request 'fix(documents): normalize units when selecting and loading rows' (#230) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 48s
Build and Release FEDEO / build-and-release (push) Successful in 6m3s
Reviewed-on: #230
2026-09-25 09:54:08 +00:00
fcf9cc3fc9 Merge pull request 'dev' (#229) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 49s
Build and Release FEDEO / build-and-release (push) Successful in 6m6s
Reviewed-on: #229
2026-09-25 09:24:39 +00:00
20091143d2 Merge pull request 'dev' (#228) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 45s
Build and Release FEDEO / build-and-release (push) Successful in 6m5s
Reviewed-on: #228
2026-09-23 17:20:58 +00:00
f708d0a2b2 Merge pull request 'dev' (#227) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 44s
Build and Release FEDEO / build-and-release (push) Successful in 6m7s
Reviewed-on: #227
2026-09-23 16:15:44 +00:00
7e9f4409f6 Merge pull request 'dev' (#226) from dev into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 33s
Build and Release FEDEO / build-and-release (push) Successful in 6m10s
Reviewed-on: #226
2026-09-23 14:57:40 +00:00
2e63af1aa2 Merge pull request 'fix(release): stream large migration checksums from git' (#225) from flfeders/codex/release-migration-checksums into main
All checks were successful
Build and Release FEDEO / verify (push) Successful in 25s
Build and Release FEDEO / build-and-release (push) Successful in 5m59s
Reviewed-on: #225
2026-09-23 14:09:57 +00:00
5ebd2d508c Merge pull request 'fix(release): exclude legacy main history from commit checks' (#224) from dev into main
Some checks failed
Build and Release FEDEO / verify (push) Successful in 23s
Build and Release FEDEO / build-and-release (push) Failing after 5m57s
Reviewed-on: #224
2026-09-23 13:03:36 +00:00
9128fbb165 Merge pull request 'fix(ci): build dev images without repository write access' (#221) from dev into main
Some checks failed
Build and Release FEDEO / verify (push) Failing after 26s
Build and Release FEDEO / build-and-release (push) Has been skipped
Reviewed-on: #221
2026-09-20 16:25:38 +00:00
8473221c13 Merge pull request '3.0.0' (#220) from dev into main
Some checks failed
Build and Release FEDEO / verify (push) Failing after 22s
Build and Release FEDEO / build-and-release (push) Has been skipped
Reviewed-on: #220
2026-09-20 16:10:46 +00:00
e7554fa2cc .gitea/ISSUE_TEMPLATE/feature_request.md aktualisiert
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 16s
Build and Push Docker Images / build-frontend (push) Successful in 16s
2026-01-22 17:42:28 +00:00
7c1fabf58a .gitea/ISSUE_TEMPLATE/bug_report.md aktualisiert
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 17s
Build and Push Docker Images / build-frontend (push) Successful in 16s
2026-01-22 17:40:58 +00:00
1203b6cbd1 .gitea/ISSUE_TEMPLATE/bug_report.md aktualisiert
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 16s
Build and Push Docker Images / build-frontend (push) Successful in 16s
2026-01-15 11:39:03 +00:00
525f2906fb .gitea/ISSUE_TEMPLATE/feature_request.md aktualisiert
Some checks failed
Build and Push Docker Images / build-frontend (push) Has been cancelled
Build and Push Docker Images / build-backend (push) Has been cancelled
2026-01-15 11:38:50 +00:00
b105382abf .gitea/ISSUE_TEMPLATE/bug_report.md aktualisiert
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 16s
Build and Push Docker Images / build-frontend (push) Successful in 15s
2026-01-15 11:38:00 +00:00
b1cdec7d17 Merge pull request 'Added feature request template' (#62) from dev into main
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 17s
Build and Push Docker Images / build-frontend (push) Successful in 16s
Reviewed-on: #62
2026-01-15 11:31:57 +00:00
f1d512b2e5 Merge pull request 'dev' (#61) from dev into main
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 17s
Build and Push Docker Images / build-frontend (push) Successful in 16s
Reviewed-on: #61
2026-01-15 11:29:15 +00:00
db21b43120 Merge pull request 'dev' (#40) from dev into main
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 18s
Build and Push Docker Images / build-frontend (push) Successful in 16s
Reviewed-on: #40
2026-01-08 22:21:06 +00:00
5 changed files with 7 additions and 223 deletions

View File

@@ -2,37 +2,18 @@
name: 🐛 Bug Report name: 🐛 Bug Report
about: Erstelle einen Bericht, um uns zu helfen, das Projekt zu verbessern. about: Erstelle einen Bericht, um uns zu helfen, das Projekt zu verbessern.
title: '[BUG] ' title: '[BUG] '
labels: bug labels: Problem
assignees: '' assignees: ''
--- ---
**Beschreibung** **Beschreibung**
Eine klare und prägnante Beschreibung des Fehlers.
**Reproduktion** **Reproduktion**
Schritte, um den Fehler zu reproduzieren:
Entweder:
1. Gehe zu '...'
2. Klicke auf '...'
3. Scrolle runter zu '...'
4. Siehe Fehler
Oder Link zur Seite
**Erwartetes Verhalten**
Eine klare Beschreibung dessen, was du erwartet hast.
**Screenshots** **Screenshots**
Falls zutreffend, füge hier Screenshots oder Gifs hinzu, um das Problem zu verdeutlichen.
**Achtung: Achte bitte auf Datenschutz deiner Daten sowie der Daten deiner Kunden. Sollten ein Screenshot nur mit Daten möglich sein, schwärze diese bitte vor dem Upload.** **Achtung: Achte bitte auf Datenschutz deiner Daten sowie der Daten deiner Kunden. Sollten ein Screenshot nur mit Daten möglich sein, schwärze diese bitte vor dem Upload.**
**Umgebung:**
- Betriebssystem: [z.B. Windows, macOS, Linux]
- Browser / Version (falls relevant): [z.B. Chrome 120]
- Projekt-Version: [z.B. v1.0.2]
**Zusätzlicher Kontext**
Füge hier alle anderen Informationen zum Problem hinzu.

View File

@@ -2,19 +2,16 @@
name: ✨ Feature Request name: ✨ Feature Request
about: Schlage eine Idee für dieses Projekt vor. about: Schlage eine Idee für dieses Projekt vor.
title: '[FEATURE] ' title: '[FEATURE] '
labels: enhancement labels: Funktionswunsch
assignees: '' assignees: ''
--- ---
**Ist dein Feature-Wunsch mit einem Problem verbunden?** **Ist dein Feature-Wunsch mit einem Problem verbunden?**
Eine klare Beschreibung des Problems (z.B. "Ich bin immer genervt, wenn...").
**Lösungsvorschlag** **Lösungsvorschlag**
Eine klare Beschreibung dessen, was du dir wünschst und wie es funktionieren soll.
**Alternativen** **Alternativen**
Hast du über alternative Lösungen oder Workarounds nachgedacht?
**Zusätzlicher Kontext**
Hier ist Platz für weitere Informationen, Skizzen oder Beispiele von anderen Tools.

View File

@@ -5,7 +5,6 @@ import { v4 as uuidv4 } from 'uuid';
import {useFunctions} from "~/composables/useFunctions.js"; import {useFunctions} from "~/composables/useFunctions.js";
import EntityModalButtons from "~/components/EntityModalButtons.vue"; import EntityModalButtons from "~/components/EntityModalButtons.vue";
import { documentTemplateHandlebars } from "~/utils/handlebars"; import { documentTemplateHandlebars } from "~/utils/handlebars";
import { getTitleSectionMove, moveTitleSection } from "~/utils/documentTitleSections.js";
const dataStore = useDataStore() const dataStore = useDataStore()
const profileStore = useProfileStore() const profileStore = useProfileStore()
@@ -1018,24 +1017,6 @@ const toggleTitleSection = (rowId) => {
collapsedTitleRowIds.value = nextCollapsedTitleRowIds collapsedTitleRowIds.value = nextCollapsedTitleRowIds
} }
const moveDocumentTitleSection = (rowId, direction) => {
if (itemInfo.value.type === 'cancellationInvoices') return
const rows = moveTitleSection(itemInfo.value.rows, rowId, direction)
if (rows === itemInfo.value.rows) return
itemInfo.value.rows = rows
setPosNumbers()
}
const getTitleSectionMoveMenuItems = (rowId) => [
{ label: 'Titelbereich nach oben', icon: 'i-heroicons-arrow-up', direction: 'up' },
{ label: 'Titelbereich nach unten', icon: 'i-heroicons-arrow-down', direction: 'down' }
].map(({ direction, ...item }) => ({
...item,
disabled: itemInfo.value.type === 'cancellationInvoices'
|| !getTitleSectionMove(itemInfo.value.rows, rowId, direction),
onSelect: () => moveDocumentTitleSection(rowId, direction)
}))
const hiddenDocumentRowIds = computed(() => { const hiddenDocumentRowIds = computed(() => {
const hiddenRowIds = new Set() const hiddenRowIds = new Set()
const collapsedTitleLevels = [] const collapsedTitleLevels = []
@@ -3135,7 +3116,7 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
v-model="itemInfo.rows" v-model="itemInfo.rows"
handle=".handle" handle=".handle"
tag="tbody" tag="tbody"
itemKey="id" itemKey="pos"
@end="setPosNumbers" @end="setPosNumbers"
> >
<template #item="{element: row}"> <template #item="{element: row}">
@@ -3160,21 +3141,6 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = {
@click="toggleTitleSection(row.id)" @click="toggleTitleSection(row.id)"
/> />
</UTooltip> </UTooltip>
<UDropdownMenu
v-if="row.mode === 'title' && itemInfo.type !== 'cancellationInvoices'"
:items="getTitleSectionMoveMenuItems(row.id)"
:content="{ align: 'start' }"
>
<UTooltip text="Titel mit untergeordneten Zeilen verschieben">
<UButton
class="shrink-0"
variant="ghost"
color="neutral"
icon="i-heroicons-arrows-up-down"
aria-label="Titel mit untergeordneten Zeilen verschieben"
/>
</UTooltip>
</UDropdownMenu>
</div> </div>
</td> </td>
<td <td

View File

@@ -1,119 +0,0 @@
const { test } = require('node:test')
const assert = require('node:assert/strict')
const fs = require('node:fs')
const vm = require('node:vm')
const ts = require('typescript')
const { parse, compileScript } = require('@vue/compiler-sfc')
const { ref, computed } = require('vue')
const helpers = import('../utils/documentTitleSections.js')
const title = (id, titleLevel = 1) => ({ id, mode: 'title', titleLevel, text: id })
const position = (id, mode = 'free') => ({ id, mode, quantity: 2, price: 10, linkedEntitys: [42] })
const ids = rows => Array.from(rows, row => row.id)
test('moving a top-level title carries every nested title and row, preserving data and order', async () => {
const { moveTitleSection } = await helpers
const rows = [position('intro', 'text'), title('a'), position('article', 'normal'),
title('a.1', 2), position('service', 'service'), title('a.1.1', 3),
position('note', 'text'), position('break', 'pagebreak'), title('b'), position('last')]
const before = JSON.stringify(rows)
const moved = moveTitleSection(rows, 'a', 'down')
assert.deepEqual(ids(moved), ['intro', 'b', 'last', 'a', 'article', 'a.1', 'service', 'a.1.1', 'note', 'break'])
assert.equal(JSON.stringify(rows), before)
for (const row of rows) assert.equal(moved.find(item => item.id === row.id), row)
assert.deepEqual(moveTitleSection(moved, 'a', 'up'), rows)
})
test('subtitles exchange complete sibling sections without leaving their parent', async () => {
const { moveTitleSection, getTitleSectionMove } = await helpers
const rows = [title('a'), position('parent-intro'), title('a.1', 2), title('a.1.1', 3),
position('child'), title('a.2', 2), position('sibling'), title('b'), title('b.1', 2)]
assert.deepEqual(ids(moveTitleSection(rows, 'a.2', 'up')),
['a', 'parent-intro', 'a.2', 'sibling', 'a.1', 'a.1.1', 'child', 'b', 'b.1'])
assert.deepEqual(ids(moveTitleSection(rows, 'a.1', 'down')),
['a', 'parent-intro', 'a.2', 'sibling', 'a.1', 'a.1.1', 'child', 'b', 'b.1'])
for (const [id, direction] of [['a.1', 'up'], ['a.2', 'down'], ['b.1', 'up']]) {
assert.equal(getTitleSectionMove(rows, id, direction), null)
assert.equal(moveTitleSection(rows, id, direction), rows)
}
})
test('level-three titles move within their level-two parent', async () => {
const { moveTitleSection } = await helpers
const rows = [title('a'), title('a.1', 2), title('x', 3), position('x-row'),
title('y', 3), position('y-row'), title('a.2', 2)]
const moved = moveTitleSection(rows, 'x', 'down')
assert.deepEqual(ids(moved), ['a', 'a.1', 'y', 'y-row', 'x', 'x-row', 'a.2'])
assert.equal(moveTitleSection(moved, 'x', 'down'), moved)
})
test('empty titles, the final section and legacy levels are supported', async () => {
const { moveTitleSection } = await helpers
const rows = [{ id: 'legacy', mode: 'title' }, title('empty', '1'), title('last', 1), position('end')]
assert.deepEqual(ids(moveTitleSection(rows, 'legacy', 'down')), ['empty', 'legacy', 'last', 'end'])
assert.deepEqual(ids(moveTitleSection(rows, 'last', 'up')), ['legacy', 'last', 'end', 'empty'])
assert.equal(moveTitleSection(rows, 'last', 'down'), rows)
})
test('missing intermediate levels still retain their enclosing section', async () => {
const { moveTitleSection } = await helpers
const rows = [title('a'), title('x', 3), position('x-row'), title('y', 3), title('b')]
assert.deepEqual(ids(moveTitleSection(rows, 'x', 'down')), ['a', 'y', 'x', 'x-row', 'b'])
assert.equal(moveTitleSection(rows, 'x', 'up'), rows)
assert.equal(moveTitleSection(rows, 'y', 'down'), rows)
})
test('invalid requests and document boundaries leave rows untouched', async () => {
const { moveTitleSection } = await helpers
const rows = [position('intro'), title('only'), position('last')]
for (const [id, direction] of [['only', 'up'], ['only', 'down'], ['intro', 'down'],
['missing', 'up'], ['only', 'sideways']]) {
assert.equal(moveTitleSection(rows, id, direction), rows)
}
const empty = []
assert.equal(moveTitleSection(empty, 'missing', 'up'), empty)
})
async function editorFixture(rows, type = 'invoices') {
const { descriptor, errors } = parse(fs.readFileSync(`${__dirname}/../pages/createDocument/edit/[[id]].vue`, 'utf8'))
assert.deepEqual(errors, [])
compileScript(descriptor, { id: 'document-editor', inlineTemplate: true })
const script = ts.createSourceFile('editor.js', descriptor.scriptSetup.content, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
const names = new Set(['moveDocumentTitleSection', 'getTitleSectionMoveMenuItems', 'setPosNumbers',
'collapsedTitleRowIds', 'toggleTitleSection', 'hiddenDocumentRowIds', 'isDocumentRowHidden'])
const source = script.statements.filter(statement => ts.isVariableStatement(statement)
&& statement.declarationList.declarations.some(declaration => names.has(declaration.name.getText(script))))
.map(statement => statement.getText(script)).join('\n')
const context = vm.createContext({ ...await helpers, ref, computed, itemInfo: ref({ type, rows }) })
vm.runInContext(source, context)
return { context, run: code => vm.runInContext(code, context) }
}
test('the editor menu moves collapsed sections and updates numbering and reactive visibility', async () => {
const { run, context } = await editorFixture([title('a'), title('a.1', 2), position('a-row'),
title('b'), position('b-row')])
run("setPosNumbers(); toggleTitleSection('a'); toggleTitleSection('a.1')")
assert.equal(run("isDocumentRowHidden('a-row')"), true)
assert.equal(run("getTitleSectionMoveMenuItems('a')[0].disabled"), true)
assert.equal(run("getTitleSectionMoveMenuItems('a')[1].disabled"), false)
run("getTitleSectionMoveMenuItems('a')[1].onSelect()")
assert.deepEqual(ids(context.itemInfo.value.rows), ['b', 'b-row', 'a', 'a.1', 'a-row'])
assert.deepEqual(Array.from(context.itemInfo.value.rows, row => row.pos), ['1', '1.1', '2', '2.1', '2.1.1'])
assert.equal(run("isDocumentRowHidden('b-row')"), false)
assert.equal(run("isDocumentRowHidden('a-row')"), true)
run("toggleTitleSection('a')")
assert.equal(run("isDocumentRowHidden('a.1')"), false)
assert.equal(run("isDocumentRowHidden('a-row')"), true)
run("toggleTitleSection('a.1')")
assert.equal(run("isDocumentRowHidden('a-row')"), false)
assert.equal(run("getTitleSectionMoveMenuItems('a')[1].disabled"), true)
assert.equal(run("getTitleSectionMoveMenuItems('a')[0].disabled"), false)
})
test('cancellation documents reject section moves even when called directly', async () => {
const { run, context } = await editorFixture([title('a'), position('a-row'), title('b')], 'cancellationInvoices')
const before = JSON.stringify(context.itemInfo.value.rows)
assert.equal(run("getTitleSectionMoveMenuItems('a').every(item => item.disabled)"), true)
run("moveDocumentTitleSection('a', 'down')")
assert.equal(JSON.stringify(context.itemInfo.value.rows), before)
})

View File

@@ -1,41 +0,0 @@
const titleLevel = row => Math.min(Math.max(Number(row.titleLevel) || 1, 1), 3)
// A section includes every row up to the next title at the same or a higher level.
const sectionEnd = (rows, start) => {
const level = titleLevel(rows[start])
let end = start + 1
while (end < rows.length && (rows[end].mode !== 'title' || titleLevel(rows[end]) > level)) end += 1
return end
}
export const getTitleSectionMove = (rows, rowId, direction) => {
if (!['up', 'down'].includes(direction)) return null
const start = rows.findIndex(row => row.id === rowId)
if (start < 0 || rows[start].mode !== 'title') return null
const level = titleLevel(rows[start])
const end = sectionEnd(rows, start)
if (direction === 'down') {
// A title at a higher level ends the parent section: never cross it.
if (end === rows.length || titleLevel(rows[end]) !== level) return null
return { start, end, target: sectionEnd(rows, end) }
}
for (let previous = start - 1; previous >= 0; previous -= 1) {
if (rows[previous].mode !== 'title' || titleLevel(rows[previous]) > level) continue
if (titleLevel(rows[previous]) < level) return null
return { start, end, target: previous }
}
return null
}
export const moveTitleSection = (rows, rowId, direction) => {
const move = getTitleSectionMove(rows, rowId, direction)
if (!move) return rows
const { start, end, target } = move
const result = rows.slice()
const section = result.splice(start, end - start)
result.splice(target > start ? target - section.length : target, 0, ...section)
return result
}