Link internvetion and invoice and quote
This commit is contained in:
parent
8ee7d8f8e9
commit
ebd171e9de
@ -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);
|
||||
}
|
||||
@ -304,13 +309,19 @@ 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
|
||||
}
|
||||
@ -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([
|
||||
@ -493,7 +510,8 @@ class InterventionController extends Controller
|
||||
'month' => $validated['month'],
|
||||
]
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
Log::error('Error fetching interventions by month: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
@ -558,7 +576,8 @@ class InterventionController extends Controller
|
||||
})->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.',
|
||||
@ -627,7 +646,8 @@ class InterventionController extends Controller
|
||||
];
|
||||
})->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,
|
||||
@ -683,7 +704,8 @@ class InterventionController extends Controller
|
||||
})->toArray()
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
Log::error('Error in debug practitioners: ' . $e->getMessage());
|
||||
return response()->json(['error' => $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}),
|
||||
|
||||
@ -29,7 +29,9 @@ class Intervention extends Model
|
||||
'status',
|
||||
'attachments_count',
|
||||
'notes',
|
||||
'created_by'
|
||||
'created_by',
|
||||
'invoice_id',
|
||||
'quote_id',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -70,4 +70,9 @@ class Quote extends Model
|
||||
->where('document_type', 'quote')
|
||||
->orderBy('changed_at', 'desc');
|
||||
}
|
||||
|
||||
public function interventions()
|
||||
{
|
||||
return $this->hasMany(Intervention::class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +81,8 @@ class InterventionRepository implements InterventionRepositoryInterface
|
||||
'location',
|
||||
'practitioners',
|
||||
'attachments',
|
||||
'notifications'
|
||||
'notifications',
|
||||
'quote'
|
||||
])->findOrFail($id);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('interventions', function (Blueprint $table) {
|
||||
$table->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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -265,6 +265,68 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Quote Tab -->
|
||||
<div v-if="activeTab === 'quote'" class="tab-pane fade show active">
|
||||
<div class="card">
|
||||
<div class="card-header pb-0">
|
||||
<div class="d-flex align-items-center">
|
||||
<h6 class="mb-0">Détails du devis</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="intervention.quote" class="row mt-4">
|
||||
<div class="col-md-6 mb-3">
|
||||
<InfoCard title="Informations" icon="fas fa-file-invoice text-info">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item mx-0 px-0">
|
||||
<strong class="text-dark">Référence:</strong>
|
||||
<span class="ms-2">{{ intervention.quote.reference || '-' }}</span>
|
||||
</li>
|
||||
<li class="list-group-item mx-0 px-0">
|
||||
<strong class="text-dark">Date:</strong>
|
||||
<span class="ms-2">{{ intervention.quote.quote_date || '-' }}</span>
|
||||
</li>
|
||||
<li class="list-group-item mx-0 px-0">
|
||||
<strong class="text-dark">Statut:</strong>
|
||||
<span class="ms-2 badge badge-sm bg-gradient-success">{{ intervention.quote.status || '-' }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</InfoCard>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<InfoCard title="Montants" icon="fas fa-euro-sign text-success">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item mx-0 px-0 d-flex justify-content-between">
|
||||
<strong class="text-dark">Total HT:</strong>
|
||||
<span>{{ intervention.quote.total_ht || '0.00' }} €</span>
|
||||
</li>
|
||||
<li class="list-group-item mx-0 px-0 d-flex justify-content-between">
|
||||
<strong class="text-dark">Total TVA:</strong>
|
||||
<span>{{ intervention.quote.total_tva || '0.00' }} €</span>
|
||||
</li>
|
||||
<li class="list-group-item mx-0 px-0 d-flex justify-content-between border-0">
|
||||
<strong class="text-dark">Total TTC:</strong>
|
||||
<span class="fw-bold">{{ intervention.quote.total_ttc || '0.00' }} €</span>
|
||||
</li>
|
||||
</ul>
|
||||
</InfoCard>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-5">
|
||||
<div class="avatar avatar-xl mb-3">
|
||||
<div class="avatar-title bg-gradient-secondary text-white h5 mb-0">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h6 class="text-sm text-muted">Aucun devis lié</h6>
|
||||
<p class="text-xs text-muted mb-3">
|
||||
Il n'y a pas de devis associé à cette intervention.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- History Tab -->
|
||||
<div v-if="activeTab === 'history'" class="tab-pane fade show active">
|
||||
<div class="card">
|
||||
|
||||
@ -27,6 +27,12 @@
|
||||
:badge="documentsCount > 0 ? documentsCount : null"
|
||||
@click="$emit('change-tab', 'documents')"
|
||||
/>
|
||||
<TabNavigationItem
|
||||
icon="fas fa-file-invoice"
|
||||
label="Devis"
|
||||
:is-active="activeTab === 'quote'"
|
||||
@click="$emit('change-tab', 'quote')"
|
||||
/>
|
||||
<TabNavigationItem
|
||||
icon="fas fa-history"
|
||||
label="Historique"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user