# 7. API E INTEGRAZIONI - GUIDA COMPLETA ## πŸ“‹ **INDICE CAPITOLO** - [7.1 Architettura API NetGescon](#71-architettura-api-netgescon) - [7.2 Autenticazione API](#72-autenticazione-api) - [7.3 Endpoint Principali](#73-endpoint-principali) - [7.4 Middleware API](#74-middleware-api) - [7.5 Documentazione API](#75-documentazione-api) - [7.6 Integrazioni Esterne](#76-integrazioni-esterne) - [7.7 Rate Limiting](#77-rate-limiting) - [7.8 Versioning API](#78-versioning-api) - [7.9 Testing API](#79-testing-api) - [7.10 Esempi Pratici](#710-esempi-pratici) --- ## 7.1 Architettura API NetGescon ### Struttura API RESTful ``` /api/v1/ β”œβ”€β”€ auth/ # Autenticazione β”‚ β”œβ”€β”€ login # POST - Login utente β”‚ β”œβ”€β”€ logout # POST - Logout utente β”‚ β”œβ”€β”€ refresh # POST - Refresh token β”‚ └── me # GET - Dati utente corrente β”œβ”€β”€ stabili/ # Gestione stabili β”‚ β”œβ”€β”€ GET / # Lista stabili β”‚ β”œβ”€β”€ POST / # Crea stabile β”‚ β”œβ”€β”€ GET /{id} # Dettaglio stabile β”‚ β”œβ”€β”€ PUT /{id} # Aggiorna stabile β”‚ └── DELETE /{id} # Elimina stabile β”œβ”€β”€ unita/ # UnitΓ  immobiliari β”‚ β”œβ”€β”€ GET / # Lista unitΓ  β”‚ β”œβ”€β”€ POST / # Crea unitΓ  β”‚ β”œβ”€β”€ GET /{id} # Dettaglio unitΓ  β”‚ β”œβ”€β”€ PUT /{id} # Aggiorna unitΓ  β”‚ └── DELETE /{id} # Elimina unitΓ  β”œβ”€β”€ condomini/ # Gestione condomini β”‚ β”œβ”€β”€ GET / # Lista condomini β”‚ β”œβ”€β”€ POST / # Crea condomino β”‚ β”œβ”€β”€ GET /{id} # Dettaglio condomino β”‚ β”œβ”€β”€ PUT /{id} # Aggiorna condomino β”‚ └── DELETE /{id} # Elimina condomino β”œβ”€β”€ contabilita/ # Sistema contabile β”‚ β”œβ”€β”€ movimenti/ # Movimenti contabili β”‚ β”œβ”€β”€ bilanci/ # Bilanci β”‚ └── reports/ # Report finanziari β”œβ”€β”€ documenti/ # Gestione documenti β”‚ β”œβ”€β”€ GET / # Lista documenti β”‚ β”œβ”€β”€ POST / # Upload documento β”‚ β”œβ”€β”€ GET /{id} # Scarica documento β”‚ └── DELETE /{id} # Elimina documento └── comunicazioni/ # Sistema comunicazioni β”œβ”€β”€ GET / # Lista comunicazioni β”œβ”€β”€ POST / # Invia comunicazione └── GET /{id} # Dettaglio comunicazione ``` ### Configurazione Routes API ```php // routes/api.php 'v1'], function () { // Autenticazione (pubbliche) Route::post('auth/login', [AuthController::class, 'login']); Route::post('auth/register', [AuthController::class, 'register']); Route::post('auth/forgot-password', [AuthController::class, 'forgotPassword']); Route::post('auth/reset-password', [AuthController::class, 'resetPassword']); // Route protette da autenticazione Route::middleware('auth:sanctum')->group(function () { // Autenticazione utente Route::post('auth/logout', [AuthController::class, 'logout']); Route::post('auth/refresh', [AuthController::class, 'refresh']); Route::get('auth/me', [AuthController::class, 'me']); // Stabili Route::apiResource('stabili', StabileController::class); Route::get('stabili/{id}/unita', [StabileController::class, 'getUnita']); Route::get('stabili/{id}/condomini', [StabileController::class, 'getCondomini']); // UnitΓ  immobiliari Route::apiResource('unita', UnitaController::class); Route::get('unita/{id}/proprietari', [UnitaController::class, 'getProprietari']); // Condomini Route::apiResource('condomini', CondominiController::class); Route::get('condomini/{id}/unita', [CondominiController::class, 'getUnita']); // ContabilitΓ  Route::prefix('contabilita')->group(function () { Route::apiResource('movimenti', ContabilitaController::class); Route::get('bilanci', [ContabilitaController::class, 'getBilanci']); Route::get('reports', [ContabilitaController::class, 'getReports']); }); // Documenti Route::apiResource('documenti', DocumentiController::class); Route::post('documenti/{id}/upload', [DocumentiController::class, 'upload']); Route::get('documenti/{id}/download', [DocumentiController::class, 'download']); // Comunicazioni Route::apiResource('comunicazioni', ComunicazioniController::class); Route::post('comunicazioni/{id}/read', [ComunicazioniController::class, 'markAsRead']); }); }); ``` --- ## 7.2 Autenticazione API ### Laravel Sanctum Setup ```php // config/sanctum.php explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '' ))), 'guard' => ['web'], 'expiration' => null, 'middleware' => [ 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, ], ]; ``` ### Auth Controller ```php // app/Http/Controllers/Api/AuthController.php all(), [ 'email' => 'required|email', 'password' => 'required|string|min:6', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation Error', 'errors' => $validator->errors() ], 400); } $user = User::where('email', $request->email)->first(); if (!$user || !Hash::check($request->password, $user->password)) { return response()->json([ 'success' => false, 'message' => 'Invalid credentials' ], 401); } if (!$user->is_active) { return response()->json([ 'success' => false, 'message' => 'Account not active' ], 401); } $token = $user->createToken('api-token')->plainTextToken; // Aggiorna ultimo login $user->last_login_at = now(); $user->save(); return response()->json([ 'success' => true, 'message' => 'Login successful', 'data' => [ 'user' => $user, 'token' => $token, 'token_type' => 'Bearer' ] ]); } /** * Logout utente */ public function logout(Request $request) { $request->user()->currentAccessToken()->delete(); return response()->json([ 'success' => true, 'message' => 'Logout successful' ]); } /** * Informazioni utente corrente */ public function me(Request $request) { $user = $request->user(); $user->load('roles', 'permissions'); return response()->json([ 'success' => true, 'data' => [ 'user' => $user, 'roles' => $user->roles->pluck('name'), 'permissions' => $user->getAllPermissions()->pluck('name') ] ]); } /** * Refresh token */ public function refresh(Request $request) { $user = $request->user(); $user->currentAccessToken()->delete(); $token = $user->createToken('api-token')->plainTextToken; return response()->json([ 'success' => true, 'data' => [ 'token' => $token, 'token_type' => 'Bearer' ] ]); } } ``` --- ## 7.3 Endpoint Principali ### Stabili API Controller ```php // app/Http/Controllers/Api/StabileController.php has('search')) { $search = $request->search; $query->where(function($q) use ($search) { $q->where('denominazione', 'like', "%{$search}%") ->orWhere('indirizzo', 'like', "%{$search}%") ->orWhere('codice_fiscale', 'like', "%{$search}%"); }); } if ($request->has('comune_id')) { $query->where('comune_id', $request->comune_id); } // Paginazione $per_page = $request->get('per_page', 15); $stabili = $query->paginate($per_page); return response()->json([ 'success' => true, 'data' => $stabili ]); } /** * Crea stabile */ public function store(Request $request) { $validator = Validator::make($request->all(), [ 'denominazione' => 'required|string|max:255', 'indirizzo' => 'required|string|max:255', 'codice_fiscale' => 'required|string|size:16|unique:stabili', 'comune_id' => 'required|exists:comuni_italiani,id', 'amministratore_id' => 'required|exists:users,id', 'data_nomina' => 'required|date', 'piano_terra' => 'required|integer|min:0', 'piano_primo' => 'required|integer|min:0', 'piano_secondo' => 'required|integer|min:0', 'piano_terzo' => 'required|integer|min:0', 'piano_quarto' => 'required|integer|min:0', 'piano_quinto' => 'required|integer|min:0', 'piano_sesto' => 'required|integer|min:0', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation Error', 'errors' => $validator->errors() ], 400); } $stabile = Stabile::create($request->all()); $stabile->load(['comune', 'amministratore']); return response()->json([ 'success' => true, 'message' => 'Stabile created successfully', 'data' => $stabile ], 201); } /** * Dettaglio stabile */ public function show($id) { $stabile = Stabile::with([ 'comune', 'amministratore', 'unita_immobiliari', 'condomini' ])->find($id); if (!$stabile) { return response()->json([ 'success' => false, 'message' => 'Stabile not found' ], 404); } return response()->json([ 'success' => true, 'data' => $stabile ]); } /** * Aggiorna stabile */ public function update(Request $request, $id) { $stabile = Stabile::find($id); if (!$stabile) { return response()->json([ 'success' => false, 'message' => 'Stabile not found' ], 404); } $validator = Validator::make($request->all(), [ 'denominazione' => 'sometimes|required|string|max:255', 'indirizzo' => 'sometimes|required|string|max:255', 'codice_fiscale' => 'sometimes|required|string|size:16|unique:stabili,codice_fiscale,' . $id, 'comune_id' => 'sometimes|required|exists:comuni_italiani,id', 'amministratore_id' => 'sometimes|required|exists:users,id', 'data_nomina' => 'sometimes|required|date', 'piano_terra' => 'sometimes|required|integer|min:0', 'piano_primo' => 'sometimes|required|integer|min:0', 'piano_secondo' => 'sometimes|required|integer|min:0', 'piano_terzo' => 'sometimes|required|integer|min:0', 'piano_quarto' => 'sometimes|required|integer|min:0', 'piano_quinto' => 'sometimes|required|integer|min:0', 'piano_sesto' => 'sometimes|required|integer|min:0', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation Error', 'errors' => $validator->errors() ], 400); } $stabile->update($request->all()); $stabile->load(['comune', 'amministratore']); return response()->json([ 'success' => true, 'message' => 'Stabile updated successfully', 'data' => $stabile ]); } /** * Elimina stabile */ public function destroy($id) { $stabile = Stabile::find($id); if (!$stabile) { return response()->json([ 'success' => false, 'message' => 'Stabile not found' ], 404); } $stabile->delete(); return response()->json([ 'success' => true, 'message' => 'Stabile deleted successfully' ]); } /** * UnitΓ  immobiliari di uno stabile */ public function getUnita($id) { $stabile = Stabile::with(['unita_immobiliari.proprietari'])->find($id); if (!$stabile) { return response()->json([ 'success' => false, 'message' => 'Stabile not found' ], 404); } return response()->json([ 'success' => true, 'data' => $stabile->unita_immobiliari ]); } /** * Condomini di uno stabile */ public function getCondomini($id) { $stabile = Stabile::with(['condomini'])->find($id); if (!$stabile) { return response()->json([ 'success' => false, 'message' => 'Stabile not found' ], 404); } return response()->json([ 'success' => true, 'data' => $stabile->condomini ]); } } ``` --- ## 7.4 Middleware API ### Rate Limiting Middleware ```php // app/Http/Middleware/ApiRateLimit.php resolveRequestSignature($request); if (RateLimiter::tooManyAttempts($key, $maxAttempts)) { return response()->json([ 'success' => false, 'message' => 'Too many requests. Please try again later.', 'retry_after' => RateLimiter::availableIn($key) ], 429); } RateLimiter::hit($key, $decayMinutes * 60); $response = $next($request); return $response->header('X-RateLimit-Limit', $maxAttempts) ->header('X-RateLimit-Remaining', RateLimiter::remainingAttempts($key, $maxAttempts)) ->header('X-RateLimit-Reset', RateLimiter::availableIn($key)); } protected function resolveRequestSignature(Request $request): string { if ($user = $request->user()) { return sha1('api|' . $user->getAuthIdentifier()); } return sha1('api|' . $request->ip()); } } ``` ### API Response Middleware ```php // app/Http/Middleware/ApiResponse.php headers->set('Content-Type', 'application/json'); $response->headers->set('X-API-Version', 'v1'); $response->headers->set('X-Powered-By', 'NetGescon API'); return $response; } } ``` --- ## 7.5 Documentazione API ### OpenAPI/Swagger Setup ```php // config/l5-swagger.php 'default', 'documentations' => [ 'default' => [ 'api' => [ 'title' => 'NetGescon API Documentation', 'version' => '1.0.0', 'description' => 'API completa per la gestione condominiale NetGescon', ], 'routes' => [ 'api' => 'api/documentation', ], 'paths' => [ 'use_absolute_path' => env('L5_SWAGGER_USE_ABSOLUTE_PATH', true), 'docs_json' => 'api-docs.json', 'docs_yaml' => 'api-docs.yaml', 'format_to_use_for_docs' => env('L5_FORMAT_TO_USE_FOR_DOCS', 'json'), 'annotations' => [ base_path('app'), ], ], ], ], 'defaults' => [ 'routes' => [ 'docs' => 'docs', 'oauth2_callback' => 'api/oauth2-callback', 'middleware' => [ 'api' => [], 'asset' => [], 'docs' => [], 'oauth2_callback' => [], ], 'group_options' => [], ], 'paths' => [ 'docs' => storage_path('api-docs'), 'views' => base_path('resources/views/vendor/l5-swagger'), 'base' => env('L5_SWAGGER_BASE_PATH', null), 'swagger_ui_assets_path' => env('L5_SWAGGER_UI_ASSETS_PATH', 'vendor/swagger-api/swagger-ui/dist/'), 'excludes' => [], ], 'scanOptions' => [ 'analyser' => null, 'analysis' => null, 'processors' => [], 'pattern' => null, 'exclude' => [], ], 'securityDefinitions' => [ 'securitySchemes' => [ 'sanctum' => [ 'type' => 'apiKey', 'description' => 'Enter token in format (Bearer )', 'name' => 'Authorization', 'in' => 'header', ], ], 'security' => [ [ 'sanctum' => [] ], ], ], ], ]; ``` ### Annotazioni Swagger ```php // app/Http/Controllers/Api/StabileController.php con annotazioni [ 'base_url' => env('AGENZIA_ENTRATE_BASE_URL', 'https://www.agenziaentrate.gov.it/ws'), 'api_key' => env('AGENZIA_ENTRATE_API_KEY'), 'timeout' => 30, ], 'registro_imprese' => [ 'base_url' => env('REGISTRO_IMPRESE_BASE_URL', 'https://www.registroimprese.it/api'), 'api_key' => env('REGISTRO_IMPRESE_API_KEY'), 'timeout' => 30, ], 'poste_italiane' => [ 'base_url' => env('POSTE_ITALIANE_BASE_URL', 'https://api.posteitaliane.it'), 'api_key' => env('POSTE_ITALIANE_API_KEY'), 'timeout' => 30, ], 'pec_provider' => [ 'base_url' => env('PEC_PROVIDER_BASE_URL'), 'api_key' => env('PEC_PROVIDER_API_KEY'), 'timeout' => 30, ], ]; ``` ### Service per Integrazioni ```php // app/Services/AgenziaEntrateService.php baseUrl = config('services.agenzia_entrate.base_url'); $this->apiKey = config('services.agenzia_entrate.api_key'); $this->timeout = config('services.agenzia_entrate.timeout'); } /** * Verifica codice fiscale */ public function verificaCodiceFiscale($codiceFiscale) { try { $response = Http::timeout($this->timeout) ->withHeaders([ 'Authorization' => 'Bearer ' . $this->apiKey, 'Content-Type' => 'application/json', ]) ->get($this->baseUrl . '/verifica-cf', [ 'codice_fiscale' => $codiceFiscale ]); if ($response->successful()) { return [ 'success' => true, 'data' => $response->json() ]; } return [ 'success' => false, 'message' => 'Errore nella verifica: ' . $response->body() ]; } catch (Exception $e) { Log::error('Errore AgenziaEntrateService::verificaCodiceFiscale', [ 'codice_fiscale' => $codiceFiscale, 'error' => $e->getMessage() ]); return [ 'success' => false, 'message' => 'Errore di connessione al servizio' ]; } } /** * Verifica partita IVA */ public function verificaPartitaIva($partitaIva) { try { $response = Http::timeout($this->timeout) ->withHeaders([ 'Authorization' => 'Bearer ' . $this->apiKey, 'Content-Type' => 'application/json', ]) ->get($this->baseUrl . '/verifica-piva', [ 'partita_iva' => $partitaIva ]); if ($response->successful()) { return [ 'success' => true, 'data' => $response->json() ]; } return [ 'success' => false, 'message' => 'Errore nella verifica: ' . $response->body() ]; } catch (Exception $e) { Log::error('Errore AgenziaEntrateService::verificaPartitaIva', [ 'partita_iva' => $partitaIva, 'error' => $e->getMessage() ]); return [ 'success' => false, 'message' => 'Errore di connessione al servizio' ]; } } } ``` --- ## 7.7 Rate Limiting ### Configurazione Rate Limiting ```php // app/Providers/RouteServiceProvider.php configureRateLimiting(); $this->routes(function () { Route::middleware('api') ->prefix('api') ->group(base_path('routes/api.php')); Route::middleware('web') ->group(base_path('routes/web.php')); }); } protected function configureRateLimiting() { // Rate limiting per API autenticata RateLimiter::for('api', function (Request $request) { return $request->user() ? Limit::perMinute(60)->by($request->user()->id) : Limit::perMinute(20)->by($request->ip()); }); // Rate limiting per login RateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->ip()); }); // Rate limiting per operazioni pesanti RateLimiter::for('heavy-operations', function (Request $request) { return $request->user() ? Limit::perMinute(10)->by($request->user()->id) : Limit::perMinute(2)->by($request->ip()); }); } } ``` ### Middleware Rate Limiting Custom ```php // app/Http/Middleware/CustomRateLimit.php resolveRequestSignature($request); // Controlla se l'utente ha superato il limite if (RateLimiter::tooManyAttempts($key, $this->maxAttempts($limiter))) { return $this->buildResponse($key, $this->maxAttempts($limiter)); } // Incrementa il contatore RateLimiter::hit($key, $this->decayMinutes($limiter) * 60); $response = $next($request); return $this->addHeaders($response, $key, $this->maxAttempts($limiter)); } protected function resolveRequestSignature(Request $request): string { if ($user = $request->user()) { return sha1($request->route()->getName() . '|' . $user->getAuthIdentifier()); } return sha1($request->route()->getName() . '|' . $request->ip()); } protected function maxAttempts(string $limiter): int { return match($limiter) { 'login' => 5, 'heavy-operations' => 10, 'api' => 60, default => 20 }; } protected function decayMinutes(string $limiter): int { return match($limiter) { 'login' => 1, 'heavy-operations' => 5, 'api' => 1, default => 1 }; } protected function buildResponse(string $key, int $maxAttempts): Response { $retryAfter = RateLimiter::availableIn($key); return response()->json([ 'success' => false, 'message' => 'Too Many Attempts', 'retry_after' => $retryAfter, 'max_attempts' => $maxAttempts ], 429); } protected function addHeaders(Response $response, string $key, int $maxAttempts): Response { $response->headers->add([ 'X-RateLimit-Limit' => $maxAttempts, 'X-RateLimit-Remaining' => RateLimiter::remainingAttempts($key, $maxAttempts), 'X-RateLimit-Reset' => RateLimiter::availableIn($key), ]); return $response; } } ``` --- ## 7.8 Versioning API ### Versioning tramite URI ```php // routes/api.php Route::group(['prefix' => 'v1', 'namespace' => 'App\Http\Controllers\Api\V1'], function () { // Route API v1 }); Route::group(['prefix' => 'v2', 'namespace' => 'App\Http\Controllers\Api\V2'], function () { // Route API v2 }); ``` ### Versioning tramite Header ```php // app/Http/Middleware/ApiVersioning.php header('Accept-Version', 'v1'); // Valida versione if (!in_array($version, ['v1', 'v2'])) { return response()->json([ 'success' => false, 'message' => 'Unsupported API version' ], 400); } // Aggiungi versione alla request $request->merge(['api_version' => $version]); $response = $next($request); // Aggiungi header versione alla response $response->headers->set('X-API-Version', $version); return $response; } } ``` --- ## 7.9 Testing API ### Test Feature API ```php // tests/Feature/Api/StabileApiTest.php user = User::factory()->create(); $this->admin = User::factory()->create(); $this->admin->assignRole('admin'); // Crea token per autenticazione $this->token = $this->admin->createToken('test-token')->plainTextToken; } /** @test */ public function it_can_list_stabili() { // Arrange Stabile::factory()->count(5)->create(); // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->get('/api/v1/stabili'); // Assert $response->assertStatus(200) ->assertJsonStructure([ 'success', 'data' => [ 'data' => [ '*' => [ 'id', 'denominazione', 'indirizzo', 'codice_fiscale', 'comune_id', 'amministratore_id', 'created_at', 'updated_at' ] ], 'links', 'meta' ] ]); } /** @test */ public function it_can_create_stabile() { // Arrange $comune = ComuneItaliano::factory()->create(); $stabileData = [ 'denominazione' => 'Condominio Test', 'indirizzo' => 'Via Test 123', 'codice_fiscale' => '12345678901234567890', 'comune_id' => $comune->id, 'amministratore_id' => $this->admin->id, 'data_nomina' => '2024-01-01', 'piano_terra' => 2, 'piano_primo' => 4, 'piano_secondo' => 4, 'piano_terzo' => 0, 'piano_quarto' => 0, 'piano_quinto' => 0, 'piano_sesto' => 0, ]; // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->post('/api/v1/stabili', $stabileData); // Assert $response->assertStatus(201) ->assertJsonStructure([ 'success', 'message', 'data' => [ 'id', 'denominazione', 'indirizzo', 'codice_fiscale', 'comune_id', 'amministratore_id', 'created_at', 'updated_at' ] ]); $this->assertDatabaseHas('stabili', [ 'denominazione' => 'Condominio Test', 'codice_fiscale' => '12345678901234567890' ]); } /** @test */ public function it_validates_stabile_creation() { // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->post('/api/v1/stabili', []); // Assert $response->assertStatus(400) ->assertJsonStructure([ 'success', 'message', 'errors' ]); } /** @test */ public function it_requires_authentication() { // Act $response = $this->withHeaders([ 'Accept' => 'application/json', ])->get('/api/v1/stabili'); // Assert $response->assertStatus(401); } /** @test */ public function it_can_show_stabile() { // Arrange $stabile = Stabile::factory()->create(); // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->get('/api/v1/stabili/' . $stabile->id); // Assert $response->assertStatus(200) ->assertJsonStructure([ 'success', 'data' => [ 'id', 'denominazione', 'indirizzo', 'codice_fiscale', 'comune', 'amministratore', 'unita_immobiliari', 'condomini' ] ]); } /** @test */ public function it_can_update_stabile() { // Arrange $stabile = Stabile::factory()->create(); $updateData = [ 'denominazione' => 'Condominio Aggiornato' ]; // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->put('/api/v1/stabili/' . $stabile->id, $updateData); // Assert $response->assertStatus(200) ->assertJsonStructure([ 'success', 'message', 'data' ]); $this->assertDatabaseHas('stabili', [ 'id' => $stabile->id, 'denominazione' => 'Condominio Aggiornato' ]); } /** @test */ public function it_can_delete_stabile() { // Arrange $stabile = Stabile::factory()->create(); // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->delete('/api/v1/stabili/' . $stabile->id); // Assert $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Stabile deleted successfully' ]); $this->assertSoftDeleted('stabili', [ 'id' => $stabile->id ]); } /** @test */ public function it_handles_not_found_stabile() { // Act $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->token, 'Accept' => 'application/json', ])->get('/api/v1/stabili/999'); // Assert $response->assertStatus(404) ->assertJson([ 'success' => false, 'message' => 'Stabile not found' ]); } } ``` --- ## 7.10 Esempi Pratici ### Client JavaScript ```javascript // js/api-client.js class NetGesconApiClient { constructor(baseUrl, token = null) { this.baseUrl = baseUrl; this.token = token; this.headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' }; if (token) { this.headers['Authorization'] = `Bearer ${token}`; } } async login(email, password) { try { const response = await fetch(`${this.baseUrl}/api/v1/auth/login`, { method: 'POST', headers: this.headers, body: JSON.stringify({ email, password }) }); const data = await response.json(); if (data.success) { this.token = data.data.token; this.headers['Authorization'] = `Bearer ${this.token}`; localStorage.setItem('netgescon_token', this.token); } return data; } catch (error) { console.error('Login error:', error); throw error; } } async logout() { try { const response = await fetch(`${this.baseUrl}/api/v1/auth/logout`, { method: 'POST', headers: this.headers }); if (response.ok) { this.token = null; delete this.headers['Authorization']; localStorage.removeItem('netgescon_token'); } return await response.json(); } catch (error) { console.error('Logout error:', error); throw error; } } async getStabili(params = {}) { try { const queryString = new URLSearchParams(params).toString(); const url = `${this.baseUrl}/api/v1/stabili${queryString ? '?' + queryString : ''}`; const response = await fetch(url, { method: 'GET', headers: this.headers }); return await response.json(); } catch (error) { console.error('Get stabili error:', error); throw error; } } async createStabile(data) { try { const response = await fetch(`${this.baseUrl}/api/v1/stabili`, { method: 'POST', headers: this.headers, body: JSON.stringify(data) }); return await response.json(); } catch (error) { console.error('Create stabile error:', error); throw error; } } async updateStabile(id, data) { try { const response = await fetch(`${this.baseUrl}/api/v1/stabili/${id}`, { method: 'PUT', headers: this.headers, body: JSON.stringify(data) }); return await response.json(); } catch (error) { console.error('Update stabile error:', error); throw error; } } async deleteStabile(id) { try { const response = await fetch(`${this.baseUrl}/api/v1/stabili/${id}`, { method: 'DELETE', headers: this.headers }); return await response.json(); } catch (error) { console.error('Delete stabile error:', error); throw error; } } } // Utilizzo const apiClient = new NetGesconApiClient('http://localhost:8000'); // Login apiClient.login('admin@example.com', 'password') .then(response => { if (response.success) { console.log('Login successful:', response.data.user); } }); // Recupera stabili apiClient.getStabili({ page: 1, per_page: 10, search: 'condominio' }) .then(response => { if (response.success) { console.log('Stabili:', response.data); } }); ``` ### Client cURL ```bash #!/bin/bash # Variabili API_BASE_URL="http://localhost:8000/api/v1" TOKEN="" # Login login() { local email=$1 local password=$2 response=$(curl -s -X POST "$API_BASE_URL/auth/login" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d "{\"email\":\"$email\",\"password\":\"$password\"}") echo "$response" # Estrai token TOKEN=$(echo "$response" | jq -r '.data.token') if [ "$TOKEN" != "null" ]; then echo "Login successful. Token: $TOKEN" else echo "Login failed" fi } # Lista stabili get_stabili() { curl -s -X GET "$API_BASE_URL/stabili" \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" } # Crea stabile create_stabile() { local data=$1 curl -s -X POST "$API_BASE_URL/stabili" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d "$data" } # Esempi di utilizzo echo "=== LOGIN ===" login "admin@example.com" "password" echo -e "\n=== LISTA STABILI ===" get_stabili | jq '.' echo -e "\n=== CREA STABILE ===" stabile_data='{ "denominazione": "Condominio Test", "indirizzo": "Via Test 123", "codice_fiscale": "12345678901234567890", "comune_id": 1, "amministratore_id": 1, "data_nomina": "2024-01-01", "piano_terra": 2, "piano_primo": 4, "piano_secondo": 4, "piano_terzo": 0, "piano_quarto": 0, "piano_quinto": 0, "piano_sesto": 0 }' create_stabile "$stabile_data" | jq '.' ``` --- ## πŸ”§ **CONFIGURAZIONE COMPLETATA** ### βœ… **Caratteristiche Implementate** 1. **Architettura RESTful** completa con versioning 2. **Autenticazione Sanctum** con token management 3. **Endpoint CRUD** per tutte le entitΓ  principali 4. **Middleware** per rate limiting e sicurezza 5. **Documentazione Swagger** automatica 6. **Testing** completo con Feature Tests 7. **Integrazioni esterne** configurabili 8. **Client JavaScript** e esempi cURL ### πŸš€ **Prossimi Passi** 1. Implementare endpoint per contabilitΓ  2. Aggiungere webhook per notifiche 3. Completare documentazione Swagger 4. Implementare cache per performance 5. Aggiungere monitoring e logging ### πŸ“š **Collegamenti Utili** - **Capitolo 4**: Database e Strutture - **Capitolo 5**: Interfaccia Universale - **Capitolo 6**: Sistema Multi-Ruolo - **Capitolo 8**: Frontend e UX --- ### 🎯 **SISTEMA IMPORT MDB IMPLEMENTATO** Il sistema di import automatico Γ¨ ora **completamente configurato** per connessione diretta al database **stabili.mdb**: #### πŸ“ **Struttura Implementata:** ``` 000-IMPORT/ β”œβ”€β”€ README-IMPORT-PROCESS.md # Guida processo completo β”œβ”€β”€ 01-GESCON/ # Schema e dati import β”‚ β”œβ”€β”€ amministratori.json # βœ… 1 amministratore (Tordini) β”‚ β”œβ”€β”€ stabili.json # βœ… 7 stabili collegati β”‚ └── stabili-mdb-schema.json # βœ… Schema completo tabella MDB β”œβ”€β”€ 02-MAPPING/ # Mappatura campi β”‚ └── campo-mapping.json # βœ… Mapping Gesconβ†’NetGescon β”œβ”€β”€ 02-MDB-CONNECTION/ # βœ… Connessione database MDB β”‚ └── mdb-import-guide.md # βœ… Guida completa import MDB └── 03-SCRIPTS/ # Script automatici β”œβ”€β”€ validation-checker.js # βœ… Validazione pre-import β”œβ”€β”€ import-runner.js # βœ… Import automatico API β”œβ”€β”€ mdb-importer.js # βœ… Import diretto da MDB β”œβ”€β”€ .env.example # βœ… Configurazione └── README-SCRIPTS.md # βœ… Guida utilizzo ``` #### πŸ—ƒοΈ **Import Diretto da MDB:** **File Target:** `stabili.mdb` (database Microsoft Access prima versione) **Tabella Principale:** `Stabili` con tutti i campi necessari **Campi Identificativi Principali:** - `cod_stabile` - Codice univoco stabile - `nome_directory` - Directory per organizzazione file - Dati bancari completi (IBAN, ABI, CAB) - Dati catastali (`AC_Foglio`, `AC_partic`) - Configurazione rate trimestrali (`ORD_*`, `RIS_*`) **Utilizzo:** ```bash # Test connessione mdb-tables -1 stabili.mdb # Import con validazione node mdb-importer.js stabili.mdb --validate-only --verbose # Import reale node mdb-importer.js stabili.mdb --api-token TOKEN --batch-size 50 ``` #### πŸš€ **Come Procedere:** **1. Setup Script Import:** ```bash cd docs/000-IMPORT/03-SCRIPTS/ cp .env.example .env # Modifica credenziali API in .env npm install axios ``` **2. Validazione Dati:** ```bash node validation-checker.js ``` **3. Test API:** ```bash node import-runner.js --validate-only ``` **4. Import Reale:** ```bash # Prima amministratori node import-runner.js --step=admin # Poi stabili node import-runner.js --step=stabili ``` #### πŸ“Š **Dati Pronti per Import:** **Amministratore Principale:** - **Cecilia Tordini** (TRDCCL74T52H501R) - Email: amm.condominialiroma@gmail.com - Gestisce 7 stabili attivi **Stabili Collegati:** - CARLO ALBERTO RACCHIA, 2 (62 condomini) - CATONE 34 - GERMANICO 85 - GRACCHI 32 (70 condomini) - OTTAVIANO 105 - GIULIO CESARE 171 (61 condomini) - GERMANICO 96 (68 condomini) - ANDREA DORIA 36 (29 condomini) - GRACCHI 6 (29 condomini) - GERMANICO 79 (17 condomini) **Totale: 336 unitΓ  da gestire** --- ## πŸ”„ **API IMPORT DATI - Sistema Migrazione** ### **🎯 Panoramica Import System** Sistema dedicato per importare dati dal **vecchio programma** a NetGescon con validazione, mappatura automatica e gestione errori avanzata. **Fasi Import:** 1. βœ… **Amministratori** β†’ Creazione utenti con codici 8 cifre 2. βœ… **Stabili** β†’ Collegamento amministratori-stabili 3. ⏳ **UnitΓ  Immobiliari** β†’ Mappatura dettagliata 4. ⏳ **Condomini/Soggetti** β†’ Anagrafica completa 5. ⏳ **ContabilitΓ ** β†’ Movimenti e registrazioni --- ## 🏒 **API IMPORT AMMINISTRATORI** ### **Endpoint Import Amministratori** ``` POST /api/v1/import/amministratori Content-Type: application/json Authorization: Bearer {token} ``` ### **Struttura Dati Input - Amministratori Reali dal CSV** ```json { "bulk_import": true, "validate_only": false, "amministratori": [ { "codice_amministratore": "TRDCCL74", "denominazione": "Amministrazione Condominio Tordini", "ragione_sociale": "Rag. Cecilia Tordini", "codice_fiscale": "TRDCCL74T52H501R", "indirizzo": "Via Famagosta 6", "cap": "00192", "citta": "ROMA", "provincia": "RM", "telefono": "351 771 7626", "email": "amm.condominialiroma@gmail.com", "pec": "cecilia.tordini@legalmail.it", "responsabile_nome": "CECILIA", "responsabile_cognome": "TORDINI", "responsabile_cf": "TRDCCL74T52H501R", "data_nascita": "1974-12-12", "comune_nascita": "ROMA", "provincia_nascita": "RM", "sesso": "F", "note": "Amministratore principale - gestisce multiple proprietΓ ", "attivo": true }, { "codice_amministratore": "CNFGZV34", "denominazione": "Studio Conforti", "ragione_sociale": "Ignazio Vittorio Conforti", "codice_fiscale": "CNFGZV34E05E914T", "indirizzo": "Via Ottaviano, 32", "cap": "00192", "citta": "ROMA", "provincia": "RM", "telefono": "335295285", "email": "ignaziovittorio.conforti@tin.it", "pec": "conforti.iv@pec.it", "responsabile_nome": "Ignazio Vittorio", "responsabile_cognome": "Conforti", "responsabile_cf": "CNFGZV34E05E914T", "data_nascita": "1934-05-05", "comune_nascita": "Marano Marchesato", "provincia_nascita": "CS", "sesso": "M", "note": "Amministratore storico", "attivo": true }, { "codice_amministratore": "BRNMHL69", "denominazione": "Studio Barone", "ragione_sociale": "Michele Barone", "codice_fiscale": "BRNMHL69M11H501K", "indirizzo": "Viale delle Milizie 9a", "cap": "00192", "citta": "ROMA", "provincia": "RM", "responsabile_nome": "MICHELE", "responsabile_cognome": "BARONE", "responsabile_cf": "BRNMHL69M11H501K", "data_nascita": "1969-08-11", "comune_nascita": "Roma", "provincia_nascita": "RM", "sesso": "M", "note": "Amministratore backup", "attivo": false } ] } ``` ### **Controller Import Amministratori** ```php // app/Http/Controllers/Api/ImportController.php importService = $importService; } /** * Import amministratori dal vecchio sistema */ public function importAmministratori(Request $request) { $validator = Validator::make($request->all(), [ 'bulk_import' => 'boolean', 'validate_only' => 'boolean', 'amministratori' => 'required|array|min:1|max:100', 'amministratori.*.codice_amministratore' => 'required|string|size:8|regex:/^\d{8}$/', 'amministratori.*.denominazione' => 'required|string|max:255', 'amministratori.*.ragione_sociale' => 'nullable|string|max:255', 'amministratori.*.partita_iva' => 'nullable|string|size:11|regex:/^\d{11}$/', 'amministratori.*.codice_fiscale' => 'required|string|size:16', 'amministratori.*.indirizzo' => 'required|string|max:255', 'amministratori.*.cap' => 'required|string|size:5|regex:/^\d{5}$/', 'amministratori.*.citta' => 'required|string|max:100', 'amministratori.*.provincia' => 'required|string|size:2', 'amministratori.*.telefono' => 'nullable|string|max:20', 'amministratori.*.email' => 'required|email|max:255', 'amministratori.*.pec' => 'nullable|email|max:255', 'amministratori.*.responsabile_nome' => 'required|string|max:100', 'amministratori.*.responsabile_cognome' => 'required|string|max:100', 'amministratori.*.responsabile_cf' => 'required|string|size:16', 'amministratori.*.data_iscrizione_albo' => 'nullable|date', 'amministratori.*.numero_albo' => 'nullable|string|max:50', 'amministratori.*.note' => 'nullable|string|max:1000', 'amministratori.*.attivo' => 'boolean' ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Errori di validazione', 'errors' => $validator->errors(), 'error_count' => $validator->errors()->count() ], 400); } // Solo validazione senza import if ($request->get('validate_only', false)) { return response()->json([ 'success' => true, 'message' => 'Validazione completata con successo', 'records_count' => count($request->amministratori), 'validation_passed' => true ]); } try { DB::beginTransaction(); $risultato = $this->importService->importAmministratori($request->amministratori); DB::commit(); return response()->json([ 'success' => true, 'message' => 'Import completato con successo', 'data' => $risultato ]); } catch (\Exception $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => 'Errore durante l\'import: ' . $e->getMessage(), 'error_details' => config('app.debug') ? $e->getTrace() : null ], 500); } } } ``` --- ## 🏘️ **API IMPORT STABILI** ### **Endpoint Import Stabili** ``` POST /api/v1/import/stabili Content-Type: application/json Authorization: Bearer {token} ``` ### **Struttura Dati Input - Basata su Dati Reali CSV** ```json { "bulk_import": true, "validate_only": false, "stabili": [ { "cod_stabile_old": "908", "denominazione": "CARLO ALBERTO RACCHIA, 2", "indirizzo": "VIA C. A. RACCHIA, 2", "cap": "00195", "citta": "ROMA", "provincia": "RM", "codice_fiscale": "80178220580", "num_condomini": 62, "num_scale": 3, "cf_amministratore": "TRDCCL74T52H501R", "iban_bancario": "IT97N0760103200000081932006", "iban_postale": "", "banca": "POSTE ITALIANE", "abi": "07601", "cab": "03200", "intestazione": "Amministrazione Condominio|Via C.A. Racchia 2 - 00195 ROMA|Cod. Fisc. 80178220580", "autorizzazione": "AUT. DB/SISB/G 53373 DEL 06.10.2021", "data_nomina": "2024-01-01", "note": "Condominio VIA C. A. RACCHIA, 2", "email_amministratore": "amm.condominialiroma@gmail.com", "pec_amministratore": "cecilia.tordini@legalmail.it", "migrazione_data": "2025-01-01", "attivo": true }, { "cod_stabile_old": "13", "denominazione": "CATONE 34 - GERMANICO 85 - GRACCHI 32", "indirizzo": "Via: Catone 34/Germanico 85/Gracchi 32", "cap": "00192", "citta": "Roma", "provincia": "RM", "codice_fiscale": "80017140585", "num_condomini": 70, "num_scale": 7, "cf_amministratore": "TRDCCL74T52H501R", "iban_bancario": "IT26Z0306905077100000013753", "banca": "ISTITUTO SAN PAOLO", "abi": "03069", "cab": "05077", "intestazione": "Amministrazione Condominio |Via CATONE 34 GERMANICO 85 GRACCHI 32|00192 ROMA", "email_amministratore": "amm.condominialiroma@gmail.com", "pec_amministratore": "cecilia.tordini@legalmail.it", "pec_condominio": "catone34@pec.it", "data_nomina": "2018-06-27", "migrazione_data": "2025-01-01", "attivo": true }, { "cod_stabile_old": "17", "denominazione": "OTTAVIANO 105 - GIULIO CESARE 171", "indirizzo": "VIA OTTAVIANO 105 - V.LE G. CESARE 171", "cap": "00192", "citta": "Roma", "provincia": "RM", "codice_fiscale": "80212560587", "num_condomini": 61, "num_scale": 0, "cf_amministratore": "TRDCCL74T52H501R", "iban_bancario": "IT88A0503403214000000000707", "banca": "BANCO BPM", "abi": "05034", "cab": "03214", "data_nomina": "2021-09-23", "email_amministratore": "amm.condominialiroma@gmail.com", "pec_amministratore": "cecilia.tordini@legalmail.it", "migrazione_data": "2025-01-01", "attivo": true } ] } ``` ### **Service Import** ```php // app/Services/ImportService.php 0, 'aggiornati' => 0, 'errori' => 0, 'duplicati' => 0, 'dettagli' => [] ]; foreach ($amministratori as $index => $ammData) { try { // Controlla duplicati $existing = Amministratore::where('codice_amministratore', $ammData['codice_amministratore'])->first(); if ($existing) { $risultato['duplicati']++; $risultato['dettagli'][] = [ 'riga' => $index + 1, 'codice' => $ammData['codice_amministratore'], 'status' => 'duplicato', 'message' => 'Amministratore giΓ  esistente' ]; continue; } // Crea utente se non esiste $user = User::where('email', $ammData['email'])->first(); if (!$user) { $user = User::create([ 'name' => $ammData['responsabile_nome'] . ' ' . $ammData['responsabile_cognome'], 'email' => $ammData['email'], 'password' => Hash::make(Str::random(12)), // Password temporanea 'email_verified_at' => now(), 'is_active' => $ammData['attivo'] ?? true, 'tipo_utente' => 'amministratore', 'codice_amministratore' => $ammData['codice_amministratore'] ]); // Assegna ruolo amministratore $user->assignRole('amministratore'); } // Trova comune $comune = ComuneItaliano::where('denominazione', $ammData['citta']) ->where('sigla_provincia', $ammData['provincia']) ->first(); if (!$comune) { throw new \Exception("Comune non trovato: {$ammData['citta']} ({$ammData['provincia']})"); } // Crea amministratore $amministratore = Amministratore::create([ 'user_id' => $user->id, 'codice_amministratore' => $ammData['codice_amministratore'], 'denominazione' => $ammData['denominazione'], 'ragione_sociale' => $ammData['ragione_sociale'], 'partita_iva' => $ammData['partita_iva'], 'codice_fiscale' => $ammData['codice_fiscale'], 'indirizzo' => $ammData['indirizzo'], 'cap' => $ammData['cap'], 'comune_id' => $comune->id, 'telefono' => $ammData['telefono'], 'email' => $ammData['email'], 'pec' => $ammData['pec'], 'responsabile_nome' => $ammData['responsabile_nome'], 'responsabile_cognome' => $ammData['responsabile_cognome'], 'responsabile_cf' => $ammData['responsabile_cf'], 'data_iscrizione_albo' => $ammData['data_iscrizione_albo'] ?? null, 'numero_albo' => $ammData['numero_albo'], 'note' => $ammData['note'], 'attivo' => $ammData['attivo'] ?? true ]); $risultato['importati']++; $risultato['dettagli'][] = [ 'riga' => $index + 1, 'codice' => $ammData['codice_amministratore'], 'status' => 'importato', 'id' => $amministratore->id, 'user_id' => $user->id ]; } catch (\Exception $e) { $risultato['errori']++; $risultato['dettagli'][] = [ 'riga' => $index + 1, 'codice' => $ammData['codice_amministratore'] ?? 'N/A', 'status' => 'errore', 'message' => $e->getMessage() ]; } } return $risultato; } /** * Import stabili con collegamento amministratori */ public function importStabili(array $stabili): array { $risultato = [ 'importati' => 0, 'aggiornati' => 0, 'errori' => 0, 'duplicati' => 0, 'dettagli' => [] ]; foreach ($stabili as $index => $stabileData) { try { // Trova amministratore $amministratore = User::where('codice_amministratore', $stabileData['codice_amministratore'])->first(); if (!$amministratore) { throw new \Exception("Amministratore non trovato: {$stabileData['codice_amministratore']}"); } // Controlla duplicati $existing = Stabile::where('codice_fiscale', $stabileData['codice_fiscale'])->first(); if ($existing) { $risultato['duplicati']++; $risultato['dettagli'][] = [ 'riga' => $index + 1, 'codice' => $stabileData['codice_stabile'], 'status' => 'duplicato', 'message' => 'Stabile giΓ  esistente' ]; continue; } // Trova comune $comune = ComuneItaliano::where('denominazione', $stabileData['citta']) ->where('sigla_provincia', $stabileData['provincia']) ->first(); if (!$comune) { throw new \Exception("Comune non trovato: {$stabileData['citta']} ({$stabileData['provincia']})"); } // Crea stabile $stabile = Stabile::create([ 'codice_stabile' => $stabileData['codice_stabile'], 'amministratore_id' => $amministratore->id, 'denominazione' => $stabileData['denominazione'], 'indirizzo' => $stabileData['indirizzo_completo'], 'cap' => $stabileData['cap'], 'comune_id' => $comune->id, 'codice_fiscale' => $stabileData['codice_fiscale'], 'partita_iva' => $stabileData['partita_iva'], 'telefono' => $stabileData['telefono'], 'email' => $stabileData['email'], 'pec' => $stabileData['pec'], 'data_nomina' => $stabileData['data_nomina_amministratore'], 'data_scadenza_mandato' => $stabileData['data_scadenza_mandato'], 'numero_unita' => $stabileData['numero_unita'], 'numero_scale' => $stabileData['numero_scale'], 'numero_piani' => $stabileData['numero_piani'], 'piano_terra' => $stabileData['piano_terra'], 'piano_primo' => $stabileData['piano_primo'], 'piano_secondo' => $stabileData['piano_secondo'], 'piano_terzo' => $stabileData['piano_terzo'], 'piano_quarto' => $stabileData['piano_quarto'], 'piano_quinto' => $stabileData['piano_quinto'], 'piano_sesto' => $stabileData['piano_sesto'], 'ascensore' => $stabileData['ascensore'] ?? false, 'riscaldamento_centralizzato' => $stabileData['riscaldamento_centralizzato'] ?? false, 'note' => $stabileData['note'], 'attivo' => $stabileData['attivo'] ?? true ]); $risultato['importati']++; $risultato['dettagli'][] = [ 'riga' => $index + 1, 'codice' => $stabileData['codice_stabile'], 'status' => 'importato', 'id' => $stabile->id, 'amministratore_id' => $amministratore->id ]; } catch (\Exception $e) { $risultato['errori']++; $risultato['dettagli'][] = [ 'riga' => $index + 1, 'codice' => $stabileData['codice_stabile'] ?? 'N/A', 'status' => 'errore', 'message' => $e->getMessage() ]; } } return $risultato; } } ``` --- ## πŸ“Š **API STATUS E MONITORAGGIO** ### **Endpoint Status Import** ``` GET /api/v1/import/status GET /api/v1/import/stats GET /api/v1/import/logs/{session_id} ``` ### **Response Status** ```json { "success": true, "data": { "database": { "amministratori": 45, "stabili": 120, "unita_immobiliari": 0, "condomini": 0 }, "import_sessions": [ { "id": "session_123", "type": "amministratori", "started_at": "2024-01-15 10:30:00", "completed_at": "2024-01-15 10:35:00", "status": "completed", "records_processed": 45, "records_imported": 42, "records_errors": 3 } ], "next_recommended": "unita_immobiliari" } } ``` --- ## πŸ§ͺ **TESTING IMPORT API** ### **Test Import Amministratori** ```bash # Test validazione curl -X POST http://localhost:8000/api/v1/import/amministratori \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "validate_only": true, "amministratori": [ { "codice_amministratore": "12345678", "denominazione": "Studio Test", "codice_fiscale": "TSTMRA80A01H501Z", "indirizzo": "Via Test 123", "cap": "00100", "citta": "Roma", "provincia": "RM", "email": "test@test.it", "responsabile_nome": "Mario", "responsabile_cognome": "Test", "responsabile_cf": "TSTMRA80A01H501Z" } ] }' # Import reale curl -X POST http://localhost:8000/api/v1/import/amministratori \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "bulk_import": true, "amministratori": [...] }' ``` --- ## πŸ“‹ **ROUTES IMPORT** ```php // routes/api.php - Aggiunta sezione import Route::middleware('auth:sanctum')->group(function () { // ... routes esistenti ... // Import System Route::prefix('import')->group(function () { // Import dati dal vecchio sistema Route::post('amministratori', [ImportController::class, 'importAmministratori']); Route::post('stabili', [ImportController::class, 'importStabili']); Route::post('unita', [ImportController::class, 'importUnita']); Route::post('condomini', [ImportController::class, 'importCondomini']); // Status e monitoring Route::get('status', [ImportController::class, 'getStatus']); Route::get('stats', [ImportController::class, 'getStats']); Route::get('logs/{session}', [ImportController::class, 'getLogs']); // Validazione preventiva Route::post('validate/amministratori', [ImportController::class, 'validateAmministratori']); Route::post('validate/stabili', [ImportController::class, 'validateStabili']); }); }); ``` --- ## 🎯 **SPECIFICA CAMPI RICHIESTI** ### **Per Amministratori:** - βœ… `codice_amministratore` (8 cifre) - **OBBLIGATORIO** - βœ… `denominazione` - **OBBLIGATORIO** - βœ… `email` - **OBBLIGATORIO** - βœ… `responsabile_nome` + `responsabile_cognome` - **OBBLIGATORIO** - βœ… `indirizzo`, `cap`, `citta`, `provincia` - **OBBLIGATORIO** - βšͺ `partita_iva`, `pec`, `telefono` - Opzionali ### **Per Stabili:** - βœ… `codice_amministratore` (collegamento) - **OBBLIGATORIO** - βœ… `denominazione` - **OBBLIGATORIO** - βœ… `indirizzo_completo` - **OBBLIGATORIO** - βœ… `cap`, `citta`, `provincia` - **OBBLIGATORIO** - βœ… `codice_fiscale` - **OBBLIGATORIO** - βœ… `data_nomina_amministratore` - **OBBLIGATORIO** - βšͺ `email`, `telefono`, `numero_unita` - Opzionali ## ❓ **PROSSIMO STEP** **Quali campi specifici puoi inviarmi dal vecchio programma per Amministratori e Stabili?** Mandami un esempio dei dati che hai cosΓ¬ posso: 1. πŸ”§ **Adattare** la mappatura dei campi 2. βœ… **Validare** la struttura dati 3. πŸš€ **Testare** l'import con dati reali --- *Sistema pronto per import massivo con validazione, rollback e monitoraggio completo!*