Просмотр исходного кода

refactor(inhabitant-classifications): consolida base de faixas por porte

- Migration agrega start/end/tbr_percentage/municipality_size_id/is_renewal
  direto no create (deleta migration de alter)
- Seeder popula 16 linhas (4 portes x 3 faixas + 4 de renovacao)
- Service deixa de propagar para unit_inhabitant_classifications
- Model recebe cast is_renewal e relacao municipalitySize

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ebagabee 3 недель назад
Родитель
Сommit
58e17a37e8

+ 6 - 0
app/Models/InhabitantClassification.php

@@ -35,7 +35,13 @@ class InhabitantClassification extends Model
 
     protected $casts = [
         'tbr_percentage' => 'decimal:4',
+        'is_renewal'     => 'boolean',
         'created_at'     => 'datetime',
         'updated_at'     => 'datetime',
     ];
+
+    public function municipalitySize(): \Illuminate\Database\Eloquent\Relations\BelongsTo
+    {
+        return $this->belongsTo(MunicipalitySize::class, 'municipality_size_id');
+    }
 }

+ 7 - 76
app/Services/InhabitantClassificationService.php

@@ -3,21 +3,22 @@
 namespace App\Services;
 
 use App\Models\InhabitantClassification;
-use App\Models\Unit;
-use App\Models\UnitInhabitantClassification;
 use Illuminate\Database\Eloquent\Collection;
 
 class InhabitantClassificationService
 {
     public function getAll(): Collection
     {
-        return InhabitantClassification::orderBy('id')->get();
+        return InhabitantClassification::with('municipalitySize')
+            ->orderBy('municipality_size_id')
+            ->orderBy('start')
+            ->get();
     }
 
     public function getSelectList(): Collection
     {
         return InhabitantClassification::orderBy('id')
-            ->get(['id', 'description', 'start', 'end', 'tbr_percentage'])
+            ->get(['id', 'description', 'start', 'end', 'tbr_percentage', 'municipality_size_id'])
             ->unique('description')
             ->values();
     }
@@ -29,9 +30,7 @@ public function findById(int $id): ?InhabitantClassification
 
     public function create(array $data): InhabitantClassification
     {
-        $model = InhabitantClassification::create($data);
-        $this->propagateToAllUnits($model);
-        return $model;
+        return InhabitantClassification::create($data);
     }
 
     public function update(int $id, array $data): ?InhabitantClassification
@@ -42,22 +41,8 @@ public function update(int $id, array $data): ?InhabitantClassification
             return null;
         }
 
-        $oldStart          = $model->start;
-        $oldEnd            = $model->end;
-        $oldTbrPercentage  = $model->tbr_percentage;
-        $oldDescription    = $model->description;
-
         $model->update($data);
-        $updated = $model->fresh();
-
-        $this->propagateUpdateToUnits($updated, [
-            'description'    => $oldDescription,
-            'start'          => $oldStart,
-            'end'            => $oldEnd,
-            'tbr_percentage' => $oldTbrPercentage,
-        ]);
-
-        return $updated;
+        return $model->fresh();
     }
 
     public function delete(int $id): bool
@@ -70,58 +55,4 @@ public function delete(int $id): bool
 
         return $model->delete();
     }
-
-    //
-
-    private function propagateToAllUnits(InhabitantClassification $parent): void
-    {
-        $existingUnitIds = UnitInhabitantClassification::where('start', $parent->start)
-            ->where('end', $parent->end)
-            ->pluck('unit_id');
-
-        Unit::whereNotIn('id', $existingUnitIds)
-            ->each(function (Unit $unit) use ($parent) {
-                UnitInhabitantClassification::create([
-                    'unit_id'        => $unit->id,
-                    'description'    => $parent->description,
-                    'start'          => $parent->start,
-                    'end'            => $parent->end,
-                    'tbr_percentage' => $parent->tbr_percentage,
-                ]);
-            });
-    }
-
-    private function propagateUpdateToUnits(InhabitantClassification $parent, array $old): void
-    {
-        // propaga a atualizacao para as classificacoes de unidades que tenham os mesmos valores antigos
-        UnitInhabitantClassification::where('start', $old['start'])
-            ->where('end', $old['end'])
-            ->where('tbr_percentage', $old['tbr_percentage'])
-            ->where('description', $old['description'])
-            ->update([
-                'description'    => $parent->description,
-                'start'          => $parent->start,
-                'end'            => $parent->end,
-                'tbr_percentage' => $parent->tbr_percentage,
-            ]);
-
-        // garante que todas as unidades tenham a classificacao, criando para as que ainda nao tem
-        $coveredUnitIds = UnitInhabitantClassification::where(function ($q) use ($parent, $old) {
-                $q->where(fn($q) => $q->where('start', $parent->start)->where('end', $parent->end))
-                  ->orWhere(fn($q) => $q->where('start', $old['start'])->where('end', $old['end']));
-            })
-            ->pluck('unit_id')
-            ->unique();
-
-        Unit::whereNotIn('id', $coveredUnitIds)
-            ->each(function (Unit $unit) use ($parent) {
-                UnitInhabitantClassification::create([
-                    'unit_id'        => $unit->id,
-                    'description'    => $parent->description,
-                    'start'          => $parent->start,
-                    'end'            => $parent->end,
-                    'tbr_percentage' => $parent->tbr_percentage,
-                ]);
-            });
-    }
 }

