Bläddra i källkod

feat: adiciona pacotes para franquia, atualiza pacote por unidade

ebagabee 1 månad sedan
förälder
incheckning
63f79ccdde

+ 68 - 0
app/Http/Controllers/ClassPackageUnitController.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Services\ClassPackageUnitService;
+use App\Http\Requests\ClassPackageUnitRequest;
+use App\Http\Resources\ClassPackageUnitResource;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Auth;
+
+class ClassPackageUnitController extends Controller
+{
+    public function __construct(
+        protected ClassPackageUnitService $service,
+    ) {}
+
+    public function index(): JsonResponse
+    {
+        $unitId = Auth::user()->load('units')->units->first()?->id;
+        if (!$unitId) {
+            return $this->successResponse(payload: []);
+        }
+        $items = $this->service->getAllByUnit($unitId);
+        return $this->successResponse(payload: ClassPackageUnitResource::collection($items));
+    }
+
+    public function byUnit(): JsonResponse
+    {
+        $unitId = Auth::user()->load('units')->units->first()?->id;
+        if (!$unitId) {
+            return $this->successResponse(payload: []);
+        }
+        $items = $this->service->getByUnit($unitId);
+        return $this->successResponse(payload: ClassPackageUnitResource::collection($items));
+    }
+
+    public function store(ClassPackageUnitRequest $request): JsonResponse
+    {
+        $unitId = Auth::user()->load('units')->units->first()?->id;
+        $data   = array_merge($request->validated(), ['unit_id' => $unitId]);
+        $item   = $this->service->create($data);
+        return $this->successResponse(payload: new ClassPackageUnitResource($item), message: __('messages.created'), code: 201);
+    }
+
+    public function show(int $id): JsonResponse
+    {
+        $item = $this->service->findById($id);
+        return $this->successResponse(payload: new ClassPackageUnitResource($item));
+    }
+
+    public function update(ClassPackageUnitRequest $request, int $id): JsonResponse
+    {
+        $item = $this->service->update($id, $request->validated());
+        return $this->successResponse(payload: new ClassPackageUnitResource($item), message: __('messages.updated'));
+    }
+
+    public function toggleVisibility(int $id): JsonResponse
+    {
+        $item = $this->service->toggleVisibility($id);
+        return $this->successResponse(payload: new ClassPackageUnitResource($item), message: __('messages.updated'));
+    }
+
+    public function destroy(int $id): JsonResponse
+    {
+        $this->service->delete($id);
+        return $this->successResponse(message: __('messages.deleted'), code: 204);
+    }
+}

+ 0 - 4
app/Http/Requests/ClassPackageRequest.php

@@ -17,10 +17,6 @@ public function rules(): array
             'contract_register_value' => "$required|numeric|min:0",
             'contrat_discount_value'  => 'nullable|numeric|min:0',
 
-            'unit_visibilities'            => 'nullable|array',
-            'unit_visibilities.*.unit_id'  => 'required|integer|exists:units,id',
-            'unit_visibilities.*.visible'  => 'required|boolean',
-
             'materials'                => 'nullable|array',
             'materials.*.product_id'   => 'required_with:materials|integer|exists:products,id',
             'materials.*.quantity'     => 'required_with:materials|integer|min:1',

+ 27 - 0
app/Http/Requests/ClassPackageUnitRequest.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class ClassPackageUnitRequest extends FormRequest
+{
+    public function rules(): array
+    {
+        $required = $this->isMethod('POST') ? 'required' : 'sometimes';
+
+        return [
+            'name'                    => "$required|string|max:255",
+            'quantity_classes'        => "$required|integer|min:1",
+            'contract_value'          => "$required|numeric|min:0",
+            'contract_register_value' => "$required|numeric|min:0",
+            'contrat_discount_value'  => 'nullable|numeric|min:0',
+            'visible'                 => 'sometimes|boolean',
+
+            'materials'              => 'nullable|array',
+            'materials.*.product_id' => 'required_with:materials|integer|exists:products,id',
+            'materials.*.quantity'   => 'required_with:materials|integer|min:1',
+            'materials.*.price'      => 'required_with:materials|numeric|min:0',
+        ];
+    }
+}

