mirror of
https://github.com/kbenestad/invoice.git
synced 2026-06-18 08:04:32 +00:00
Build invoicing app: app/index.html and app/config.yml
Static single-page invoice app per spec. Features: - Multilingual UI (EN/DE/FR/NO) loaded from config.yml; form always prints in the default language - Sender fields + invoice details (date, project code, invoice no.) persisted in localStorage; invoice number auto-increments after each generation - Predefined charge-to recipients selectable from config, or manual entry - Dynamic invoice line items: predefined products with UOM/price pre-fill, free-text fallback, foreign-currency sub-row with exchange-rate and per-item price that calculates back to local currency - Subtotal / tax (configurable rates) / paid / to-pay calculated live - "Generate Invoice" renders a clean A4-formatted preview overlay; "Print / Save as PDF" triggers browser print-to-PDF https://claude.ai/code/session_015iyCBgoTXNNqaErR287U1u
This commit is contained in:
parent
c5d3019e3f
commit
8bac41fe77
2 changed files with 1649 additions and 0 deletions
416
app/config.yml
Normal file
416
app/config.yml
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
# Invoice App Configuration
|
||||||
|
# (C) 2026 Kristian Benestad. Licensed under the Apache 2 license.
|
||||||
|
|
||||||
|
# ── Default language ───────────────────────────────────────────────────────────
|
||||||
|
default-code: en
|
||||||
|
default-name: English
|
||||||
|
default-direction: ltr
|
||||||
|
|
||||||
|
# ── Available languages ────────────────────────────────────────────────────────
|
||||||
|
languages:
|
||||||
|
- code: en
|
||||||
|
name: English
|
||||||
|
direction: ltr
|
||||||
|
- code: de
|
||||||
|
name: Deutsch
|
||||||
|
direction: ltr
|
||||||
|
- code: fr
|
||||||
|
name: Français
|
||||||
|
direction: ltr
|
||||||
|
- code: "no"
|
||||||
|
name: Norsk
|
||||||
|
direction: ltr
|
||||||
|
|
||||||
|
# ── Tax / VAT options ──────────────────────────────────────────────────────────
|
||||||
|
tax-rates:
|
||||||
|
- label-en: "No Tax (0%)"
|
||||||
|
label-de: "Keine Steuer (0%)"
|
||||||
|
label-fr: "Sans taxe (0%)"
|
||||||
|
label-no: "Ingen mva (0%)"
|
||||||
|
rate: 0
|
||||||
|
- label-en: "VAT 5%"
|
||||||
|
label-de: "MwSt. 5%"
|
||||||
|
label-fr: "TVA 5%"
|
||||||
|
label-no: "Mva 5%"
|
||||||
|
rate: 5
|
||||||
|
- label-en: "VAT 10%"
|
||||||
|
label-de: "MwSt. 10%"
|
||||||
|
label-fr: "TVA 10%"
|
||||||
|
label-no: "Mva 10%"
|
||||||
|
rate: 10
|
||||||
|
- label-en: "VAT 20%"
|
||||||
|
label-de: "MwSt. 20%"
|
||||||
|
label-fr: "TVA 20%"
|
||||||
|
label-no: "Mva 20%"
|
||||||
|
rate: 20
|
||||||
|
- label-en: "VAT 25%"
|
||||||
|
label-de: "MwSt. 25%"
|
||||||
|
label-fr: "TVA 25%"
|
||||||
|
label-no: "Mva 25%"
|
||||||
|
rate: 25
|
||||||
|
|
||||||
|
# ── Units of measure ──────────────────────────────────────────────────────────
|
||||||
|
uom:
|
||||||
|
- code: EA
|
||||||
|
labels:
|
||||||
|
en: Each
|
||||||
|
de: Stück
|
||||||
|
fr: Pièce
|
||||||
|
"no": Stykk
|
||||||
|
- code: HR
|
||||||
|
labels:
|
||||||
|
en: Hour
|
||||||
|
de: Stunde
|
||||||
|
fr: Heure
|
||||||
|
"no": Time
|
||||||
|
- code: DY
|
||||||
|
labels:
|
||||||
|
en: Day
|
||||||
|
de: Tag
|
||||||
|
fr: Jour
|
||||||
|
"no": Dag
|
||||||
|
- code: WK
|
||||||
|
labels:
|
||||||
|
en: Week
|
||||||
|
de: Woche
|
||||||
|
fr: Semaine
|
||||||
|
"no": Uke
|
||||||
|
- code: MO
|
||||||
|
labels:
|
||||||
|
en: Month
|
||||||
|
de: Monat
|
||||||
|
fr: Mois
|
||||||
|
"no": Måned
|
||||||
|
- code: LS
|
||||||
|
labels:
|
||||||
|
en: Lump Sum
|
||||||
|
de: Pauschale
|
||||||
|
fr: Forfait
|
||||||
|
"no": Engangsbeløp
|
||||||
|
|
||||||
|
# ── Predefined recipients (charge-to) ─────────────────────────────────────────
|
||||||
|
charge-to:
|
||||||
|
- display: Acme Corporation
|
||||||
|
name: Acme Corporation Ltd.
|
||||||
|
address1: 123 Business Avenue
|
||||||
|
address2: Suite 400
|
||||||
|
address3: New York, NY 10001
|
||||||
|
address4: ""
|
||||||
|
country: US
|
||||||
|
phone: "+1-212-555-0100"
|
||||||
|
email: accounts@acmecorp.example
|
||||||
|
vat-id: "US-EIN-12-3456789"
|
||||||
|
- display: Example NGO
|
||||||
|
name: Example Non-Profit Organisation
|
||||||
|
address1: 45 Charity Lane
|
||||||
|
address2: London EC1A 1BB
|
||||||
|
address3: ""
|
||||||
|
address4: ""
|
||||||
|
country: GB
|
||||||
|
phone: "+44 20 7123 4567"
|
||||||
|
email: finance@examplengo.example
|
||||||
|
vat-id: "GB123456789"
|
||||||
|
|
||||||
|
# ── Project codes ──────────────────────────────────────────────────────────────
|
||||||
|
project-codes:
|
||||||
|
- Project 100
|
||||||
|
- Project 110
|
||||||
|
- Project 230
|
||||||
|
|
||||||
|
# ── Predefined products / services ────────────────────────────────────────────
|
||||||
|
products:
|
||||||
|
- code: CONSULTING
|
||||||
|
description:
|
||||||
|
en: Consulting Services
|
||||||
|
de: Beratungsleistungen
|
||||||
|
fr: Services de conseil
|
||||||
|
"no": Konsulenttjenester
|
||||||
|
uom: HR
|
||||||
|
price: 100.00
|
||||||
|
- code: TRAINING
|
||||||
|
description:
|
||||||
|
en: Training
|
||||||
|
de: Schulung
|
||||||
|
fr: Formation
|
||||||
|
"no": Opplæring
|
||||||
|
uom: DY
|
||||||
|
price: 800.00
|
||||||
|
- code: REPORT
|
||||||
|
description:
|
||||||
|
en: Written Report
|
||||||
|
de: Schriftlicher Bericht
|
||||||
|
fr: Rapport écrit
|
||||||
|
"no": Skriftlig rapport
|
||||||
|
uom: EA
|
||||||
|
price: 500.00
|
||||||
|
- code: REVIEW
|
||||||
|
description:
|
||||||
|
en: Review & Feedback
|
||||||
|
de: Überprüfung & Feedback
|
||||||
|
fr: Révision et retours
|
||||||
|
"no": Gjennomgang og tilbakemelding
|
||||||
|
uom: HR
|
||||||
|
price: 120.00
|
||||||
|
|
||||||
|
# ── Foreign currency options ───────────────────────────────────────────────────
|
||||||
|
currencies:
|
||||||
|
- USD
|
||||||
|
- EUR
|
||||||
|
- GBP
|
||||||
|
- JPY
|
||||||
|
- CHF
|
||||||
|
- CAD
|
||||||
|
- AUD
|
||||||
|
- NOK
|
||||||
|
- SEK
|
||||||
|
- DKK
|
||||||
|
- THB
|
||||||
|
- SGD
|
||||||
|
- HKD
|
||||||
|
- INR
|
||||||
|
- MXN
|
||||||
|
- BRL
|
||||||
|
- ZAR
|
||||||
|
|
||||||
|
# ── UI label translations ──────────────────────────────────────────────────────
|
||||||
|
translations:
|
||||||
|
language:
|
||||||
|
en: Language
|
||||||
|
de: Sprache
|
||||||
|
fr: Langue
|
||||||
|
"no": Språk
|
||||||
|
invoice:
|
||||||
|
en: INVOICE
|
||||||
|
de: RECHNUNG
|
||||||
|
fr: FACTURE
|
||||||
|
"no": FAKTURA
|
||||||
|
sender-name:
|
||||||
|
en: Your Name / Organisation
|
||||||
|
de: Ihr Name / Organisation
|
||||||
|
fr: Votre nom / Organisation
|
||||||
|
"no": Ditt navn / Organisasjon
|
||||||
|
sender-address1:
|
||||||
|
en: Address Line 1
|
||||||
|
de: Adresszeile 1
|
||||||
|
fr: "Ligne d'adresse 1"
|
||||||
|
"no": Adresselinje 1
|
||||||
|
sender-address2:
|
||||||
|
en: Address Line 2
|
||||||
|
de: Adresszeile 2
|
||||||
|
fr: "Ligne d'adresse 2"
|
||||||
|
"no": Adresselinje 2
|
||||||
|
sender-address3:
|
||||||
|
en: Address Line 3
|
||||||
|
de: Adresszeile 3
|
||||||
|
fr: "Ligne d'adresse 3"
|
||||||
|
"no": Adresselinje 3
|
||||||
|
sender-address4:
|
||||||
|
en: Address Line 4
|
||||||
|
de: Adresszeile 4
|
||||||
|
fr: "Ligne d'adresse 4"
|
||||||
|
"no": Adresselinje 4
|
||||||
|
sender-country:
|
||||||
|
en: Country
|
||||||
|
de: Land
|
||||||
|
fr: Pays
|
||||||
|
"no": Land
|
||||||
|
sender-phone:
|
||||||
|
en: Phone
|
||||||
|
de: Telefon
|
||||||
|
fr: Téléphone
|
||||||
|
"no": Telefon
|
||||||
|
sender-email:
|
||||||
|
en: Email
|
||||||
|
de: E-Mail
|
||||||
|
fr: "E-mail"
|
||||||
|
"no": E-post
|
||||||
|
charge-to:
|
||||||
|
en: "Charge to"
|
||||||
|
de: "In Rechnung an"
|
||||||
|
fr: "Facturer à"
|
||||||
|
"no": "Faktureres til"
|
||||||
|
charge-to-name:
|
||||||
|
en: Organisation / Name
|
||||||
|
de: Organisation / Name
|
||||||
|
fr: Organisation / Nom
|
||||||
|
"no": Organisasjon / Navn
|
||||||
|
charge-to-address1:
|
||||||
|
en: Address Line 1
|
||||||
|
de: Adresszeile 1
|
||||||
|
fr: "Ligne d'adresse 1"
|
||||||
|
"no": Adresselinje 1
|
||||||
|
charge-to-address2:
|
||||||
|
en: Address Line 2
|
||||||
|
de: Adresszeile 2
|
||||||
|
fr: "Ligne d'adresse 2"
|
||||||
|
"no": Adresselinje 2
|
||||||
|
charge-to-address3:
|
||||||
|
en: Address Line 3
|
||||||
|
de: Adresszeile 3
|
||||||
|
fr: "Ligne d'adresse 3"
|
||||||
|
"no": Adresselinje 3
|
||||||
|
charge-to-address4:
|
||||||
|
en: Address Line 4
|
||||||
|
de: Adresszeile 4
|
||||||
|
fr: "Ligne d'adresse 4"
|
||||||
|
"no": Adresselinje 4
|
||||||
|
charge-to-country:
|
||||||
|
en: Country
|
||||||
|
de: Land
|
||||||
|
fr: Pays
|
||||||
|
"no": Land
|
||||||
|
charge-to-phone:
|
||||||
|
en: Phone
|
||||||
|
de: Telefon
|
||||||
|
fr: Téléphone
|
||||||
|
"no": Telefon
|
||||||
|
charge-to-email:
|
||||||
|
en: Email
|
||||||
|
de: E-Mail
|
||||||
|
fr: "E-mail"
|
||||||
|
"no": E-post
|
||||||
|
vat-id:
|
||||||
|
en: VAT / Tax ID
|
||||||
|
de: USt-IdNr.
|
||||||
|
fr: "N° TVA / ID fiscal"
|
||||||
|
"no": MVA-nummer
|
||||||
|
invoice-date:
|
||||||
|
en: Invoice date
|
||||||
|
de: Rechnungsdatum
|
||||||
|
fr: Date de facture
|
||||||
|
"no": Fakturadato
|
||||||
|
project-code:
|
||||||
|
en: Project code
|
||||||
|
de: Projektnummer
|
||||||
|
fr: Code projet
|
||||||
|
"no": Prosjektkode
|
||||||
|
invoice-no:
|
||||||
|
en: "Invoice no."
|
||||||
|
de: "Rechnungsnr."
|
||||||
|
fr: "N° de facture"
|
||||||
|
"no": "Fakturanr."
|
||||||
|
qty:
|
||||||
|
en: QTY
|
||||||
|
de: Menge
|
||||||
|
fr: Qté
|
||||||
|
"no": Ant.
|
||||||
|
uom:
|
||||||
|
en: UOM
|
||||||
|
de: Einh.
|
||||||
|
fr: UM
|
||||||
|
"no": Enh.
|
||||||
|
description:
|
||||||
|
en: Description
|
||||||
|
de: Beschreibung
|
||||||
|
fr: Description
|
||||||
|
"no": Beskrivelse
|
||||||
|
price:
|
||||||
|
en: Unit Price
|
||||||
|
de: Einzelpreis
|
||||||
|
fr: Prix unitaire
|
||||||
|
"no": Enhetspris
|
||||||
|
line-total:
|
||||||
|
en: Line Total
|
||||||
|
de: Betrag
|
||||||
|
fr: Total ligne
|
||||||
|
"no": Linjebeløp
|
||||||
|
foreign-currency:
|
||||||
|
en: Foreign currency
|
||||||
|
de: Fremdwährung
|
||||||
|
fr: Devise étrangère
|
||||||
|
"no": Utenlandsk valuta
|
||||||
|
currency-code:
|
||||||
|
en: Currency code
|
||||||
|
de: Währungscode
|
||||||
|
fr: Code devise
|
||||||
|
"no": Valutakode
|
||||||
|
exchange-rate:
|
||||||
|
en: "Exchange rate (1 foreign = X local)"
|
||||||
|
de: "Wechselkurs (1 Fremd = X Inland)"
|
||||||
|
fr: "Taux de change (1 étranger = X local)"
|
||||||
|
"no": "Valutakurs (1 utenlandsk = X lokal)"
|
||||||
|
per-item:
|
||||||
|
en: Price per item (foreign currency)
|
||||||
|
de: Preis je Einheit (Fremdwährung)
|
||||||
|
fr: Prix par unité (devise étrangère)
|
||||||
|
"no": Pris per enhet (utenlandsk valuta)
|
||||||
|
total-local:
|
||||||
|
en: Line total (local currency)
|
||||||
|
de: Zeilenbetrag (Landeswährung)
|
||||||
|
fr: Total ligne (devise locale)
|
||||||
|
"no": Linjebeløp (lokal valuta)
|
||||||
|
add-line:
|
||||||
|
en: "+ Add new line"
|
||||||
|
de: "+ Neue Zeile hinzufügen"
|
||||||
|
fr: "+ Ajouter une ligne"
|
||||||
|
"no": "+ Legg til ny linje"
|
||||||
|
subtotal:
|
||||||
|
en: Subtotal
|
||||||
|
de: Zwischensumme
|
||||||
|
fr: Sous-total
|
||||||
|
"no": Delsum
|
||||||
|
tax:
|
||||||
|
en: Tax
|
||||||
|
de: Steuer
|
||||||
|
fr: Taxe
|
||||||
|
"no": Mva
|
||||||
|
paid:
|
||||||
|
en: Paid
|
||||||
|
de: Bezahlt
|
||||||
|
fr: Payé
|
||||||
|
"no": Betalt
|
||||||
|
to-pay:
|
||||||
|
en: To Pay
|
||||||
|
de: Zu zahlen
|
||||||
|
fr: À payer
|
||||||
|
"no": Å betale
|
||||||
|
generate-invoice:
|
||||||
|
en: Generate Invoice
|
||||||
|
de: Rechnung erstellen
|
||||||
|
fr: Générer la facture
|
||||||
|
"no": Generer faktura
|
||||||
|
other:
|
||||||
|
en: Other
|
||||||
|
de: Andere
|
||||||
|
fr: Autre
|
||||||
|
"no": Annet
|
||||||
|
select:
|
||||||
|
en: "— Select —"
|
||||||
|
de: "— Auswählen —"
|
||||||
|
fr: "— Sélectionner —"
|
||||||
|
"no": "— Velg —"
|
||||||
|
yes:
|
||||||
|
en: "Yes"
|
||||||
|
de: "Ja"
|
||||||
|
fr: "Oui"
|
||||||
|
"no": "Ja"
|
||||||
|
no-option:
|
||||||
|
en: "No"
|
||||||
|
de: "Nein"
|
||||||
|
fr: "Non"
|
||||||
|
"no": "Nei"
|
||||||
|
print-invoice:
|
||||||
|
en: "Print / Save as PDF"
|
||||||
|
de: "Drucken / Als PDF speichern"
|
||||||
|
fr: "Imprimer / Enregistrer en PDF"
|
||||||
|
"no": "Skriv ut / Lagre som PDF"
|
||||||
|
close:
|
||||||
|
en: Close
|
||||||
|
de: Schließen
|
||||||
|
fr: Fermer
|
||||||
|
"no": Lukk
|
||||||
|
invoice-lines:
|
||||||
|
en: Invoice Lines
|
||||||
|
de: Rechnungspositionen
|
||||||
|
fr: Lignes de facture
|
||||||
|
"no": Fakturalinjer
|
||||||
|
sender-section:
|
||||||
|
en: From
|
||||||
|
de: Von
|
||||||
|
fr: De
|
||||||
|
"no": Fra
|
||||||
|
invoice-details-section:
|
||||||
|
en: Invoice Details
|
||||||
|
de: Rechnungsdetails
|
||||||
|
fr: Détails de la facture
|
||||||
|
"no": Fakturaopplysninger
|
||||||
1233
app/index.html
Normal file
1233
app/index.html
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue