diff --git a/thanasoft-back/app/Http/Controllers/Api/InterventionController.php b/thanasoft-back/app/Http/Controllers/Api/InterventionController.php index 9c88e6f..57592a6 100644 --- a/thanasoft-back/app/Http/Controllers/Api/InterventionController.php +++ b/thanasoft-back/app/Http/Controllers/Api/InterventionController.php @@ -77,8 +77,9 @@ class InterventionController extends Controller DeceasedRepositoryInterface $deceasedRepository, QuoteRepositoryInterface $quoteRepository, ProductRepositoryInterface $productRepository, - private readonly \App\Repositories\ClientLocationRepositoryInterface $clientLocationRepository - ) { + private \App\Repositories\ClientLocationRepositoryInterface $clientLocationRepository + ) + { $this->interventionRepository = $interventionRepository; $this->interventionPractitionerRepository = $interventionPractitionerRepository; $this->clientRepository = $clientRepository; @@ -110,7 +111,8 @@ class InterventionController extends Controller $interventions = $this->interventionRepository->getAllPaginated($filters, $perPage); return response()->json(new InterventionCollection($interventions)); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error fetching interventions list: ' . $e->getMessage()); return response()->json([ @@ -131,7 +133,8 @@ class InterventionController extends Controller $intervention = $this->interventionRepository->create($validated); return response()->json(new InterventionResource($intervention), Response::HTTP_CREATED); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error creating intervention: ' . $e->getMessage()); return response()->json([ @@ -155,7 +158,8 @@ class InterventionController extends Controller $deceased = null; if (!empty($validated['deceased_id'])) { $deceased = $this->deceasedRepository->findById($validated['deceased_id']); - } else { + } + else { $deceasedData = $validated['deceased']; $deceased = $this->deceasedRepository->create($deceasedData); } @@ -163,7 +167,8 @@ class InterventionController extends Controller // Step 2: Link existing client or create a new one if (!empty($validated['client_id'])) { $client = $this->clientRepository->find($validated['client_id']); - } else { + } + else { $clientData = $validated['client']; $client = $this->clientRepository->create($clientData); } @@ -194,11 +199,11 @@ class InterventionController extends Controller } if ($locationId) { - // Fetch location to add details to notes if needed, or just rely on relation. - // For now, let's keep the legacy behavior of adding text to notes for quick reference, - // but also link the ID. Use the provided data or fetch? - // If we have an ID, we might not have the text data in $locationData if it came from search. - // So we only append text notes if we have $locationData (Create mode or if frontend sends it). + // Fetch location to add details to notes if needed, or just rely on relation. + // For now, let's keep the legacy behavior of adding text to notes for quick reference, + // but also link the ID. Use the provided data or fetch? + // If we have an ID, we might not have the text data in $locationData if it came from search. + // So we only append text notes if we have $locationData (Create mode or if frontend sends it). } if (!empty($locationData)) { @@ -234,7 +239,7 @@ class InterventionController extends Controller if (!empty($validated['intervention']['principal_practitioner_id'])) { $this->interventionPractitionerRepository->createAssignment( $intervention->id, - (int) $validated['intervention']['principal_practitioner_id'], + (int)$validated['intervention']['principal_practitioner_id'], 'principal' ); } @@ -243,22 +248,22 @@ class InterventionController extends Controller foreach ($validated['intervention']['assistant_practitioner_ids'] as $assistantId) { $this->interventionPractitionerRepository->createAssignment( $intervention->id, - (int) $assistantId, + (int)$assistantId, 'assistant' ); } } if ( - empty($validated['intervention']['principal_practitioner_id']) && - !empty($validated['intervention']['practitioners']) && - is_array($validated['intervention']['practitioners']) + empty($validated['intervention']['principal_practitioner_id']) && + !empty($validated['intervention']['practitioners']) && + is_array($validated['intervention']['practitioners']) ) { foreach ($validated['intervention']['practitioners'] as $index => $practitionerId) { $role = $index === 0 ? 'principal' : 'assistant'; $this->interventionPractitionerRepository->createAssignment( $intervention->id, - (int) $practitionerId, + (int)$practitionerId, $role ); } @@ -304,15 +309,21 @@ class InterventionController extends Controller ] ]; - $this->quoteRepository->create($quoteData); - Log::info('Quote auto-created for intervention', ['intervention_id' => $intervention->id]); - } else { + $quote = $this->quoteRepository->create($quoteData); + + // Update the intervention with the newly created quote ID + $intervention->update(['quote_id' => $quote->id]); + + Log::info('Quote auto-created for intervention', ['intervention_id' => $intervention->id, 'quote_id' => $quote->id]); + } + else { Log::warning('No intervention product found, skipping auto-quote creation', ['intervention_id' => $intervention->id]); } - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Failed to auto-create quote for intervention: ' . $e->getMessage()); - // Silently fail for the quote part to not block intervention creation + // Silently fail for the quote part to not block intervention creation } @@ -321,21 +332,21 @@ class InterventionController extends Controller if (!empty($documents)) { foreach ($documents as $documentData) { if (isset($documentData['file']) && $documentData['file']->isValid()) { - // Store the file and create intervention attachment - // This is a placeholder - implement actual file upload logic - // $path = $documentData['file']->store('intervention_documents'); - // Create intervention attachment record + // Store the file and create intervention attachment + // This is a placeholder - implement actual file upload logic + // $path = $documentData['file']->store('intervention_documents'); + // Create intervention attachment record } } } // Return all created data return [ - 'intervention' => $intervention, - 'deceased' => $deceased, - 'client' => $client, - 'contact_id' => $contactId, - 'documents_count' => count($documents) + 'intervention' => $intervention, + 'deceased' => $deceased, + 'client' => $client, + 'contact_id' => $contactId, + 'documents_count' => count($documents) ]; }); @@ -357,13 +368,15 @@ class InterventionController extends Controller ] ], Response::HTTP_CREATED); - } catch (\Illuminate\Validation\ValidationException $e) { + } + catch (\Illuminate\Validation\ValidationException $e) { // Validation errors are handled by the FormRequest return response()->json([ 'message' => 'Données invalides', 'errors' => $e->errors() ], Response::HTTP_UNPROCESSABLE_ENTITY); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error creating intervention with all data: ' . $e->getMessage(), [ 'trace' => $e->getTraceAsString(), 'input' => $request->except(['documents']) // Don't log file data @@ -385,7 +398,8 @@ class InterventionController extends Controller $intervention = $this->interventionRepository->findById($id); return response()->json(new InterventionResource($intervention)); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error fetching intervention details: ' . $e->getMessage()); return response()->json([ @@ -408,7 +422,8 @@ class InterventionController extends Controller $updatedIntervention = $this->interventionRepository->update($intervention, $validated); return response()->json(new InterventionResource($updatedIntervention)); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error updating intervention: ' . $e->getMessage()); return response()->json([ @@ -429,7 +444,8 @@ class InterventionController extends Controller $this->interventionRepository->delete($intervention); return response()->json(null, Response::HTTP_NO_CONTENT); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error deleting intervention: ' . $e->getMessage()); return response()->json([ @@ -457,7 +473,8 @@ class InterventionController extends Controller ); return response()->json(new InterventionResource($updatedIntervention)); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error changing intervention status: ' . $e->getMessage()); return response()->json([ @@ -485,15 +502,16 @@ class InterventionController extends Controller return response()->json([ 'data' => $interventions->map(function ($intervention) { - return new InterventionResource($intervention); - }), + return new InterventionResource($intervention); + }), 'meta' => [ 'total' => $interventions->count(), 'year' => $validated['year'], 'month' => $validated['month'], ] ]); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error fetching interventions by month: ' . $e->getMessage()); return response()->json([ @@ -550,15 +568,16 @@ class InterventionController extends Controller 'message' => 'Assignment(s) créé(s) avec succès.', 'practitioners_count' => $practitioners->count(), 'practitioners' => $practitioners->map(function ($p) { - return [ + return [ 'id' => $p->id, 'full_name' => $p->full_name ?? ($p->first_name . ' ' . $p->last_name), 'role' => $p->pivot->role ?? 'unknown' ]; - })->toArray() + })->toArray() ], Response::HTTP_OK); - } catch (\Exception $e) { + } + catch (\Exception $e) { return response()->json([ 'message' => 'Une erreur est survenue lors de la création de l\'assignment.', @@ -620,14 +639,15 @@ class InterventionController extends Controller 'message' => 'Praticien désassigné avec succès.', 'remaining_practitioners_count' => $remainingPractitioners->count(), 'remaining_practitioners' => $remainingPractitioners->map(function ($p) { - return [ + return [ 'id' => $p->id, 'employee_name' => $p->employee->full_name ?? ($p->employee->first_name . ' ' . $p->employee->last_name), 'role' => $p->pivot->role ?? 'unknown' ]; - })->toArray() + })->toArray() ], Response::HTTP_OK); - } else { + } + else { Log::warning('No practitioner assignment found to delete', [ 'intervention_id' => $interventionId, 'practitioner_id' => $practitionerId @@ -638,7 +658,8 @@ class InterventionController extends Controller ], Response::HTTP_NOT_FOUND); } - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error unassigning practitioner from intervention: ' . $e->getMessage(), [ 'intervention_id' => $interventionId, 'practitioner_id' => $practitionerId, @@ -675,17 +696,18 @@ class InterventionController extends Controller 'database_records' => $dbPractitioners, 'eager_loaded_count' => $eagerPractitioners->count(), 'eager_loaded_data' => $eagerPractitioners->map(function ($p) { - return [ + return [ 'id' => $p->id, 'full_name' => $p->full_name ?? ($p->first_name . ' ' . $p->last_name), 'role' => $p->pivot->role ?? 'unknown' ]; - })->toArray() + })->toArray() ]); - } catch (\Exception $e) { + } + catch (\Exception $e) { Log::error('Error in debug practitioners: ' . $e->getMessage()); return response()->json(['error' => $e->getMessage()], 500); } } -} +} \ No newline at end of file diff --git a/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php b/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php index 8712404..c0f8753 100644 --- a/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php +++ b/thanasoft-back/app/Http/Resources/Intervention/InterventionResource.php @@ -19,6 +19,11 @@ class InterventionResource extends JsonResource { return [ 'id' => $this->id, + 'quote_id' => $this->quote_id, + 'invoice_id' => $this->invoice_id, + 'quote' => $this->whenLoaded('quote', function () { + return new \App\Http\Resources\QuoteResource($this->quote); + }), 'client' => $this->whenLoaded('client', function () { return new ClientResource($this->client); }), diff --git a/thanasoft-back/app/Models/Intervention.php b/thanasoft-back/app/Models/Intervention.php index 1572028..c69bc31 100644 --- a/thanasoft-back/app/Models/Intervention.php +++ b/thanasoft-back/app/Models/Intervention.php @@ -29,7 +29,9 @@ class Intervention extends Model 'status', 'attachments_count', 'notes', - 'created_by' + 'created_by', + 'invoice_id', + 'quote_id', ]; /** @@ -79,9 +81,9 @@ class Intervention extends Model */ public function practitioners(): BelongsToMany { - return $this->belongsToMany(Thanatopractitioner::class, 'intervention_practitioner', 'intervention_id', 'practitioner_id') - ->withPivot('role', 'assigned_at') - ->withTimestamps(); + return $this->belongsToMany(Thanatopractitioner::class , 'intervention_practitioner', 'intervention_id', 'practitioner_id') + ->withPivot('role', 'assigned_at') + ->withTimestamps(); } /** @@ -97,10 +99,10 @@ class Intervention extends Model */ public function principalPractitioner(): BelongsToMany { - return $this->belongsToMany(Thanatopractitioner::class, 'intervention_practitioner', 'intervention_id', 'practitioner_id') - ->wherePivot('role', 'principal') - ->withPivot('role', 'assigned_at') - ->withTimestamps(); + return $this->belongsToMany(Thanatopractitioner::class , 'intervention_practitioner', 'intervention_id', 'practitioner_id') + ->wherePivot('role', 'principal') + ->withPivot('role', 'assigned_at') + ->withTimestamps(); } /** @@ -108,10 +110,10 @@ class Intervention extends Model */ public function assistantPractitioners(): BelongsToMany { - return $this->belongsToMany(Thanatopractitioner::class, 'intervention_practitioner', 'intervention_id', 'practitioner_id') - ->wherePivot('role', 'assistant') - ->withPivot('role', 'assigned_at') - ->withTimestamps(); + return $this->belongsToMany(Thanatopractitioner::class , 'intervention_practitioner', 'intervention_id', 'practitioner_id') + ->wherePivot('role', 'assistant') + ->withPivot('role', 'assigned_at') + ->withTimestamps(); } /** @@ -119,7 +121,7 @@ class Intervention extends Model */ public function creator(): BelongsTo { - return $this->belongsTo(User::class, 'created_by'); + return $this->belongsTo(User::class , 'created_by'); } /** @@ -135,7 +137,7 @@ class Intervention extends Model */ public function fileAttachments() { - return $this->morphMany(FileAttachment::class, 'attachable')->orderBy('sort_order'); + return $this->morphMany(FileAttachment::class , 'attachable')->orderBy('sort_order'); } /** @@ -153,4 +155,14 @@ class Intervention extends Model { return $this->hasMany(InterventionNotification::class); } -} + + public function invoice(): BelongsTo + { + return $this->belongsTo(Invoice::class); + } + + public function quote(): BelongsTo + { + return $this->belongsTo(Quote::class); + } +} \ No newline at end of file diff --git a/thanasoft-back/app/Models/Quote.php b/thanasoft-back/app/Models/Quote.php index 0f18fe0..c010ee2 100644 --- a/thanasoft-back/app/Models/Quote.php +++ b/thanasoft-back/app/Models/Quote.php @@ -70,4 +70,9 @@ class Quote extends Model ->where('document_type', 'quote') ->orderBy('changed_at', 'desc'); } + + public function interventions() + { + return $this->hasMany(Intervention::class); + } } diff --git a/thanasoft-back/app/Repositories/InterventionRepository.php b/thanasoft-back/app/Repositories/InterventionRepository.php index 60a3f17..d153757 100644 --- a/thanasoft-back/app/Repositories/InterventionRepository.php +++ b/thanasoft-back/app/Repositories/InterventionRepository.php @@ -81,7 +81,8 @@ class InterventionRepository implements InterventionRepositoryInterface 'location', 'practitioners', 'attachments', - 'notifications' + 'notifications', + 'quote' ])->findOrFail($id); } diff --git a/thanasoft-back/database/migrations/2026_03_17_124022_add_invoice_id_intervention.php b/thanasoft-back/database/migrations/2026_03_17_124022_add_invoice_id_intervention.php new file mode 100644 index 0000000..c0a8220 --- /dev/null +++ b/thanasoft-back/database/migrations/2026_03_17_124022_add_invoice_id_intervention.php @@ -0,0 +1,31 @@ +foreignId('invoice_id')->nullable()->constrained('invoices')->nullOnDelete(); + $table->foreignId('quote_id')->nullable()->constrained('quotes')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('interventions', function (Blueprint $table) { + $table->dropForeign(['invoice_id']); + $table->dropColumn('invoice_id'); + $table->dropForeign(['quote_id']); + $table->dropColumn('quote_id'); + }); + } +}; \ No newline at end of file diff --git a/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailContent.vue b/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailContent.vue index ef685c0..f777a92 100644 --- a/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailContent.vue +++ b/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailContent.vue @@ -265,6 +265,68 @@ /> + +
+
+
+
+
Détails du devis
+
+
+
+
+
+ +
    +
  • + Référence: + {{ intervention.quote.reference || '-' }} +
  • +
  • + Date: + {{ intervention.quote.quote_date || '-' }} +
  • +
  • + Statut: + {{ intervention.quote.status || '-' }} +
  • +
+
+
+
+ +
    +
  • + Total HT: + {{ intervention.quote.total_ht || '0.00' }} € +
  • +
  • + Total TVA: + {{ intervention.quote.total_tva || '0.00' }} € +
  • +
  • + Total TTC: + {{ intervention.quote.total_ttc || '0.00' }} € +
  • +
+
+
+
+
+
+
+ +
+
+
Aucun devis lié
+

+ Il n'y a pas de devis associé à cette intervention. +

+
+
+
+
+
diff --git a/thanasoft-front/src/components/molecules/intervention/InterventionTabNavigation.vue b/thanasoft-front/src/components/molecules/intervention/InterventionTabNavigation.vue index fa755f2..de60059 100644 --- a/thanasoft-front/src/components/molecules/intervention/InterventionTabNavigation.vue +++ b/thanasoft-front/src/components/molecules/intervention/InterventionTabNavigation.vue @@ -27,6 +27,12 @@ :badge="documentsCount > 0 ? documentsCount : null" @click="$emit('change-tab', 'documents')" /> +