Aufträge: Appwrite-Schema der Collections auftraege und projektRechnungen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
16f08b5ac3
commit
0ab3cbbaa3
3 files changed
+140
-3
No files matched your search
@@ -0,0 +1,70 @@
|
||||
// test/auftraegeSchema.test.js
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { AUFTRAEGE_COLLECTIONS, AUFTRAEGE_DATETIMES, AUFTRAEGE_ZUSATZ_ATTRIBUTE } from '../server/services/auftraege/schema.js'
|
||||
import { schemaKeys } from './support/schema.js'
|
||||
|
||||
// Spalten aus Übersicht V1 in Kurzschrift: sN = Text mit Größe N, i = ganze Zahl, b = Wahrheitswert, d = Datum und Zeit
|
||||
const SPALTEN = {
|
||||
auftraege: `nummer:s20 jahr:i laufnummer:i status:s20 kundenId:s64 kundeJson:s2000 projektId:s64 titel:s200
|
||||
zeitquelleJson:s1000 art:s20 positionenJson:s20000 stundensatzCents:i schaetzungMinuten:i summeCents:i
|
||||
zahlungsplanJson:s1000 zahlungszielTage:i auftragVom:s10 auftragWeg:s20 startAm:s10 fertigBis:s10
|
||||
texteFassung:s20 agbFassung:s20 pdfDatei:s300 pdfSha256:s64 gesendetAm:d gesendetAn:s255 mailStatus:s20
|
||||
mailFehler:s500 tokenHash:s64 tokenGueltigBis:d bestaetigtAm:d bestaetigtUeber:s10 bestaetigtPdfSha256:s64
|
||||
fruehbeginn:b fruehbeginnFassung:s20 ersetztId:s64 ersetztDurchId:s64 storniertAm:d widerrufenAm:d
|
||||
erstelltVon:s255 erstelltAm:d geaendertAm:d`,
|
||||
projektRechnungen: `invoiceId:s64 kundenId:s64 projektId:s64 auftragId:s64 art:s20 betragCents:i
|
||||
arbeitsblattIdsJson:s20000 rechnungsnummer:s64 aktiv:b erstelltVon:s255 erstelltAm:d`,
|
||||
}
|
||||
function attribut(kurz) {
|
||||
const [key, art] = kurz.split(':')
|
||||
if (art === 'i') return { key, type: 'integer', required: false }
|
||||
if (art === 'b') return { key, type: 'boolean', required: false }
|
||||
if (art === 'd') return { key, type: 'datetime', required: false }
|
||||
return { key, type: 'string', size: Number(art.slice(1)), required: false }
|
||||
}
|
||||
const erwartet = (name) => SPALTEN[name].trim().split(/\s+/).map(attribut)
|
||||
|
||||
test('zwei Collections mit den Spalten aus Übersicht V1', () => {
|
||||
assert.deepEqual(Object.keys(AUFTRAEGE_COLLECTIONS), ['auftraege', 'projektRechnungen'])
|
||||
assert.deepEqual(AUFTRAEGE_COLLECTIONS.auftraege.attributes, erwartet('auftraege'))
|
||||
assert.deepEqual(AUFTRAEGE_COLLECTIONS.projektRechnungen.attributes, erwartet('projektRechnungen'))
|
||||
assert.equal(AUFTRAEGE_COLLECTIONS.auftraege.attributes.length, 42)
|
||||
assert.equal(AUFTRAEGE_COLLECTIONS.projektRechnungen.attributes.length, 11)
|
||||
})
|
||||
|
||||
test('Indizes und Zusatzfelder wie in Übersicht V1', () => {
|
||||
assert.deepEqual(AUFTRAEGE_COLLECTIONS.auftraege.indexes, [
|
||||
{ key: 'idx_kunde', type: 'key', attributes: ['kundenId'] },
|
||||
{ key: 'idx_projekt', type: 'key', attributes: ['projektId'] },
|
||||
{ key: 'idx_jahr', type: 'key', attributes: ['jahr'] },
|
||||
{ key: 'idx_token', type: 'key', attributes: ['tokenHash'] },
|
||||
{ key: 'idx_nummer', type: 'key', attributes: ['nummer'] },
|
||||
])
|
||||
assert.deepEqual(AUFTRAEGE_COLLECTIONS.projektRechnungen.indexes, [
|
||||
{ key: 'idx_kunde', type: 'key', attributes: ['kundenId'] },
|
||||
{ key: 'idx_projekt', type: 'key', attributes: ['projektId'] },
|
||||
{ key: 'idx_auftrag', type: 'key', attributes: ['auftragId'] },
|
||||
])
|
||||
assert.deepEqual(AUFTRAEGE_ZUSATZ_ATTRIBUTE, {
|
||||
customers: [{ key: 'kundenart', type: 'string', size: 20, required: false }],
|
||||
financeSettings: [{ key: 'steuernummer', type: 'string', size: 40, required: false }],
|
||||
})
|
||||
})
|
||||
|
||||
test('AUFTRAEGE_DATETIMES nennt je Collection genau die Datumsfelder', () => {
|
||||
assert.deepEqual(AUFTRAEGE_DATETIMES, {
|
||||
auftraege: ['gesendetAm', 'tokenGueltigBis', 'bestaetigtAm', 'storniertAm', 'widerrufenAm', 'erstelltAm', 'geaendertAm'],
|
||||
projektRechnungen: ['erstelltAm'],
|
||||
})
|
||||
})
|
||||
|
||||
test('schemaKeys kennt die neuen Collections und die Steuernummer; Bestehendes bleibt', () => {
|
||||
assert.deepEqual(schemaKeys('auftraege'), AUFTRAEGE_COLLECTIONS.auftraege.attributes.map((a) => a.key))
|
||||
assert.deepEqual(schemaKeys('projektRechnungen'), AUFTRAEGE_COLLECTIONS.projektRechnungen.attributes.map((a) => a.key))
|
||||
const einstellungen = schemaKeys('financeSettings')
|
||||
for (const key of ['steuernummer', 'bankDetailsText', 'taxReservePercent']) assert.ok(einstellungen.includes(key), key)
|
||||
assert.ok(!schemaKeys('financeFixedCosts').includes('steuernummer'))
|
||||
assert.ok(schemaKeys('portalFeatures').includes('unlockedBy') && schemaKeys('termineConfig').includes('einstellung'))
|
||||
assert.throws(() => schemaKeys('gibtEsNicht'), /fehlt im Setup-Skript/)
|
||||
})
|
||||
+10
-3
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'
|
||||
import { TERMINE_COLLECTIONS } from '../../server/services/termine/schema.js'
|
||||
import { SHOP_COLLECTIONS } from '../../server/services/shop/schema.js'
|
||||
import { ZEITEN_COLLECTIONS, ZEITEN_FEATURE_ATTRIBUTES } from '../../server/services/zeiten/schema.js'
|
||||
import { AUFTRAEGE_COLLECTIONS, AUFTRAEGE_ZUSATZ_ATTRIBUTE } from '../../server/services/auftraege/schema.js'
|
||||
import { FEATURE_CONSENT_ATTRIBUTES, PORTAL_FEATURE_ATTRIBUTES } from '../../scripts/feature-setup.mjs'
|
||||
|
||||
// Setup-Skripte, die Collections in Appwrite anlegen (Blockformat: 'id', ... \n ],)
|
||||
@@ -16,9 +17,12 @@ const SETUP_SCRIPTS = ['setup-abo-collections.mjs', 'setup-finanzen.mjs']
|
||||
* die Zeiten-Collections aus server/services/zeiten/schema.js (scripts/zeiten-setup.mjs);
|
||||
* portalFeatures bekommt zusaetzlich die Attribute aus scripts/feature-setup.mjs (PORTAL_FEATURE_ATTRIBUTES) und die
|
||||
* Zeiten-Felder aus server/services/zeiten/schema.js (ZEITEN_FEATURE_ATTRIBUTES, angelegt von scripts/zeiten-setup.mjs);
|
||||
* featureConsents (Zustimmungsnachweis) steht ganz dort (FEATURE_CONSENT_ATTRIBUTES).
|
||||
* featureConsents (Zustimmungsnachweis) steht ganz dort (FEATURE_CONSENT_ATTRIBUTES);
|
||||
* die Aufträge-Collections kommen aus server/services/auftraege/schema.js (scripts/auftraege-setup.mjs), und ihre
|
||||
* Zusatzfelder bestehender Collections (AUFTRAEGE_ZUSATZ_ATTRIBUTE, z. B. financeSettings.steuernummer) werden angehängt.
|
||||
*/
|
||||
export function schemaKeys(collection) {
|
||||
if (Object.hasOwn(AUFTRAEGE_COLLECTIONS, collection)) return AUFTRAEGE_COLLECTIONS[collection].attributes.map((a) => a.key)
|
||||
if (Object.hasOwn(TERMINE_COLLECTIONS, collection)) return TERMINE_COLLECTIONS[collection].attributes.map((a) => a.key)
|
||||
if (Object.hasOwn(SHOP_COLLECTIONS, collection)) return SHOP_COLLECTIONS[collection].attributes.map((a) => a.key)
|
||||
if (Object.hasOwn(ZEITEN_COLLECTIONS, collection)) return ZEITEN_COLLECTIONS[collection].attributes.map((a) => a.key)
|
||||
@@ -29,8 +33,11 @@ export function schemaKeys(collection) {
|
||||
if (start < 0) continue
|
||||
const block = src.slice(start, src.indexOf('\n ],', start))
|
||||
const keys = [...block.matchAll(/key: '(\w+)'/g)].map((m) => m[1])
|
||||
return collection === 'portalFeatures'
|
||||
? [...keys, ...PORTAL_FEATURE_ATTRIBUTES.map((a) => a.key), ...ZEITEN_FEATURE_ATTRIBUTES.map((a) => a.key)]
|
||||
if (collection === 'portalFeatures') {
|
||||
return [...keys, ...PORTAL_FEATURE_ATTRIBUTES.map((a) => a.key), ...ZEITEN_FEATURE_ATTRIBUTES.map((a) => a.key)]
|
||||
}
|
||||
return Object.hasOwn(AUFTRAEGE_ZUSATZ_ATTRIBUTE, collection)
|
||||
? [...keys, ...AUFTRAEGE_ZUSATZ_ATTRIBUTE[collection].map((a) => a.key)]
|
||||
: keys
|
||||
}
|
||||
throw new Error(`Collection ${collection} fehlt im Setup-Skript`)
|
||||
|
||||
Reference in new issue
Block a user