@php use App\Support\Money; /** * v3.15.0 §2 — La fiche de l'AO, telle qu'elle est à l'écran, mise en document. * Aucune analyse ajoutée : les mêmes rubriques, les mêmes chiffres, dans le même ordre. */ $couleurStatut = [ 'en_preparation' => '#6b7280', 'depose' => '#0EA5E9', 'admissible' => '#2563EB', 'retenu' => '#16A34A', 'ecarte' => '#DC2626', 'annule' => '#6b7280', ]; $montant = fn ($v) => $v !== null ? Money::format($v) : '—'; $pourcent = fn (?float $v) => $v === null ? '—' : ($v > 0 ? '+' : '').number_format($v, 1, ',', ' ').' %'; $depot = null; if ($tender->deposit_date !== null) { $depot = $tender->deposit_date->format('d/m/Y'); if (filled($tender->deposit_time)) { $depot .= ' à '.$tender->deposit_time; } if (filled($tender->deposit_mode)) { $depot .= ' ('.($tender->deposit_mode === 'electronique' ? 'électronique' : $tender->deposit_mode).')'; } } @endphp @extends('pdf.layout') @section('styles') h2 { font-size: 12px; color: #0F172A; margin: 18px 0 4px; padding-bottom: 3px; border-bottom: 2px solid #F59E0B; } table.fiche { width: 100%; border-collapse: collapse; } table.fiche th { width: 30%; text-align: left; vertical-align: top; padding: 5px 6px; font-size: 10px; color: #475569; background: #F8FAFC; border-bottom: 1px solid #e5e7eb; font-weight: normal; } table.fiche td { padding: 5px 6px; font-size: 10px; border-bottom: 1px solid #e5e7eb; vertical-align: top; } .badge { display: inline-block; padding: 1px 7px; border-radius: 8px; color: #fff; font-size: 9px; font-weight: bold; } .pastille { display: inline-block; padding: 0 5px; border-radius: 7px; font-size: 8px; background: #E2E8F0; color: #475569; } tr.retenue td { background: #FEF3C7; font-weight: bold; } tr.ecartee td { color: #94a3b8; } .note { font-size: 8px; color: #94a3b8; margin-top: 4px; } .bloc { border: 1px solid #e5e7eb; background: #F8FAFC; padding: 7px 9px; font-size: 10px; white-space: pre-wrap; } .vide { color: #9ca3af; font-size: 10px; padding: 6px 0; } .lien { color: #2563EB; word-wrap: break-word; } /* Un montant coupé en deux lignes ne se lit plus : les colonnes chiffrées ne se cassent pas. */ table.data td.num, table.data th.num { white-space: nowrap; } .cadre { border-left: 4px solid #F59E0B; background: #F8FAFC; padding: 7px 10px; font-size: 10px; margin-bottom: 8px; } .cadre ul { margin: 4px 0 0 14px; padding: 0; } .avertissement { font-size: 9px; color: #92400e; background: #FEF3C7; border: 1px solid #F59E0B; padding: 5px 8px; margin-bottom: 8px; } .prix td { text-align: center; } .prix .plancher { color: #DC2626; } .prix .cible { color: #16A34A; } .prix .plafond { color: #2563EB; } .justification { font-size: 9px; color: #4b5563; margin-top: 4px; } @endsection @section('content')

Identité

Référence {{ $tender->reference ?: '—' }} Statut {{ $tender->status->label() }}  {{ $tender->stage->label() }}
Objet {{ $tender->object ?: '—' }}
Acheteur {{ $tender->buyer_name ?: '—' }} Mode de passation {{ $tender->mode_passation?->label() ?? '—' }}
Date limite de dépôt {{ $depot ?? '—' }} Durée {{ $tender->duration_label ?: '—' }}

Montants

Notre offre
{{ $montant($tender->our_offer) }}
@if ($tender->enGroupement() && $tender->our_offer !== null)
notre part {{ rtrim(rtrim(number_format($tender->partPourcent(), 2, ',', ' '), '0'), ',') }} % : {{ Money::format($tender->notrePart($tender->our_offer)) }}
@endif
Estimation dossier
{{ $montant($tender->estimated_amount) }}
Prix marché estimé
{{ $montant($tender->market_price) }}
Caution provisoire
{{ $montant($tender->caution_required) }}
@unless ($tender->cautionANotreCharge())
{{ $tender->estBonDeCommande() ? 'sans objet (bon de commande)' : 'portée par le mandataire' }}
@endunless
@if (filled($tender->pmmp_url) || filled($tender->ref_consultation) || filled($tender->org_acronyme))

Portail PMMP

@if ($tender->pmmpLink() !== null) @endif
Consultation {{ $tender->pmmpLink() }}
refConsultation {{ $tender->ref_consultation ?: '—' }} orgAcronyme {{ $tender->org_acronyme ?: '—' }}
@endif @if ($tender->enGroupement())

Groupement

Type {{ $tender->groupement_type?->label() ?? '—' }} Notre quote-part {{ rtrim(rtrim(number_format($tender->partPourcent(), 2, ',', ' '), '0'), ',') }} %
@endif @if ($decide)

Résultat & concurrence

Concurrent retenu
{{ $tender->winner_name ?: '—' }}
Offre gagnante
{{ $montant($tender->winning_offer) }}
Date du résultat
{{ $tender->result_date?->format('d/m/Y') ?? '—' }}
@if ($tender->jury_date !== null)
jury : {{ $tender->jury_date->format('d/m/Y') }}
@endif
Soumissionnaires
{{ $tender->total_bidders ?? '—' }}
Notre position
{{ $notrePosition }}
@if ($comparison === [])
Aucun résultat par concurrent n'a été saisi pour cet appel d'offres.
@else @foreach ($comparison as $ligne) @endforeach
Concurrent Offre Rang Écart / estim. Écart / notre offre Issue
{{ $ligne['competitor'] }} {{ $montant($ligne['offer']) }} {{ $ligne['rank'] ?? '—' }} {{ $pourcent($ligne['gap_reference']) }} {{ $pourcent($ligne['gap_our_offer']) }} @if ($ligne['retained']) Retenu @elseif ($ligne['eliminated']) Écarté @if ($ligne['stage'] !== null){{ $ligne['stage']->label() }}@endif @else Admissible @endif
Écart / notre offre : positif = concurrent plus cher que nous.
@endif @endif {{-- v3.16.0 §2.3 — La projection ne s'imprime qu'AVANT dépôt : passé le dépôt, c'est le bloc « Résultat & concurrence » ci-dessus qui parle. Les deux ne figurent jamais ensemble. --}} @if ($projection !== null) @php $prix = is_array($projection['recommended_price'] ?? null) ? $projection['recommended_price'] : []; $bidders = array_filter(is_array($projection['expected_bidders'] ?? null) ? $projection['expected_bidders'] : [], 'is_array'); $scenarios = array_filter(is_array($projection['scenarios'] ?? null) ? $projection['scenarios'] : [], 'is_array'); $caveats = array_filter(is_array($projection['caveats'] ?? null) ? $projection['caveats'] : [], 'is_string'); @endphp

Projection avant dépôt

Estimation IA à valider — la décision de prix reste au gérant. Les concurrents et les montants proviennent de notre historique ; le prix conseillé ne descend jamais sous notre plancher de marge.
@if (($projection['historique_suffisant'] ?? true) === false)
Historique insuffisant pour projeter : aucun concurrent connu sur cet acheteur ni sur ce type d'objet.
@endif @if ($scenarios !== [])
Scénarios
@endif @if ($bidders !== []) @foreach ($bidders as $bidder) @endforeach
Soumissionnaire attenduPrix attenduBase historique
{{ $bidder['name'] ?? '—' }} {{ isset($bidder['expected_price']) && is_numeric($bidder['expected_price']) ? Money::format($bidder['expected_price']) : '—' }} {{ $bidder['basis'] ?? '—' }}
@endif
Plancher de marge
{{ $montant($prix['floor'] ?? null) }}
Prix conseillé
{{ $montant($prix['target'] ?? null) }}
Plafond de compétitivité
{{ $montant($prix['ceiling'] ?? null) }}
Confiance
{{ $projection['confidence'] ?? '—' }}
@if ($tender->ai_projection_at !== null)
projeté le {{ $tender->ai_projection_at->format('d/m/Y') }}
@endif
@if (filled($prix['rationale'] ?? null))
{{ $prix['rationale'] }}
@endif @if ($caveats !== [])
Ce que cette projection ne dit pas
@endif @endif @if (filled($tender->notes))

Notes

{{ $tender->notes }}
@endif @endsection