GoBD-konforme Rechnungsverwaltung hinzugefügt

2025-10-18 10:00:12 +02:00
commit d208463778
+701
@@ -0,0 +1,701 @@
Für eine **GoBD-konforme Rechnungsverwaltung** in deinem ERP/CRM-System müssen bestimmte **Funktionen und Prozesse** implementiert sein, um den Anforderungen der **Grundsätze zur ordnungsmäßigen Führung und Aufbewahrung von Büchern, Aufzeichnungen und Unterlagen in elektronischer Form (GoBD)** zu entsprechen. Hier sind die **konkreten Funktionen**, die du implementieren solltest, inklusive technischer Umsetzungsvorschläge für Laravel/Vue.js:
---
## **1. GoBD-Kernanforderungen und deren Umsetzung**
Die GoBD (ab 2020 gültig) stellt folgende **zentrale Anforderungen** an digitale Rechnungssysteme:
| **GoBD-Anforderung** | **Konkrete Umsetzung in deinem ERP/CRM** | **Technische Lösung (Laravel/Vue.js)** |
|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| **1. Nachvollziehbarkeit** | Jede Rechnung muss **lückenlos nachvollziehbar** sein (wer, wann, was geändert hat). | **Laravel Auditing** für Änderungen + **Revision History** in der Datenbank. |
| **2. Unveränderbarkeit** | Gestellte Rechnungen dürfen **nicht mehr geändert** werden (nur Stornierung oder Gutschrift möglich). | **Immutable Records**: Rechnungen nach dem "Stellen" als `readonly` markieren. |
| **3. Vollständigkeit** | **Keine Lücken** in der Rechnungsnummernfolge. | **Automatische Nummerngenerierung** mit Transaktionen (siehe Code-Beispiele unten). |
| **4. Aufbewahrungspflicht** | Rechnungen müssen **10 Jahre** unverändert archiviert werden. | **Automatische Backups** (z. B. mit `spatie/laravel-backup`) + **Speicherung als PDF/XML**. |
| **5. Zeitgerechte Buchung** | Rechnungen müssen **zeitnah** (innerhalb weniger Tage) erfasst werden. | **Automatische Erinnerungen** für offene Entwürfe (z. B. mit Laravel Scheduling). |
| **6. Ordnungsgemäße Speicherung** | Rechnungen müssen in **maschinenlesbarer Form** (z. B. ZUGFeRD/XML) gespeichert werden. | **PDF + XML-Export** (z. B. mit `zugferd/php`). |
| **7. Zugriffsschutz** | Nur **autorisierte Personen** dürfen Rechnungen ändern/löschen. | **Laravel Policies** für Berechtigungen. |
| **8. Prüfbarkeit** | Rechnungen müssen **jederzeit prüfbar** sein (z. B. durch Finanzamt). | **Prüfpade (Audit Logs)** + **Dokumentation von Änderungen**. |
| **9. Belegabbildung** | Rechnungen müssen **1:1 dem Original entsprechen** (keine Datenverluste bei Export/Import). | **ZUGFeRD/XRechnung** für elektronische Rechnungen. |
| **10. Verfahrensdokumentation** | Es muss eine **Dokumentation der Prozesse** existieren (z. B. wie Rechnungen erstellt/archiviert werden). | **Automatisch generierte Dokumentation** (z. B. mit Laravel Nova oder Markdown-Export). |
---
## **2. Konkrete Funktionen für deine Rechnungsverwaltung**
### **A. Rechnungserstellung (GoBD-konform)**
| **Funktion** | **Beschreibung** | **Technische Umsetzung** |
|----------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| **Entwürfe vs. gestellte Rechnungen** | Rechnungen werden **erst als Entwurf** gespeichert und erhalten **erst bei "Stellen" eine Nummer**. | **Status-Feld** (`draft`, `issued`, `sent`, `paid`, `cancelled`) + `number`-Feld (nullable). |
| **Automatische Nummerngenerierung** | Rechnungsnummern werden **fortlaufend und ohne Lücken** vergeben (z. B. `RE-2023-001`). | **Datenbank-Transaktionen** + `generateNumber()`-Methode (siehe Code unten). |
| **Unveränderlichkeit nach Stellung** | Gestellte Rechnungen können **nicht mehr bearbeitet**, sondern nur **storniert** werden. | **`readonly`-Felder im Frontend** + **Datenbank-Constraints**. |
| **ZUGFeRD/XRechnung-Export** | Rechnungen werden im **GoBD-konformen Format** (PDF + XML) exportiert. | **`zugferd/php`-Bibliothek** (siehe Beispiel unten). |
| **Mehrwertsteuer-Validierung** | Prüft **gültige USt-IDs** (z. B. über die [VIES-API](http://ec.europa.eu/taxation_customs/vies/)). | **HTTP-Request an VIES-API** (siehe Code unten). |
---
### **B. Archivierung und Aufbewahrung**
| **Funktion** | **Beschreibung** | **Technische Umsetzung** |
|----------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| **Automatische PDF/XML-Speicherung** | Jede gestellte Rechnung wird als **PDF + XML** in einem **unveränderlichen Speicher** abgelegt. | **Laravel Storage** (z. B. S3) + **Backups mit `spatie/laravel-backup`**. |
| **10-Jahres-Aufbewahrung** | Rechnungen werden **automatisch archiviert** und sind **10 Jahre lang verfügbar**. | **Laravel Retention Policies** (z. B. mit `aws/s3-lifecycle`) oder **Datenbank-Archivtabellen**. |
| **Unveränderliche Backups** | Backups werden **verschlüsselt und signiert** abgelegt, um Manipulationen zu verhindern. | **`spatie/laravel-backup` mit Verschlüsselung**. |
| **Prüfpfade (Audit Logs)** | Alle Änderungen an Rechnungen werden **protokolliert** (wer, wann, was). | **`laravel-auditing`** (siehe Beispiel unten). |
---
### **C. Sicherheit und Zugriffskontrolle**
| **Funktion** | **Beschreibung** | **Technische Umsetzung** |
|----------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| **Rollenbasierte Berechtigungen** | Nur **Buchhalter/Admins** dürfen Rechnungen stellen/stornieren. | **Laravel Policies** (z. B. `InvoicePolicy`). |
| **Zwei-Faktor-Authentifizierung** | Zusätzliche Sicherheit für Rechnungsfreigaben. | **`laravel-fortify`** oder **`laravel-2fa`**. |
| **IP- und Zeitstempel-Logging** | Protokolliert **wer, wann und von wo** Rechnungen bearbeitet wurden. | **Middleware für Request-Logging**. |
| **Datenverschlüsselung** | Sensible Rechnungsdaten (z. B. Kundendaten) werden **verschlüsselt** gespeichert. | **Laravel Encryption** (`Illuminate\Support\Facades\Crypt`) oder **`spatie/laravel-encryption`**. |
---
### **D. Prüfung und Dokumentation**
| **Funktion** | **Beschreibung** | **Technische Umsetzung** |
|----------------------------|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| **Automatische Prüfprotokolle** | Generiert **Prüfberichte** für das Finanzamt (z. B. monatliche Zusammenfassungen). | **Laravel Reports** (z. B. mit `laravel-excel`). |
| **Verfahrensdokumentation** | Dokumentiert **wie Rechnungen erstellt/archiviert** werden (Pflicht nach GoBD). | **Markdown-Dokumentation** im Projekt + **automatische API-Docs** (z. B. mit Swagger). |
| **Revisionssichere Protokolle** | Alle Änderungen werden **unveränderlich** protokolliert. | **Datenbank-Trigger** oder **Laravel Events**. |
---
## **3. Code-Beispiele für GoBD-konforme Funktionen**
### **A. Rechnungsmodell mit GoBD-Logik**
```php
// app/Models/Invoice.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Auditable;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
use Illuminate\Support\Facades\Storage;
class Invoice extends Model implements AuditableContract
{
use Auditable; // Für Audit Logs
protected $casts = [
'date' => 'date',
'billing_address' => 'array',
'shipping_address' => 'array',
'metadata' => 'array', // Zusätzliche Metadaten (z. B. für GoBD)
];
protected $auditInclude = [
'number',
'date',
'status',
'total',
'customer_id',
];
/**
* Generiert eine GoBD-konforme Rechnungsnummer.
*/
public static function generateNumber(): string
{
$year = now()->format('Y');
$prefix = 'RE'; // Präfix für Rechnungen
return DB::transaction(function () use ($year, $prefix) {
$lastInvoice = self::where('number', 'like', "{$prefix}-{$year}-%")
->lockForUpdate() // Sperrt die Zeilen für andere Transaktionen
->latest()
->first();
$nextNumber = $lastInvoice ? (int) substr($lastInvoice->number, -3) + 1 : 1;
return sprintf('%s-%s-%03d', $prefix, $year, $nextNumber);
});
}
/**
* Stellt die Rechnung (vergibt Nummer und macht sie unveränderlich).
*/
public function issue(): void
{
if ($this->status !== 'draft') {
throw new \Exception('Nur Entwürfe können gestellt werden.');
}
DB::transaction(function () {
$this->update([
'number' => self::generateNumber(),
'date' => now(),
'status' => 'issued',
]);
// PDF/XML generieren und speichern (GoBD-Pflicht!)
$this->generateAndStorePdf();
});
}
/**
* Generiert ein ZUGFeRD-konformes PDF/XML und speichert es.
*/
protected function generateAndStorePdf(): void
{
$pdfService = new \App\Services\ZugferdPdfService();
$pdfPath = $pdfService->generate($this);
// Speichere PDF in unveränderlichem Storage (z. B. S3 mit Versionierung)
Storage::disk('invoices')->put(
"{$this->number}.pdf",
file_get_contents($pdfPath)
);
// Optional: XML separat speichern (für maschinelle Verarbeitung)
$xml = $pdfService->getXml($this);
Storage::disk('invoices')->put(
"{$this->number}.xml",
$xml
);
// Pfad in der Datenbank speichern (für spätere Referenz)
$this->update([
'pdf_path' => "invoices/{$this->number}.pdf",
'xml_path' => "invoices/{$this->number}.xml",
]);
}
/**
* Storniert die Rechnung (erstellt eine Gutschrift).
*/
public function cancel(string $reason): void
{
if ($this->status === 'cancelled') {
throw new \Exception('Rechnung ist bereits storniert.');
}
DB::transaction(function () use ($reason) {
$this->update([
'status' => 'cancelled',
'cancelled_at' => now(),
'cancel_reason' => $reason,
]);
// Erstelle eine Gutschrift (Credit Note)
$creditNote = $this->replicate();
$creditNote->number = 'CN-' . substr($this->number, 3); // CN-2023-001
$creditNote->status = 'issued';
$creditNote->type = 'credit_note';
$creditNote->total = -$this->total; // Negativer Betrag
$creditNote->save();
});
}
}
```
---
### **B. Audit-Logging mit `laravel-auditing`**
1. **Paket installieren**:
```bash
composer require owen-it/laravel-auditing
php artisan vendor:publish --provider="OwenIt\Auditing\AuditingServiceProvider"
php artisan migrate
```
2. **Model anpassen** (bereits im Beispiel oben enthalten).
3. **Audit-Logs abfragen**:
```php
// Alle Änderungen einer Rechnung abrufen
$audits = $invoice->audits()
->with('user') // Wer hat geändert?
->latest()
->get();
```
---
### **C. ZUGFeRD-PDF-Generierung**
```php
// app/Services/ZugferdPdfService.php
namespace App\Services;
use App\Models\Invoice;
use ZUGFeRD\ZUGFeRDDocument;
class ZugferdPdfService
{
public function generate(Invoice $invoice): string
{
$document = new ZUGFeRDDocument();
$document->setNumber($invoice->number);
$document->setDate($invoice->date);
$document->setSeller([
'name' => config('app.name'),
'street' => config('app.address.street'),
'zip' => config('app.address.zip'),
'city' => config('app.address.city'),
'country' => config('app.address.country'),
'vat_id' => config('app.vat_id'),
]);
$document->setBuyer([
'name' => $invoice->customer->full_name,
'street' => $invoice->billing_address['street'],
'zip' => $invoice->billing_address['postal_code'],
'city' => $invoice->billing_address['city'],
'country' => $invoice->billing_address['country'],
'vat_id' => $invoice->customer->vat_id,
]);
foreach ($invoice->items as $item) {
$document->addItem([
'name' => $item->description,
'quantity' => $item->quantity,
'price' => $item->unit_price,
'vat_rate' => $item->vat_rate,
]);
}
$pdfPath = tempnam(sys_get_temp_dir(), 'zugferd_') . '.pdf';
$document->render($pdfPath);
return $pdfPath;
}
public function getXml(Invoice $invoice): string
{
// Hier würde die XML-Generierung erfolgen (vereinfacht)
return '<invoice>' . $invoice->toXml() . '</invoice>';
}
}
```
---
### **D. VIES-API für USt-ID-Prüfung**
```php
// app/Services/ViesService.php
namespace App\Services;
use Illuminate\Support\Facades\Http;
class ViesService
{
public function validateVatId(string $countryCode, string $vatId): bool
{
$response = Http::get("http://ec.europa.eu/taxation_customs/vies/viesquer.do", [
'countryCode' => $countryCode,
'vatNumber' => $vatId,
]);
$body = $response->body();
return str_contains($body, 'VAT number is valid');
}
}
```
**Verwendung im Model/Controller**:
```php
// Im Customer-Model oder Invoice-Controller
$isValid = (new ViesService())->validateVatId(
substr($invoice->customer->vat_id, 0, 2), // Ländercode (z. B. "DE")
substr($invoice->customer->vat_id, 2) // USt-ID ohne Ländercode
);
```
---
### **E. Automatische Backups mit `spatie/laravel-backup`**
1. **Paket installieren**:
```bash
composer require spatie/laravel-backup
php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider"
php artisan backup:install
```
2. **Konfiguration** (`config/backup.php`):
```php
'disks' => [
'invoices', // Speichert Backups der Rechnungen
],
'monitor_backups' => [
[
'name' => 'invoices',
'disks' => ['invoices'],
'health_checks' => [
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaxAgeInDays::class => 30,
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaxStorageInMb::class => 5000,
],
],
],
```
3. **Backup-Job einrichten** (`app/Console/Kernel.php`):
```php
protected function schedule(Schedule $schedule)
{
$schedule->command('backup:run --only-db --disable-notifications')->dailyAt('3:00');
$schedule->command('backup:clean')->dailyAt('4:00');
}
```
---
### **F. GoBD-konforme Speicherung (Storage-Konfiguration)**
```php
// config/filesystems.php
'disks' => [
'invoices' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_INVOICES_BUCKET'),
'visibility' => 'private',
'options' => [
'Versioning' => 'Enabled', // Wichtig für GoBD!
],
],
],
```
---
### **G. Policies für Berechtigungen**
```php
// app/Policies/InvoicePolicy.php
namespace App\Policies;
use App\Models\Invoice;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class InvoicePolicy
{
use HandlesAuthorization;
public function create(User $user)
{
return $user->can('create invoices');
}
public function issue(User $user, Invoice $invoice)
{
return $user->can('issue invoices') && $invoice->status === 'draft';
}
public function cancel(User $user, Invoice $invoice)
{
return $user->can('cancel invoices') && $invoice->status !== 'cancelled';
}
public function delete(User $user, Invoice $invoice)
{
return false; // Rechnungen dürfen nie gelöscht werden (GoBD)!
}
}
```
**Registrierung im `AuthServiceProvider`**:
```php
// app/Providers/AuthServiceProvider.php
protected $policies = [
Invoice::class => InvoicePolicy::class,
];
```
---
## **4. Frontend-Integration (Vue.js + Inertia)**
### **A. Rechnungsformular mit GoBD-Hinweisen**
```vue
<!-- resources/js/Pages/Invoices/Create.vue -->
<template>
<div>
<h1>{{ invoice.id ? 'Rechnung bearbeiten' : 'Neue Rechnung erstellen' }}</h1>
<form @submit.prevent="save">
<div class="form-group">
<label>Kunde</label>
<select v-model="form.customer_id" required>
<option v-for="customer in customers" :value="customer.id" :key="customer.id">
{{ customer.full_name }}
</option>
</select>
</div>
<div class="form-group">
<label>Rechnungsdatum</label>
<input type="date" v-model="form.date" :readonly="invoice.status !== 'draft'" />
<small v-if="invoice.status !== 'draft'">
Das Datum kann nach dem Stellen der Rechnung nicht mehr geändert werden (GoBD).
</small>
</div>
<div class="form-group">
<label>Rechnungsnummer</label>
<input type="text" v-model="form.number" readonly />
<small>
Die Rechnungsnummer wird automatisch beim Stellen der Rechnung vergeben (GoBD-konform).
</small>
</div>
<!-- Weitere Felder für Positionen, Steuern etc. -->
<div class="actions">
<button type="submit" v-if="invoice.status === 'draft'">Als Entwurf speichern</button>
<button type="button" @click="issue" v-if="invoice.status === 'draft'">
Rechnung stellen (GoBD)
</button>
</div>
</form>
</div>
</template>
<script setup>
import { useForm } from '@inertiajs/vue3';
const props = defineProps({
invoice: Object,
customers: Array,
});
const form = useForm({
customer_id: props.invoice?.customer_id || '',
date: props.invoice?.date || new Date().toISOString().split('T')[0],
number: props.invoice?.number || 'Entwurf (noch keine Nummer)',
// ... weitere Felder
});
const save = () => {
if (props.invoice?.id) {
form.put(`/api/invoices/${props.invoice.id}`);
} else {
form.post('/api/invoices');
}
};
const issue = () => {
if (confirm('Soll die Rechnung gestellt werden? Danach kann sie nicht mehr bearbeitet werden (GoBD).')) {
form.post(`/api/invoices/${props.invoice.id}/issue`, {}, {
onSuccess: () => alert('Rechnung wurde gestellt!'),
});
}
};
</script>
```
---
### **B. Rechnungsliste mit GoBD-Filtern**
```vue
<!-- resources/js/Pages/Invoices/Index.vue -->
<template>
<div>
<h1>Rechnungen</h1>
<div class="filters">
<select v-model="statusFilter">
<option value="">Alle</option>
<option value="draft">Entwürfe</option>
<option value="issued">Gestellt</option>
<option value="sent">Versendet</option>
<option value="paid">Bezahlt</option>
<option value="cancelled">Storniert</option>
</select>
<input type="text" v-model="searchQuery" placeholder="Suche..." />
</div>
<table>
<thead>
<tr>
<th>Nummer</th>
<th>Datum</th>
<th>Kunde</th>
<th>Betrag</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr v-for="invoice in filteredInvoices" :key="invoice.id">
<td>{{ invoice.number || 'Entwurf' }}</td>
<td>{{ invoice.date || '—' }}</td>
<td>{{ invoice.customer.full_name }}</td>
<td>{{ invoice.total }} €</td>
<td>
<span class="badge" :class="statusBadgeClass(invoice.status)">
{{ invoice.status }}
</span>
</td>
<td>
<button v-if="invoice.status === 'draft'" @click="edit(invoice.id)">Bearbeiten</button>
<button v-if="invoice.status === 'draft'" @click="issue(invoice.id)">
Rechnung stellen
</button>
<a v-if="invoice.status !== 'draft'" :href="`/invoices/${invoice.id}/pdf`" target="_blank">
PDF
</a>
<button v-if="invoice.status !== 'draft' && invoice.status !== 'cancelled'"
@click="cancel(invoice.id)">
Stornieren
</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import axios from 'axios';
const invoices = ref([]);
const statusFilter = ref('');
const searchQuery = ref('');
const fetchInvoices = async () => {
const response = await axios.get('/api/invoices');
invoices.value = response.data.data;
};
const filteredInvoices = computed(() => {
return invoices.value
.filter(invoice => !statusFilter.value || invoice.status === statusFilter.value)
.filter(invoice =>
invoice.number?.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
invoice.customer.full_name.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});
const statusBadgeClass = (status) => ({
'badge-draft': status === 'draft',
'badge-issued': status === 'issued',
'badge-sent': status === 'sent',
'badge-paid': status === 'paid',
'badge-cancelled': status === 'cancelled',
});
const issue = async (id) => {
await axios.post(`/api/invoices/${id}/issue`);
await fetchInvoices();
};
const cancel = async (id) => {
const reason = prompt('Grund für die Stornierung (GoBD-Pflicht):');
if (reason) {
await axios.post(`/api/invoices/${id}/cancel`, { reason });
await fetchInvoices();
}
};
fetchInvoices();
</script>
<style>
.badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.875rem;
}
.badge-draft { background: #6c757d; color: white; }
.badge-issued { background: #0d6efd; color: white; }
.badge-sent { background: #198754; color: white; }
.badge-paid { background: #198754; color: white; }
.badge-cancelled { background: #dc3545; color: white; }
</style>
```
---
## **5. GoBD-konforme Dokumentation**
Erstelle eine **Verfahrensdokumentation** für dein Rechnungssystem (Pflicht nach GoBD!). Beispiel:
```markdown
# Verfahrensdokumentation: Rechnungsverwaltung (GoBD-konform)
## 1. Systembeschreibung
- **Zweck**: Erstellung, Verwaltung und Archivierung von Rechnungen gemäß §14 UStG und GoBD.
- **Technologie**: Laravel (Backend), Vue.js (Frontend), SQLite/MySQL (Datenbank), S3 (Speicher).
## 2. Prozesse
### 2.1 Rechnungserstellung
1. Nutzer erstellt **Entwurf** (Status: `draft`).
2. System speichert Entwurf **ohne Rechnungsnummer**.
3. Bei "Rechnung stellen":
- System vergibt **fortlaufende Nummer** (z. B. `RE-2023-001`).
- Status wird auf `issued` gesetzt.
- **PDF/XML** wird generiert und **unveränderlich** gespeichert (S3 mit Versionierung).
- **Audit-Log** wird erstellt.
### 2.2 Rechnungsstornierung
1. Nutzer wählt "Stornieren" und gibt **Grund** an (GoBD-Pflicht!).
2. System:
- Setzt Status auf `cancelled`.
- Erstellt **Gutschrift** (Credit Note) mit negativem Betrag.
- Speichert Stornierungsgrund im **Audit-Log**.
### 2.3 Archivierung
- **Automatische Backups** täglich um 3:00 Uhr (`spatie/laravel-backup`).
- **Aufbewahrungsfrist**: 10 Jahre (S3 Lifecycle Policy).
- **Speicherformat**: PDF + XML (ZUGFeRD-konform).
## 3. Technische Maßnahmen
| Maßnahme | Umsetzung |
|------------------------------|---------------------------------------------------------------------------------------------|
| Unveränderbarkeit | Rechnungen nach Stellung als `readonly` markiert. Datenbank-Trigger verhindern Änderungen. |
| Nachvollziehbarkeit | `laravel-auditing` protokolliert alle Änderungen (wer, wann, was). |
| Vollständigkeit | Fortlaufende Nummernvergabe ohne Lücken (Datenbank-Transaktionen). |
| Aufbewahrung | Tägliche Backups auf S3 mit Versionierung (10 Jahre Aufbewahrung). |
| Zugriffsschutz | Laravel Policies schränken Berechtigungen ein (z. B. nur Buchhalter dürfen stornieren). |
| Prüfbarkeit | Audit-Logs und Revisionsprotokolle sind über Admin-Oberfläche einsehbar. |
## 4. Verantwortlichkeiten
| Rolle | Verantwortung |
|---------------------|-------------------------------------------------------------------------------------------------|
| Buchhaltung | Erstellung, Stellung und Stornierung von Rechnungen. |
| IT-Administration | Wartung der Systeme, Backups, Zugriffsrechte. |
| Geschäftsführung | Freigabe von Prozessen, regelmäßige Prüfung der GoBD-Konformität. |
## 5. Prüfprotokolle
- **Monatlich**: Automatischer Report aller gestellten/stornierten Rechnungen (via `laravel-excel`).
- **Jährlich**: Manuelle Prüfung der Archivierung durch Steuerberater.
```
---
## **6. Checkliste für GoBD-Konformität**
| **Anforderung** | **Umsetzung in deinem System** | **Erledigt?** |
|--------------------------------|-----------------------------------------------------------------------------------------------------------------|---------------|
| **Nachvollziehbarkeit** | Audit-Logs mit `laravel-auditing` + Revision History. | ☐ |
| **Unveränderlichkeit** | Rechnungen nach Stellung als `readonly` + Datenbank-Constraints. | ☐ |
| **Vollständigkeit** | Fortlaufende Nummernvergabe ohne Lücken. | ☐ |
| **Aufbewahrung (10 Jahre)** | Automatische Backups auf S3 mit Versionierung. | ☐ |
| **Maschinenlesbare Formate** | ZUGFeRD/XML-Export für jede Rechnung. | ☐ |
| **Zugriffsschutz** | Laravel Policies für Berechtigungen. | ☐ |
| **Prüfbarkeit** | Audit-Logs und Revisionsprotokolle. | ☐ |
| **Verfahrensdokumentation** | Markdown-Dokumentation im Projekt (siehe oben). | ☐ |
| **VIES-Prüfung** | Automatische Validierung von USt-IDs über die VIES-API. | ☐ |
| **Echtzeit-Protokollierung** | Laravel Echo für Benachrichtigungen bei Änderungen. | ☐ |
---
## **7. Tools für die GoBD-Umsetzung**
| **Tool** | **Zweck** | **Link** |
|------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| **laravel-auditing** | Protokolliert alle Änderungen an Modellen (GoBD-Pflicht!). | [https://github.com/owen-it/laravel-auditing](https://github.com/owen-it/laravel-auditing) |
| **spatie/laravel-backup** | Automatische Backups für 10 Jahre Aufbewahrung. | [https://spatie.be/docs/laravel-backup](https://spatie.be/docs/laravel-backup) |
| **zugferd/php** | Generiert GoBD-konforme ZUGFeRD-Rechnungen (PDF + XML). | [https://github.com/zugferd/php](https://github.com/zugferd/php) |
| **laravel-excel** | Exportiert Rechnungslisten für Prüfprotokolle. | [https://laravel-excel.com/](https://laravel-excel.com/) |
| **laravel-nova** | Admin-Oberfläche für Prüfung/Audit-Logs. | [https://nova.laravel.com/](https://nova.laravel.com/) |
| **AWS S3 + Versioning** | Unveränderliche Speicherung von Rechnungs-PDFs. | [https://aws.amazon.com/s3/](https://aws.amazon.com/s3/) |
| **Laravel Echo + Pusher** | Echtzeit-Benachrichtigungen bei Rechnungsänderungen. | [https://laravel.com/docs/broadcasting](https://laravel.com/docs/broadcasting) |
---
## **8. Zusammenfassung: Was du jetzt tun solltest**
1. **Datenbank-Struktur anpassen**:
- `number` als `string` (nicht `integer`!) speichern.
- Status-Feld (`draft`, `issued`, etc.) und Audit-Logs hinzufügen.
2. **Rechnungsmodell erweitern**:
- `generateNumber()` mit Transaktionen.
- `issue()`-Methode für das "Stellen" von Rechnungen.
- ZUGFeRD-PDF/XML-Generierung.
3. **GoBD-konforme Speicherung einrichten**:
- S3 mit Versionierung für Rechnungs-PDFs.
- Automatische Backups mit `spatie/laravel-backup`.
4. **Frontend anpassen**:
- Rechnungsformular mit GoBD-Hinweisen.
- Status-Filter für die Liste.
5. **Dokumentation erstellen**:
- Verfahrensdokumentation (Markdown) im Projekt ablegen.
6. **Tests durchführen**:
- Prüfe, ob Rechnungen **nicht bearbeitbar** sind nach dem Stellen.
- Teste **Nummernvergabe ohne Lücken**.
- Überprüfe **Backups und Wiederherstellung**.
---
## **Frage an dich**
Möchtest du:
1. Ein **komplettes Beispiel für die S3-Konfiguration** (mit Versionierung)?
2. Hilfe bei der **Einrichtung von Laravel Auditing** für detaillierte Protokolle?
3. Ein **Beispiel für die monatlichen Prüfberichte** (mit Laravel Excel)?