21-11-2025

This commit is contained in:
Kevin Adametz 2025-11-21 18:21:23 +01:00
parent fa2ebd457d
commit 07959c0ba2
113 changed files with 4730 additions and 898 deletions

View file

@ -0,0 +1,136 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$teams = config('permission.teams');
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
throw_if(empty($tableNames), new Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.'));
throw_if($teams && empty($columnNames['team_foreign_key'] ?? null), new Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.'));
Schema::create($tableNames['permissions'], static function (Blueprint $table) {
// $table->engine('InnoDB');
$table->bigIncrements('id'); // permission id
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) {
// $table->engine('InnoDB');
$table->bigIncrements('id'); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
$table->unsignedBigInteger($pivotPermission);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
} else {
$table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
}
});
Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
$table->unsignedBigInteger($pivotRole);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
} else {
$table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
}
});
Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
$table->unsignedBigInteger($pivotPermission);
$table->unsignedBigInteger($pivotRole);
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*/
public function down(): void
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::drop($tableNames['role_has_permissions']);
Schema::drop($tableNames['model_has_roles']);
Schema::drop($tableNames['model_has_permissions']);
Schema::drop($tableNames['roles']);
Schema::drop($tableNames['permissions']);
}
};

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('hubs', function (Blueprint $table) {
$table->id();
$table->string('name'); // z.B. "OWL" oder "Rhein-Main"
$table->string('slug')->unique(); // Für saubere URLs, z.B. "owl"
$table->string('keyvisual_url')->nullable(); // Keyvisual-Bild des Hubs
$table->string('emblem_url')->nullable(); // Wappen-URL des Hubs
$table->boolean('is_active')->default(false);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('hubs');
}
};

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('hub_locations', function (Blueprint $table) {
$table->id();
// Beziehung zum Hub
$table->foreignId('hub_id')->constrained()->onDelete('cascade');
$table->string('city_name')->nullable(); // z.B. "Bielefeld"
$table->string('zip_code'); // z.B. "33602"
// Index für schnelle PLZ-Suche
$table->index('zip_code');
// Verhindert doppelte PLZ-Einträge pro Hub
$table->unique(['hub_id', 'zip_code']);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('hub_locations');
}
};

View file

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('partners', function (Blueprint $table) {
$table->id();
$table->string('company_name');
$table->string('slug')->unique();
// Partner-Typ (gemäß Wissensbasis)
$table->string('type'); // 'Retailer', 'Manufacturer', 'Estate-Agent'
// Hub-Zugehörigkeit (kann NULL sein für "globale" Hersteller)
$table->foreignId('hub_id')->nullable()->constrained()->onDelete('set null');
$table->text('description')->nullable();
$table->string('logo_url')->nullable();
$table->boolean('is_active')->default(false);
// Spezifische Felder für 'Retailer' (Händler)
$table->integer('delivery_radius_km')->nullable();
$table->integer('assembly_radius_km')->nullable();
// Flexible Provisions-Engine (pro Partner)
// Speichert den Wert in Cents, um Rundungsfehler zu vermeiden
$table->integer('provision_fixed_amount')->nullable();
// Speichert als 10.5 für 10.5%
$table->decimal('provision_rate_percentage', 5, 2)->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('partners');
}
};

View file

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('roles', function (Blueprint $table) {
$table->string('color', 50)->default('zinc')->after('guard_name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('roles', function (Blueprint $table) {
$table->dropColumn('color');
});
}
};

View file

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
// Verknüpft einen User mit einer Partner-Firma
// 'after' ist optional, aber gut für die DB-Lesbarkeit
$table->foreignId('partner_id')
->nullable()
->after('id')
->constrained('partners')
->onDelete('set null'); // Wenn Partner gelöscht wird, bleibt User bestehen
});
}
public function down(): void
{
// Entferne Foreign Key zuerst
try {
Schema::table('users', function (Blueprint $table) {
$table->dropForeign(['partner_id']);
});
} catch (\Exception $e) {
// Foreign Key existiert nicht oder hat anderen Namen - weitermachen
}
// Entferne Spalte
if (Schema::hasColumn('users', 'partner_id')) {
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('partner_id');
});
}
}
};

