initial commit

This commit is contained in:
bastien
2026-06-28 12:29:53 +02:00
parent 03e85d2f9d
commit b2443b654c
220 changed files with 24969 additions and 1 deletions
@@ -0,0 +1,51 @@
<?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('users', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->string('first_name', 100);
$table->string('last_name', 100);
$table->string('email')->unique();
$table->string('phone', 20)->unique()->nullable();
$table->date('birthdate')->nullable();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('locale', 10)->default('fr');
$table->boolean('is_active')->default(true);
$table->timestamp('anonymized_at')->nullable();
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
@@ -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('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->bigInteger('expiration')->index();
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->bigInteger('expiration')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};
@@ -0,0 +1,57 @@
<?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('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedSmallInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->string('connection', 100);
$table->string('queue', 100);
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent()->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};
@@ -0,0 +1,328 @@
<?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('organizations', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->string('name', 200);
$table->string('code', 50)->unique()->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
Schema::create('establishments', function (Blueprint $table) {
$table->id();
$table->foreignId('organization_id')->constrained();
$table->uuid('uuid')->unique();
$table->string('name', 200);
$table->text('address');
$table->string('city', 100)->nullable();
$table->string('zip_code', 10)->nullable();
$table->decimal('latitude', 10, 8)->nullable();
$table->decimal('longitude', 11, 8)->nullable();
$table->string('timezone', 50)->default('Europe/Paris');
$table->boolean('is_active')->default(true);
$table->timestamps();
});
Schema::create('supervisors', function (Blueprint $table) {
$table->id();
$table->foreignId('organization_id')->constrained();
$table->foreignId('establishment_id')->nullable()->constrained();
$table->uuid('uuid')->unique();
$table->string('first_name', 100);
$table->string('last_name', 100);
$table->string('email')->unique();
$table->string('password');
$table->enum('role', ['platform_admin', 'owner', 'manager', 'viewer'])->default('manager');
$table->boolean('is_active')->default(true);
$table->timestamp('last_login_at')->nullable();
$table->rememberToken();
$table->timestamps();
});
Schema::create('gdpr_consents', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->enum('type', ['data_processing', 'marketing', 'analytics']);
$table->boolean('accepted');
$table->string('policy_version', 50);
$table->string('policy_text_hash');
$table->enum('source', ['mobile', 'web', 'backoffice']);
$table->string('consent_language', 10)->default('fr');
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->timestamp('accepted_at');
$table->timestamp('revoked_at')->nullable();
});
Schema::create('user_devices', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->enum('platform', ['android', 'ios', 'web']);
$table->string('push_token')->unique();
$table->string('app_version', 50)->nullable();
$table->string('device_name', 100)->nullable();
$table->timestamp('last_seen_at')->nullable();
$table->timestamp('revoked_at')->nullable();
$table->timestamps();
});
Schema::create('notification_preferences', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->unique()->constrained();
$table->boolean('transaction_enabled')->default(true);
$table->boolean('reminder_enabled')->default(true);
$table->boolean('marketing_enabled')->default(false);
$table->boolean('system_enabled')->default(true);
$table->timestamps();
});
Schema::create('machines', function (Blueprint $table) {
$table->id();
$table->foreignId('establishment_id')->constrained();
$table->uuid('uuid')->unique();
$table->string('name', 100);
$table->enum('type', ['washer_small', 'washer_large', 'dryer_small', 'dryer_large']);
$table->string('qr_code')->unique();
$table->enum('status', ['available', 'reserved', 'running', 'maintenance', 'offline', 'error'])->default('available');
$table->foreignId('current_user_id')->nullable()->constrained('users');
$table->timestamp('cycle_started_at')->nullable();
$table->timestamp('cycle_ends_at')->nullable();
$table->timestamp('last_heartbeat_at')->nullable();
$table->timestamps();
});
Schema::create('machine_integrations', function (Blueprint $table) {
$table->id();
$table->foreignId('machine_id')->constrained();
$table->string('provider', 100);
$table->string('external_machine_id', 100);
$table->string('external_site_id', 100)->nullable();
$table->enum('mode', ['pull', 'push', 'hybrid', 'simulated'])->default('simulated');
$table->json('config')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->unique(['provider', 'external_machine_id']);
});
Schema::create('machine_commands', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->foreignId('machine_id')->constrained();
$table->string('provider', 100);
$table->enum('command_type', ['start_cycle', 'stop_cycle', 'refresh_status']);
$table->json('payload')->nullable();
$table->enum('status', ['pending', 'sent', 'acknowledged', 'failed', 'timeout', 'cancelled'])->default('pending');
$table->string('external_reference')->nullable();
$table->string('correlation_id')->nullable();
$table->foreignId('requested_by_user_id')->nullable()->constrained('users');
$table->foreignId('requested_by_supervisor_id')->nullable()->constrained('supervisors');
$table->timestamp('sent_at')->nullable();
$table->timestamp('responded_at')->nullable();
$table->timestamps();
});
Schema::create('machine_events', function (Blueprint $table) {
$table->id();
$table->foreignId('machine_id')->constrained();
$table->string('provider', 100);
$table->string('external_event_id')->nullable();
$table->enum('event_type', [
'heartbeat', 'machine_online', 'machine_offline',
'cycle_started', 'cycle_completed', 'cycle_failed',
'error_reported', 'status_changed',
]);
$table->json('payload')->nullable();
$table->timestamp('occurred_at');
$table->timestamp('received_at');
$table->timestamp('processed_at')->nullable();
$table->enum('processing_status', ['pending', 'processed', 'failed', 'ignored'])->default('pending');
$table->timestamps();
});
Schema::create('machine_status_history', function (Blueprint $table) {
$table->id();
$table->foreignId('machine_id')->constrained();
$table->string('previous_status', 50)->nullable();
$table->string('new_status', 50);
$table->string('source', 100);
$table->string('reason')->nullable();
$table->timestamp('created_at')->nullable();
});
Schema::create('pricing_rules', function (Blueprint $table) {
$table->id();
$table->foreignId('establishment_id')->constrained();
$table->foreignId('machine_id')->nullable()->constrained();
$table->enum('machine_type', ['washer_small', 'washer_large', 'dryer_small', 'dryer_large'])->nullable();
$table->enum('day_type', ['weekday', 'weekend', 'holiday', 'all'])->default('all');
$table->time('slot_start');
$table->time('slot_end');
$table->decimal('price', 8, 2);
$table->string('label', 100)->nullable();
$table->boolean('requires_app')->default(false);
$table->smallInteger('priority')->default(100);
$table->boolean('is_active')->default(true);
$table->timestamps();
});
Schema::create('promotions', function (Blueprint $table) {
$table->id();
$table->foreignId('establishment_id')->constrained();
$table->enum('machine_type', ['washer_small', 'washer_large', 'dryer_small', 'dryer_large', 'all'])->default('all');
$table->enum('discount_type', ['percent', 'fixed']);
$table->decimal('discount_value', 8, 2);
$table->timestamp('starts_at');
$table->timestamp('ends_at');
$table->text('description')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
Schema::create('wallets', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->unique()->constrained();
$table->char('currency', 3)->default('EUR');
$table->decimal('current_balance', 10, 2)->default(0);
$table->enum('status', ['active', 'blocked', 'closed'])->default('active');
$table->timestamps();
});
Schema::create('payment_transactions', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->foreignId('user_id')->constrained();
$table->string('provider', 50);
$table->string('provider_payment_id')->nullable();
$table->decimal('amount', 10, 2);
$table->char('currency', 3)->default('EUR');
$table->enum('status', ['initiated', 'pending', 'succeeded', 'failed', 'cancelled', 'refunded'])->default('initiated');
$table->string('idempotency_key', 100)->unique();
$table->string('return_url')->nullable();
$table->json('raw_payload')->nullable();
$table->timestamps();
});
Schema::create('wallet_transactions', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->foreignId('wallet_id')->constrained();
$table->enum('type', ['credit', 'debit', 'hold', 'release', 'refund', 'adjustment']);
$table->decimal('amount', 10, 2);
$table->decimal('balance_before', 10, 2);
$table->decimal('balance_after', 10, 2);
$table->string('source_type', 100);
$table->unsignedBigInteger('source_id')->nullable();
$table->string('idempotency_key', 100)->nullable();
$table->json('metadata')->nullable();
$table->timestamp('created_at')->nullable();
});
Schema::create('bookings', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->foreignId('user_id')->constrained();
$table->foreignId('machine_id')->constrained();
$table->timestamp('slot_start');
$table->timestamp('slot_end');
$table->decimal('booking_fee', 8, 2)->default(0);
$table->decimal('reserved_amount', 8, 2)->default(0);
$table->decimal('penalty_amount', 8, 2)->default(0);
$table->enum('status', ['pending', 'confirmed', 'cancelled', 'expired', 'active', 'completed', 'no_show'])->default('pending');
$table->timestamp('cancelled_at')->nullable();
$table->timestamp('penalty_applied_at')->nullable();
$table->timestamps();
});
Schema::create('washes', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->unique();
$table->foreignId('user_id')->constrained();
$table->foreignId('machine_id')->constrained();
$table->foreignId('booking_id')->nullable()->constrained();
$table->foreignId('machine_command_id')->nullable()->constrained();
$table->enum('trigger_method', ['qr_code', 'booking', 'supervisor', 'system']);
$table->enum('status', ['pending_start', 'running', 'completed', 'failed', 'cancelled'])->default('pending_start');
$table->string('program', 100)->nullable();
$table->timestamp('started_at')->nullable();
$table->timestamp('ended_at')->nullable();
$table->unsignedInteger('duration_minutes')->nullable();
$table->decimal('cost', 8, 2);
$table->timestamps();
});
Schema::create('push_notifications', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained();
$table->string('type', 100);
$table->string('title');
$table->text('body');
$table->json('data')->nullable();
$table->enum('status', ['pending', 'sent', 'failed'])->default('pending');
$table->timestamp('sent_at')->nullable();
$table->timestamps();
});
Schema::create('audit_logs', function (Blueprint $table) {
$table->id();
$table->string('actor_type', 50);
$table->unsignedBigInteger('actor_id');
$table->foreignId('organization_id')->nullable()->constrained();
$table->foreignId('establishment_id')->nullable()->constrained();
$table->string('action', 100);
$table->string('target_type', 100);
$table->unsignedBigInteger('target_id')->nullable();
$table->json('before_data')->nullable();
$table->json('after_data')->nullable();
$table->string('ip_address', 45)->nullable();
$table->timestamp('created_at')->nullable();
});
Schema::create('daily_establishment_stats', function (Blueprint $table) {
$table->id();
$table->foreignId('establishment_id')->constrained();
$table->date('stat_date');
$table->unsignedInteger('total_washes')->default(0);
$table->decimal('total_revenue', 10, 2)->default(0);
$table->unsignedInteger('bookings_count')->default(0);
$table->unsignedInteger('no_show_count')->default(0);
$table->decimal('top_up_amount', 10, 2)->default(0);
$table->decimal('occupancy_rate', 5, 2)->default(0);
$table->timestamps();
$table->unique(['establishment_id', 'stat_date']);
});
}
public function down(): void
{
Schema::dropIfExists('daily_establishment_stats');
Schema::dropIfExists('audit_logs');
Schema::dropIfExists('push_notifications');
Schema::dropIfExists('washes');
Schema::dropIfExists('bookings');
Schema::dropIfExists('wallet_transactions');
Schema::dropIfExists('payment_transactions');
Schema::dropIfExists('wallets');
Schema::dropIfExists('promotions');
Schema::dropIfExists('pricing_rules');
Schema::dropIfExists('machine_status_history');
Schema::dropIfExists('machine_events');
Schema::dropIfExists('machine_commands');
Schema::dropIfExists('machine_integrations');
Schema::dropIfExists('machines');
Schema::dropIfExists('notification_preferences');
Schema::dropIfExists('user_devices');
Schema::dropIfExists('gdpr_consents');
Schema::dropIfExists('supervisors');
Schema::dropIfExists('establishments');
Schema::dropIfExists('organizations');
}
};
@@ -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.
*/
public function up(): void
{
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
$table->morphs('tokenable');
$table->text('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('personal_access_tokens');
}
};