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

Merge remote-tracking branch 'refs/remotes/origin/development' into development

Gustavo Mantovani 1 месяц назад
Родитель
Сommit
22b75e1c49

+ 3 - 2
app/Http/Controllers/MediaController.php

@@ -6,6 +6,7 @@
 use App\Http\Requests\MediaRequest;
 use App\Http\Resources\MediaResource;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
 
 class MediaController extends Controller
 {
@@ -13,9 +14,9 @@ public function __construct(
         protected MediaService $service,
     ) {}
 
-    public function index(): JsonResponse
+    public function index(Request $request): JsonResponse
     {
-        $items = $this->service->getAll();
+        $items = $this->service->getAll($request->only(['origem', 'origem_id']));
         return $this->successResponse(payload: MediaResource::collection($items));
     }
 

+ 15 - 2
app/Http/Controllers/StudentContractController.php

@@ -6,6 +6,8 @@
 use App\Http\Requests\StudentContractRequest;
 use App\Http\Resources\StudentContractResource;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
 
 class StudentContractController extends Controller
 {
@@ -15,13 +17,17 @@ public function __construct(
 
     public function index(): JsonResponse
     {
-        $items = $this->service->getAll();
+        $unitId    = Auth::user()->load('units')->units->first()?->id;
+        $studentId = request()->integer('student_id') ?: null;
+        $items     = $this->service->getAll($unitId, $studentId);
         return $this->successResponse(payload: StudentContractResource::collection($items));
     }
 
     public function store(StudentContractRequest $request): JsonResponse
     {
-        $item = $this->service->create($request->validated());
+        $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 StudentContractResource($item), message: __('messages.created'), code: 201);
     }
 
@@ -37,6 +43,13 @@ public function update(StudentContractRequest $request, int $id): JsonResponse
         return $this->successResponse(payload: new StudentContractResource($item), message: __('messages.updated'));
     }
 
+    public function attachFile(Request $request, int $id): JsonResponse
+    {
+        $request->validate(['file' => 'required|file|mimes:jpeg,png,gif,pdf,mp4,mov,avi|max:20480']);
+        $item = $this->service->attachFile($id, $request->file('file'));
+        return $this->successResponse(payload: new StudentContractResource($item), message: __('messages.updated'));
+    }
+
     public function destroy(int $id): JsonResponse
     {
         $this->service->delete($id);

+ 7 - 17
app/Http/Requests/MediaRequest.php

@@ -9,26 +9,16 @@ class MediaRequest extends FormRequest
     public function rules(): array
     {
         $rules = [
-            // Add your validation rules here
-            //'field' => 'sometimes|string|max:255',
+            'name'      => 'required|string|max:255',
+            'origem'    => 'required|string|max:255',
+            'origem_id' => 'required|integer',
+            'file'      => 'sometimes|file|mimes:jpeg,png,gif,pdf,mp4,mov,avi|max:20480',
         ];
 
-        // Different rules for creation
-        //if ($this->isMethod('POST')) {
-            // Make fields required if needed
-            // $rules['field'] = 'required|string|max:255';
-        //}
+        if ($this->isMethod('POST')) {
+            $rules['file'] = 'required|file|mimes:jpeg,png,gif,pdf,mp4,mov,avi|max:20480';
+        }
 
         return $rules;
     }
-
-    /**
-    * Add custom messages when needed
-    * public function messages(): array
-    * {
-    *   return [
-    *        'field.required' => __('message.algo'),
-    *    ];
-    * }
-    */
 }

+ 23 - 21
app/Http/Requests/StudentContractRequest.php

@@ -8,27 +8,29 @@ class StudentContractRequest extends FormRequest
 {
     public function rules(): array
     {
-        $rules = [
-            // Add your validation rules here
-            //'field' => 'sometimes|string|max:255',
+        return [
+            'student_id'               => 'required|exists:students,id',
+            'protocol'                 => 'sometimes|nullable|string|max:255',
+            'signature_date'           => 'sometimes|nullable|date_format:Y-m-d',
+            'end_date'                 => 'sometimes|nullable|date_format:Y-m-d',
+            'class_package_unit_id'    => 'sometimes|nullable|exists:class_package_units,id',
+            'class_quantity'           => 'sometimes|nullable|integer|min:1',
+            'weekday'                  => 'sometimes|nullable|integer|min:0|max:6',
+            'start_time'               => 'sometimes|nullable|date_format:H:i',
+            'end_time'                 => 'sometimes|nullable|date_format:H:i',
+            'second_weekday'           => 'sometimes|nullable|integer|min:0|max:6',
+            'second_start_time'        => 'sometimes|nullable|date_format:H:i',
+            'second_end_time'          => 'sometimes|nullable|date_format:H:i',
+            'due_date'                 => 'sometimes|nullable|date_format:Y-m-d',
+            'tax_register'             => 'sometimes|nullable|numeric|min:0',
+            'down_payment'             => 'sometimes|nullable|numeric|min:0',
+            'installments'             => 'sometimes|nullable|integer|min:1|max:12',
+            'early_payment_discount'   => 'sometimes|nullable|numeric|min:0|max:100',
+            'material_value'           => 'sometimes|nullable|numeric|min:0',
+            'material_installments'    => 'sometimes|nullable|integer|min:1|max:12',
+            'interest_rate'            => 'sometimes|nullable|numeric|min:0',
+            'payment_method'           => 'sometimes|nullable|string|in:pix,credit_card,debit_card',
+            'fine_cancelled'           => 'sometimes|nullable|numeric|min:0|max:100',
         ];
-
-        // Different rules for creation
-        //if ($this->isMethod('POST')) {
-            // Make fields required if needed
-            // $rules['field'] = 'required|string|max:255';
-        //}
-
-        return $rules;
     }
-
-    /**
-    * Add custom messages when needed
-    * public function messages(): array
-    * {
-    *   return [
-    *        'field.required' => __('message.algo'),
-    *    ];
-    * }
-    */
 }

+ 8 - 18
app/Http/Resources/MediaResource.php

@@ -6,31 +6,21 @@
 use Illuminate\Http\Request;
 use Illuminate\Http\Resources\Json\JsonResource;
 use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
+use Illuminate\Support\Facades\Storage;
 use App\Models\Media;
 
 class MediaResource extends JsonResource
 {
-    /**
-     * Transform the resource into an array.
-     *
-     * @return array<string, mixed>
-     */
     public function toArray(Request $request): array
     {
         return [
-            'id' => $this->id,
-            'name' => $this->name,
-            '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'),
-            // Add your fields here
-
-            // Conditional fields
-            // $this->mergeWhen($request->user()?->isAdmin(), [
-            //     'internal_notes' => $this->internal_notes,
-            // ]),
-
-            // Relationships
-            // 'user' => new UserResource($this->whenLoaded('user')),
+            'id'         => $this->id,
+            'name'       => $this->name,
+            'origem'     => $this->origem,
+            'origem_id'  => $this->origem_id,
+            'file_url'   => $this->url ? Storage::url($this->url) : null,
+            'file_type'  => $this->file_type,
+            'created_at' => Carbon::parse($this->created_at)->format('d/m/Y'),
         ];
     }
 

+ 31 - 18
app/Http/Resources/StudentContractResource.php

@@ -6,31 +6,44 @@
 use Illuminate\Http\Request;
 use Illuminate\Http\Resources\Json\JsonResource;
 use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
+use Illuminate\Support\Facades\Storage;
 use App\Models\StudentContract;
 
 class StudentContractResource extends JsonResource
 {
-    /**
-     * Transform the resource into an array.
-     *
-     * @return array<string, mixed>
-     */
     public function toArray(Request $request): array
     {
         return [
-            'id' => $this->id,
-            'name' => $this->name,
-            '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'),
-            // Add your fields here
-
-            // Conditional fields
-            // $this->mergeWhen($request->user()?->isAdmin(), [
-            //     'internal_notes' => $this->internal_notes,
-            // ]),
-
-            // Relationships
-            // 'user' => new UserResource($this->whenLoaded('user')),
+            'id'                      => $this->id,
+            'protocol'                => $this->protocol,
+            'student_id'              => $this->student_id,
+            'unit_id'                 => $this->unit_id,
+            'class_package_unit_id'   => $this->class_package_unit_id,
+            'signature_date'          => $this->signature_date ? Carbon::parse($this->signature_date)->format('d/m/Y') : null,
+            'end_date'                => $this->end_date ? Carbon::parse($this->end_date)->format('d/m/Y') : null,
+            'class_quantity'          => $this->class_quantity,
+            'weekday'                 => $this->weekday,
+            'start_time'              => $this->start_time,
+            'end_time'                => $this->end_time,
+            'second_weekday'          => $this->second_weekday,
+            'second_start_time'       => $this->second_start_time,
+            'second_end_time'         => $this->second_end_time,
+            'due_date'                => $this->due_date ? Carbon::parse($this->due_date)->format('d/m/Y') : null,
+            'recurring_day'           => $this->recurring_day,
+            'tax_register'            => $this->tax_register,
+            'down_payment'            => $this->down_payment,
+            'installments'            => $this->installments,
+            'early_payment_discount'  => $this->early_payment_discount,
+            'material_value'          => $this->material_value,
+            'material_installments'   => $this->material_installments,
+            'interest_rate'           => $this->interest_rate,
+            'payment_method'          => $this->payment_method,
+            'fine_cancelled'          => $this->fine_cancelled,
+            'status'                  => $this->status,
+            'file_url'                => $this->file_url ? Storage::url($this->file_url) : null,
+            'file_type'               => $this->file_type,
+            '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'),
         ];
     }
 

+ 5 - 0
app/Models/FranchiseeContract.php

@@ -80,4 +80,9 @@ public function unit(): BelongsTo
     {
         return $this->belongsTo(Unit::class, 'unit_id');
     }
+
+    public function inhabitantClassification(): BelongsTo
+    {
+        return $this->belongsTo(InhabitantClassification::class, 'inhabitant_classification_id');
+    }
 }

