@extends('admin.layout')
@section('title', __('Commande') . ' #' . ($commande['id'] ?? $modele->id))
@section('content')
@include('admin._retour', ['vers' => route('commandes.index')])
@php
// Les passages chez le laqueur, du premier au dernier. Le 1 est le
// laquage normal ; les suivants sont des relaquages, avec fautif et
// raison. Un passage sans date de retour est en cours.
//
// Calcules avant le titre : ses boutons en dependent.
$laquages = $modele->laquages;
$laquageOuvert = $laquages->firstWhere('date_retour', null);
@endphp
{{-- Toute la fiche est une zone (data-zone) : apres un geste,
js/sur-place.js la remplace sans recharger la page. En-tete, bandeaux,
suivi, onglets et fenetres dependent tous de l'etat de la commande ;
pas de zones plus fines, les blocs sont des enfants directs de la
section et le css compte dessus (.fiche-commande > .card). --}}
{{-- `data-messages` : apres un geste fait sur place, ces bandeaux
flottent en haut a droite au lieu de rester ici, hors de vue. --}}
@if (session('success'))
{{ session('success') }}
@endif
@if (session('error'))
{{ session('error') }}
@endif
{{-- Une saisie refusee dans une fenetre (fautif oublie au relaquage) :
la fenetre s'est refermee, l'erreur se lit ici. --}}
@if ($errors->any())
{{ $errors->first() }}
@endif
@if ($commande === null)
{{ __('Le service commandes est momentanément indisponible : le client et le devis Gestan ne peuvent pas être affichés. Seules les données locales sont visibles ci-dessous.') }}
@endif
@php
// Repli sur le modèle local quand l'api n'a pas répondu.
$c = $commande ?? [
'id' => $modele->id,
'client_id' => $modele->client_id,
'devis_id' => $modele->devis_id,
'couleur' => $modele->couleur,
'statut' => $modele->statut,
'etat' => $modele->etat,
'priorite' => $modele->priorite,
'finition' => $modele->finition,
'platine' => $modele->platine,
'destination' => $modele->destination,
'url' => $modele->url,
'longueur' => $modele->longueur,
'hauteur' => $modele->hauteur,
'quantite' => $modele->quantite,
'prix_usine' => $modele->prix_usine,
'maconnerie' => $modele->maconnerie,
'cintrage' => $modele->cintrage,
'negoce' => $modele->negoce,
'created_at' => $modele->created_at,
'libelle_modele' => $modele->modele,
'type' => $modele->type ? ['nom' => $modele->type->nom] : null,
];
$client = $c['client'] ?? null;
$devis = $c['devis'] ?? null;
// Les mises a jour du plan, la plus recente d'abord : la derniere
// nourrit le bandeau tant que l'usine ne l'a pas vue, et toutes
// restent comptees sous le lien du plan une fois vues. Une note
// reprise de l'ancien logiciel n'a pas de date : elle passe apres.
$notesPlan = $modele->observations
->filter(fn ($observation) => (int) $observation->type === \App\Models\Observation::TYPE_PLAN)
->sortByDesc(fn ($observation) => $observation->created_at?->timestamp ?? 0)
->values();
// « nom prenom — 12/09/2026 ». L'auteur peut manquer : note creee
// hors requete web, ou utilisateur supprime — `user_id` n'a pas de
// contrainte, la relation rend alors null. On n'affiche alors que
// la date, plutot qu'un tiret qui ferait croire a une erreur.
$signature = function ($observation) {
$auteur = $observation->user
? trim($observation->user->nom . ' ' . $observation->user->prenom)
: '';
$date = $observation->created_at?->format('d/m/Y');
return trim($auteur . ($auteur && $date ? ' — ' : '') . $date);
};
$dates = [
__('Réception accueil') => $c['date_accueil'] ?? null,
__('Bureau d’étude') => $c['date_etude'] ?? null,
__('Côtes validées') => $c['date_cotes_ok'] ?? null,
__('Visuel validé') => $c['date_visuel_ok'] ?? null,
__('Envoi laquage') => $c['date_laq_envoi'] ?? null,
__('Réception laquage') => $c['date_laq_recep'] ?? null,
__('Entrepôt') => $c['date_entrepot'] ?? null,
__('Pose') => $c['date_pose'] ?? null,
];
@endphp
{{-- ---------------- En-tete ----------------
Ce qu'on vient lire d'un coup d'œil (quoi, ou ça en est) et ce
qu'on vient faire, avant tout le reste. Les deux signalements,
plus rares, sont ranges derriere « … » : l'en-tete tient sur une
ligne. Refabriquer ouvre la creation pre-remplie avec cette
commande (CommandeController::refabriquer). --}}
@php
$typeNom = \App\Helpers\CommandeHelper::nomTraduit($c['type']['nom'] ?? null, '');
$peutSignaler = auth()->user()->canAny(['commandes.edit', 'commandes.laquage']) && !$modele->incomplet;
$peutPlan = auth()->user()->can('commandes.edit');
@endphp
{{ __('Commandée le :date', ['date' => \Carbon\Carbon::parse($c['created_at'])->format('d/m/Y')]) }}
{{-- Sortie du standby : l'echeance part d'ici, pas de la
commande (CommandeHelper::depart). A cote de la date
de commande plutot que dans la frise, qui tient ses
huit etapes sur une ligne. --}}
@if (!empty($c['lancee_le']))
· {{ __('lancée le :date', ['date' => \Carbon\Carbon::parse($c['lancee_le'])->format('d/m/Y')]) }}
@endif
@endif
@can('commandes.view')
{{ __('Fiche') }}
@endcan
@can('commandes.edit')
{{ __('Modifier') }}
@endcan
@can('commandes.create')
{{ __('Refabriquer') }}
@endcan
{{-- Laquage : un seul des deux boutons a la fois, selon qu'un passage
est ouvert. Un relaquage, c'est la meme commande qui repart :
aucune commande fille (contrairement a Refabriquer). --}}
@can('commandes.laquage')
@if ($laquageOuvert)
@else
@endif
@endcan
{{-- Plan mis a jour : toujours proposable, meme deja signale et pas
encore vu (une note de plus, une notification de plus).
Incomplete : le signalement part d'ici ; une fois signalee, les
actions passent dans le bandeau, sous ce qui manque. --}}
@if ($peutPlan || $peutSignaler)
@if ($peutPlan)
@endif
@if ($peutSignaler)
@endif
@endif
{{-- Incomplete : en tete, c'est ce qu'on doit voir en ouvrant la fiche.
Ce qui manque est dans la derniere note INCOMPLET (code 3 de
`observation_types`) ; elle reste aussi listee avec les autres. --}}
@if ($modele->incomplet)
@php
$noteIncomplet = $modele->observations
->filter(fn ($observation) => (int) $observation->type === 3)
->sortByDesc('created_at')
->first();
@endphp
@endif
{{-- Plan mis a jour, pas encore vu par l'usine : ce qui change est dans
la derniere note PLAN. Passee par __() : la note d'un lien modifie
est un texte fixe, traduit pour l'usine ; une note libre, elle,
ressort telle quelle. --}}
@if ($modele->maj_plan)
@php $notePlan = $notesPlan->first(); @endphp
{{ __('Plan mis à jour') }}
@if (!empty($c['url']))
· {{ __('Ouvrir le PDF') }}
@endif
@if ($notePlan)
@if (auth()->user()->estPortugal())
@else
{{ __('En attente de l’usine') }}
@endif
@endif
@php
$refab = $modele->refabrication;
$refaite = $modele->refabricationsEnfants;
@endphp
{{-- ---------------- Refabrication ----------------
Pourquoi cette commande existe, ou ce qu'elle a donne : une ligne
sous l'en-tete, pas un onglet, on doit le savoir en ouvrant. La
table `refabrications` est locale : l'api ne la sert pas, c'est
le seul endroit ou elle s'affiche. --}}
@if ($refab || $refaite->isNotEmpty())
@if ($refab)
{{ __('Cette commande refait la commande') }}
#{{ $refab->commande_parente_id }}
@if ($refab->commandeParente?->type)
({{ \App\Helpers\CommandeHelper::nomTraduit($refab->commandeParente->type->nom) }})
@endif
@if ($refab->created_at)
— {{ \Carbon\Carbon::parse($refab->created_at)->format('d/m/Y') }}
@endif
· {{ __('Fautif') }}
@if (filled($refab->fautif))
{{ \App\Helpers\CommandeHelper::fautif($refab->fautif) }}
@else
{{ __('Non renseigné') }}
@endif
{{-- 60 lignes sur 66 n'en portent pas : le dire plutot
que de laisser un vide. --}}
· {{ __('Raison') }}
{{ filled($refab->raison) ? $refab->raison : __('Non renseignée') }}
@endif
{{-- L'autre sens : cette commande a du etre refaite. --}}
@if ($refaite->isNotEmpty())
{{ __('Cette commande a été refaite par') }}
@foreach ($refaite as $enfant)
#{{ $enfant->commande_id }}@if (!$loop->last), @endif
@endforeach
@endif
@endif
{{-- ---------------- Suivi ----------------
Les huit dates du deroule, en frise : une etape datee est faite.
Un laquage parti et pas revenu le dit a la place de sa date. --}}
@php
$validateur = \App\Helpers\CommandeHelper::validateurVisuel($modele->validateur_visuel, '');
$laquageEnCours = !empty($c['date_laq_envoi']) && empty($c['date_laq_recep']);
@endphp
{{-- Lu sur $modele et non sur $c : le select de l'api ne porte pas
cette colonne, le modele local si, et il est toujours charge. --}}
@if ($validateur !== '')
{{ __('Visuel validé par') }} {{ $validateur }}
@endif
{{-- ---------------- Client / devis Gestan ----------------
A droite sur PC, en premier sur telephone. --}}
{{-- Le chantier : l'adresse saisie a la commande pour ce
devis (adresses_chantier), sinon celle du devis — la
meme regle que le planning (PlanningService::lieu). --}}
@php
$adresseChantier = \App\Models\AdresseChantier::pourCommande($c['devis_id'] ?? null, $modele->id);
@endphp
@if ($adresseChantier?->complete())
{{ __('Chantier') }} : {{ $adresseChantier->complete() }}
({{ __('saisie à la commande') }})
{{-- ---------------- Produit ----------------
Statut, etat, priorite, type, modele et options sont dans
l'en-tete. Une valeur vide ne s'affiche pas. --}}
@if (!empty($c['url']))
{{ __('Ouvrir le PDF') }}
{{-- Faute de plan, les poteaux 150 ont une fiche de fabrication
generee pour l'atelier. Le test porte sur la valeur brute :
le cast remonte le modele en majuscules. --}}
@elseif ($modele->getRawOriginal('modele') === 'poteau 150')
{{ __('Générer la fiche de fabrication') }}
@else
{{ __('Pas de plan') }}
@endif
{{-- Que le plan a change, une fois l'usine au courant : le
bandeau s'en va avec « Plan vu », pas l'information. Le
detail de chaque mise a jour est dans les observations. --}}
@if ($notesPlan->isNotEmpty() && !$modele->maj_plan)
@php
$derniere = $notesPlan->first()->created_at;
$fois = trans_choice('{1} Mis à jour une fois|[2,*] Mis à jour :n fois', $notesPlan->count(), ['n' => $notesPlan->count()]);
@endphp
·
{{ $derniere
? __(':fois, dernière le :date', ['fois' => $fois, 'date' => $derniere->format('d/m/Y')])
: __(':fois, dans l’ancien logiciel', ['fois' => $fois]) }}
@endif
{{-- ---------------- Contexte, en onglets ----------------
Ce qu'on consulte, pas ce qu'on vient lire en ouvrant. L'onglet
ouvert est le premier qui a quelque chose a montrer. --}}
@php
$decoupes = $c['decoupes'] ?? [];
$onglets = [
'observations' => [__('Observations'), $fil->count()],
'decoupes' => [__('Découpes'), count($decoupes)],
'laquages' => [__('Laquages'), $laquages->count()],
'historique' => [__('Historique'), count($historique)],
];
$ouvert = collect($onglets)->search(fn ($o) => $o[1] > 0) ?: 'observations';
@endphp
@foreach ($onglets as $cle => [$titre, $nombre])
@endforeach
{{-- Le fil des observations : commande et decoupes, ajout en
tete, l'auteur corrige ou retire les siennes. --}}
{{-- Un aller-retour chez le laqueur par ligne. `commandes` ne
garde que le dernier passage (colonnes date_laq_*),
l'historique complet vit dans la table `laquages`. --}}
{{-- Saisie du depart et du retour. Deux petites fenetres plutot qu'un
ecran : c'est une ligne a poser, depuis la fiche ou on la lit. --}}
@can('commandes.laquage')
@unless ($laquageOuvert)
@php $relaquage = $laquages->isNotEmpty(); @endphp
@else
@endunless
@endcan
{{-- Les decoupes oubliees a la saisie. Le meme formulaire que sous une
ligne de la liste ; ce que la commande porte deja se lit dans
l'onglet, juste derriere. --}}
@can('commandes.decoupes')
{{ __('La découpe est ajoutée en attente. Tant qu’elle n’est pas partie, la poubelle de sa ligne la retire.') }}
@include('admin.commandes._decoupes_catalogue', ['decoupes' => $catalogueDecoupes])
@endcan
{{-- Le suivi et les pieces jointes d'une ligne de decoupe (onglet
Decoupes) : les fenetres de l'ecran des decoupes. Deja dans la
zone de la fiche, d'ou `zone` a faux. --}}
@include('admin.commande_decoupes._modale-suivi', ['catalogue' => $catalogueDecoupes ?? collect(), 'zone' => false])
@include('admin.commande_decoupes._modale-joindre')
{{-- Ce qui change sur le plan. L'usine est prevenue a l'enregistrement. --}}
@can('commandes.edit')
@endcan
{{-- Ce qui manque. Rappelee sur une commande deja incomplete, elle
ajoute une note : la plus recente s'affiche dans le bandeau, les
autres restent dans les observations. --}}
@canany(['commandes.edit', 'commandes.laquage'])
@endcanany
{{-- La structure de la fiche (en-tete, blocs, onglets) est celle des fiches
Gestan ; les etiquettes (etat, statut, fautif) sont celles des listes. --}}
@endsection