Procházet zdrojové kódy

✨ feat(appointment): adicionar módulo de agendamentos

Fase: dev | Origin: melhoria-interna
Gustavo Zanatta před 1 týdnem
rodič
revize
866be7ef6d

+ 57 - 0
app/Http/Controllers/AppointmentController.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Requests\AppointmentRequest;
+use App\Http\Resources\AppointmentResource;
+use App\Services\AppointmentService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Auth;
+
+class AppointmentController extends Controller
+{
+    public function __construct(protected AppointmentService $service) {}
+
+    public function index(): JsonResponse
+    {
+        $items = $this->service->getAll();
+        return $this->successResponse(payload: AppointmentResource::collection($items));
+    }
+
+    public function myAppointments(): JsonResponse
+    {
+        $items = $this->service->getAllByUser(Auth::id());
+        return $this->successResponse(payload: AppointmentResource::collection($items));
+    }
+
+    public function store(AppointmentRequest $request): JsonResponse
+    {
+        $item = $this->service->create($request->validated());
+        return $this->successResponse(
+            payload: new AppointmentResource($item),
+            message: __('messages.created'),
+            code: 201,
+        );
+    }
+
+    public function show(int $id): JsonResponse
+    {
+        $item = $this->service->findById($id);
+        return $this->successResponse(payload: new AppointmentResource($item));
+    }
+
+    public function update(AppointmentRequest $request, int $id): JsonResponse
+    {
+        $item = $this->service->update($id, $request->validated());
+        return $this->successResponse(
+            payload: new AppointmentResource($item),
+            message: __('messages.updated'),
+        );
+    }
+
+    public function destroy(int $id): JsonResponse
+    {
+        $this->service->delete($id);
+        return $this->successResponse(message: __('messages.deleted'), code: 204);
+    }
+}

+ 33 - 0
app/Http/Requests/AppointmentRequest.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Http\Requests;
+
+use App\Enums\AppointmentStatusEnum;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rule;
+
+class AppointmentRequest extends FormRequest
+{
+    public function rules(): array
+    {
+        $rules = [
+            'user_id'                      => 'sometimes|integer|exists:users,id',
+            'partner_agreement_id'         => 'sometimes|integer|exists:partner_agreements,id',
+            'partner_agreement_service_id' => 'sometimes|integer|exists:partner_agreement_services,id',
+            'date'                         => 'sometimes|date',
+            'time'                         => 'sometimes|date_format:H:i',
+            'observations'                 => 'sometimes|nullable|string',
+            'status'                       => ['sometimes', Rule::enum(AppointmentStatusEnum::class)],
+        ];
+
+        if ($this->isMethod('post')) {
+            $rules['user_id']                      = 'required|integer|exists:users,id';
+            $rules['partner_agreement_id']         = 'required|integer|exists:partner_agreements,id';
+            $rules['partner_agreement_service_id'] = 'required|integer|exists:partner_agreement_services,id';
+            $rules['date']                         = 'required|date';
+            $rules['time']                         = 'required|date_format:H:i';
+        }
+
+        return $rules;
+    }
+}

+ 31 - 0
app/Http/Resources/AppointmentResource.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Http\Resources;
+
+use Carbon\Carbon;
+use Illuminate\Http\Request;
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class AppointmentResource extends JsonResource
+{
+    public function toArray(Request $request): array
+    {
+        return [
+            'id'                           => $this->id,
+            'order_number'                 => $this->order_number,
+            'user_id'                      => $this->user_id,
+            'user'                         => $this->whenLoaded('user', fn() => new UserResource($this->user)),
+            'partner_agreement_id'         => $this->partner_agreement_id,
+            'partner_agreement'            => $this->whenLoaded('partnerAgreement', fn() => new PartnerAgreementResource($this->partnerAgreement)),
+            'partner_agreement_service_id' => $this->partner_agreement_service_id,
+            'partner_agreement_service'    => $this->whenLoaded('partnerAgreementService', fn() => new PartnerAgreementServiceResource($this->partnerAgreementService)),
+            'date'                         => $this->date?->format('Y-m-d'),
+            'time'                         => $this->time,
+            'observations'                 => $this->observations,
+            'requested_at'                 => $this->requested_at?->format('Y-m-d H:i:s'),
+            'status'                       => $this->status,
+            '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'),
+        ];
+    }
+}