+ 9 - 4
app/Models/InhabitantClassification.php

@@ -8,16 +8,20 @@
 /**
  * @property int $id
  * @property string $description
- * @property string $acronym
+ * @property int $start
+ * @property int $end
+ * @property numeric $tbr_percentage
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification query()
- * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereAcronym($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereCreatedAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereDescription($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereEnd($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereStart($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereTbrPercentage($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|InhabitantClassification whereUpdatedAt($value)
  * @mixin \Eloquent
  */
@@ -30,7 +34,8 @@ class InhabitantClassification extends Model
     protected $guarded = ['id'];
 
     protected $casts = [
-        'created_at' => 'datetime',
-        'updated_at' => 'datetime',
+        'tbr_percentage' => 'decimal:4',
+        'created_at'     => 'datetime',
+        'updated_at'     => 'datetime',
     ];
 }

+ 5 - 3
app/Models/Media.php

@@ -4,16 +4,18 @@
 
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
 
 /**
  * @property int $id
  * @property string $origem
  * @property int $origem_id
+ * @property string|null $name
  * @property string $url
  * @property string $file_type
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
- * @property string|null $deleted_at
+ * @property \Illuminate\Support\Carbon|null $deleted_at
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Media newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Media newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Media query()
@@ -29,7 +31,7 @@
  */
 class Media extends Model
 {
-    use HasFactory;
+    use HasFactory, SoftDeletes;
 
     protected $table = 'media';
 
@@ -40,7 +42,7 @@ class Media extends Model
     protected $casts = [
         'created_at' => 'datetime',
         'updated_at' => 'datetime',
-        // Add your casts here (e.g., 'is_active' => 'boolean')
+        'deleted_at' => 'datetime',
     ];
 
     // Relationships

+ 23 - 13
app/Models/StudentContract.php

@@ -4,6 +4,8 @@
 
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
  * @property int $id
@@ -23,6 +25,8 @@
  * @property int $modality_id
  * @property string|null $notes
  * @property string $status
+ * @property string|null $file_url
+ * @property string|null $file_type
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property string|null $deleted_at
@@ -53,26 +57,32 @@
  */
 class StudentContract extends Model
 {
-    use HasFactory;
+    use HasFactory, SoftDeletes;
 
     protected $table = 'student_contracts';
 
-    protected $guarded = [
-        'id', // Add more fields that shouldn't be edited here
-    ];
+    protected $guarded = ['id'];
 
     protected $casts = [
-        'created_at' => 'datetime',
-        'updated_at' => 'datetime',
-        // Add your casts here (e.g., 'is_active' => 'boolean')
+        'signature_date'  => 'date:Y-m-d',
+        'end_date'        => 'date:Y-m-d',
+        'due_date'        => 'date:Y-m-d',
+        'created_at'      => 'datetime',
+        'updated_at'      => 'datetime',
     ];
 
-    // Relationships
-
-    // Business Logic Methods
-
-    // Custom Finders
+    public function student(): BelongsTo
+    {
+        return $this->belongsTo(Student::class);
+    }
 
-    // Query Scopes
+    public function unit(): BelongsTo
+    {
+        return $this->belongsTo(Unit::class);
+    }
 
+    public function classPackageUnit(): BelongsTo
+    {
+        return $this->belongsTo(ClassPackageUnit::class);
+    }
 }

+ 9 - 4
app/Models/UnitInhabitantClassification.php

@@ -10,16 +10,20 @@
  * @property int $id
  * @property int $unit_id
  * @property string $description
- * @property string $acronym
+ * @property int $start
+ * @property int $end
+ * @property numeric $tbr_percentage
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification query()
- * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereAcronym($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereCreatedAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereDescription($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereEnd($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereStart($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereTbrPercentage($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereUnitId($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|UnitInhabitantClassification whereUpdatedAt($value)
  * @mixin \Eloquent
@@ -33,8 +37,9 @@ class UnitInhabitantClassification extends Model
     protected $guarded = ['id'];
 
     protected $casts = [
-        'created_at' => 'datetime',
-        'updated_at' => 'datetime',
+        'tbr_percentage' => 'decimal:4',
+        'created_at'     => 'datetime',
+        'updated_at'     => 'datetime',
     ];
 
     public function unit(): BelongsTo

+ 2 - 1
app/Services/FranchiseeContractService.php

@@ -34,7 +34,8 @@ public function findById(int $id): ?FranchiseeContract
 
     public function getByUnitId(int $unitId): Collection
     {
-        return FranchiseeContract::where('unit_id', $unitId)
+        return FranchiseeContract::with('inhabitantClassification')
+            ->where('unit_id', $unitId)
             ->orderBy('created_at', 'desc')
             ->get();
     }

+ 36 - 6
app/Services/MediaService.php

@@ -4,13 +4,23 @@
 
 use App\Models\Media;
 use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Facades\Storage;
 
 class MediaService
 {
-    public function getAll(): Collection
+    public function getAll(array $filters = []): Collection
     {
-        return Media::orderBy('created_at', 'desc')
-            ->get();
+        $query = Media::orderBy('created_at', 'desc');
+
+        if (!empty($filters['origem'])) {
+            $query->where('origem', $filters['origem']);
+        }
+
+        if (!empty($filters['origem_id'])) {
+            $query->where('origem_id', $filters['origem_id']);
+        }
+
+        return $query->get();
     }
 
     public function findById(int $id): ?Media
@@ -20,7 +30,18 @@ public function findById(int $id): ?Media
 
     public function create(array $data): Media
     {
-        return Media::create($data);
+        /** @var \Illuminate\Http\UploadedFile $file */
+        $file     = $data['file'];
+        $url      = $file->store('student-media');
+        $fileType = $file->getMimeType();
+
+        return Media::create([
+            'name'      => $data['name'],
+            'origem'    => $data['origem'],
+            'origem_id' => $data['origem_id'],
+            'url'       => $url,
+            'file_type' => $fileType,
+        ]);
     }
 
     public function update(int $id, array $data): ?Media
@@ -31,6 +52,15 @@ public function update(int $id, array $data): ?Media
             return null;
         }
 
+        if (isset($data['file'])) {
+            Storage::delete($model->url);
+            /** @var \Illuminate\Http\UploadedFile $file */
+            $file            = $data['file'];
+            $data['url']     = $file->store('student-media');
+            $data['file_type'] = $file->getMimeType();
+            unset($data['file']);
+        }
+
         $model->update($data);
         return $model->fresh();
     }
@@ -43,8 +73,8 @@ public function delete(int $id): bool
             return false;
         }
 
+        Storage::delete($model->url);
+
         return $model->delete();
     }
-
-    // Add custom business logic methods here
 }

+ 39 - 4
app/Services/StudentContractService.php

@@ -3,13 +3,17 @@
 namespace App\Services;
 
 use App\Models\StudentContract;
+use Carbon\Carbon;
 use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Facades\Storage;
 
 class StudentContractService
 {
-    public function getAll(): Collection
+    public function getAll(int $unitId, ?int $studentId = null): Collection
     {
-        return StudentContract::orderBy('created_at', 'desc')
+        return StudentContract::where('unit_id', $unitId)
+            ->when($studentId, fn ($q) => $q->where('student_id', $studentId))
+            ->orderBy('created_at', 'desc')
             ->get();
     }
 
@@ -20,6 +24,10 @@ public function findById(int $id): ?StudentContract
 
     public function create(array $data): StudentContract
     {
+        if (!empty($data['due_date'])) {
+            $data['recurring_day'] = Carbon::parse($data['due_date'])->day;
+        }
+
         return StudentContract::create($data);
     }
 
@@ -31,10 +39,35 @@ public function update(int $id, array $data): ?StudentContract
             return null;
         }
 
+        if (!empty($data['due_date'])) {
+            $data['recurring_day'] = Carbon::parse($data['due_date'])->day;
+        }
+
         $model->update($data);
         return $model->fresh();
     }
 
+    public function attachFile(int $id, $file): ?StudentContract
+    {
+        $model = $this->findById($id);
+
+        if (!$model) {
+            return null;
+        }
+
+        if ($model->file_url) {
+            Storage::delete($model->file_url);
+        }
+
+        /** @var \Illuminate\Http\UploadedFile $file */
+        $model->update([
+            'file_url'  => $file->store('student-contracts'),
+            'file_type' => $file->getMimeType(),
+        ]);
+
+        return $model->fresh();
+    }
+
     public function delete(int $id): bool
     {
         $model = $this->findById($id);
@@ -43,8 +76,10 @@ public function delete(int $id): bool
             return false;
         }
 
+        if ($model->file_url) {
+            Storage::delete($model->file_url);
+        }
+
         return $model->delete();
     }
-
-    // Add custom business logic methods here
 }