+ 11 - 1
database/migrations/2026_04_23_165628_create_inhabitant_classifications_table.php

@@ -10,9 +10,19 @@ public function up(): void
     {
         Schema::create('inhabitant_classifications', function (Blueprint $table) {
             $table->id();
+            $table->foreignId('municipality_size_id')
+                ->nullable()
+                ->constrained('municipality_sizes')
+                ->nullOnDelete();
             $table->string('description', 150);
-            $table->string('acronym', 2)->unique();
+            $table->unsignedTinyInteger('start')->nullable();
+            $table->unsignedTinyInteger('end')->nullable();
+            $table->decimal('tbr_percentage', 5, 4)->nullable();
+            $table->boolean('is_renewal')->default(false);
             $table->timestamps();
+
+            $table->index('municipality_size_id');
+            $table->unique(['municipality_size_id', 'start', 'end', 'is_renewal'], 'ic_size_range_unique');
         });
     }
 

+ 0 - 68
database/migrations/2026_05_06_180111_update_inhabitant_classifications_table.php

@@ -1,68 +0,0 @@
-<?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('inhabitant_classifications', function (Blueprint $table) {
-            $table->dropUnique(['acronym']);
-            $table->dropColumn('acronym');
-
-            $table->tinyInteger('start')
-                ->unsigned()
-                ->nullable();
-
-            $table->tinyInteger('end')
-                ->unsigned()
-                ->nullable();
-
-            $table->decimal('tbr_percentage', 5, 4)
-                ->nullable();
-
-            $table->unique(['description', 'start', 'end']);
-        });
-
-        Schema::table('unit_inhabitant_classifications', function (Blueprint $table) {
-            $table->dropUnique(['unit_id', 'acronym']);
-            $table->dropColumn('acronym');
-
-            $table->tinyInteger('start')
-                ->unsigned()
-                ->nullable();
-
-            $table->tinyInteger('end')
-                ->unsigned()
-                ->nullable();
-
-            $table->decimal('tbr_percentage', 5, 4)
-                ->nullable();
-
-            $table->unique(['unit_id', 'description', 'start', 'end']);
-        });
-    }
-
-    public function down(): void
-    {
-        Schema::table('inhabitant_classifications', function (Blueprint $table) {
-            $table->dropUnique(['description', 'start', 'end']);
-            $table->dropColumn(['start', 'end', 'tbr_percentage']);
-
-            $table->string('acronym', 2)
-                ->unique()
-                ->nullable();
-        });
-
-        Schema::table('unit_inhabitant_classifications', function (Blueprint $table) {
-            $table->dropUnique(['unit_id', 'description', 'start', 'end']);
-            $table->dropColumn(['start', 'end', 'tbr_percentage']);
-
-            $table->string('acronym', 2)->after('description')->nullable();
-
-            $table->unique(['unit_id', 'acronym']);
-        });
-    }
-};

+ 50 - 18
database/seeders/InhabitantClassificationSeeder.php

@@ -3,32 +3,64 @@
 namespace Database\Seeders;
 
 use App\Models\InhabitantClassification;
