feat(invoices): M1 skeleton — module registration + empty ListView

New outbound-finance module that issues invoices to clients. M1 scope:
- types, constants, collections with demo seed (not auto-loaded)
- module.config registered in module-registry
- Dexie v27 with invoices / invoiceClients / invoiceSettings tables
- encryption registry entries for all three tables (type-safe via entry<T>)
- app entry (requiredTier: alpha) + gradient icon (emerald→teal, QR corner)
- route /invoices mounts ListView with empty state

Money stored as integers in minor units (Rappen/cents) to avoid float
drift. Totals kept plaintext for liveQuery aggregation; lines encrypted
as a whole array so titles ride alongside. Settings is a singleton with
stable sentinel id so sync dedupes on it.

Plan: docs/plans/invoices-module.md. Next: M2 CRUD + number generator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-20 15:28:09 +02:00
parent 11f768b8e5
commit 2cf89ce26a
12 changed files with 666 additions and 0 deletions

View file

@ -231,6 +231,11 @@ export const APP_ICONS = {
// gradient sits next to music/photos/picture in the Kreativität & Medien row.
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="lb" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#a855f7"/><stop offset="100%" style="stop-color:#d946ef"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#lb)"/><rect x="22" y="28" width="10" height="44" rx="2" fill="white" fill-opacity="0.95"/><rect x="34" y="24" width="10" height="48" rx="2" fill="white" fill-opacity="0.8"/><rect x="46" y="30" width="10" height="42" rx="2" fill="white" fill-opacity="0.95"/><rect x="58" y="28" width="22" height="44" rx="3" fill="white"/><rect x="62" y="34" width="4" height="4" fill="#a855f7"/><rect x="72" y="34" width="4" height="4" fill="#a855f7"/><rect x="62" y="44" width="4" height="4" fill="#a855f7"/><rect x="72" y="44" width="4" height="4" fill="#a855f7"/><rect x="62" y="54" width="4" height="4" fill="#a855f7"/><rect x="72" y="54" width="4" height="4" fill="#a855f7"/><rect x="62" y="64" width="4" height="4" fill="#a855f7"/><rect x="72" y="64" width="4" height="4" fill="#a855f7"/><rect x="20" y="74" width="62" height="4" rx="2" fill="white" fill-opacity="0.5"/></svg>`
),
invoices: svgToDataUrl(
// Document with a QR-code corner (CH QR-Bill) + a diagonal amount line.
// Emerald→teal sits next to finance green in the Arbeit & Finanzen row.
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="iv" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#059669"/><stop offset="100%" style="stop-color:#14b8a6"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#iv)"/><path d="M28 22h34l14 14v42a4 4 0 0 1-4 4H28a4 4 0 0 1-4-4V26a4 4 0 0 1 4-4z" fill="white" fill-opacity="0.95"/><path d="M62 22v10a4 4 0 0 0 4 4h10" fill="none" stroke="#059669" stroke-width="2" stroke-opacity="0.35"/><rect x="32" y="44" width="24" height="3" rx="1" fill="#059669" fill-opacity="0.6"/><rect x="32" y="52" width="20" height="3" rx="1" fill="#059669" fill-opacity="0.45"/><rect x="32" y="60" width="28" height="3" rx="1" fill="#059669" fill-opacity="0.6"/><rect x="60" y="58" width="14" height="14" rx="1" fill="#059669"/><rect x="62" y="60" width="3" height="3" fill="white"/><rect x="69" y="60" width="3" height="3" fill="white"/><rect x="62" y="67" width="3" height="3" fill="white"/><rect x="66" y="64" width="2" height="2" fill="white"/><rect x="69" y="67" width="3" height="3" fill="white"/></svg>`
),
} as const;
export type AppIconId = keyof typeof APP_ICONS;

View file

@ -1020,6 +1020,23 @@ export const MANA_APPS: ManaApp[] = [
status: 'development',
requiredTier: 'guest',
},
{
id: 'invoices',
name: 'Rechnungen',
description: {
de: 'Rechnungen stellen mit QR-Bill',
en: 'Issue invoices with Swiss QR-Bill',
},
longDescription: {
de: 'Rechnungen an Kunden stellen — mit PDF-Export, Schweizer QR-Rechnung, Mehrwertsteuer und Zahlungsverfolgung. Ergänzt das Finance-Modul um die Outbound-Seite.',
en: 'Issue invoices to clients — with PDF export, Swiss QR-Bill, VAT handling and payment tracking. Adds the outbound side to the Finance module.',
},
icon: APP_ICONS.invoices,
color: '#059669',
comingSoon: false,
status: 'development',
requiredTier: 'alpha',
},
];
/**