+ 108 - 0
database/migrations/2026_05_06_000002_update_student_contracts_table.php

@@ -0,0 +1,108 @@
+<?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('student_contracts', function (Blueprint $table) {
+            // Make legacy columns nullable only if they exist
+            $columns = [
+                'protocol'                        => fn () => $table->string('protocol')->nullable()->change(),
+                'contract_id'                     => fn () => $table->foreignId('contract_id')->nullable()->change(),
+                'classes_packages_franchisee_id'  => fn () => $table->foreignId('classes_packages_franchisee_id')->nullable()->change(),
+                'started_date'                    => fn () => $table->date('started_date')->nullable()->change(),
+                'end_date'                        => fn () => $table->date('end_date')->nullable()->change(),
+                'start_time'                      => fn () => $table->time('start_time')->nullable()->change(),
+                'end_time'                        => fn () => $table->time('end_time')->nullable()->change(),
+                'payment_method_id'               => fn () => $table->foreignId('payment_method_id')->nullable()->change(),
+                'modality_id'                     => fn () => $table->foreignId('modality_id')->nullable()->change(),
+            ];
+
+            foreach ($columns as $column => $alter) {
+                if (Schema::hasColumn('student_contracts', $column)) {
+                    $alter();
+                }
+            }
+
+            // New columns — only add if not already present
+            if (!Schema::hasColumn('student_contracts', 'class_package_unit_id')) {
+                $table->foreignId('class_package_unit_id')
+                    ->nullable()
+                    ->constrained('class_package_units')
+                    ->nullOnDelete();
+            }
+            if (!Schema::hasColumn('student_contracts', 'signature_date')) {
+                $table->date('signature_date')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'class_quantity')) {
+                $table->integer('class_quantity')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'weekday')) {
+                $table->tinyInteger('weekday')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'second_weekday')) {
+                $table->tinyInteger('second_weekday')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'second_start_time')) {
+                $table->time('second_start_time')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'second_end_time')) {
+                $table->time('second_end_time')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'due_date')) {
+                $table->date('due_date')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'recurring_day')) {
+                $table->tinyInteger('recurring_day')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'down_payment')) {
+                $table->decimal('down_payment', 10, 2)->default(0);
+            }
+            if (!Schema::hasColumn('student_contracts', 'installments')) {
+                $table->tinyInteger('installments')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'early_payment_discount')) {
+                $table->decimal('early_payment_discount', 5, 2)->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'material_value')) {
+                $table->decimal('material_value', 10, 2)->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'material_installments')) {
+                $table->tinyInteger('material_installments')->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'interest_rate')) {
+                $table->decimal('interest_rate', 5, 2)->nullable();
+            }
+            if (!Schema::hasColumn('student_contracts', 'payment_method')) {
+                $table->string('payment_method')->nullable();
+            }
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('student_contracts', function (Blueprint $table) {
+            if (Schema::hasColumn('student_contracts', 'class_package_unit_id')) {
+                $table->dropForeign(['class_package_unit_id']);
+                $table->dropColumn('class_package_unit_id');
+            }
+
+            $newColumns = [
+                'signature_date', 'class_quantity', 'weekday', 'second_weekday',
+                'second_start_time', 'second_end_time', 'due_date', 'recurring_day',
+                'down_payment', 'installments', 'early_payment_discount',
+                'material_value', 'material_installments', 'interest_rate', 'payment_method',
+            ];
+
+            foreach ($newColumns as $column) {
+                if (Schema::hasColumn('student_contracts', $column)) {
+                    $table->dropColumn($column);
+                }
+            }
+        });
+    }
+};