+ 39 - 0
app/Models/Appointment.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Models;
+
+use App\Enums\AppointmentStatusEnum;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class Appointment extends Model
+{
+    use SoftDeletes;
+
+    protected $guarded = ['id'];
+
+    protected function casts(): array
+    {
+        return [
+            'date'         => 'date',
+            'requested_at' => 'datetime',
+            'status'       => AppointmentStatusEnum::class,
+        ];
+    }
+
+    public function user(): BelongsTo
+    {
+        return $this->belongsTo(User::class);
+    }
+
+    public function partnerAgreement(): BelongsTo
+    {
+        return $this->belongsTo(PartnerAgreement::class);
+    }
+
+    public function partnerAgreementService(): BelongsTo
+    {
+        return $this->belongsTo(PartnerAgreementService::class);
+    }
+}

+ 70 - 0
app/Services/AppointmentService.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Appointment;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Str;
+
+class AppointmentService
+{
+    public function getAll(): Collection
+    {
+        return Appointment::with(['user', 'partnerAgreement', 'partnerAgreementService'])
+            ->orderBy('date', 'desc')
+            ->get();
+    }
+
+    public function getAllByUser(int $userId): Collection
+    {
+        return Appointment::with(['partnerAgreement', 'partnerAgreementService'])
+            ->where('user_id', $userId)
+            ->orderBy('date', 'desc')
+            ->get();
+    }
+
+    public function findById(int $id): ?Appointment
+    {
+        return Appointment::with(['user', 'partnerAgreement', 'partnerAgreementService'])->find($id);
+    }
+
+    public function create(array $data): Appointment
+    {
+        $data['order_number'] = $this->generateOrderNumber();
+        $data['requested_at'] = now();
+
+        return Appointment::create($data);
+    }
+
+    public function update(int $id, array $data): ?Appointment
+    {
+        $model = Appointment::find($id);
+
+        if (!$model) {
+            return null;
+        }
+
+        $model->update($data);
+        return $model->fresh(['user', 'partnerAgreement', 'partnerAgreementService']);
+    }
+
+    public function delete(int $id): bool
+    {
+        $model = Appointment::find($id);
+
+        if (!$model) {
+            return false;
+        }
+
+        return $model->delete();
+    }
+
+    private function generateOrderNumber(): string
+    {
+        do {
+            $number = 'AGD-' . strtoupper(Str::random(8));
+        } while (Appointment::where('order_number', $number)->exists());
+
+        return $number;
+    }
+}

+ 37 - 0
database/migrations/2025_01_01_000012_create_appointments_table.php

@@ -0,0 +1,37 @@
+<?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::create('appointments', function (Blueprint $table) {
+            $table->id();
+            $table->string('order_number')->unique();
+            $table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
+            $table->foreignId('partner_agreement_id')->constrained('partner_agreements')->cascadeOnDelete();
+            $table->foreignId('partner_agreement_service_id')->constrained('partner_agreement_services')->cascadeOnDelete();
+            $table->date('date');
+            $table->time('time');
+            $table->text('observations')->nullable();
+            $table->timestamp('requested_at')->nullable();
+            $table->string('status')->default('pendente');
+            $table->timestamps();
+            $table->softDeletes();
+
+            $table->index('user_id');
+            $table->index('partner_agreement_id');
+            $table->index('partner_agreement_service_id');
+            $table->index('status');
+            $table->index('date');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('appointments');
+    }
+};

+ 18 - 0
routes/authRoutes/appointment.php

@@ -0,0 +1,18 @@
+<?php
+
+use App\Http\Controllers\AppointmentController;
+use Illuminate\Support\Facades\Route;
+
+Route::controller(AppointmentController::class)->prefix('appointment')->group(function () {
+    Route::get('/', 'index')->middleware('permission:agendamento,view');
+
+    Route::get('/my', 'myAppointments')->middleware('permission:agendamento,view');
+
+    Route::post('/', 'store')->middleware('permission:agendamento,add');
+
+    Route::get('/{id}', 'show')->middleware('permission:agendamento,view');
+
+    Route::put('/{id}', 'update')->middleware('permission:agendamento,edit');
+
+    Route::delete('/{id}', 'destroy')->middleware('permission:agendamento,delete');
+});