+use App\Models\MunicipalitySize;
 use Illuminate\Database\Seeder;
 
 class InhabitantClassificationSeeder extends Seeder
 {
     public function run(): void
     {
-        $classifications = [
-            ['description' => '50k',         'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000],
-            ['description' => '50k',         'start' => 4,  'end' => 12, 'tbr_percentage' => 0.4000],
-            ['description' => '50k',         'start' => 13, 'end' => 60, 'tbr_percentage' => 0.6000],
-            ['description' => '50k - 100k',  'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000],
-            ['description' => '50k - 100k',  'start' => 4,  'end' => 12, 'tbr_percentage' => 0.5000],
-            ['description' => '50k - 100k',  'start' => 13, 'end' => 60, 'tbr_percentage' => 0.7500],
-            ['description' => '100k - 200k', 'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000],
-            ['description' => '100k - 200k', 'start' => 4,  'end' => 12, 'tbr_percentage' => 0.7500],
-            ['description' => '100k - 200k', 'start' => 13, 'end' => 60, 'tbr_percentage' => 0.1000],
-            ['description' => '> 200k',      'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000],
-            ['description' => '> 200k',      'start' => 4,  'end' => 12, 'tbr_percentage' => 1.0000],
-            ['description' => '> 200k',      'start' => 13, 'end' => 60, 'tbr_percentage' => 1.5000],
+        $sizes = MunicipalitySize::pluck('id', 'acronym');
+
+        if ($sizes->isEmpty()) {
+            return;
+        }
+
+        $rules = [
+            'PP' => [
+                ['description' => 'Até 50k — isenção inicial (meses 1 a 3)', 'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000, 'is_renewal' => false],
+                ['description' => 'Até 50k — meses 4 a 12',                  'start' => 4,  'end' => 12, 'tbr_percentage' => 0.4000, 'is_renewal' => false],
+                ['description' => 'Até 50k — meses 13 a 60',                 'start' => 13, 'end' => 60, 'tbr_percentage' => 0.6000, 'is_renewal' => false],
+                ['description' => 'Renovação até 50k',                       'start' => 1,  'end' => 60, 'tbr_percentage' => 0.6000, 'is_renewal' => true],
+            ],
+            'MP' => [
+                ['description' => 'De 50k a 100k — isenção inicial (meses 1 a 3)', 'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000, 'is_renewal' => false],
+                ['description' => 'De 50k a 100k — meses 4 a 12',                  'start' => 4,  'end' => 12, 'tbr_percentage' => 0.5000, 'is_renewal' => false],
+                ['description' => 'De 50k a 100k — meses 13 a 60',                 'start' => 13, 'end' => 60, 'tbr_percentage' => 0.7500, 'is_renewal' => false],
+                ['description' => 'Renovação 50k–100k',                            'start' => 1,  'end' => 60, 'tbr_percentage' => 0.7500, 'is_renewal' => true],
+            ],
+            'GP' => [
+                ['description' => 'De 100k a 200k — isenção inicial (meses 1 a 3)', 'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000, 'is_renewal' => false],
+                ['description' => 'De 100k a 200k — meses 4 a 12',                  'start' => 4,  'end' => 12, 'tbr_percentage' => 0.7500, 'is_renewal' => false],
+                ['description' => 'De 100k a 200k — meses 13 a 60',                 'start' => 13, 'end' => 60, 'tbr_percentage' => 1.0000, 'is_renewal' => false],
+                ['description' => 'Renovação 100k–200k',                            'start' => 1,  'end' => 60, 'tbr_percentage' => 1.0000, 'is_renewal' => true],
+            ],
+            'MGP' => [
+                ['description' => 'Acima de 200k — isenção inicial (meses 1 a 3)', 'start' => 1,  'end' => 3,  'tbr_percentage' => 0.0000, 'is_renewal' => false],
+                ['description' => 'Acima de 200k — meses 4 a 12',                  'start' => 4,  'end' => 12, 'tbr_percentage' => 1.0000, 'is_renewal' => false],
+                ['description' => 'Acima de 200k — meses 13 a 60',                 'start' => 13, 'end' => 60, 'tbr_percentage' => 1.5000, 'is_renewal' => false],
+                ['description' => 'Renovação +200k',                                'start' => 1,  'end' => 60, 'tbr_percentage' => 1.5000, 'is_renewal' => true],
+            ],
         ];
 
-        foreach ($classifications as $item) {
-            InhabitantClassification::firstOrCreate(
-                ['description' => $item['description'], 'start' => $item['start'], 'end' => $item['end']],
-                $item,
-            );
+        foreach ($rules as $acronym => $brackets) {
+            $sizeId = $sizes[$acronym] ?? null;
+
+            if (!$sizeId) {
+                continue;
+            }
+
+            foreach ($brackets as $bracket) {
+                InhabitantClassification::firstOrCreate(
+                    [
+                        'municipality_size_id' => $sizeId,
+                        'start'                => $bracket['start'],
+                        'end'                  => $bracket['end'],
+                        'is_renewal'           => $bracket['is_renewal'],
+                    ],
+                    array_merge($bracket, ['municipality_size_id' => $sizeId]),
+                );
+            }
         }
     }
 }