+ 4 - 4
database/migrations/2026_05_06_180111_update_inhabitant_classifications_table.php

@@ -23,7 +23,7 @@ public function up(): void
             $table->decimal('tbr_percentage', 5, 4)
                 ->nullable();
 
-            $table->unique(['start', 'end']);
+            $table->unique(['description', 'start', 'end']);
         });
 
         Schema::table('unit_inhabitant_classifications', function (Blueprint $table) {
@@ -41,14 +41,14 @@ public function up(): void
             $table->decimal('tbr_percentage', 5, 4)
                 ->nullable();
 
-            $table->unique(['unit_id', 'start', 'end']);
+            $table->unique(['unit_id', 'description', 'start', 'end']);
         });
     }
 
     public function down(): void
     {
         Schema::table('inhabitant_classifications', function (Blueprint $table) {
-            $table->dropUnique(['start', 'end']);
+            $table->dropUnique(['description', 'start', 'end']);
             $table->dropColumn(['start', 'end', 'tbr_percentage']);
 
             $table->string('acronym', 2)
@@ -57,7 +57,7 @@ public function down(): void
         });
 
         Schema::table('unit_inhabitant_classifications', function (Blueprint $table) {
-            $table->dropUnique(['unit_id', 'start', 'end']);
+            $table->dropUnique(['unit_id', 'description', 'start', 'end']);
             $table->dropColumn(['start', 'end', 'tbr_percentage']);
 
             $table->string('acronym', 2)->after('description')->nullable();

+ 22 - 0
database/migrations/2026_05_06_190000_add_name_to_media_table.php

@@ -0,0 +1,22 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('media', function (Blueprint $table) {
+            $table->string('name')->nullable()->after('origem_id');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('media', function (Blueprint $table) {
+            $table->dropColumn('name');
+        });
+    }
+};

+ 23 - 0
database/migrations/2026_05_06_191000_add_file_to_student_contracts_table.php

@@ -0,0 +1,23 @@
+<?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('student_contracts', function (Blueprint $table) {
+            $table->string('file_url')->nullable()->after('status');
+            $table->string('file_type')->nullable()->after('file_url');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('student_contracts', function (Blueprint $table) {
+            $table->dropColumn(['file_url', 'file_type']);
+        });
+    }
+};

+ 13 - 7
database/seeders/InhabitantClassificationSeeder.php

@@ -10,17 +10,23 @@ class InhabitantClassificationSeeder extends Seeder
     public function run(): void
     {
         $classifications = [
-            ['acronym' => 'P1', 'description' => 'Até 30.000 habitantes'],
-            ['acronym' => 'P2', 'description' => 'De 30.001 a 70.000 habitantes'],
-            ['acronym' => 'M1', 'description' => 'De 70.001 a 150.000 habitantes'],
-            ['acronym' => 'M2', 'description' => 'De 150.001 a 300.000 habitantes'],
-            ['acronym' => 'G1', 'description' => 'De 300.001 a 500.000 habitantes'],
-            ['acronym' => 'G2', 'description' => 'Acima de 500.000 habitantes'],
+            ['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],
         ];
 
         foreach ($classifications as $item) {
             InhabitantClassification::firstOrCreate(
-                ['acronym' => $item['acronym']],
+                ['description' => $item['description'], 'start' => $item['start'], 'end' => $item['end']],
                 $item,
             );
         }

+ 13 - 1
database/seeders/PermissionSeeder.php

@@ -46,6 +46,18 @@ public function run(): void
                 "bits"        => Permission::ALL_PERMS,
                 "children"    => [],
             ],
+            [
+                "scope"       => "student-contract",
+                "description" => "Contratos de Alunos",
+                "bits"        => Permission::ALL_PERMS,
+                "children"    => [],
+            ],
+            [
+                "scope"       => "media",
+                "description" => "Mídias de Alunos",
+                "bits"        => Permission::ALL_PERMS,
+                "children"    => [],
+            ],
             [
                 "scope"       => "tbr",
                 "description" => "TBR",
@@ -164,7 +176,7 @@ private function createPermissionsAndChildren(
             if (!empty($children)) {
                 $this->createPermissionsAndChildren(
                     permissions: $children,
-                    parent:      $permissionNode,
+                    parent: $permissionNode,
                 );
             }
         }

+ 1 - 0
database/seeders/UserTypePermissionSeeder.php

@@ -40,6 +40,7 @@ public function run(): void
                         ['scope' => 'tbr-calculation',                  'bits' => Permission::VIEW],
                         ['scope' => 'inhabitant-classification',        'bits' => Permission::VIEW],
                         ['scope' => 'unit-inhabitant-classification',   'bits' => Permission::ALL_PERMS],
+                        ['scope' => 'media',                            'bits' => Permission::ALL_PERMS],
                     ];
                     break;
                 case UserTypeEnum::GUEST:

+ 5 - 5
routes/authRoutes/media.php

@@ -4,13 +4,13 @@
 use App\Http\Controllers\MediaController;
 
 Route::controller(MediaController::class)->prefix('media')->group(function () {
-    Route::get('/', 'index')->middleware('permission:media,view');
+    Route::get('/', 'index');
 
-    Route::post('/', 'store')->middleware('permission:media,add');
+    Route::post('/', 'store');
 
-    Route::get('/{id}', 'show')->middleware('permission:media,view');
+    Route::get('/{id}', 'show');
 
-    Route::put('/{id}', 'update')->middleware('permission:media,edit');
+    Route::put('/{id}', 'update');
 
-    Route::delete('/{id}', 'destroy')->middleware('permission:media,delete');
+    Route::delete('/{id}', 'destroy');
 });

+ 7 - 5
routes/authRoutes/student_contract.php

@@ -4,13 +4,15 @@
 use App\Http\Controllers\StudentContractController;
 
 Route::controller(StudentContractController::class)->prefix('student-contract')->group(function () {
-    Route::get('/', 'index')->middleware('permission:student-contract,view');
+    Route::get('/', 'index');
 
-    Route::post('/', 'store')->middleware('permission:student-contract,add');
+    Route::post('/', 'store');
 
-    Route::get('/{id}', 'show')->middleware('permission:student-contract,view');
+    Route::get('/{id}', 'show');
 
-    Route::put('/{id}', 'update')->middleware('permission:student-contract,edit');
+    Route::put('/{id}', 'update');
 
-    Route::delete('/{id}', 'destroy')->middleware('permission:student-contract,delete');
+    Route::post('/{id}/file', 'attachFile');
+
+    Route::delete('/{id}', 'destroy');
 });