Feature: Anlage-Links auf Bearbeitungsmaske, Backfill anlage_nr, README
- Anlage-Spalte in allen Buchungstabellen verlinkt jetzt auf /assets/{id}/edit
- bk.asset_id in UstvaCalculator und EuerCalculator-Queries ergänzt
- Fallback-Anzeige: Bezeichnung wenn anlage_nr leer ist
- Migration 032: anlage_nr für bestehende Anlagegüter nachgenerieren
- README: Feature Anlagevermögen dokumentiert
This commit is contained in:
parent
2611d2b4b9
commit
49d145e251
9 changed files with 73 additions and 7 deletions
|
|
@ -2,6 +2,14 @@
|
|||
|
||||
---
|
||||
|
||||
## 2026-04-12 – Anlagevermögen: Anzeige-Fix und Daten-Backfill
|
||||
|
||||
### Fix
|
||||
- Buchungstabellen (Beleg-Detail, Journal, UStVA-Drill-down, EÜR-Drill-down): Anlage-Spalte zeigt jetzt auch die Bezeichnung als Fallback, wenn `anlage_nr` leer ist
|
||||
- `migrations/032_anlage_nr_backfill.sql`: Anlage-Nummern für bestehende Anlagegüter ohne `anlage_nr` werden nachgeneriert
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-12 – Anlagevermögen (Wirtschaftsgüter)
|
||||
|
||||
### Neu
|
||||
|
|
|
|||
29
README.md
29
README.md
|
|
@ -11,7 +11,8 @@ Self-hosted Buchhaltungs- und Steueranwendung für deutsche Freiberufler und Kle
|
|||
- **Ausgangsrechnungen** – DIN 5008 Layout, PDF-Export via mPDF
|
||||
- **UStVA** – Kennzahlen-Berechnung aus Buchungen, ELSTER XML-Export
|
||||
- **EÜR** – Jahresauswertung mit PDF-Export
|
||||
- **Einstellungen** – Firmendaten, Logo, ELSTER-Steuernummer
|
||||
- **Anlagevermögen** – Anlagenspiegel für Wirtschaftsgüter: automatische Anlagen-Nummerierung (konfiguriertes Präfix + Jahrgang + laufende Nummer), Verknüpfung mit Anlagevermögenskonten des SKR03; beim Buchen auf ein Anlagevermögenskonto öffnet sich automatisch ein Dialog zur Auswahl (neues Wirtschaftsgut anlegen oder bestehendes verknüpfen); Anlagennummer wird in allen Buchungsübersichten (Beleg-Detail, Journal, UStVA-Drill-down, EÜR-Drill-down) als direkter Link zur Bearbeitungsmaske des Anlageguts angezeigt
|
||||
- **Einstellungen** – Firmendaten, Logo, ELSTER-Steuernummer, Nummernkreis-Präfixe (Belege, Rechnungen, Anlagen)
|
||||
- **Benutzerverwaltung** – Benutzer anlegen, bearbeiten, deaktivieren und löschen; session-basierte Authentifizierung mit 24 h Timeout
|
||||
|
||||
## Tech Stack
|
||||
|
|
@ -178,11 +179,37 @@ docker compose exec web php scripts/nextcloud-import.php
|
|||
|
||||
### Automatisch per Cron (auf dem Host)
|
||||
|
||||
Für den automatischen Abruf muss ein Cron-Eintrag auf dem Host angelegt werden. Dabei gibt es zwei Varianten:
|
||||
|
||||
**Option 1: Root-Crontab** (`sudo crontab -e`)
|
||||
|
||||
```cron
|
||||
*/15 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \
|
||||
php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1
|
||||
```
|
||||
|
||||
Kein `sudo` im Befehl nötig. Nachteil: Root-Crons sind schwerer zu überblicken.
|
||||
|
||||
**Option 2: User-Crontab** (`crontab -e`) – empfohlen
|
||||
|
||||
Damit `docker compose exec` ohne `sudo` funktioniert, muss der User in der Docker-Gruppe sein:
|
||||
|
||||
```bash
|
||||
sudo usermod -aG docker $USER
|
||||
# Danach neu einloggen
|
||||
```
|
||||
|
||||
Anschließend in der User-Crontab (ohne `sudo`):
|
||||
|
||||
```cron
|
||||
*/15 * * * * docker compose -f /home/fds/projects/Steuerkram/docker-compose.yml exec -T web \
|
||||
php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1
|
||||
```
|
||||
|
||||
> **Hinweis:** Das `-T`-Flag ist bei Cron zwingend erforderlich – ohne TTY schlägt `docker compose exec` sonst still fehl.
|
||||
>
|
||||
> **Empfehlung:** User-Crontab + Docker-Gruppe. Das ist konsistent mit dem bestehenden Setup (alle anderen Jobs laufen ebenfalls in der User-Crontab).
|
||||
|
||||
Das Skript gibt beim Start eine Zusammenfassung aus (importiert / übersprungen / Fehler) und beendet sich mit Exit-Code 1 bei Fehlern.
|
||||
|
||||
## Datenpersistenz
|
||||
|
|
|
|||
23
migrations/032_anlage_nr_backfill.sql
Normal file
23
migrations/032_anlage_nr_backfill.sql
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
-- Migration 032: Anlage-Nummern für bestehende Anlagegüter ohne Nummer nachgenerieren
|
||||
-- Weist assets mit leerem anlage_nr Nummern im konfigurierten Format zu.
|
||||
|
||||
CREATE TEMPORARY TABLE IF NOT EXISTS _anlage_backfill_tmp AS
|
||||
SELECT
|
||||
a.id,
|
||||
YEAR(COALESCE(a.anschaffungsdatum, NOW())) AS asset_year,
|
||||
cs.anlage_nr_prefix AS prefix,
|
||||
cs.anlage_nr_next + (ROW_NUMBER() OVER (ORDER BY a.id) - 1) AS seq_nr
|
||||
FROM assets a
|
||||
CROSS JOIN company_settings cs
|
||||
WHERE a.anlage_nr = '' AND cs.id = 1;
|
||||
|
||||
UPDATE assets a
|
||||
JOIN _anlage_backfill_tmp t ON a.id = t.id
|
||||
SET a.anlage_nr = CONCAT(t.prefix, '-', t.asset_year, '-', LPAD(t.seq_nr, 4, '0'))
|
||||
WHERE a.anlage_nr = '';
|
||||
|
||||
UPDATE company_settings
|
||||
SET anlage_nr_next = anlage_nr_next + (SELECT COUNT(*) FROM _anlage_backfill_tmp)
|
||||
WHERE id = 1 AND (SELECT COUNT(*) FROM _anlage_backfill_tmp) > 0;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS _anlage_backfill_tmp;
|
||||
|
|
@ -80,7 +80,7 @@ class EuerCalculator
|
|||
return Database::fetchAll(
|
||||
'SELECT bk.id, bk.buchungs_datum, bk.buchungstyp, bk.ust_satz,
|
||||
bk.betrag_netto, bk.betrag_ust, bk.betrag_brutto, bk.buchungstext,
|
||||
bk.is_reversal, bk.beleg_id, bl.beleg_nr,
|
||||
bk.is_reversal, bk.beleg_id, bk.asset_id, bl.beleg_nr,
|
||||
k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
|
||||
z.kontonummer AS zahlungs_nr, z.bezeichnung AS zahlungs_name,
|
||||
a.anlage_nr, a.bezeichnung AS anlage_bezeichnung
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ class UstvaCalculator
|
|||
|
||||
$select = 'SELECT bk.id, bk.buchungs_datum, bk.buchungstyp, bk.steuerart, bk.ust_satz,
|
||||
bk.betrag_netto, bk.betrag_ust, bk.betrag_brutto, bk.buchungstext,
|
||||
bk.is_reversal, bk.beleg_id, bl.beleg_nr,
|
||||
bk.is_reversal, bk.beleg_id, bk.asset_id, bl.beleg_nr,
|
||||
k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
|
||||
z.kontonummer AS zahlungs_nr, z.bezeichnung AS zahlungs_name,
|
||||
a.anlage_nr, a.bezeichnung AS anlage_bezeichnung
|
||||
|
|
|
|||
|
|
@ -225,7 +225,9 @@ $suppliers = $suppliers ?? [];
|
|||
</td>
|
||||
<td class="td-mono text-sm">
|
||||
<?php if (!empty($bk['anlage_nr'])): ?>
|
||||
<a href="/assets" title="<?= $h($bk['anlage_bezeichnung']) ?>"><?= $h($bk['anlage_nr']) ?></a>
|
||||
<a href="/assets/<?= (int)$bk['asset_id'] ?>/edit" title="<?= $h($bk['anlage_bezeichnung']) ?>"><?= $h($bk['anlage_nr']) ?></a>
|
||||
<?php elseif (!empty($bk['anlage_bezeichnung'])): ?>
|
||||
<a href="/assets/<?= (int)$bk['asset_id'] ?>/edit"><?= $h($bk['anlage_bezeichnung']) ?></a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">–</span>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,9 @@
|
|||
</td>
|
||||
<td class="td-mono text-sm">
|
||||
<?php if (!empty($bk['anlage_nr'])): ?>
|
||||
<a href="/assets" title="<?= $h($bk['anlage_bezeichnung']) ?>"><?= $h($bk['anlage_nr']) ?></a>
|
||||
<a href="/assets/<?= (int)$bk['asset_id'] ?>/edit" title="<?= $h($bk['anlage_bezeichnung']) ?>"><?= $h($bk['anlage_nr']) ?></a>
|
||||
<?php elseif (!empty($bk['anlage_bezeichnung'])): ?>
|
||||
<a href="/assets/<?= (int)$bk['asset_id'] ?>/edit"><?= $h($bk['anlage_bezeichnung']) ?></a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">–</span>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
|
|
@ -65,7 +65,9 @@ $fmt = fn($v) => number_format((float)$v, 2, ',', '.') . ' €';
|
|||
<td class="td-right td-mono"><?= $fmt($brutto) ?></td>
|
||||
<td class="td-mono text-sm">
|
||||
<?php if (!empty($b['anlage_nr'])): ?>
|
||||
<a href="/assets" title="<?= $h($b['anlage_bezeichnung']) ?>"><?= $h($b['anlage_nr']) ?></a>
|
||||
<a href="/assets/<?= (int)$b['asset_id'] ?>/edit" title="<?= $h($b['anlage_bezeichnung']) ?>"><?= $h($b['anlage_nr']) ?></a>
|
||||
<?php elseif (!empty($b['anlage_bezeichnung'])): ?>
|
||||
<a href="/assets/<?= (int)$b['asset_id'] ?>/edit"><?= $h($b['anlage_bezeichnung']) ?></a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">–</span>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ $signedBetrag = fn(array $b, string $field): float =>
|
|||
<td class="td-right td-mono"><?= $fmtKz($brutto) ?></td>
|
||||
<td class="td-mono text-sm">
|
||||
<?php if (!empty($b['anlage_nr'])): ?>
|
||||
<a href="/assets" title="<?= $h($b['anlage_bezeichnung']) ?>"><?= $h($b['anlage_nr']) ?></a>
|
||||
<a href="/assets/<?= (int)$b['asset_id'] ?>/edit" title="<?= $h($b['anlage_bezeichnung']) ?>"><?= $h($b['anlage_nr']) ?></a>
|
||||
<?php elseif (!empty($b['anlage_bezeichnung'])): ?>
|
||||
<a href="/assets/<?= (int)$b['asset_id'] ?>/edit"><?= $h($b['anlage_bezeichnung']) ?></a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">–</span>
|
||||
<?php endif; ?>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue