feat: install user console commands and document Office independence
This commit is contained in:
1 parent
6438558ea3
commit
f447603af4
6 files changed
+179
-10
No files matched your search
@@ -291,3 +291,59 @@ Checkout-Updater nicht.
|
|||||||
Technische Grundlagen: [pip Versionsauflösung und Installationsbericht](https://pip.pypa.io/en/stable/cli/pip_install/),
|
Technische Grundlagen: [pip Versionsauflösung und Installationsbericht](https://pip.pypa.io/en/stable/cli/pip_install/),
|
||||||
[Python venv und feste Umgebungspfade](https://docs.python.org/3/library/venv.html).
|
[Python venv und feste Umgebungspfade](https://docs.python.org/3/library/venv.html).
|
||||||
Umgebungen bleiben deshalb an ihrem Erstellungsort; nur der Zeiger wird gewechselt.
|
Umgebungen bleiben deshalb an ihrem Erstellungsort; nur der Zeiger wird gewechselt.
|
||||||
|
|
||||||
|
|
||||||
|
## Konsolenbefehle installieren
|
||||||
|
|
||||||
|
Einmal im Checkout `python3 scripts/install_commands.py` ausführen. Der Installer
|
||||||
|
benötigt kein sudo und verlinkt `einvoice` sowie `jr-einvoice` nach `~/.local/bin`.
|
||||||
|
Die verlinkten Starter finden den Checkout auch beim Aufruf aus einem anderen
|
||||||
|
Verzeichnis und verwenden den aktiven Update-/Rollback-Stand. Beide Befehle werden
|
||||||
|
mit `--help` geprüft. Bei Fehlern setzt der Installer seine Änderungen zurück.
|
||||||
|
Vorhandene andere Dateien/Symlinks werden unter `*.before-einvoice-<Zeit-ID>`
|
||||||
|
gesichert; bereits korrekt eingerichtete Links bleiben erhalten.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Einmalig:
|
||||||
|
cd /home/johannes/localdev/einvoice
|
||||||
|
python3 scripts/install_commands.py
|
||||||
|
|
||||||
|
# Danach auch außerhalb des Checkouts:
|
||||||
|
einvoice --help
|
||||||
|
einvoice --customers
|
||||||
|
einvoice --update --status
|
||||||
|
einvoice --update --apply
|
||||||
|
einvoice --update --rollback
|
||||||
|
jr-einvoice --help
|
||||||
|
```
|
||||||
|
|
||||||
|
Abweichender Zielordner: `--bin-dir /absoluter/pfad`. Falls dieser Ordner nicht im
|
||||||
|
PATH steht, zeigt der Installer die passende Export-Zeile an; dauerhaft in die eigene
|
||||||
|
Shell-Konfiguration übernehmen. Auf Johannes' Rechner ist `~/.local/bin` bereits im
|
||||||
|
PATH, daher ist keine Änderung der Shell-Konfiguration nötig. Bei einem alten
|
||||||
|
Shell-Cache `rehash` (zsh) oder `hash -r` (bash) verwenden. Eine aktivierte `.venv`
|
||||||
|
kann den alten Einstieg vorziehen; mit `deactivate` die verwalteten Befehle nutzen.
|
||||||
|
Der Checkout muss am Installationsort erhalten bleiben. Nach Verschieben den
|
||||||
|
Installer am neuen Ort erneut ausführen. Die Starter benötigen bash, readlink und
|
||||||
|
Python 3; diese sind auf dem vorgesehenen Linux-System vorhanden.
|
||||||
|
|
||||||
|
## Office-Unabhängigkeit und LibreOffice
|
||||||
|
|
||||||
|
Die Quell-, Vorlagen- und Datenformate der Anwendung sind YAML, HTML/Jinja2, SVG
|
||||||
|
und Markdown. Erzeugt werden PDF/A-3, strukturiertes XML, Prüfberichte, JSON-Manifeste
|
||||||
|
und optional EML. Weder DOC/DOCX, XLS/XLSX, PPT/PPTX noch COM-/Microsoft-Office-APIs
|
||||||
|
werden verwendet. Es gibt keine Abhängigkeit von Word, Excel, Outlook oder einer
|
||||||
|
Microsoft-365-Lizenz. Die Python-Abhängigkeiten und die externen Werkzeuge
|
||||||
|
WeasyPrint/Mustang/Java benötigen keine Office-Installation.
|
||||||
|
|
||||||
|
Für diese Rechnungslösung muss deshalb beim Wechsel zu LibreOffice nichts nach
|
||||||
|
ODT/ODS konvertiert werden. ODF-Import/-Export ist keine Funktion des Tools;
|
||||||
|
Dokumentvorlagen werden weiterhin als HTML/Jinja2 bearbeitet, Daten als YAML.
|
||||||
|
Die versandfertige E-Rechnung ist das validierte PDF mit eingebettetem XML.
|
||||||
|
Dieses Original nicht in Office/LibreOffice bearbeiten oder als neues PDF exportieren,
|
||||||
|
da XML-Einbettung und PDF/A-Eigenschaften dabei verloren gehen können.
|
||||||
|
|
||||||
|
Der optionale EML-Entwurf ist eine Datei ohne Outlook-Automation. Mailprogramm,
|
||||||
|
Mailkonto, Zugang und tatsächlicher Versand liegen außerhalb der Toolchain.
|
||||||
|
Die Prüfung betrifft dieses eInvoice-Projekt; andere persönliche oder betriebliche
|
||||||
|
Office-Dateien und Microsoft-365-Dienste wurden damit nicht geprüft oder migriert.
|
||||||
@@ -29,3 +29,7 @@ rollback:
|
|||||||
python3 scripts/update.py --rollback
|
python3 scripts/update.py --rollback
|
||||||
update-status:
|
update-status:
|
||||||
python3 scripts/update.py --status
|
python3 scripts/update.py --status
|
||||||
|
|
||||||
|
.PHONY: install-commands
|
||||||
|
install-commands:
|
||||||
|
python3 scripts/install_commands.py
|
||||||
@@ -11,7 +11,7 @@ Auf diesem Rechner sind Python-Umgebung, Absenderdaten und Mustang eingerichtet.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/johannes/localdev/einvoice
|
cd /home/johannes/localdev/einvoice
|
||||||
export PATH="$PWD/bin:$PATH"
|
python3 scripts/install_commands.py # einmalig; auf diesem Rechner bereits eingerichtet
|
||||||
|
|
||||||
einvoice --create \
|
einvoice --create \
|
||||||
--Customer "Meine Kundenfirma GmbH" \
|
--Customer "Meine Kundenfirma GmbH" \
|
||||||
@@ -103,21 +103,50 @@ Die Konsole zeigt Schritte, Versionsänderungen und Fehler; vollständige Logs l
|
|||||||
in `.runtime/logs/`. Netzwerk ist nur für neue Kandidaten nötig. Ohne `--apply`
|
in `.runtime/logs/`. Netzwerk ist nur für neue Kandidaten nötig. Ohne `--apply`
|
||||||
werden Downloads, Installation und Tests ausgeführt, aber keine Umschaltung.
|
werden Downloads, Installation und Tests ausgeführt, aber keine Umschaltung.
|
||||||
|
|
||||||
**Für die verwaltete Laufzeit die Starter aus `bin/` verwenden:**
|
**Konsolenbefehle einmalig im Benutzer-Suchpfad einrichten:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export PATH="/home/johannes/localdev/einvoice/bin:$PATH" # bei Bedarf in ~/.zshrc aufnehmen
|
python3 scripts/install_commands.py
|
||||||
# alternativ ohne PATH-Einrichtung:
|
# Danach aus jedem Verzeichnis, ohne venv-Aktivierung:
|
||||||
./bin/einvoice --customers
|
einvoice --customers
|
||||||
./bin/einvoice --update --apply
|
einvoice --update --apply
|
||||||
|
einvoice --update --rollback
|
||||||
|
jr-einvoice --help
|
||||||
```
|
```
|
||||||
|
|
||||||
Diese PATH-Zeile nach einer eventuellen venv-Aktivierung ausführen. Direkte Aufrufe
|
Der Installer verlinkt beide Starter nach `~/.local/bin` und prüft ihre Funktion.
|
||||||
von `.venv/bin/einvoice` verwenden weiterhin die ursprüngliche Entwicklerumgebung.
|
Dort vorhandene andere Befehle werden vor dem Ersetzen gesichert. Wiederholtes
|
||||||
|
Ausführen ist möglich. Auf diesem Rechner ist das Verzeichnis bereits im PATH.
|
||||||
|
Bei einem gespeicherten alten Shell-Pfad einmal `rehash` (zsh) bzw. `hash -r`
|
||||||
|
(bash) ausführen. Eine aktivierte `.venv` kann die Befehle überdecken: dann
|
||||||
|
`deactivate` ausführen. Direkte Aufrufe von `.venv/bin/einvoice` verwenden weiterhin
|
||||||
|
die ursprüngliche Entwicklerumgebung.
|
||||||
Java und native Bibliotheken werden mitgetestet, aber über den Systempaketmanager
|
Java und native Bibliotheken werden mitgetestet, aber über den Systempaketmanager
|
||||||
aktualisiert; dessen Änderungen kann dieser Updater nicht zurückrollen.
|
aktualisiert; dessen Änderungen kann dieser Updater nicht zurückrollen.
|
||||||
Details und Grenzen: [Update-Betrieb](Documentation.md#updates-und-rollback).
|
Details und Grenzen: [Update-Betrieb](Documentation.md#updates-und-rollback).
|
||||||
|
|
||||||
|
## Ohne Microsoft Office / Microsoft 365
|
||||||
|
|
||||||
|
Die Rechnungslösung benötigt weder Word, Excel, Outlook noch Microsoft 365.
|
||||||
|
Es gibt keine DOC/DOCX-, XLS/XLSX- oder PPT/PPTX-Vorlagen und keine Office-Automation.
|
||||||
|
Auch LibreOffice muss zur Rechnungserstellung nicht installiert sein.
|
||||||
|
|
||||||
|
| Zweck | Verwendetes Format |
|
||||||
|
| --- | --- |
|
||||||
|
| Rechnungsdaten und Stammdaten | YAML (`.yaml`) |
|
||||||
|
| Layout | HTML/Jinja2 (`.html.j2`), Logo als SVG |
|
||||||
|
| Fertige E-Rechnung | PDF/A-3 mit eingebettetem EN16931-XML |
|
||||||
|
| Optionaler Mailentwurf | EML (`.eml`), ohne Outlook-Anbindung |
|
||||||
|
| Dokumentation | Markdown (`.md`) |
|
||||||
|
|
||||||
|
Ein Wechsel auf LibreOffice erfordert für dieses Projekt keine Dateikonvertierung.
|
||||||
|
ODT-/ODS-Dateien werden aktuell weder eingelesen noch erzeugt. Layoutänderungen
|
||||||
|
finden in der HTML-Vorlage statt. Das fertige E-Rechnungs-PDF direkt versenden;
|
||||||
|
nicht zum Bearbeiten konvertieren oder neu als PDF exportieren, da eingebettete
|
||||||
|
Rechnungsdaten oder die geprüfte PDF/A-Struktur verloren gehen können.
|
||||||
|
Die tatsächliche E-Mail-Zustellung erfolgt weiterhin über dein separat eingerichtetes
|
||||||
|
Mailprogramm bzw. Konto; sie wird von diesem Tool nicht bereitgestellt.
|
||||||
|
|
||||||
## Daten und Einrichtung
|
## Daten und Einrichtung
|
||||||
|
|
||||||
- Absender: **johannes@jr-it-services.de**, lokale Vorlage `private/presets/seller.default.yaml`.
|
- Absender: **johannes@jr-it-services.de**, lokale Vorlage `private/presets/seller.default.yaml`.
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
launcher="$(readlink -f -- "${BASH_SOURCE[0]}")"
|
||||||
|
root="$(cd -- "$(dirname -- "$launcher")/.." && pwd)"
|
||||||
if [[ "${1:-}" == --update ]]; then
|
if [[ "${1:-}" == --update ]]; then
|
||||||
shift
|
shift
|
||||||
exec python3 "$root/scripts/update.py" "$@"
|
exec python3 "$root/scripts/update.py" "$@"
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
launcher="$(readlink -f -- "${BASH_SOURCE[0]}")"
|
||||||
|
root="$(cd -- "$(dirname -- "$launcher")/.." && pwd)"
|
||||||
if [[ "${1:-}" == --update ]]; then
|
if [[ "${1:-}" == --update ]]; then
|
||||||
shift
|
shift
|
||||||
exec python3 "$root/scripts/update.py" "$@"
|
exec python3 "$root/scripts/update.py" "$@"
|
||||||
|
|||||||
Executable
+78
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Install per-user launchers without sudo or changing shell configuration."""
|
||||||
|
import argparse
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
def install(bin_dir: Path) -> None:
|
||||||
|
root = Path(__file__).resolve().parent.parent
|
||||||
|
bin_dir = bin_dir.expanduser().absolute()
|
||||||
|
bin_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
changed = []
|
||||||
|
try:
|
||||||
|
for name in ('einvoice', 'jr-einvoice'):
|
||||||
|
source = root / 'bin' / name
|
||||||
|
target = bin_dir / name
|
||||||
|
if target.is_symlink() and target.resolve() == source:
|
||||||
|
print(f'Bereits eingerichtet: {target}', flush=True)
|
||||||
|
continue
|
||||||
|
backup = None
|
||||||
|
if target.exists() or target.is_symlink():
|
||||||
|
if target.is_dir():
|
||||||
|
raise RuntimeError(f'Ziel ist ein Verzeichnis: {target}')
|
||||||
|
stamp = datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')
|
||||||
|
backup = target.with_name(f'{name}.before-einvoice-{stamp}-{uuid.uuid4().hex[:8]}')
|
||||||
|
if target.is_symlink():
|
||||||
|
backup.symlink_to(os.readlink(target))
|
||||||
|
else:
|
||||||
|
import shutil
|
||||||
|
shutil.copy2(target, backup)
|
||||||
|
print(f'Bisheriger Befehl gesichert: {backup}', flush=True)
|
||||||
|
temporary = target.with_name(f'.{name}-{uuid.uuid4().hex}.next')
|
||||||
|
try:
|
||||||
|
temporary.symlink_to(source)
|
||||||
|
os.replace(temporary, target)
|
||||||
|
finally:
|
||||||
|
temporary.unlink(missing_ok=True)
|
||||||
|
changed.append((target, backup))
|
||||||
|
print(f'Eingerichtet: {target} -> {source}', flush=True)
|
||||||
|
# A different cwd verifies that symlinks resolve to the checkout correctly.
|
||||||
|
for name in ('einvoice', 'jr-einvoice'):
|
||||||
|
subprocess.run([str(bin_dir / name), '--help'], cwd=bin_dir,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
|
||||||
|
print('OK: Beide Konsolenbefehle geprüft.', flush=True)
|
||||||
|
except BaseException:
|
||||||
|
for target, backup in reversed(changed):
|
||||||
|
if backup is None:
|
||||||
|
target.unlink(missing_ok=True)
|
||||||
|
else:
|
||||||
|
os.replace(backup, target)
|
||||||
|
raise
|
||||||
|
if str(bin_dir) not in os.environ.get('PATH', '').split(os.pathsep):
|
||||||
|
import shlex
|
||||||
|
print(f'Für diese Shell ausführen: export PATH={shlex.quote(str(bin_dir))}:"$PATH"')
|
||||||
|
print('Ab jetzt: einvoice --help | einvoice --update --status | jr-einvoice --help')
|
||||||
|
print('Falls die Shell noch den alten Pfad merkt: rehash (zsh) bzw. hash -r (bash).')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='einvoice und jr-einvoice im Benutzer-Suchpfad einrichten.')
|
||||||
|
parser.add_argument('--bin-dir', type=Path, default=Path.home() / '.local/bin')
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
install(args.bin_dir)
|
||||||
|
except (Exception, KeyboardInterrupt) as exc:
|
||||||
|
print(f'FEHLER: {exc}; geänderte Befehle wurden zurückgesetzt.', file=sys.stderr)
|
||||||
|
if isinstance(exc, subprocess.CalledProcessError) and exc.stderr:
|
||||||
|
print(exc.stderr.strip(), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in new issue
Block a user