Files
BauIN/database/migrations/2026_10_05_080000_create_fachliche_grundtabellen.php
ChristophandClaude Opus 5.5 6aac8b0a16 Datenmodell Kern: Projekt, PFA, Vertrag, LV, Nachtrag, Dokument
- Mandant, Auftragnehmer, Projekt mit Suchreihenfolge, Abschnitt (PFA), Vertrag,
  Leistungsverzeichnis, Nachtrag (MKA nur als Bezug), Nachtragsposition, Dokument
- Projektmitglieder mit Projektrolle, Freigaben für vertrauliche Dokumente
- Mandant, Land und Projekt werden vom übergeordneten Datensatz übernommen;
  Fehlzuordnungen über Mandanten oder Projekte hinweg werden abgewiesen
- Feste Morph-Map, Factories, Demodaten (Musterbahn, PFA 1–4, N14/N16/N21), Tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:34:24 +02:00

184 lines
7.9 KiB
PHP

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Kern des Datenmodells: Mandant → Projekt → Abschnitt (PFA) → Vertrag → LVs und Nachträge.
* Beschreibung in docs/datenmodell.md.
*/
public function up(): void
{
Schema::create('mandanten', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
Schema::table('users', function (Blueprint $table) {
$table->foreignId('mandant_id')->nullable()->after('id')->constrained('mandanten')->restrictOnDelete();
});
Schema::create('auftragnehmer', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->string('name');
$table->timestamps();
$table->unique(['mandant_id', 'land', 'name']);
});
Schema::create('projekte', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->string('name');
$table->string('nummer', 100)->nullable();
$table->string('auftraggeber');
$table->string('vertragsgrundlage', 20)->default('vob_b');
$table->string('abschnitt_bezeichnung', 50)->default('PFA');
$table->string('status', 20)->default('aktiv');
$table->boolean('suche_andere_vertraege_im_abschnitt')->default(false);
$table->boolean('suche_andere_abschnitte')->default(true);
$table->boolean('suche_nur_gleicher_auftragnehmer')->default(false);
$table->timestamps();
$table->index(['mandant_id', 'land', 'status']);
});
Schema::create('projekt_mitglieder', function (Blueprint $table) {
$table->foreignId('projekt_id')->constrained('projekte')->cascadeOnDelete();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->string('rolle', 20);
$table->timestamps();
$table->primary(['projekt_id', 'user_id']);
});
Schema::create('abschnitte', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->foreignId('projekt_id')->constrained('projekte')->restrictOnDelete();
$table->string('bezeichnung', 100);
$table->unsignedSmallInteger('sortierung')->default(0);
$table->timestamps();
$table->unique(['projekt_id', 'bezeichnung']);
});
Schema::create('vertraege', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->foreignId('projekt_id')->constrained('projekte')->restrictOnDelete();
$table->foreignId('abschnitt_id')->constrained('abschnitte')->restrictOnDelete();
$table->foreignId('auftragnehmer_id')->constrained('auftragnehmer')->restrictOnDelete();
$table->string('bezeichnung', 150);
$table->string('vertragsnummer', 100)->nullable();
$table->date('vertrag_vom')->nullable();
$table->timestamps();
});
Schema::create('leistungsverzeichnisse', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->foreignId('projekt_id')->constrained('projekte')->restrictOnDelete();
$table->foreignId('vertrag_id')->constrained('vertraege')->restrictOnDelete();
$table->string('bezeichnung');
$table->string('import_status', 20)->default('wartet');
$table->text('import_fehler')->nullable();
$table->timestamps();
});
Schema::create('nachtraege', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->foreignId('projekt_id')->constrained('projekte')->restrictOnDelete();
$table->foreignId('vertrag_id')->constrained('vertraege')->restrictOnDelete();
$table->string('nummer', 50);
$table->date('eingang_am');
$table->string('betreff');
$table->json('mka_bezug')->nullable();
$table->string('status', 20)->default('eingegangen');
$table->foreignId('zugewiesen_an')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
$table->unique(['vertrag_id', 'nummer']);
$table->index(['projekt_id', 'status']);
});
Schema::create('nachtragspositionen', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->foreignId('projekt_id')->constrained('projekte')->restrictOnDelete();
$table->foreignId('nachtrag_id')->constrained('nachtraege')->cascadeOnDelete();
$table->string('oz', 50);
$table->string('kurztext', 500);
$table->text('langtext')->nullable();
$table->decimal('menge', 15, 3)->nullable();
$table->string('einheit', 20)->nullable();
$table->decimal('einheitspreis', 15, 2)->nullable();
$table->unsignedInteger('sortierung')->default(0);
$table->timestamps();
$table->unique(['nachtrag_id', 'oz']);
});
Schema::create('dokumente', function (Blueprint $table) {
$table->id();
$table->foreignId('mandant_id')->constrained('mandanten')->restrictOnDelete();
$table->char('land', 2);
$table->foreignId('projekt_id')->nullable()->constrained('projekte')->restrictOnDelete();
$table->nullableMorphs('dokumentable');
$table->string('kategorie', 40);
$table->string('dateiname');
$table->string('pfad', 500);
$table->string('mime_type', 150);
$table->unsignedBigInteger('groesse');
$table->char('sha256', 64);
$table->boolean('vertraulich')->default(false);
$table->foreignId('hochgeladen_von')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
$table->index(['projekt_id', 'kategorie']);
$table->index('sha256');
});
Schema::create('dokument_freigaben', function (Blueprint $table) {
$table->foreignId('dokument_id')->constrained('dokumente')->cascadeOnDelete();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->timestamps();
$table->primary(['dokument_id', 'user_id']);
});
}
public function down(): void
{
Schema::dropIfExists('dokument_freigaben');
Schema::dropIfExists('dokumente');
Schema::dropIfExists('nachtragspositionen');
Schema::dropIfExists('nachtraege');
Schema::dropIfExists('leistungsverzeichnisse');
Schema::dropIfExists('vertraege');
Schema::dropIfExists('abschnitte');
Schema::dropIfExists('projekt_mitglieder');
Schema::dropIfExists('projekte');
Schema::dropIfExists('auftragnehmer');
Schema::table('users', function (Blueprint $table) {
$table->dropConstrainedForeignId('mandant_id');
});
Schema::dropIfExists('mandanten');
}
};