feat: add sous-devis generation from commandes
From a commande, users can now create one or more sub-quotes (sous-devis) to ventilate purchases across communes for approval. - Add parent_devis_id and commande_id FKs on devis table - Order model: sousDevis() hasMany relation - Devis model: commande() and parent() relations - DevisController: createFromCommande / storeFromCommande methods - OrderController: load and pass sous_devis on show - Commandes/Show: "Créer un sous-devis" button + ventilation panel - Devis/CreateFromCommande: form pre-loaded with commande context - Devis/Index: badge "Sous-devis · CMD-XXXX" on linked quotes - Devis/Show: back-link to originating commande Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
7312c7640b
commit
8d51f4de3e
@@ -12,9 +12,9 @@ class DevisController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$devis = Devis::latest()->get();
|
||||
$devis = Devis::with(['sousDevis', 'parent'])->latest()->get();
|
||||
return Inertia::render('Devis/Index', [
|
||||
'devis' => $devis
|
||||
'devis' => $devis,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -68,12 +68,97 @@ class DevisController extends Controller
|
||||
'adresse' => "Béziers, France",
|
||||
]);
|
||||
|
||||
$devi->load(['sousDevis', 'parent']);
|
||||
|
||||
return Inertia::render('Devis/Show', [
|
||||
'devis' => $devi,
|
||||
'settings' => $settings
|
||||
'settings' => $settings,
|
||||
]);
|
||||
}
|
||||
|
||||
public function createFromCommande(\App\Models\Order $commande)
|
||||
{
|
||||
return Inertia::render('Devis/CreateFromCommande', [
|
||||
'commande' => [
|
||||
'id' => $commande->id,
|
||||
'number' => $commande->number,
|
||||
'label' => $commande->label,
|
||||
'supplier' => $commande->supplier,
|
||||
'quote_number' => $commande->quote_number,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeFromCommande(Request $request, \App\Models\Order $commande)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'destinataire_type' => 'required|string|in:commune,service_interne',
|
||||
'destinataire_nom' => 'required|string|max:255',
|
||||
'referent' => 'nullable|string|max:255',
|
||||
'date_devis' => 'required|date',
|
||||
'status' => 'required|string|in:draft,sent,accepted,rejected',
|
||||
'items' => 'required|array|min:1',
|
||||
'tva_rate' => 'nullable|numeric|min:0|max:100',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$totalHt = 0;
|
||||
foreach ($validated['items'] as $item) {
|
||||
$totalHt += floatval($item['quantite'] ?? 0) * floatval($item['prix_unitaire'] ?? 0);
|
||||
}
|
||||
|
||||
$tvaRate = floatval($validated['tva_rate'] ?? 20);
|
||||
|
||||
$validated['total_ht'] = $totalHt;
|
||||
$validated['total_ttc'] = $totalHt * (1 + ($tvaRate / 100));
|
||||
$validated['commande_id'] = $commande->id;
|
||||
$validated['commande_number'] = $commande->number;
|
||||
|
||||
Devis::create($validated);
|
||||
|
||||
return redirect()->route('commandes.show', $commande->id)
|
||||
->with('success', 'Sous-devis créé avec succès.');
|
||||
}
|
||||
|
||||
public function createSousDevis(Devis $devi)
|
||||
{
|
||||
return Inertia::render('Devis/CreateSousDevis', [
|
||||
'parentDevis' => $devi,
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeSousDevis(Request $request, Devis $devi)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'destinataire_type' => 'required|string|in:commune,service_interne',
|
||||
'destinataire_nom' => 'required|string|max:255',
|
||||
'referent' => 'nullable|string|max:255',
|
||||
'date_devis' => 'required|date',
|
||||
'status' => 'required|string|in:draft,sent,accepted,rejected',
|
||||
'items' => 'required|array|min:1',
|
||||
'tva_rate' => 'nullable|numeric|min:0|max:100',
|
||||
'notes' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$totalHt = 0;
|
||||
foreach ($validated['items'] as $item) {
|
||||
$totalHt += floatval($item['quantite'] ?? 0) * floatval($item['prix_unitaire'] ?? 0);
|
||||
}
|
||||
|
||||
$tvaRate = floatval($validated['tva_rate'] ?? 20);
|
||||
|
||||
$validated['total_ht'] = $totalHt;
|
||||
$validated['total_ttc'] = $totalHt * (1 + ($tvaRate / 100));
|
||||
$validated['parent_devis_id'] = $devi->id;
|
||||
|
||||
Devis::create($validated);
|
||||
|
||||
return redirect()->route('devis.show', $devi->id)
|
||||
->with('success', 'Sous-devis créé avec succès.');
|
||||
}
|
||||
|
||||
public function edit(Devis $devi)
|
||||
{
|
||||
return Inertia::render('Devis/Edit', [
|
||||
|
||||
@@ -194,10 +194,18 @@ class OrderController extends Controller
|
||||
{
|
||||
Gate::authorize('view', $order);
|
||||
|
||||
$order->load(['attachments', 'statusLogs.user']);
|
||||
$order->load(['attachments', 'statusLogs.user', 'sousDevis']);
|
||||
|
||||
return Inertia::render('Commandes/Show', [
|
||||
'order' => new OrderResource($order),
|
||||
'order' => new OrderResource($order),
|
||||
'sousDevis' => $order->sousDevis->map(fn($d) => [
|
||||
'id' => $d->id,
|
||||
'number' => $d->number,
|
||||
'title' => $d->title,
|
||||
'destinataire_nom' => $d->destinataire_nom,
|
||||
'total_ttc' => (float) $d->total_ttc,
|
||||
'status' => $d->status,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user