diff --git a/thanasoft-back/app/Http/Controllers/Api/InterventionController.php b/thanasoft-back/app/Http/Controllers/Api/InterventionController.php index 3080c46..ef61317 100644 --- a/thanasoft-back/app/Http/Controllers/Api/InterventionController.php +++ b/thanasoft-back/app/Http/Controllers/Api/InterventionController.php @@ -4,11 +4,12 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Requests\StoreInterventionRequest; -use App\Http\Requests\StoreInterventionWithAllDataRequest; +use App\HttpRequests\StoreInterventionWithAllDataRequest; use App\Http\Requests\UpdateInterventionRequest; use App\Http\Resources\Intervention\InterventionResource; use App\Http\Resources\Intervention\InterventionCollection; use App\Repositories\InterventionRepositoryInterface; +use App\Repositories\InterventionPractitionerRepositoryInterface; use App\Repositories\ClientRepositoryInterface; use App\Repositories\ContactRepositoryInterface; use App\Repositories\DeceasedRepositoryInterface; @@ -25,6 +26,11 @@ class InterventionController extends Controller */ protected $interventionRepository; + /** + * @var InterventionPractitionerRepositoryInterface + */ + protected $interventionPractitionerRepository; + /** * @var ClientRepositoryInterface */ @@ -44,17 +50,20 @@ class InterventionController extends Controller * InterventionController constructor. * * @param InterventionRepositoryInterface $interventionRepository + * @param InterventionPractitionerRepositoryInterface $interventionPractitionerRepository * @param ClientRepositoryInterface $clientRepository * @param ContactRepositoryInterface $contactRepository * @param DeceasedRepositoryInterface $deceasedRepository */ public function __construct( InterventionRepositoryInterface $interventionRepository, + InterventionPractitionerRepositoryInterface $interventionPractitionerRepository, ClientRepositoryInterface $clientRepository, ContactRepositoryInterface $contactRepository, DeceasedRepositoryInterface $deceasedRepository ) { $this->interventionRepository = $interventionRepository; + $this->interventionPractitionerRepository = $interventionPractitionerRepository; $this->clientRepository = $clientRepository; $this->contactRepository = $contactRepository; $this->deceasedRepository = $deceasedRepository; @@ -361,13 +370,13 @@ class InterventionController extends Controller } /** - * Assign a practitioner to an intervention + * Create assignment of practitioners to an intervention * * @param Request $request * @param int $id * @return JsonResponse */ - public function assignPractitioner(Request $request, int $id): JsonResponse + public function createAssignment(Request $request, int $id): JsonResponse { try { $validated = $request->validate([ @@ -376,6 +385,7 @@ class InterventionController extends Controller 'assistant_practitioner_ids.*' => 'integer|exists:thanatopractitioners,id', ]); + $intervention = $this->interventionRepository->findById($id); if (!$intervention) { @@ -384,41 +394,164 @@ class InterventionController extends Controller ], Response::HTTP_NOT_FOUND); } - // Sync practitioners with their roles - $practitioners = []; - + // Remove existing principal practitioner first if (isset($validated['principal_practitioner_id'])) { - $practitioners[$validated['principal_practitioner_id']] = ['role' => 'principal']; + $principalId = $validated['principal_practitioner_id']; + $this->interventionPractitionerRepository->createAssignment($id, $principalId, 'principal'); } - if (isset($validated['assistant_practitioner_ids'])) { + // Handle assistant practitioners + if (isset($validated['assistant_practitioner_ids']) && is_array($validated['assistant_practitioner_ids'])) { foreach ($validated['assistant_practitioner_ids'] as $assistantId) { - $practitioners[$assistantId] = ['role' => 'assistant']; + $this->interventionPractitionerRepository->createAssignment($id, $assistantId, 'assistant'); } } - // Sync the practitioners (this will replace existing assignments) - $intervention->practitioners()->sync($practitioners); - - // Reload the intervention with relationships - $intervention = $this->interventionRepository->findById($id); + // Load the intervention with practitioners to return updated data + $intervention->load('practitioners'); + $practitioners = $intervention->practitioners; return response()->json([ 'data' => new InterventionResource($intervention), - 'message' => 'Praticien(s) assigné(s) avec succès.' + 'message' => 'Assignment(s) créé(s) avec succès.', + 'practitioners_count' => $practitioners->count(), + 'practitioners' => $practitioners->map(function($p) { + return [ + 'id' => $p->id, + 'full_name' => $p->full_name ?? ($p->first_name . ' ' . $p->last_name), + 'role' => $p->pivot->role ?? 'unknown' + ]; + })->toArray() ], Response::HTTP_OK); } catch (\Exception $e) { - Log::error('Error assigning practitioner to intervention: ' . $e->getMessage(), [ - 'intervention_id' => $id, + + return response()->json([ + 'message' => 'Une erreur est survenue lors de la création de l\'assignment.', + 'error' => $e->getMessage() + ], Response::HTTP_INTERNAL_SERVER_ERROR); + } + } + + /** + * Unassign a practitioner from an intervention + * + * @param Request $request + * @param int $interventionId + * @param int $practitionerId + * @return JsonResponse + */ + public function unassignPractitioner(Request $request, int $interventionId, int $practitionerId): JsonResponse + { + try { + Log::info('Unassigning practitioner from intervention', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId + ]); + + // Validate that the intervention exists + $intervention = $this->interventionRepository->findById($interventionId); + + if (!$intervention) { + return response()->json([ + 'message' => 'Intervention non trouvée.' + ], Response::HTTP_NOT_FOUND); + } + + // Check if the practitioner is actually assigned to this intervention + $isAssigned = $this->interventionPractitionerRepository->isPractitionerAssigned($interventionId, $practitionerId); + + if (!$isAssigned) { + return response()->json([ + 'message' => 'Le praticien n\'est pas assigné à cette intervention.' + ], Response::HTTP_NOT_FOUND); + } + + // Remove the practitioner assignment + $deleted = $this->interventionPractitionerRepository->removeAssignment($interventionId, $practitionerId); + + if ($deleted > 0) { + Log::info('Practitioner unassigned successfully', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId, + 'deleted_records' => $deleted + ]); + + // Reload intervention with remaining practitioners + $intervention->load('practitioners'); + $remainingPractitioners = $intervention->practitioners; + + return response()->json([ + 'data' => new InterventionResource($intervention), + 'message' => 'Praticien désassigné avec succès.', + 'remaining_practitioners_count' => $remainingPractitioners->count(), + 'remaining_practitioners' => $remainingPractitioners->map(function($p) { + return [ + 'id' => $p->id, + 'employee_name' => $p->employee->full_name ?? ($p->employee->first_name . ' ' . $p->employee->last_name), + 'role' => $p->pivot->role ?? 'unknown' + ]; + })->toArray() + ], Response::HTTP_OK); + } else { + Log::warning('No practitioner assignment found to delete', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId + ]); + + return response()->json([ + 'message' => 'Aucun assignment de praticien trouvé à supprimer.' + ], Response::HTTP_NOT_FOUND); + } + + } catch (\Exception $e) { + Log::error('Error unassigning practitioner from intervention: ' . $e->getMessage(), [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId, + 'request_data' => $request->all(), 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return response()->json([ - 'message' => 'Une erreur est survenue lors de l\'assignation du praticien.', + 'message' => 'Une erreur est survenue lors de la désassignation du praticien.', 'error' => $e->getMessage() ], Response::HTTP_INTERNAL_SERVER_ERROR); } } + + /** + * Debug endpoint to check practitioners in database + */ + public function debugPractitioners(int $id): JsonResponse + { + try { + $intervention = $this->interventionRepository->findById($id); + + // Direct database query + $dbPractitioners = DB::table('intervention_practitioner') + ->where('intervention_id', $id) + ->get(); + + // Eager loaded practitioners + $eagerPractitioners = $intervention->practitioners()->get(); + + return response()->json([ + 'intervention_id' => $id, + 'database_records' => $dbPractitioners, + 'eager_loaded_count' => $eagerPractitioners->count(), + 'eager_loaded_data' => $eagerPractitioners->map(function($p) { + return [ + 'id' => $p->id, + 'full_name' => $p->full_name ?? ($p->first_name . ' ' . $p->last_name), + 'role' => $p->pivot->role ?? 'unknown' + ]; + })->toArray() + ]); + + } catch (\Exception $e) { + Log::error('Error in debug practitioners: ' . $e->getMessage()); + return response()->json(['error' => $e->getMessage()], 500); + } + } } diff --git a/thanasoft-back/app/Http/Resources/Employee/ThanatopractitionerResource.php b/thanasoft-back/app/Http/Resources/Employee/ThanatopractitionerResource.php index 97c787d..8c131f1 100644 --- a/thanasoft-back/app/Http/Resources/Employee/ThanatopractitionerResource.php +++ b/thanasoft-back/app/Http/Resources/Employee/ThanatopractitionerResource.php @@ -3,6 +3,8 @@ namespace App\Http\Resources\Employee; use Illuminate\Http\Resources\Json\JsonResource; +use App\Http\Resources\Employee\EmployeeResource; +use App\Http\Resources\Employee\PractitionerDocumentResource; class ThanatopractitionerResource extends JsonResource { @@ -17,6 +19,10 @@ class ThanatopractitionerResource extends JsonResource return [ 'id' => $this->id, 'employee_id' => $this->employee_id, + 'employee_name' => $this->when( + $this->relationLoaded('employee'), + $this->employee->full_name ?? ($this->employee->first_name . ' ' . $this->employee->last_name) + ), 'diploma_number' => $this->diploma_number, 'diploma_date' => $this->diploma_date?->format('Y-m-d'), 'authorization_number' => $this->authorization_number, diff --git a/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php b/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php index 17dbda5..8712404 100644 --- a/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php +++ b/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php @@ -37,11 +37,44 @@ class InterventionResource extends JsonResource 'duration_min' => $this->duration_min, 'status' => $this->status, 'practitioners' => $this->whenLoaded('practitioners', function () { - return ThanatopractitionerResource::collection($this->practitioners); + return $this->practitioners->map(function ($practitioner) { + return [ + 'id' => $practitioner->id, + 'employee_id' => $practitioner->employee_id, + 'employee_name' => $practitioner->employee->full_name ?? ($practitioner->employee->first_name . ' ' . $practitioner->employee->last_name), + 'diploma_number' => $practitioner->diploma_number, + 'diploma_date' => $practitioner->diploma_date?->format('Y-m-d'), + 'authorization_number' => $practitioner->authorization_number, + 'authorization_issue_date' => $practitioner->authorization_issue_date?->format('Y-m-d'), + 'authorization_expiry_date' => $practitioner->authorization_expiry_date?->format('Y-m-d'), + 'notes' => $practitioner->notes, + 'is_authorization_valid' => $practitioner->is_authorization_valid, + 'created_at' => $practitioner->created_at?->format('Y-m-d H:i:s'), + 'updated_at' => $practitioner->updated_at?->format('Y-m-d H:i:s'), + 'role' => $practitioner->pivot->role ?? null, + ]; + }); }), 'principal_practitioner' => $this->whenLoaded('practitioners', function () { $principal = $this->practitioners->where('pivot.role', 'principal')->first(); - return $principal ? new ThanatopractitionerResource($principal) : null; + if (!$principal) { + return null; + } + return [ + 'id' => $principal->id, + 'employee_id' => $principal->employee_id, + 'employee_name' => $principal->employee->full_name ?? ($principal->employee->first_name . ' ' . $principal->employee->last_name), + 'diploma_number' => $principal->diploma_number, + 'diploma_date' => $principal->diploma_date?->format('Y-m-d'), + 'authorization_number' => $principal->authorization_number, + 'authorization_issue_date' => $principal->authorization_issue_date?->format('Y-m-d'), + 'authorization_expiry_date' => $principal->authorization_expiry_date?->format('Y-m-d'), + 'notes' => $principal->notes, + 'is_authorization_valid' => $principal->is_authorization_valid, + 'created_at' => $principal->created_at?->format('Y-m-d H:i:s'), + 'updated_at' => $principal->updated_at?->format('Y-m-d H:i:s'), + 'role' => $principal->pivot->role ?? null, + ]; }), 'attachments_count' => $this->attachments_count, 'notes' => $this->notes, diff --git a/thanasoft-back/app/Models/InterventionPractitioner.php b/thanasoft-back/app/Models/InterventionPractitioner.php new file mode 100644 index 0000000..418dc1b --- /dev/null +++ b/thanasoft-back/app/Models/InterventionPractitioner.php @@ -0,0 +1,72 @@ + 'datetime' + ]; + + /** + * Get the intervention that owns the practitioner assignment. + */ + public function intervention(): BelongsTo + { + return $this->belongsTo(Intervention::class); + } + + /** + * Get the practitioner assigned to the intervention. + */ + public function practitioner(): BelongsTo + { + return $this->belongsTo(Thanatopractitioner::class, 'practitioner_id'); + } + + /** + * Scope to get principal practitioners. + */ + public function scopePrincipal($query) + { + return $query->where('role', 'principal'); + } + + /** + * Scope to get assistant practitioners. + */ + public function scopeAssistant($query) + { + return $query->where('role', 'assistant'); + } +} diff --git a/thanasoft-back/app/Providers/AppServiceProvider.php b/thanasoft-back/app/Providers/AppServiceProvider.php index b77805f..10641f7 100644 --- a/thanasoft-back/app/Providers/AppServiceProvider.php +++ b/thanasoft-back/app/Providers/AppServiceProvider.php @@ -60,6 +60,8 @@ class AppServiceProvider extends ServiceProvider $this->app->bind(\App\Repositories\InterventionRepositoryInterface::class, \App\Repositories\InterventionRepository::class); $this->app->bind(\App\Repositories\DeceasedRepositoryInterface::class, \App\Repositories\DeceasedRepository::class); + + $this->app->bind(\App\Repositories\InterventionPractitionerRepositoryInterface::class, \App\Repositories\InterventionPractitionerRepository::class); } /** diff --git a/thanasoft-back/app/Repositories/InterventionPractitionerRepository.php b/thanasoft-back/app/Repositories/InterventionPractitionerRepository.php new file mode 100644 index 0000000..5c36d5a --- /dev/null +++ b/thanasoft-back/app/Repositories/InterventionPractitionerRepository.php @@ -0,0 +1,151 @@ +isPractitionerAssigned($interventionId, $practitionerId)) { + // If exists, update the role + $this->updatePractitionerRole($interventionId, $practitionerId, $role); + + // Return the updated record + return InterventionPractitioner::where('intervention_id', $interventionId) + ->where('practitioner_id', $practitionerId) + ->first(); + } + + $assignment = InterventionPractitioner::create([ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId, + 'role' => $role, + 'assigned_at' => $assignedAt ?: now() + ]); + + Log::info('Intervention-practitioner assignment created', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId, + 'role' => $role, + 'assignment_id' => $assignment->id + ]); + + return $assignment; + } catch (\Exception $e) { + Log::error('Error creating intervention-practitioner assignment', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId, + 'role' => $role, + 'error' => $e->getMessage() + ]); + throw $e; + } + } + + /** + * Remove all practitioner assignments for an intervention. + */ + public function removeAllAssignments(int $interventionId): int + { + $deleted = InterventionPractitioner::where('intervention_id', $interventionId)->delete(); + + Log::info('Removed all practitioner assignments for intervention', [ + 'intervention_id' => $interventionId, + 'deleted_count' => $deleted + ]); + + return $deleted; + } + + /** + * Remove specific practitioner assignment. + */ + public function removeAssignment(int $interventionId, int $practitionerId): int + { + $deleted = InterventionPractitioner::where('intervention_id', $interventionId) + ->where('practitioner_id', $practitionerId) + ->delete(); + + if ($deleted > 0) { + Log::info('Removed intervention-practitioner assignment', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId + ]); + } + + return $deleted; + } + + /** + * Check if a practitioner is already assigned to an intervention. + */ + public function isPractitionerAssigned(int $interventionId, int $practitionerId): bool + { + return InterventionPractitioner::where('intervention_id', $interventionId) + ->where('practitioner_id', $practitionerId) + ->exists(); + } + + /** + * Get all practitioner assignments for an intervention. + */ + public function getAssignmentsForIntervention(int $interventionId) + { + return InterventionPractitioner::with('practitioner') + ->where('intervention_id', $interventionId) + ->get(); + } + + /** + * Get principal practitioners for an intervention. + */ + public function getPrincipalPractitioners(int $interventionId) + { + return InterventionPractitioner::with('practitioner') + ->where('intervention_id', $interventionId) + ->principal() + ->get(); + } + + /** + * Get assistant practitioners for an intervention. + */ + public function getAssistantPractitioners(int $interventionId) + { + return InterventionPractitioner::with('practitioner') + ->where('intervention_id', $interventionId) + ->assistant() + ->get(); + } + + /** + * Update practitioner role for an intervention. + */ + public function updatePractitionerRole(int $interventionId, int $practitionerId, string $role): bool + { + $updated = InterventionPractitioner::where('intervention_id', $interventionId) + ->where('practitioner_id', $practitionerId) + ->update([ + 'role' => $role, + 'assigned_at' => now() + ]); + + if ($updated > 0) { + Log::info('Updated practitioner role for intervention', [ + 'intervention_id' => $interventionId, + 'practitioner_id' => $practitionerId, + 'new_role' => $role + ]); + } + + return $updated > 0; + } +} diff --git a/thanasoft-back/app/Repositories/InterventionPractitionerRepositoryInterface.php b/thanasoft-back/app/Repositories/InterventionPractitionerRepositoryInterface.php new file mode 100644 index 0000000..61d52bb --- /dev/null +++ b/thanasoft-back/app/Repositories/InterventionPractitionerRepositoryInterface.php @@ -0,0 +1,79 @@ +paginate($perPage); @@ -149,8 +149,43 @@ class InterventionRepository implements InterventionRepositoryInterface return Intervention::query() ->whereBetween('scheduled_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59']) - ->with(['client', 'deceased', 'location', 'assignedPractitioner']) + ->with(['client', 'deceased', 'location', 'practitioners']) ->orderBy('scheduled_at', 'asc') ->get(); } + + /** + * Add practitioners to an intervention + * + * @param Intervention $intervention + * @param array $practitionerData + * @return array Array with 'principal' and 'assistant' results + */ + public function addPractitioners(Intervention $intervention, array $practitionerData): array + { + // This method is deprecated in favor of using InterventionPractitionerRepository directly + // Implementation kept for interface compatibility + $results = [ + 'principal' => null, + 'assistant' => [] + ]; + + if (isset($practitionerData['principal_practitioner_id'])) { + $results['principal'] = [ + 'id' => $practitionerData['principal_practitioner_id'], + 'status' => 'handled_by_practitioner_repository' + ]; + } + + if (isset($practitionerData['assistant_practitioner_ids']) && is_array($practitionerData['assistant_practitioner_ids'])) { + foreach ($practitionerData['assistant_practitioner_ids'] as $assistantId) { + $results['assistant'][] = [ + 'id' => $assistantId, + 'status' => 'handled_by_practitioner_repository' + ]; + } + } + + return $results; + } } diff --git a/thanasoft-back/app/Repositories/InterventionRepositoryInterface.php b/thanasoft-back/app/Repositories/InterventionRepositoryInterface.php index 7cf3185..ec24142 100644 --- a/thanasoft-back/app/Repositories/InterventionRepositoryInterface.php +++ b/thanasoft-back/app/Repositories/InterventionRepositoryInterface.php @@ -66,4 +66,13 @@ interface InterventionRepositoryInterface * @return \Illuminate\Database\Eloquent\Collection */ public function getByMonth(int $year, int $month): \Illuminate\Database\Eloquent\Collection; + + /** + * Add practitioners to an intervention + * + * @param Intervention $intervention + * @param array $practitionerData + * @return array Array with 'principal' and 'assistant' results + */ + public function addPractitioners(Intervention $intervention, array $practitionerData): array; } diff --git a/thanasoft-back/routes/api.php b/thanasoft-back/routes/api.php index ae8a44e..dd52e3e 100644 --- a/thanasoft-back/routes/api.php +++ b/thanasoft-back/routes/api.php @@ -131,7 +131,9 @@ Route::middleware('auth:sanctum')->group(function () { Route::put('/{intervention}', [InterventionController::class, 'update']); Route::delete('/{intervention}', [InterventionController::class, 'destroy']); Route::patch('/{intervention}/status', [InterventionController::class, 'changeStatus']); - Route::patch('/{intervention}/assign', [InterventionController::class, 'assignPractitioner']); + Route::patch('/{intervention}/assign', [InterventionController::class, 'createAssignment']); + Route::patch('/{intervention}/{practitionerId}/unassignPractitioner', [InterventionController::class, 'unassignPractitioner']); + Route::get('/{intervention}/debug', [InterventionController::class, 'debugPractitioners']); }); }); diff --git a/thanasoft-front/src/components/Organism/Interventions/InterventionDetailPresentation.vue b/thanasoft-front/src/components/Organism/Interventions/InterventionDetailPresentation.vue index e5613b6..463a7ed 100644 --- a/thanasoft-front/src/components/Organism/Interventions/InterventionDetailPresentation.vue +++ b/thanasoft-front/src/components/Organism/Interventions/InterventionDetailPresentation.vue @@ -19,15 +19,13 @@