Added the support for generating docs from template doc
This commit is contained in:
@@ -17,7 +17,8 @@ class ActionFactory extends Factory
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
'name' => $this->faker->unique()->words(2, true),
|
||||
'color_tag' => $this->faker->optional()->safeColorName(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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('roles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('permissions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('permission_role', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('permission_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
$table->unique(['permission_id', 'role_id']);
|
||||
});
|
||||
|
||||
Schema::create('role_user', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('role_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
$table->unique(['role_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('role_user');
|
||||
Schema::dropIfExists('permission_role');
|
||||
Schema::dropIfExists('permissions');
|
||||
Schema::dropIfExists('roles');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
<?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
|
||||
{
|
||||
if (Schema::getConnection()->getDriverName() === 'sqlite') {
|
||||
// Rebuild role_user without surrogate id
|
||||
if (Schema::hasTable('role_user')) {
|
||||
Schema::create('role_user_temp', function (Blueprint $table) {
|
||||
$table->foreignId('role_id');
|
||||
$table->foreignId('user_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
// Copy distinct rows
|
||||
try {
|
||||
\DB::statement('INSERT INTO role_user_temp (role_id, user_id, created_at, updated_at) SELECT role_id, user_id, created_at, updated_at FROM role_user GROUP BY role_id, user_id');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
Schema::drop('role_user');
|
||||
Schema::rename('role_user_temp', 'role_user');
|
||||
// Add composite primary key via raw SQL
|
||||
try {
|
||||
\DB::statement('CREATE UNIQUE INDEX role_user_role_user_unique ON role_user(role_id, user_id)');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
}
|
||||
if (Schema::hasTable('permission_role')) {
|
||||
Schema::create('permission_role_temp', function (Blueprint $table) {
|
||||
$table->foreignId('permission_id');
|
||||
$table->foreignId('role_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
try {
|
||||
\DB::statement('INSERT INTO permission_role_temp (permission_id, role_id, created_at, updated_at) SELECT permission_id, role_id, created_at, updated_at FROM permission_role GROUP BY permission_id, role_id');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
Schema::drop('permission_role');
|
||||
Schema::rename('permission_role_temp', 'permission_role');
|
||||
try {
|
||||
\DB::statement('CREATE UNIQUE INDEX permission_role_permission_role_unique ON permission_role(permission_id, role_id)');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
return; // sqlite path done
|
||||
}
|
||||
// role_user
|
||||
if (Schema::hasColumn('role_user', 'id')) {
|
||||
// Drop id column; Postgres requires dropping pk constraint implicitly named maybe 'role_user_pkey'
|
||||
// Attempt raw drop primary if exists
|
||||
try {
|
||||
\DB::statement('ALTER TABLE role_user DROP CONSTRAINT role_user_pkey');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
Schema::table('role_user', function (Blueprint $table) {
|
||||
$table->dropColumn('id');
|
||||
});
|
||||
}
|
||||
// Add composite primary key if not present
|
||||
try {
|
||||
\DB::statement('ALTER TABLE role_user ADD PRIMARY KEY (role_id, user_id)');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
|
||||
// permission_role
|
||||
if (Schema::hasColumn('permission_role', 'id')) {
|
||||
try {
|
||||
\DB::statement('ALTER TABLE permission_role DROP CONSTRAINT permission_role_pkey');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
Schema::table('permission_role', function (Blueprint $table) {
|
||||
$table->dropColumn('id');
|
||||
});
|
||||
}
|
||||
try {
|
||||
\DB::statement('ALTER TABLE permission_role ADD PRIMARY KEY (permission_id, role_id)');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::getConnection()->getDriverName() === 'sqlite') {
|
||||
// Recreate tables with id column again (best-effort)
|
||||
if (Schema::hasTable('role_user')) {
|
||||
Schema::create('role_user_orig', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('role_id');
|
||||
$table->foreignId('user_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
try {
|
||||
\DB::statement('INSERT INTO role_user_orig (role_id, user_id, created_at, updated_at) SELECT role_id, user_id, created_at, updated_at FROM role_user');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
Schema::drop('role_user');
|
||||
Schema::rename('role_user_orig', 'role_user');
|
||||
try {
|
||||
\DB::statement('CREATE UNIQUE INDEX role_user_role_user_unique ON role_user(role_id, user_id)');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
}
|
||||
if (Schema::hasTable('permission_role')) {
|
||||
Schema::create('permission_role_orig', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('permission_id');
|
||||
$table->foreignId('role_id');
|
||||
$table->timestamps();
|
||||
});
|
||||
try {
|
||||
\DB::statement('INSERT INTO permission_role_orig (permission_id, role_id, created_at, updated_at) SELECT permission_id, role_id, created_at, updated_at FROM permission_role');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
Schema::drop('permission_role');
|
||||
Schema::rename('permission_role_orig', 'permission_role');
|
||||
try {
|
||||
\DB::statement('CREATE UNIQUE INDEX permission_role_permission_role_unique ON permission_role(permission_id, role_id)');
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// Re-add id columns (simple auto increment) and drop composite PKs
|
||||
Schema::table('role_user', function (Blueprint $table) {
|
||||
try {
|
||||
$table->dropPrimary();
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
if (! Schema::hasColumn('role_user', 'id')) {
|
||||
$table->id()->first();
|
||||
}
|
||||
$table->unique(['role_id', 'user_id']);
|
||||
});
|
||||
Schema::table('permission_role', function (Blueprint $table) {
|
||||
try {
|
||||
$table->dropPrimary();
|
||||
} catch (Throwable $e) { /* ignore */
|
||||
}
|
||||
if (! Schema::hasColumn('permission_role', 'id')) {
|
||||
$table->id()->first();
|
||||
}
|
||||
$table->unique(['permission_id', 'role_id']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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('document_templates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('custom_name')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('core_entity'); // e.g. 'contract'
|
||||
$table->json('entities'); // list of related entities allowed
|
||||
$table->json('columns'); // map entity => allowed columns
|
||||
$table->unsignedInteger('version')->default(1);
|
||||
$table->string('engine')->default('tokens');
|
||||
$table->string('file_path');
|
||||
$table->string('file_hash', 64);
|
||||
$table->unsignedBigInteger('file_size');
|
||||
$table->string('mime_type', 120);
|
||||
$table->boolean('active')->default(true);
|
||||
$table->foreignId('created_by')->constrained('users');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users');
|
||||
$table->timestamps();
|
||||
$table->index(['core_entity', 'active']);
|
||||
});
|
||||
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('documents', 'template_id')) {
|
||||
$table->foreignId('template_id')->nullable()->after('uuid')->constrained('document_templates');
|
||||
}
|
||||
if (! Schema::hasColumn('documents', 'template_version')) {
|
||||
$table->unsignedInteger('template_version')->nullable()->after('template_id');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('documents', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('documents', 'template_version')) {
|
||||
$table->dropColumn('template_version');
|
||||
}
|
||||
if (Schema::hasColumn('documents', 'template_id')) {
|
||||
$table->dropConstrainedForeignId('template_id');
|
||||
}
|
||||
});
|
||||
Schema::dropIfExists('document_templates');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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('document_templates', function (Blueprint $table) {
|
||||
$table->json('tokens')->nullable()->after('columns');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('document_templates', function (Blueprint $table) {
|
||||
$table->dropColumn('tokens');
|
||||
});
|
||||
}
|
||||
};
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?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('document_templates', function (Blueprint $table) {
|
||||
$table->string('output_filename_pattern')->nullable()->after('file_size');
|
||||
$table->string('date_format')->nullable()->after('output_filename_pattern');
|
||||
$table->boolean('fail_on_unresolved')->default(true)->after('date_format');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('document_templates', function (Blueprint $table) {
|
||||
$table->dropColumn(['output_filename_pattern', 'date_format', 'fail_on_unresolved']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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('document_generation_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('document_id')->constrained('documents')->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->string('ip')->nullable();
|
||||
$table->string('user_agent')->nullable();
|
||||
$table->json('context')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('document_generation_logs');
|
||||
}
|
||||
};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?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('document_templates', function (Blueprint $table) {
|
||||
$table->json('formatting_options')->nullable()->after('fail_on_unresolved');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('document_templates', function (Blueprint $table) {
|
||||
$table->dropColumn('formatting_options');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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('document_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('file_name_pattern')->nullable();
|
||||
$table->string('date_format')->nullable();
|
||||
$table->string('unresolved_policy')->nullable();
|
||||
$table->boolean('preview_enabled')->default(true);
|
||||
$table->json('whitelist')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('document_settings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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('document_settings', function (Blueprint $table) {
|
||||
$table->json('date_formats')->nullable()->after('whitelist');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('document_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('date_formats');
|
||||
});
|
||||
}
|
||||
};
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?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('document_templates', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('document_templates', 'meta')) {
|
||||
$table->json('meta')->nullable()->after('formatting_options');
|
||||
}
|
||||
if (! Schema::hasColumn('document_templates', 'action_id')) {
|
||||
$table->foreignId('action_id')->nullable()->after('meta')->constrained()->nullOnDelete();
|
||||
}
|
||||
if (! Schema::hasColumn('document_templates', 'decision_id')) {
|
||||
$table->foreignId('decision_id')->nullable()->after('action_id')->constrained()->nullOnDelete();
|
||||
}
|
||||
if (! Schema::hasColumn('document_templates', 'activity_note_template')) {
|
||||
$table->text('activity_note_template')->nullable()->after('decision_id');
|
||||
}
|
||||
$table->index(['action_id', 'decision_id'], 'document_templates_action_decision_idx');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('document_templates', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('document_templates', 'activity_note_template')) {
|
||||
$table->dropColumn('activity_note_template');
|
||||
}
|
||||
if (Schema::hasColumn('document_templates', 'decision_id')) {
|
||||
$table->dropConstrainedForeignId('decision_id');
|
||||
}
|
||||
if (Schema::hasColumn('document_templates', 'action_id')) {
|
||||
$table->dropConstrainedForeignId('action_id');
|
||||
}
|
||||
if (Schema::hasColumn('document_templates', 'meta')) {
|
||||
$table->dropColumn('meta');
|
||||
}
|
||||
if (Schema::hasColumn('document_templates', 'action_id') || Schema::hasColumn('document_templates', 'decision_id')) {
|
||||
$table->dropIndex('document_templates_action_decision_idx');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Models\Role;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class RolePermissionSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// Define a baseline set of permissions aligned with Jetstream's default tokens
|
||||
$permissions = collect([
|
||||
['slug' => 'create', 'name' => 'Create'],
|
||||
['slug' => 'read', 'name' => 'Read'],
|
||||
['slug' => 'update', 'name' => 'Update'],
|
||||
['slug' => 'delete', 'name' => 'Delete'],
|
||||
['slug' => 'manage-settings', 'name' => 'Manage Settings'],
|
||||
['slug' => 'manage-imports', 'name' => 'Manage Imports'],
|
||||
['slug' => 'manage-document-templates', 'name' => 'Manage Document Templates'],
|
||||
]);
|
||||
|
||||
$permissions->each(function ($perm) {
|
||||
Permission::firstOrCreate(['slug' => $perm['slug']], [
|
||||
'name' => $perm['name'],
|
||||
'description' => $perm['name'].' permission',
|
||||
]);
|
||||
});
|
||||
|
||||
$admin = Role::firstOrCreate(['slug' => 'admin'], [
|
||||
'name' => 'Administrator',
|
||||
'description' => 'Full access to all features',
|
||||
]);
|
||||
$staff = Role::firstOrCreate(['slug' => 'staff'], [
|
||||
'name' => 'Staff',
|
||||
'description' => 'Standard internal user',
|
||||
]);
|
||||
$viewer = Role::firstOrCreate(['slug' => 'viewer'], [
|
||||
'name' => 'Viewer',
|
||||
'description' => 'Read-only access',
|
||||
]);
|
||||
|
||||
// Attach permissions
|
||||
$admin->permissions()->sync(Permission::pluck('id'));
|
||||
$staff->permissions()->sync(Permission::whereIn('slug', ['create', 'read', 'update'])->pluck('id'));
|
||||
$viewer->permissions()->sync(Permission::where('slug', 'read')->pluck('id'));
|
||||
|
||||
// Optionally ensure first user is admin
|
||||
$firstUser = User::query()->orderBy('id')->first();
|
||||
if ($firstUser && ! $firstUser->roles()->where('roles.id', $admin->id)->exists()) {
|
||||
$firstUser->roles()->attach($admin->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user