View file

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('attributes', function (Blueprint $table) {
$table->id();
$table->string('name'); // z.B. "Farbe", "Größe", "Material"
$table->string('slug')->unique();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('attributes');
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('attribute_values', function (Blueprint $table) {
$table->id();
$table->foreignId('attribute_id')->constrained('attributes')->onDelete('cascade');
$table->string('value'); // z.B. "Anthrazit", "3-Sitzer", "Eiche"
$table->string('slug')->unique();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('attribute_values');
}
};

View file

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('media', function (Blueprint $table) {
$table->id();
$table->string('model_type'); // "App\Models\Product" ODER "App\Models\ProductVariant"
$table->unsignedBigInteger('model_id');
$table->string('file_path');
$table->string('type')->default('image'); // 'image', 'video', 'pdf', '3d_model'
$table->string('alt_text')->nullable();
$table->integer('order_column')->default(0);
$table->timestamps();
$table->index(['model_type', 'model_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('media');
}
};

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
// database/migrations/xxxx_create_brands_table.php
public function up(): void
{
Schema::create('brands', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->string('logo_url')->nullable();
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('brands');
}
};

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
// database/migrations/xxxx_create_collections_table.php
public function up(): void
{
Schema::create('collections', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('collections');
}
};

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
// database/migrations/xxxx_create_categories_table.php
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
// Für Baumstruktur (z.B. "Sofas" gehört zu "Wohnzimmer")
$table->foreignId('parent_id')
->nullable()
->constrained('categories')
->onDelete('set null');
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('categories');
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tax_rates', function (Blueprint $table) {
$table->id();
$table->string('name'); // z.B. "Regelsatz", "Ermäßigt"
$table->decimal('rate_percentage', 5, 2); // z.B. 19.00, 7.00
$table->boolean('is_default')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tax_rates');
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('shipping_classes', function (Blueprint $table) {
$table->id();
$table->string('name'); // z.B. "Paketversand", "Spedition (2-Mann)"
$table->text('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('shipping_classes');
}
};

View file

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tags');
}
};

View file

@ -0,0 +1,61 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
// database/migrations/xxxx_create_products_table.php
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->foreignId('partner_id')->constrained('partners')->onDelete('cascade');
// NEU: Verknüpfung zu Marke/Hersteller
$table->foreignId('brand_id')->nullable()->constrained('brands')->onDelete('set null');
// NEU: Verknüpfung zu Kollektion/Serie
$table->foreignId('collection_id')->nullable()->constrained('collections')->onDelete('set null');
$table->string('name'); // z.B. "3-Sitzer Sofa 'Stockholm'"
$table->string('slug')->unique();
$table->string('status')->default('draft'); // draft, active, archived
$table->text('description_short')->nullable();
$table->text('description_long')->nullable();
$table->text('care_instructions')->nullable();
// Basis-Maße (können von Varianten überschrieben werden, wenn nötig)
$table->integer('width_cm')->nullable();
$table->integer('height_cm')->nullable();
$table->integer('depth_cm')->nullable();
// PERFEKTER ANWENDUNGSFALL FÜR JSON:
// Spezifische Maße, nach denen nie gefiltert wird.
$table->json('dimensions_specific')->nullable();
// Bsp: {"seat_height_cm": 45, "seat_depth_cm": 60}
// Logistik-Basis
$table->string('assembly_status')->nullable(); // z.B. 'flat_pack', 'partially_assembled', 'fully_assembled'
// SEO
$table->string('meta_title')->nullable();
$table->text('meta_description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('products');
}
};

View file

@ -0,0 +1,62 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
// database/migrations/xxxx_create_product_variants_table.php
public function up(): void
{
Schema::create('product_variants', function (Blueprint $table) {
$table->id();
// Jede Variante gehört zu einem "Parent"-Produkt
$table->foreignId('product_id')->constrained('products')->onDelete('cascade');
$table->string('name_suffix')->nullable(); // z.B. "Anthrazit / 3-Sitzer"
$table->boolean('is_master_variant')->default(false); // Die "Standard"-Variante
// Eindeutige IDs
$table->string('sku')->unique(); // Interne SKU
$table->string('han_mpn')->nullable()->index(); // Hersteller-Nr.
$table->string('ean_gtin')->nullable()->index(); // Barcode
// Preis-Logik (in Cents)
$table->integer('selling_price'); // VK
$table->integer('msrp')->nullable(); // UVP (Streichpreis)
$table->integer('purchase_price')->nullable(); // EK
$table->foreignId('tax_rate_id')->constrained('tax_rates');
// Lager-Logik
$table->integer('stock_quantity')->default(0);
$table->integer('stock_min_threshold')->nullable(); // Meldebestand
$table->string('availability_status')->nullable(); // z.B. 'in_stock', 'out_of_stock'
$table->string('delivery_time_text')->nullable(); // z.B. "6-8 Wochen" (ersetzt altes Feld)
// WICHTIG: Re-Integration des Mietmodells aus dem Initial-Briefing
$table->boolean('is_rentable')->default(false);
$table->json('rental_duration_options')->nullable(); // [6, 12, 24]
$table->string('rental_rate_formula')->nullable();
$table->decimal('residual_value_percentage', 5, 2)->nullable();
// Variante-spezifische Maße & Gewicht (falls abweichend vom Parent)
$table->integer('variant_weight_g')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('product_variants');
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// ... create_product_variant_attributes_table.php
Schema::create('product_variant_attributes', function (Blueprint $table) {
// Composite Primary Key
$table->foreignId('product_variant_id')->constrained('product_variants')->onDelete('cascade');
$table->foreignId('attribute_value_id')->constrained('attribute_values')->onDelete('cascade');
$table->primary(['product_variant_id', 'attribute_value_id'], 'variant_attribute_primary');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('product_variant_attributes');
}
};

View file

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('partner_invitations', function (Blueprint $table) {
$table->id();
$table->string('company_name');
$table->enum('partner_type', ['Retailer', 'Manufacturer', 'Estate-Agent', 'Customer'])->default('Retailer');
$table->string('email');
$table->string('token', 64)->unique();
$table->enum('status', ['pending', 'accepted', 'expired', 'cancelled'])->default('pending');
$table->timestamp('expires_at');
$table->foreignId('invited_by')->constrained('users')->onDelete('cascade');
$table->foreignId('partner_id')->nullable()->constrained('partners')->onDelete('set null');
$table->timestamp('accepted_at')->nullable();
$table->timestamps();
$table->index(['token', 'status']);
$table->index(['email', 'status']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Foreign Keys werden automatisch mit der Tabelle gelöscht
Schema::dropIfExists('partner_invitations');
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('category_product', function (Blueprint $table) {
$table->foreignId('category_id')->constrained()->onDelete('cascade');
$table->foreignId('product_id')->constrained()->onDelete('cascade');
// Primärschlüssel aus beiden IDs
$table->primary(['category_id', 'product_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('category_product');
}
};

View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('product_tag', function (Blueprint $table) {
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
// Primärschlüssel aus beiden IDs
$table->primary(['product_id', 'tag_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('product_tag');
}
};

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('related_products', function (Blueprint $table) {
// Das "Basis"-Produkt
$table->foreignId('product_id')->constrained('products')->onDelete('cascade');
// Das "verknüpfte" Produkt
$table->foreignId('related_product_id')->constrained('products')->onDelete('cascade');
$table->primary(['product_id', 'related_product_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('related_products');
}
};

View file

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('product_logistics', function (Blueprint $table) {
$table->id();
// 1-zu-1 Beziehung zur Variante
$table->foreignId('product_variant_id')->constrained('product_variants')->onDelete('cascade');
$table->foreignId('shipping_class_id')->nullable()->constrained('shipping_classes');
// Verpackungsmaße
$table->integer('package_width_cm')->nullable();
$table->integer('package_height_cm')->nullable();
$table->integer('package_depth_cm')->nullable();
$table->integer('package_weight_g')->nullable(); // Bruttogewicht
$table->integer('package_count')->default(1); // Anzahl Packstücke
$table->string('location_bin')->nullable(); // Lagerort
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('product_logistics');
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('roles', function (Blueprint $table) {
$table->string('display_name')->nullable()->after('name');
$table->string('icon')->nullable()->after('display_name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('roles', function (Blueprint $table) {
$table->dropColumn(['display_name', 'icon']);
});
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('partner_invitations', function (Blueprint $table) {
$table->string('contact_first_name')->nullable()->after('company_name');
$table->string('contact_last_name')->nullable()->after('contact_first_name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('partner_invitations', function (Blueprint $table) {
$table->dropColumn(['contact_first_name', 'contact_last_name']);
});
}
};

View file

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('roles', function (Blueprint $table) {
$table->boolean('can_be_invited')->default(false)->after('icon');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('roles', function (Blueprint $table) {
$table->dropColumn('can_be_invited');
});
}
};

View file

@ -0,0 +1,101 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// Füge zuerst role_id als nullable hinzu (falls nicht existiert)
if (!Schema::hasColumn('partner_invitations', 'role_id')) {
Schema::table('partner_invitations', function (Blueprint $table) {
$table->unsignedBigInteger('role_id')->nullable()->after('contact_last_name');
});
}
// Migriere bestehende Daten: partner_type -> role_id
if (Schema::hasColumn('partner_invitations', 'partner_type')) {
DB::table('partner_invitations')->get()->each(function ($invitation) {
$roleName = $invitation->partner_type;
$role = DB::table('roles')->where('name', $roleName)->first();
if ($role) {
DB::table('partner_invitations')
->where('id', $invitation->id)
->update(['role_id' => $role->id]);
}
});
// Jetzt partner_type entfernen und role_id als required machen
Schema::table('partner_invitations', function (Blueprint $table) {
$table->dropColumn('partner_type');
});
}
// Mache role_id required und füge Foreign Key hinzu
try {
Schema::table('partner_invitations', function (Blueprint $table) {
$table->unsignedBigInteger('role_id')->nullable(false)->change();
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
});
} catch (\Exception $e) {
// Foreign Key existiert bereits, nur die Spalte ändern
Schema::table('partner_invitations', function (Blueprint $table) {
$table->unsignedBigInteger('role_id')->nullable(false)->change();
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Füge partner_type wieder hinzu (als nullable, da wir Daten migrieren)
if (!Schema::hasColumn('partner_invitations', 'partner_type')) {
Schema::table('partner_invitations', function (Blueprint $table) {
$table->enum('partner_type', ['Retailer', 'Manufacturer', 'Estate-Agent', 'Customer'])->nullable()->after('email');
});
}
// Migriere Daten zurück: role_id -> partner_type
if (Schema::hasColumn('partner_invitations', 'role_id')) {
DB::table('partner_invitations')->get()->each(function ($invitation) {
if ($invitation->role_id) {
$role = DB::table('roles')->where('id', $invitation->role_id)->first();
if ($role) {
DB::table('partner_invitations')
->where('id', $invitation->id)
->update(['partner_type' => $role->name]);
}
}
});
}
// Entferne role_id Foreign Key und Spalte
try {
Schema::table('partner_invitations', function (Blueprint $table) {
$table->dropForeign(['role_id']);
});
} catch (\Exception $e) {
// Foreign Key existiert nicht oder hat anderen Namen - weitermachen
}
if (Schema::hasColumn('partner_invitations', 'role_id')) {
Schema::table('partner_invitations', function (Blueprint $table) {
$table->dropColumn('role_id');
});
}
// Mache partner_type wieder required mit default
Schema::table('partner_invitations', function (Blueprint $table) {
$table->enum('partner_type', ['Retailer', 'Manufacturer', 'Estate-Agent', 'Customer'])->default('Retailer')->change();
});
}
};

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('partners', function (Blueprint $table) {
$table->boolean('setup_completed')->default(false)->after('is_active');
$table->timestamp('setup_completed_at')->nullable()->after('setup_completed');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('partners', function (Blueprint $table) {
$table->dropColumn(['setup_completed', 'setup_completed_at']);
});
}
};

View file

@ -5,6 +5,7 @@ namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
class DatabaseSeeder extends Seeder
{
@ -16,8 +17,9 @@ class DatabaseSeeder extends Seeder
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
'name' => 'Kevin Adametz',
'email' => 'kevin.adametz@me.com',
'password' => Hash::make('xunfew-0Jygjy-minnyt'),
]);
}
}

View file

@ -0,0 +1,171 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class RoleSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Reset cached roles and permissions
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
// --- Definiere Permissions ---
$permissions = [
// Hub Management
'view hubs',
'create hubs',
'edit hubs',
'delete hubs',
// Partner Management
'view partners',
'create partners',
'edit partners',
'delete partners',
'manage provisions', // Provisions-Regeln verwalten
// Product Management
'view products',
'create products',
'edit products',
'delete products',
'manage rental options', // Miet-Parameter verwalten
// Order Management (für später)
'view orders',
'manage orders',
// User & Role Management
'view users',
'manage users',
'manage roles',
// Frontend/Customer facing
'access dashboard', // Genereller Backend-Zugriff
'place orders' // Für Kunden
];
// Erstelle Permissions
foreach ($permissions as $permission) {
Permission::create(['name' => $permission]);
}
// --- Definiere Rollen und weise Permissions zu ---
// 1. Customer (Endkunde)
$customerRole = Role::create([
'name' => 'Customer',
'display_name' => 'Customer (Kunde)',
'icon' => 'user',
'color' => 'indigo',
'can_be_invited' => true
]);
$customerRole->givePermissionTo([
'view products',
'place orders',
'view orders' // Eigene Bestellungen sehen
]);
// 2. Estate-Agent (Makler)
$estateAgentRole = Role::create([
'name' => 'Estate-Agent',
'display_name' => 'Estate-Agent (Makler)',
'icon' => 'home',
'color' => 'lime',
'can_be_invited' => true
]);
$estateAgentRole->givePermissionTo([
'access dashboard',
'view partners', // Damit sie sehen können, wen sie empfehlen
'view hubs'
// Makler bekommen KEINE Produkt- oder Order-Rechte
]);
// 3. Retailer (Lokaler Händler)
$retailerRole = Role::create([
'name' => 'Retailer',
'display_name' => 'Retailer (Händler)',
'icon' => 'building-storefront',
'color' => 'teal',
'can_be_invited' => true
]);
$retailerRole->givePermissionTo([
'access dashboard',
'view products',
'create products',
'edit products', // Später eingeschränkt auf EIGENE Produkte
'delete products', // Später eingeschränkt auf EIGENE Produkte
'manage rental options',
'view orders', // Eigene Bestellungen
'manage orders' // Eigene Bestellungen
]);
// 4. Manufacturer (Hersteller)
$manufacturerRole = Role::create([
'name' => 'Manufacturer',
'display_name' => 'Manufacturer (Hersteller)',
'icon' => 'wrench-screwdriver',
'color' => 'orange',
'can_be_invited' => true
]);
$manufacturerRole->givePermissionTo([
'access dashboard',
'view products',
'create products',
'edit products', // Später eingeschränkt auf EIGENE Produkte
'delete products', // Später eingeschränkt auf EIGENE Produkte
'manage rental options',
'view orders', // Eigene Bestellungen
'manage orders' // Eigene Bestellungen
]);
// 5. Admin (B2In Management / Marcel)
$adminRole = Role::create([
'name' => 'Admin',
'display_name' => 'Admin (Administrator)',
'icon' => 'user-circle',
'color' => 'purple',
'can_be_invited' => false // Admins werden NICHT eingeladen
]);
$adminRole->givePermissionTo([
'access dashboard',
'view hubs',
'create hubs',
'edit hubs',
'delete hubs',
'view partners',
'create partners',
'edit partners',
'delete partners',
'manage provisions',
'view products',
'create products',
'edit products',
'delete products',
'manage rental options',
'view orders',
'manage orders',
'view users',
'manage users',
'manage roles'
]);
// 6. Super-Admin (Entwickler)
// Super-Admins bekommen automatisch ALLE Rechte.
// Das Paket erkennt die Rolle 'Super-Admin', wenn wir ein Gate definieren.
$superAdminRole = Role::create([
'name' => 'Super-Admin',
'display_name' => 'Super-Admin (Entwickler)',
'icon' => 'shield-check',
'color' => 'red',
'can_be_invited' => false // Super-Admins werden NICHT eingeladen
]);
}
}

View file

@ -1,76 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RolesAndPermissionsSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// Rollen
Role::create(['name' => 'superadmin']);
Role::create(['name' => 'admin']);
Role::create(['name' => 'trader']);
Role::create(['name' => 'customer']);
// Beispiel-Permissions
// Trader
Permission::create(['name' => 'products_manage']);
// Customer
Permission::create(['name' => 'orders_view']);
// Admin
// CMS
Permission::create(['name' => 'cms_manage']);
Permission::create(['name' => 'cms_view']);
Permission::create(['name' => 'cms_create']);
Permission::create(['name' => 'cms_delete']);
Permission::create(['name' => 'cms_list']);
// User
Permission::create(['name' => 'user_edit']);
Permission::create(['name' => 'user_view']);
Permission::create(['name' => 'user_create']);
Permission::create(['name' => 'user_delete']);
Permission::create(['name' => 'user_list']);
// Superadmin
// alles
/*Permission::create(['name' => 'products_manage']);
Permission::create(['name' => 'orders_view']);
Permission::create(['name' => 'user_settings']);
Permission::create(['name' => 'system_settings']);
Permission::create(['name' => 'user_manage']);
Permission::create(['name' => 'order_manage']);
Permission::create(['name' => 'product_manage']);
Permission::create(['name' => 'user_view']);
Permission::create(['name' => 'order_view']);
Permission::create(['name' => 'product_view']);
Permission::create(['name' => 'system_view']);
Permission::create(['name' => 'user_create']);
Permission::create(['name' => 'order_create']);
Permission::create(['name' => 'product_create']);
Permission::create(['name' => 'system_create']);
Permission::create(['name' => 'order_edit']);
Permission::create(['name' => 'product_edit']);
Permission::create(['name' => 'system_edit']);
Permission::create(['name' => 'user_delete']);
Permission::create(['name' => 'order_delete']);
Permission::create(['name' => 'product_delete']);
Permission::create(['name' => 'system_delete']);
Permission::create(['name' => 'user_list']);
Permission::create(['name' => 'order_list']);
Permission::create(['name' => 'product_list']);
Permission::create(['name' => 'system_list']);*/
}
}