feat: Introduce client management and integration updates
- Added client_id field to StoreTenantRequest and UpdateTenantRequest for tenant management. - Updated TenantResource to include client_id in the response. - Created migration to establish clients table and link tenants to clients. - Migrated existing tenant integrations to client_integrations table. - Grouped specific tenants under a shared client (OnTicket) in a new migration. - Updated seeders to reflect new client structure and relationships. - Adjusted integration configurations to require client instead of tenant. - Added tests for client integration functionality and ensured existing tests reflect the new client structure.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('clients', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('code')->unique();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->foreignId('client_id')->nullable()->after('id')->constrained('clients')->restrictOnDelete();
|
||||
});
|
||||
|
||||
DB::table('tenants')
|
||||
->select(['id', 'codigo', 'nombre', 'created_at', 'updated_at'])
|
||||
->orderBy('id')
|
||||
->each(function (object $tenant): void {
|
||||
$clientId = DB::table('clients')->insertGetId([
|
||||
'code' => $tenant->codigo,
|
||||
'name' => $tenant->nombre,
|
||||
'created_at' => $tenant->created_at ?? now(),
|
||||
'updated_at' => $tenant->updated_at ?? now(),
|
||||
]);
|
||||
|
||||
DB::table('tenants')->where('id', $tenant->id)->update(['client_id' => $clientId]);
|
||||
});
|
||||
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->unsignedBigInteger('client_id')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tenants', function (Blueprint $table): void {
|
||||
$table->dropConstrainedForeignId('client_id');
|
||||
});
|
||||
|
||||
Schema::dropIfExists('clients');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('client_integrations', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->foreignId('client_id')->constrained('clients')->cascadeOnDelete();
|
||||
$table->string('integration_code');
|
||||
$table->longText('integration_data')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('integration_code')
|
||||
->references('integration_code')
|
||||
->on('integrations')
|
||||
->cascadeOnDelete();
|
||||
$table->unique(['client_id', 'integration_code']);
|
||||
});
|
||||
|
||||
DB::table('tenant_integration')
|
||||
->join('tenants', 'tenants.codigo', '=', 'tenant_integration.tenant_code')
|
||||
->select([
|
||||
'tenants.client_id',
|
||||
'tenant_integration.integration_code',
|
||||
'tenant_integration.integration_data',
|
||||
'tenant_integration.created_at',
|
||||
'tenant_integration.updated_at',
|
||||
])
|
||||
->orderBy('tenant_integration.id')
|
||||
->each(function (object $configuration): void {
|
||||
DB::table('client_integrations')->updateOrInsert(
|
||||
[
|
||||
'client_id' => $configuration->client_id,
|
||||
'integration_code' => $configuration->integration_code,
|
||||
],
|
||||
[
|
||||
'integration_data' => $configuration->integration_data,
|
||||
'created_at' => $configuration->created_at,
|
||||
'updated_at' => $configuration->updated_at,
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
Schema::table('integrations', function (Blueprint $table): void {
|
||||
$table->boolean('requires_client_configuration')->default(true);
|
||||
});
|
||||
|
||||
DB::table('integrations')->update([
|
||||
'requires_client_configuration' => DB::raw('requires_tenant_configuration'),
|
||||
]);
|
||||
|
||||
Schema::table('integrations', function (Blueprint $table): void {
|
||||
$table->dropColumn('requires_tenant_configuration');
|
||||
});
|
||||
|
||||
Schema::dropIfExists('tenant_integration');
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('integrations', function (Blueprint $table): void {
|
||||
$table->boolean('requires_tenant_configuration')->default(true);
|
||||
});
|
||||
|
||||
DB::table('integrations')->update([
|
||||
'requires_tenant_configuration' => DB::raw('requires_client_configuration'),
|
||||
]);
|
||||
|
||||
Schema::table('integrations', function (Blueprint $table): void {
|
||||
$table->dropColumn('requires_client_configuration');
|
||||
});
|
||||
|
||||
Schema::create('tenant_integration', function (Blueprint $table): void {
|
||||
$table->id();
|
||||
$table->string('integration_code');
|
||||
$table->string('tenant_code');
|
||||
$table->longText('integration_data')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('integration_code')->references('integration_code')->on('integrations')->cascadeOnDelete();
|
||||
$table->foreign('tenant_code')->references('codigo')->on('tenants')->cascadeOnDelete();
|
||||
$table->unique(['integration_code', 'tenant_code']);
|
||||
});
|
||||
|
||||
DB::table('client_integrations')
|
||||
->join('tenants', 'tenants.client_id', '=', 'client_integrations.client_id')
|
||||
->select([
|
||||
'tenants.codigo as tenant_code',
|
||||
'client_integrations.integration_code',
|
||||
'client_integrations.integration_data',
|
||||
'client_integrations.created_at',
|
||||
'client_integrations.updated_at',
|
||||
])
|
||||
->orderBy('client_integrations.id')
|
||||
->each(function (object $configuration): void {
|
||||
DB::table('tenant_integration')->insert((array) $configuration);
|
||||
});
|
||||
|
||||
Schema::dropIfExists('client_integrations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
private const CLIENT_CODE = 'onticket';
|
||||
|
||||
private const TENANT_CODES = [
|
||||
'fiesta_futbol_infantil',
|
||||
'desfile_pura_tendencia',
|
||||
];
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
$now = now();
|
||||
|
||||
$clientId = DB::table('clients')->where('code', self::CLIENT_CODE)->value('id');
|
||||
|
||||
if ($clientId === null) {
|
||||
$clientId = DB::table('clients')->insertGetId([
|
||||
'code' => self::CLIENT_CODE,
|
||||
'name' => 'OnTicket',
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
} else {
|
||||
DB::table('clients')->where('id', $clientId)->update([
|
||||
'name' => 'OnTicket',
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
$tenants = DB::table('tenants')
|
||||
->whereIn('codigo', self::TENANT_CODES)
|
||||
->orderByRaw("CASE codigo WHEN 'fiesta_futbol_infantil' THEN 0 ELSE 1 END")
|
||||
->get(['codigo', 'client_id']);
|
||||
|
||||
foreach ($tenants as $tenant) {
|
||||
DB::table('client_integrations')
|
||||
->where('client_id', $tenant->client_id)
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->each(function (object $integration) use ($clientId): void {
|
||||
DB::table('client_integrations')->insertOrIgnore([
|
||||
'client_id' => $clientId,
|
||||
'integration_code' => $integration->integration_code,
|
||||
'integration_data' => $integration->integration_data,
|
||||
'created_at' => $integration->created_at,
|
||||
'updated_at' => $integration->updated_at,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
DB::table('tenants')
|
||||
->whereIn('codigo', self::TENANT_CODES)
|
||||
->update(['client_id' => $clientId]);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
foreach (self::TENANT_CODES as $tenantCode) {
|
||||
$tenant = DB::table('tenants')->where('codigo', $tenantCode)->first(['id']);
|
||||
|
||||
if (! $tenant) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DB::table('clients')->insertOrIgnore([
|
||||
'code' => $tenantCode,
|
||||
'name' => $tenantCode === 'fiesta_futbol_infantil'
|
||||
? 'Fiesta Fútbol Infantil'
|
||||
: 'Desfile Pura Tendencia',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$individualClientId = DB::table('clients')->where('code', $tenantCode)->value('id');
|
||||
$sharedClientId = DB::table('clients')->where('code', self::CLIENT_CODE)->value('id');
|
||||
|
||||
DB::table('client_integrations')
|
||||
->where('client_id', $sharedClientId)
|
||||
->orderBy('id')
|
||||
->get()
|
||||
->each(function (object $integration) use ($individualClientId): void {
|
||||
DB::table('client_integrations')->insertOrIgnore([
|
||||
'client_id' => $individualClientId,
|
||||
'integration_code' => $integration->integration_code,
|
||||
'integration_data' => $integration->integration_data,
|
||||
'created_at' => $integration->created_at,
|
||||
'updated_at' => $integration->updated_at,
|
||||
]);
|
||||
});
|
||||
|
||||
DB::table('tenants')->where('id', $tenant->id)->update([
|
||||
'client_id' => $individualClientId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -11,6 +11,7 @@ use App\Domains\Catalog\Models\Attribute;
|
||||
use App\Domains\Catalog\Models\CatalogItem;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Services\CatalogService;
|
||||
use App\Domains\Client\Models\Client;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Services\TenantService;
|
||||
use Illuminate\Database\Seeder;
|
||||
@@ -20,6 +21,8 @@ use RuntimeException;
|
||||
|
||||
class DesfilePuraTendenciaSeeder extends Seeder
|
||||
{
|
||||
private const ONTICKET_CLIENT_CODE = 'onticket';
|
||||
|
||||
private const TENANT_CODE = 'desfile_pura_tendencia';
|
||||
|
||||
public function __construct(
|
||||
@@ -29,20 +32,30 @@ class DesfilePuraTendenciaSeeder extends Seeder
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
if (Tenant::query()->where('codigo', self::TENANT_CODE)->exists()) {
|
||||
$client = Client::query()->firstOrCreate(
|
||||
['code' => self::ONTICKET_CLIENT_CODE],
|
||||
['name' => 'OnTicket'],
|
||||
);
|
||||
|
||||
$tenant = Tenant::query()->where('codigo', self::TENANT_CODE)->first();
|
||||
|
||||
if ($tenant) {
|
||||
$tenant->update(['client_id' => $client->id]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DB::transaction(function (): void {
|
||||
$this->createTenant();
|
||||
DB::transaction(function () use ($client): void {
|
||||
$this->createTenant($client);
|
||||
$this->createEventDate();
|
||||
$this->createEntryCatalog();
|
||||
});
|
||||
}
|
||||
|
||||
private function createTenant(): void
|
||||
private function createTenant(Client $client): void
|
||||
{
|
||||
$this->tenantService->create([
|
||||
'client_id' => $client->id,
|
||||
'codigo' => self::TENANT_CODE,
|
||||
'nombre' => 'Desfile Pura Tendencia',
|
||||
'dominio' => 'desfile-pura-tendencia.localhost',
|
||||
|
||||
@@ -17,7 +17,7 @@ class EmailIntegrationSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Email',
|
||||
'url' => null,
|
||||
'requires_tenant_configuration' => false,
|
||||
'requires_client_configuration' => false,
|
||||
'integration_data_schema' => [
|
||||
'MAIL_MAILER' => 'required|string|in:smtp',
|
||||
'MAIL_SCHEME' => 'required|string|in:smtp',
|
||||
|
||||
@@ -17,11 +17,11 @@ class TelepagosIntegrationSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Telepagos',
|
||||
'url' => 'https://api.telepagos.com.ar',
|
||||
'requires_tenant_configuration' => true,
|
||||
'requires_client_configuration' => true,
|
||||
'integration_data_schema' => [
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
]
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
@@ -30,11 +30,11 @@ class TelepagosIntegrationSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Telepagos Homologación',
|
||||
'url' => 'https://api.homo.telepagos.com.ar',
|
||||
'requires_tenant_configuration' => true,
|
||||
'requires_client_configuration' => true,
|
||||
'integration_data_schema' => [
|
||||
'username' => 'required|string',
|
||||
'password' => 'required|string',
|
||||
]
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Database\Seeders;
|
||||
|
||||
use App\Domains\Attachable\Models\Attachment;
|
||||
use App\Domains\Attachable\Services\AttachmentService;
|
||||
use App\Domains\Client\Models\Client;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Domains\Tenant\Services\TenantService;
|
||||
use Illuminate\Database\Seeder;
|
||||
@@ -13,6 +14,8 @@ use Throwable;
|
||||
|
||||
class TenantSeeder extends Seeder
|
||||
{
|
||||
private const ONTICKET_CLIENT_CODE = 'onticket';
|
||||
|
||||
private const SOCIAL_MEDIA = [
|
||||
[
|
||||
'code' => 'instagram',
|
||||
@@ -40,6 +43,11 @@ class TenantSeeder extends Seeder
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$onTicketClient = Client::query()->firstOrCreate(
|
||||
['code' => self::ONTICKET_CLIENT_CODE],
|
||||
['name' => 'OnTicket'],
|
||||
);
|
||||
|
||||
$this->deleteTenant('sonder');
|
||||
|
||||
Tenant::query()
|
||||
@@ -101,6 +109,7 @@ class TenantSeeder extends Seeder
|
||||
->delete();
|
||||
|
||||
$this->tenantService->create([
|
||||
'client_id' => $onTicketClient->id,
|
||||
'codigo' => 'fiesta_futbol_infantil',
|
||||
'nombre' => 'Fiesta Fútbol Infantil',
|
||||
'dominio' => $fiestaDomain,
|
||||
|
||||
Reference in New Issue
Block a user