+ 0 - 7
app/Http/Resources/ClassPackageResource.php

@@ -23,13 +23,6 @@ public function toArray(Request $request): array
             'created_at'              => Carbon::parse($this->created_at)->format('Y-m-d H:i:s'),
             'updated_at'              => Carbon::parse($this->updated_at)->format('Y-m-d H:i:s'),
 
-            'unit_visibilities' => $this->whenLoaded('units', fn() =>
-                $this->units->map(fn($unit) => [
-                    'unit_id' => $unit->id,
-                    'visible' => (bool) $unit->pivot->visible,
-                ])
-            ),
-
             'materials' => $this->whenLoaded('products', fn() =>
                 $this->products->map(fn($product) => [
                     'product_id' => $product->id,

+ 37 - 0
app/Http/Resources/ClassPackageUnitResource.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Http\Resources;
+
+use Carbon\Carbon;
+use Illuminate\Http\Request;
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class ClassPackageUnitResource extends JsonResource
+{
+    public function toArray(Request $request): array
+    {
+        return [
+            'id'                      => $this->id,
+            'unit_id'                 => $this->unit_id,
+            'class_package_id'        => $this->class_package_id,
+            'name'                    => $this->name,
+            'quantity_classes'        => $this->quantity_classes,
+            'contract_value'          => $this->contract_value,
+            'contract_material_value' => $this->contract_material_value,
+            'contract_register_value' => $this->contract_register_value,
+            'contrat_discount_value'  => $this->contrat_discount_value,
+            'visible'                 => $this->visible,
+            'created_at'              => Carbon::parse($this->created_at)->format('Y-m-d H:i:s'),
+            'updated_at'              => Carbon::parse($this->updated_at)->format('Y-m-d H:i:s'),
+
+            'materials' => $this->whenLoaded('products', fn() =>
+                $this->products->map(fn($item) => [
+                    'product_id' => $item->product_id,
+                    'name'       => $item->product?->name,
+                    'quantity'   => $item->quantity,
+                    'price'      => (float) $item->price,
+                ])
+            ),
+        ];
+    }
+}

+ 3 - 4
app/Models/ClassPackage.php

@@ -5,6 +5,7 @@
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Database\Eloquent\SoftDeletes;
 
 /**
@@ -36,11 +37,9 @@ class ClassPackage extends Model
         'updated_at'              => 'datetime',
     ];
 
-    public function units(): BelongsToMany
+    public function unitPackages(): HasMany
     {
-        return $this->belongsToMany(Unit::class, 'class_package_units')
-            ->withPivot('visible')
-            ->withTimestamps();
+        return $this->hasMany(ClassPackageUnit::class);
     }
 
     public function products(): BelongsToMany

+ 58 - 0
app/Models/ClassPackageUnit.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+/**
+ * @property int $id
+ * @property int $unit_id
+ * @property int|null $class_package_id
+ * @property string $name
+ * @property int $quantity_classes
+ * @property float $contract_value
+ * @property float $contract_material_value
+ * @property float $contract_register_value
+ * @property float|null $contrat_discount_value
+ * @property bool $visible
+ * @property \Carbon\Carbon $created_at
+ * @property \Carbon\Carbon $updated_at
+ */
+class ClassPackageUnit extends Model
+{
+    use HasFactory, SoftDeletes;
+
+    protected $table = 'class_package_units';
+
+    protected $guarded = ['id'];
+
+    protected $casts = [
+        'quantity_classes'        => 'integer',
+        'contract_value'          => 'float',
+        'contract_material_value' => 'float',
+        'contract_register_value' => 'float',
+        'contrat_discount_value'  => 'float',
+        'visible'                 => 'boolean',
+        'created_at'              => 'datetime',
+        'updated_at'              => 'datetime',
+    ];
+
+    public function unit(): BelongsTo
+    {
+        return $this->belongsTo(Unit::class);
+    }
+
+    public function basePackage(): BelongsTo
+    {
+        return $this->belongsTo(ClassPackage::class, 'class_package_id');
+    }
+
+    public function products(): HasMany
+    {
+        return $this->hasMany(ClassPackageUnitProduct::class);
+    }
+}

+ 42 - 0
app/Models/ClassPackageUnitProduct.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+/**
+ * @property int $id
+ * @property int $class_package_unit_id
+ * @property int $product_id
+ * @property int $quantity
+ * @property float $price
+ * @property \Carbon\Carbon $created_at
+ * @property \Carbon\Carbon $updated_at
+ */
+class ClassPackageUnitProduct extends Model
+{
+    use HasFactory;
+
+    protected $table = 'class_package_unit_products';
+
+    protected $guarded = ['id'];
+
+    protected $casts = [
+        'quantity'   => 'integer',
+        'price'      => 'float',
+        'created_at' => 'datetime',
+        'updated_at' => 'datetime',
+    ];
+
+    public function packageUnit(): BelongsTo
+    {
+        return $this->belongsTo(ClassPackageUnit::class, 'class_package_unit_id');
+    }
+
+    public function product(): BelongsTo
+    {
+        return $this->belongsTo(Product::class);
+    }
+}

+ 18 - 47
app/Services/ClassPackageService.php

@@ -8,38 +8,34 @@
 
 class ClassPackageService
 {
-    public function getAll(): Collection
-    {
-        return ClassPackage::orderBy('created_at', 'desc')->get();
-    }
+    public function __construct(
+        protected ClassPackageUnitService $unitService,
+    ) {}
 
-    public function getByUnit(int $unitId): Collection
+    public function getAll(): Collection
     {
-        return ClassPackage::with('products')
-            ->whereHas('units', fn($q) => $q->where('units.id', $unitId)->where('visible', true))
-            ->orderBy('name')
-            ->get();
+        return ClassPackage::with('products')->orderBy('created_at', 'desc')->get();
     }
 
     public function findById(int $id): ?ClassPackage
     {
-        return ClassPackage::with(['units', 'products'])->find($id);
+        return ClassPackage::with('products')->find($id);
     }
 
     public function create(array $data): ClassPackage
     {
-        $unitVisibilities = $data['unit_visibilities'] ?? [];
-        $materials        = $data['materials'] ?? [];
-        unset($data['unit_visibilities'], $data['materials']);
+        $materials = $data['materials'] ?? [];
+        unset($data['materials']);
 
         $data['contract_material_value'] = $this->calcMaterialValue($materials);
 
         $package = ClassPackage::create($data);
 
-        $this->syncUnitVisibilities($package, $unitVisibilities);
         $this->syncMaterials($package, $materials);
 
-        return $package->load(['units', 'products']);
+        $this->replicateToAllUnits($package->load('products'));
+
+        return $package->load('products');
     }
 
     public function update(int $id, array $data): ?ClassPackage
@@ -47,9 +43,8 @@ public function update(int $id, array $data): ?ClassPackage
         $package = $this->findById($id);
         if (!$package) return null;
 
-        $unitVisibilities = $data['unit_visibilities'] ?? null;
-        $materials        = $data['materials'] ?? null;
-        unset($data['unit_visibilities'], $data['materials']);
+        $materials = $data['materials'] ?? null;
+        unset($data['materials']);
 
         if ($materials !== null) {
             $data['contract_material_value'] = $this->calcMaterialValue($materials);
@@ -57,15 +52,11 @@ public function update(int $id, array $data): ?ClassPackage
 
         $package->update($data);
 
-        if ($unitVisibilities !== null) {
-            $this->syncUnitVisibilities($package, $unitVisibilities);
-        }
-
         if ($materials !== null) {
             $this->syncMaterials($package, $materials);
         }
 
-        return $package->fresh(['units', 'products']);
+        return $package->fresh('products');
     }
 
     public function delete(int $id): bool
@@ -76,31 +67,11 @@ public function delete(int $id): bool
         return $package->delete();
     }
 
-    public function getAllUnitsWithVisibility(int $packageId): \Illuminate\Support\Collection
-    {
-        $allUnits = Unit::orderBy('fantasy_name')->get(['id', 'fantasy_name']);
-        $package  = ClassPackage::with('units')->find($packageId);
-
-        $visibilityMap = $package
-            ? $package->units->keyBy('id')
-            : collect();
-
-        return $allUnits->map(fn($unit) => [
-            'id'           => $unit->id,
-            'fantasy_name' => $unit->fantasy_name,
-            'visible'      => $visibilityMap->has($unit->id)
-                ? (bool) $visibilityMap[$unit->id]->pivot->visible
-                : true,
-        ]);
-    }
-
-    private function syncUnitVisibilities(ClassPackage $package, array $unitVisibilities): void
+    public function replicateToAllUnits(ClassPackage $package): void
     {
-        $syncData = [];
-        foreach ($unitVisibilities as $uv) {
-            $syncData[$uv['unit_id']] = ['visible' => $uv['visible']];
-        }
-        $package->units()->sync($syncData);
+        Unit::all()->each(function (Unit $unit) use ($package) {
+            $this->unitService->replicateFromBasePackage($unit->id, $package);
+        });
     }
 
     private function syncMaterials(ClassPackage $package, array $materials): void

+ 130 - 0
app/Services/ClassPackageUnitService.php

@@ -0,0 +1,130 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\ClassPackageUnit;
+use App\Models\ClassPackageUnitProduct;
+use Illuminate\Database\Eloquent\Collection;
+
+class ClassPackageUnitService
+{
+    public function getByUnit(int $unitId): Collection
+    {
+        return ClassPackageUnit::with('products.product')
+            ->where('unit_id', $unitId)
+            ->where('visible', true)
+            ->orderBy('name')
+            ->get();
+    }
+
+    public function getAllByUnit(int $unitId): Collection
+    {
+        return ClassPackageUnit::with('products.product')
+            ->where('unit_id', $unitId)
+            ->orderBy('name')
+            ->get();
+    }
+
+    public function findById(int $id): ?ClassPackageUnit
+    {
+        return ClassPackageUnit::with('products.product')->find($id);
+    }
+
+    public function create(array $data): ClassPackageUnit
+    {
+        $materials = $data['materials'] ?? [];
+        unset($data['materials']);
+
+        $data['contract_material_value'] = $this->calcMaterialValue($materials);
+
+        $packageUnit = ClassPackageUnit::create($data);
+
+        $this->syncProducts($packageUnit, $materials);
+
+        return $packageUnit->load('products.product');
+    }
+
+    public function update(int $id, array $data): ?ClassPackageUnit
+    {
+        $packageUnit = $this->findById($id);
+        if (!$packageUnit) return null;
+
+        $materials = $data['materials'] ?? null;
+        unset($data['materials']);
+
+        if ($materials !== null) {
+            $data['contract_material_value'] = $this->calcMaterialValue($materials);
+        }
+
+        $packageUnit->update($data);
+
+        if ($materials !== null) {
+            $this->syncProducts($packageUnit, $materials);
+        }
+
+        return $packageUnit->fresh('products.product');
+    }
+
+    public function toggleVisibility(int $id): ?ClassPackageUnit
+    {
+        $packageUnit = ClassPackageUnit::find($id);
+        if (!$packageUnit) return null;
+
+        $packageUnit->update(['visible' => !$packageUnit->visible]);
+
+        return $packageUnit->fresh();
+    }
+
+    public function delete(int $id): bool
+    {
+        $packageUnit = ClassPackageUnit::find($id);
+        if (!$packageUnit) return false;
+
+        return $packageUnit->delete();
+    }
+
+    public function replicateFromBasePackage(int $unitId, \App\Models\ClassPackage $basePackage): ClassPackageUnit
+    {
+        $packageUnit = ClassPackageUnit::create([
+            'unit_id'                 => $unitId,
+            'class_package_id'        => $basePackage->id,
+            'name'                    => $basePackage->name,
+            'quantity_classes'        => $basePackage->quantity_classes,
+            'contract_value'          => $basePackage->contract_value,
+            'contract_material_value' => $basePackage->contract_material_value,
+            'contract_register_value' => $basePackage->contract_register_value,
+            'contrat_discount_value'  => $basePackage->contrat_discount_value,
+            'visible'                 => true,
+        ]);
+
+        $basePackage->products->each(function ($product) use ($packageUnit) {
+            ClassPackageUnitProduct::create([
+                'class_package_unit_id' => $packageUnit->id,
+                'product_id'            => $product->id,
+                'quantity'              => $product->pivot->quantity,
+                'price'                 => $product->pivot->price,
+            ]);
+        });
+
+        return $packageUnit;
+    }
+
+    private function syncProducts(ClassPackageUnit $packageUnit, array $materials): void
+    {
+        $packageUnit->products()->delete();
+
+        foreach ($materials as $m) {
+            ClassPackageUnitProduct::create([
+                'class_package_unit_id' => $packageUnit->id,
+                'product_id'            => $m['product_id'],
+                'quantity'              => $m['quantity'],
+                'price'                 => $m['price'],
+            ]);
+        }
+    }
+
+    private function calcMaterialValue(array $materials): float
+    {
+        return array_reduce($materials, fn($carry, $m) => $carry + ($m['quantity'] * $m['price']), 0.0);
+    }
+}

+ 14 - 0
app/Services/UnitService.php

@@ -2,6 +2,7 @@
 
 namespace App\Services;
 
+use App\Models\ClassPackage;
 use App\Models\Franchisee;
 use App\Models\FranchiseeUnit;
 use App\Models\Unit;
@@ -11,6 +12,10 @@
 
 class UnitService
 {
+    public function __construct(
+        protected ClassPackageUnitService $packageUnitService,
+    ) {}
+
     public function getAll(): Collection
     {
         return Unit::with(['city', 'state'])
@@ -57,6 +62,8 @@ public function create(array $data): Unit
             'unit_id'       => $unit->id,
         ]);
 
+        $this->replicatePackagesToUnit($unit->id);
+
         return $unit;
     }
 
@@ -90,6 +97,13 @@ public function delete(int $id): bool
         return $model->delete();
     }
 
+    private function replicatePackagesToUnit(int $unitId): void
+    {
+        ClassPackage::with('products')->get()->each(function (ClassPackage $package) use ($unitId) {
+            $this->packageUnitService->replicateFromBasePackage($unitId, $package);
+        });
+    }
+
     private function handleAvatar(array $data, ?string $oldAvatarPath = null): array
     {
         if (!isset($data['avatar'])) {

+ 80 - 0
database/migrations/2026_05_06_000001_restructure_class_package_units.php

@@ -0,0 +1,80 @@
+<?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
+    {
+        // Migrate student_contracts: drop old FK, rename column, nullify data (old IDs invalid)
+        Schema::table('student_contracts', function (Blueprint $table) {
+            $table->dropForeign(['classes_packages_franchisee_id']);
+            $table->dropIndex(['classes_packages_franchisee_id']);
+        });
+
+        Schema::table('student_contracts', function (Blueprint $table) {
+            $table->renameColumn('classes_packages_franchisee_id', 'class_package_unit_id');
+        });
+
+        // Nullify stale references before adding new nullable FK
+        DB::table('student_contracts')->update(['class_package_unit_id' => null]);
+
+        Schema::dropIfExists('class_package_units');
+        Schema::dropIfExists('class_package_franchisees');
+
+        Schema::create('class_package_units', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('unit_id')->constrained('units')->cascadeOnDelete();
+            $table->foreignId('class_package_id')->nullable()->constrained('class_packages')->nullOnDelete();
+            $table->string('name');
+            $table->integer('quantity_classes');
+            $table->decimal('contract_value', 10, 2);
+            $table->decimal('contract_material_value', 10, 2);
+            $table->decimal('contract_register_value', 10, 2);
+            $table->decimal('contrat_discount_value', 10, 2)->nullable();
+            $table->boolean('visible')->default(true);
+            $table->timestamps();
+            $table->softDeletes();
+
+            $table->index('unit_id');
+            $table->index('class_package_id');
+            $table->index('visible');
+        });
+
+        // Add nullable FK on student_contracts now that class_package_units exists
+        Schema::table('student_contracts', function (Blueprint $table) {
+            $table->unsignedBigInteger('class_package_unit_id')->nullable()->change();
+            $table->foreign('class_package_unit_id')
+                ->references('id')
+                ->on('class_package_units')
+                ->nullOnDelete();
+            $table->index('class_package_unit_id');
+        });
+
+        Schema::create('class_package_unit_products', function (Blueprint $table) {
+            $table->id();
+            $table->foreignId('class_package_unit_id')->constrained('class_package_units')->cascadeOnDelete();
+            $table->foreignId('product_id')->constrained('products')->cascadeOnDelete();
+            $table->integer('quantity')->default(1);
+            $table->decimal('price', 10, 2);
+            $table->timestamps();
+
+            $table->unique(['class_package_unit_id', 'product_id']);
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('class_package_unit_products');
+
+        Schema::table('student_contracts', function (Blueprint $table) {
+            $table->dropForeign(['class_package_unit_id']);
+            $table->dropIndex(['class_package_unit_id']);
+        });
+
+        Schema::dropIfExists('class_package_units');
+    }
+};

+ 0 - 282
database/seeders/DeveloperTestSeeder.php

@@ -2,26 +2,14 @@
 
 namespace Database\Seeders;
 
-use App\Enums\UserTypeEnum;
-use App\Models\City;
-use App\Models\ClassPackage;
-use App\Models\Franchisee;
 use App\Models\Product;
-use App\Models\State;
-use App\Models\Unit;
-use App\Models\UnitUser;
-use App\Models\User;
 use Illuminate\Database\Seeder;
-use Illuminate\Support\Facades\DB;
 
 class DeveloperTestSeeder extends Seeder
 {
     public function run(): void
     {
         $this->seedProducts();
-        $this->seedClassPackages();
-        $this->seedClassPackageProducts();
-        $this->seedTestFranchisees();
     }
 
     private function seedProducts(): void
@@ -163,274 +151,4 @@ private function seedProducts(): void
             Product::firstOrCreate(['sku' => $product['sku']], $product);
         }
     }
-
-    private function seedClassPackages(): void
-    {
-        $packages = [
-            [
-                'name'                    => 'Plano Anual',
-                'quantity_classes'        => 78,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Mensal',
-                'quantity_classes'        => 4,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano VIP',
-                'quantity_classes'        => 4,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Trimestral',
-                'quantity_classes'        => 12,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Semestral',
-                'quantity_classes'        => 24,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Lúdico',
-                'quantity_classes'        => 24,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Play',
-                'quantity_classes'        => 78,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Plus',
-                'quantity_classes'        => 4,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Mais Conhecimento',
-                'quantity_classes'        => 4,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Reforço',
-                'quantity_classes'        => 12,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Aprendiz',
-                'quantity_classes'        => 24,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-            [
-                'name'                    => 'Plano Senior',
-                'quantity_classes'        => 24,
-                'contract_value'          => 1198.90,
-                'contract_material_value' => 299.90,
-                'contract_register_value' => 89.90,
-                'contrat_discount_value'  => null,
-            ],
-        ];
-
-        foreach ($packages as $package) {
-            ClassPackage::firstOrCreate(['name' => $package['name']], $package);
-        }
-    }
-
-    private function seedClassPackageProducts(): void
-    {
-        // Base products included in every package
-        $baseSkus = ['AP-NV1', 'CAD-ATI', 'PAST-ALUNO'];
-
-        // Additional products for kit-based packages
-        $kitSkus = ['KIT-MAT-BAS', 'LV-ALUNO-GC'];
-
-        $kitPackageNames = ['Plano VIP', 'Plano Plus', 'Plano Anual', 'Plano Play'];
-
-        $products = Product::whereIn('sku', array_merge($baseSkus, $kitSkus))->get()->keyBy('sku');
-        $packages = ClassPackage::all();
-
-        foreach ($packages as $package) {
-            $skus = in_array($package->name, $kitPackageNames)
-                ? array_merge($baseSkus, $kitSkus)
-                : $baseSkus;
-
-            foreach ($skus as $sku) {
-                $product = $products->get($sku);
-                if (!$product) {
-                    continue;
-                }
-
-                DB::table('class_package_products')->insertOrIgnore([
-                    'class_package_id' => $package->id,
-                    'product_id'       => $product->id,
-                    'quantity'         => 1,
-                    'price'            => $product->price_sale,
-                    'created_at'       => now(),
-                    'updated_at'       => now(),
-                ]);
-            }
-        }
-    }
-
-    private function seedTestFranchisees(): void
-    {
-        $prState   = State::where('code', 'PR')->first();
-        $alState   = State::where('code', 'AL')->first();
-        $toledoCity = City::where('name', 'Toledo')->where('state_id', $prState->id)->first();
-        $maceioCity = City::where('name', 'Maceió')->where('state_id', $alState->id)->first();
-
-        $gabriel = User::firstOrCreate(
-            ['email' => 'gabriel@softpar.inf.br'],
-            [
-                'name'      => 'Gabriel',
-                'password'  => 'S@ft2080.',
-                'user_type' => UserTypeEnum::ADMIN_FRANCHISEE,
-            ]
-        );
-
-        $heloisa = User::firstOrCreate(
-            ['email' => 'heloisa@softpar.inf.br'],
-            [
-                'name'      => 'Heloisa',
-                'password'  => 'S@ft2080.',
-                'user_type' => UserTypeEnum::ADMIN_FRANCHISEE,
-            ]
-        );
-
-        $gabrielFranchisee = Franchisee::firstOrCreate(
-            ['cpf' => '000.000.001-00'],
-            [
-                'name'         => 'Gabriel (Teste)',
-                'street'       => 'Rua Sete de Setembro',
-                'address_number' => '100',
-                'neighborhood' => 'Centro',
-                'postal_code'  => '85900-000',
-                'city_id'      => $toledoCity->id,
-                'state_id'     => $prState->id,
-            ]
-        );
-
-        $heloisaFranchisee = Franchisee::firstOrCreate(
-            ['cpf' => '000.000.002-00'],
-            [
-                'name'         => 'Heloisa (Teste)',
-                'street'       => 'Rua do Comércio',
-                'address_number' => '200',
-                'neighborhood' => 'Centro',
-                'postal_code'  => '57000-000',
-                'city_id'      => $maceioCity->id,
-                'state_id'     => $alState->id,
-            ]
-        );
-
-        $gabrielUnit = Unit::firstOrCreate(
-            ['cnpj' => '00.000.001/0001-00'],
-            [
-                'fantasy_name'   => 'Unidade Gabriel (Teste)',
-                'social_reason'  => 'Unidade Gabriel Teste LTDA',
-                'phone_number'   => '(45) 99999-0001',
-                'street'         => 'Rua Sete de Setembro',
-                'address_number' => '100',
-                'neighborhood'   => 'Centro',
-                'postal_code'    => '85900-000',
-                'city_id'        => $toledoCity->id,
-                'state_id'       => $prState->id,
-                'email'          => 'unidade.gabriel@gc.com.br',
-                'name_responsible' => 'Gabriel',
-            ]
-        );
-
-        $heloisaUnit = Unit::firstOrCreate(
-            ['cnpj' => '00.000.002/0001-00'],
-            [
-                'fantasy_name'   => 'Unidade Heloisa (Teste)',
-                'social_reason'  => 'Unidade Heloisa Teste LTDA',
-                'phone_number'   => '(82) 99999-0002',
-                'street'         => 'Rua do Comércio',
-                'address_number' => '200',
-                'neighborhood'   => 'Centro',
-                'postal_code'    => '57000-000',
-                'city_id'        => $maceioCity->id,
-                'state_id'       => $alState->id,
-                'email'          => 'unidade.heloisa@gc.com.br',
-                'name_responsible' => 'Heloisa',
-            ]
-        );
-
-        // Link franchisees to units
-        DB::table('franchisee_units')->insertOrIgnore([
-            [
-                'franchisee_id' => $gabrielFranchisee->id,
-                'unit_id'       => $gabrielUnit->id,
-                'created_at'    => now(),
-                'updated_at'    => now(),
-            ],
-            [
-                'franchisee_id' => $heloisaFranchisee->id,
-                'unit_id'       => $heloisaUnit->id,
-                'created_at'    => now(),
-                'updated_at'    => now(),
-            ],
-        ]);
-
-        // Link users to units
-        UnitUser::firstOrCreate(['unit_id' => $gabrielUnit->id, 'user_id' => $gabriel->id]);
-        UnitUser::firstOrCreate(['unit_id' => $heloisaUnit->id, 'user_id' => $heloisa->id]);
-
-        // Link all class packages to both test units
-        $packageIds = ClassPackage::pluck('id');
-
-        foreach ($packageIds as $packageId) {
-            DB::table('class_package_units')->insertOrIgnore([
-                [
-                    'class_package_id' => $packageId,
-                    'unit_id'          => $gabrielUnit->id,
-                    'visible'          => true,
-                    'created_at'       => now(),
-                    'updated_at'       => now(),
-                ],
-                [
-                    'class_package_id' => $packageId,
-                    'unit_id'          => $heloisaUnit->id,
-                    'visible'          => true,
-                    'created_at'       => now(),
-                    'updated_at'       => now(),
-                ],
-            ]);
-        }
-    }
 }

+ 12 - 0
database/seeders/PermissionSeeder.php

@@ -34,6 +34,18 @@ public function run(): void
                     ],
                 ],
             ],
+            [
+                "scope" => "class-package",
+                "description" => "Pacotes (Franqueadora)",
+                "bits" => Permission::ALL_PERMS,
+                "children" => [],
+            ],
+            [
+                "scope" => "class-package-unit",
+                "description" => "Pacotes (Unidade)",
+                "bits" => Permission::ALL_PERMS,
+                "children" => [],
+            ],
             [
                 "scope" => "tbr",
                 "description" => "TBR",

+ 14 - 0
routes/authRoutes/class_package_unit.php

@@ -0,0 +1,14 @@
+<?php
+
+use Illuminate\Support\Facades\Route;
+use App\Http\Controllers\ClassPackageUnitController;
+
+Route::controller(ClassPackageUnitController::class)->prefix('class-package-unit')->group(function () {
+    Route::get('/', 'index');
+    Route::get('/visible', 'byUnit');
+    Route::post('/', 'store');
+    Route::get('/{id}', 'show');
+    Route::put('/{id}', 'update');
+    Route::patch('/{id}/toggle-visibility', 'toggleVisibility');
+    Route::delete('/{id}', 'destroy');
+});