@php use App\Support\Money; @endphp @extends('pdf.layout-rapport') @section('content') {{-- v3.5 §2 — « Je veux tous les détails disponibles de A à Z ». Onze sections, dans un ordre stable, chacune avec son sous-total. Long et complet plutôt que court et partiel : c'est exactement le reproche fait au rapport précédent, qui ne reprenait qu'une fraction de ce que l'écran montrait. --}} {{-- ─── 1. Identité ────────────────────────────────────────────────────────────────────── --}}

1. Identité du marché

@foreach ($identite as [$cle, $valeur]) @endforeach
{{ $cle }}{{ $valeur }}
{{-- ─── 2. Position financière — les quatre lignes (v3.7 §B1) ──────────────────────────── --}}

2. Position financière — les quatre lignes

Contractuel, facturé, encaissé, reste à encaisser. Jamais mélangés : un marché entièrement facturé et pas encore payé n'est pas un marché déficitaire, c'est un marché qui attend son règlement.

@if ($position['reste_a_facturer_ttc'] > 0) @endif @if ($position['hors_tresorerie_ttc'] > 0) @endif
LigneCe qu'elle mesureHTTTC
Contractuel Le montant du marché, tel que la fiche le porte {{ Money::format($position['contractuel_ht']) }} {{ Money::format($position['contractuel_ttc']) }}
Facturé {{ $position['nombre_factures'] }} facture(s) émise(s), brouillons et annulations exclus {{ Money::format($position['facture_ht']) }} {{ Money::format($position['facture_ttc']) }}
Encaissé {{ $position['nombre_factures_encaissees'] }} facture(s) réglée(s) {{ Money::format($position['encaisse_ht']) }} {{ Money::format($position['encaisse_ttc']) }}
Reste à encaisser Facturé − encaissé : ce que le client doit encore {{ Money::format($position['reste_a_encaisser_ht']) }} {{ Money::format($position['reste_a_encaisser_ttc']) }}
Reste à facturer Contractuel − facturé : ce qui n'a pas encore été émis — {{ Money::format($position['reste_a_facturer_ttc']) }}
Pour compte de tiers {{ $position['nombre_hors_tresorerie'] }} facture(s) hors trésorerie — la société les émet, l'argent ne passe pas par ses comptes {{ Money::format($position['hors_tresorerie_ht']) }} {{ Money::format($position['hors_tresorerie_ttc']) }}
{{ ucfirst($position['etat_libelle']) }}. {{ $position['phrase'] }}
{{-- ─── 3. Les trois registres (v3.4 §2) ───────────────────────────────────────────────── --}}

3. Les trois registres de chiffre d'affaires

Trois écrans ont longtemps affiché « CA encaissé » pour trois choses différentes. Ils ne sont pas réunifiés — un décompte n'est pas une facture, une facture n'est pas un encaissement — ils sont nommés.

RegistreCe qu'il mesureMontant TTC
Décomptes Jalons du marché passés « encaissé » — la vérité du marché public {{ Money::format($registres['decomptes_ttc']) }}
Encaissements Opérations de trésorerie entrantes validées — ce que la banque a vu {{ Money::format($registres['encaissements_ttc']) }}
Facturé Factures émises, brouillons et annulations exclus {{ Money::format($registres['facture_ttc']) }}
Écart entre le plus haut et le plus bas {{ Money::format(max($registres['decomptes_ttc'], $registres['encaissements_ttc'], $registres['facture_ttc']) - min($registres['decomptes_ttc'], $registres['encaissements_ttc'], $registres['facture_ttc'])) }}
@if ($registres['divergent'])
Les trois registres divergent. C'est fréquent et légitime, mais vérifiez qu'aucune pièce ne manque : un décompte encaissé sans facture émise, ou une facture réglée dont l'encaissement n'a pas été saisi, se lisent tous deux ici.
@else

Les trois registres concordent.

@endif {{-- ─── 4. Décomptes, factures, encaissements ──────────────────────────────────────────── --}}

4. Décomptes, factures, encaissements

4.1 — Décomptes (jalons du marché)

@if ($decomptes->isEmpty())

Aucun décompte enregistré sur ce marché.

@else @foreach ($decomptes as $decompte) @endforeach
DécompteMontant HTMontant TTC ÉchéanceFacturé leEncaissé leÉtat
{{ $decompte->label }} {{ Money::format($decompte->amount_ht) }} {{ Money::format($decompte->amount_ttc) }} {{ $decompte->expected_date?->format('d/m/Y') ?? '—' }} {{ $decompte->invoiced_at?->format('d/m/Y') ?? '—' }} {{ $decompte->collected_at?->format('d/m/Y') ?? '—' }} {{ $decompte->status?->label() ?? '—' }}
Sous-total — {{ $decomptes->count() }} décompte(s) {{ Money::format($decomptes->sum('amount_ht')) }} {{ Money::format($decomptes->sum('amount_ttc')) }}
@endif

4.2 — Factures émises

@if ($factures->isEmpty())

Aucune facture émise sur ce marché.

@else @foreach ($factures as $facture) @endforeach
NuméroDateÉchéanceTotal HT Total TTCNet à payerÉtat
{{ $facture->number ?? ('#'.$facture->id) }} {{ $facture->date?->format('d/m/Y') ?? '—' }} {{ $facture->due_date?->format('d/m/Y') ?? '—' }} {{ Money::format($facture->total_ht) }} {{ Money::format($facture->total_ttc) }} {{ Money::format($facture->net_payable) }} {{ $facture->status?->label() ?? '—' }}
Sous-total — {{ $factures->count() }} facture(s) {{ Money::format($factures->sum('total_ht')) }} {{ Money::format($factures->sum('total_ttc')) }} {{ Money::format($factures->sum('net_payable')) }}
@endif

4.3 — Encaissements (trésorerie entrante validée)

@if ($encaissements->isEmpty())

Aucun encaissement validé rattaché à ce marché.

@else @foreach ($encaissements as $encaissement) @endforeach
DateLibelléCompteMontant TTCÉtat
{{ $encaissement->date?->format('d/m/Y') ?? '—' }} {{ $encaissement->label }} {{ $encaissement->account?->name ?? '—' }} {{ Money::format($encaissement->amount_ttc) }} {{ $encaissement->status?->label() ?? '—' }}
Sous-total — {{ $encaissements->count() }} encaissement(s) {{ Money::format($encaissements->sum('amount_ttc')) }}
@endif {{-- ─── 4. Bons de commande ────────────────────────────────────────────────────────────── --}}

5. Bons de commande

Dans ce socle, le bon de commande est un mode de passation du marché et non une pièce séparée : il commande le circuit de dépôt (caution, acte d'engagement).

@foreach ($bonsDeCommande as [$cle, $valeur]) @endforeach
{{ $cle }}{{ $valeur }}
{{-- ─── 5. Coût humain ─────────────────────────────────────────────────────────────────── --}}

6. Coût humain

@if ($coutHumain->isEmpty())

Aucune heure approuvée sur ce marché, et aucun coût RH n'y a été ventilé.

@else @foreach ($coutHumain as $ligne) @foreach ($ligne['mois'] as $mois) @endforeach @endforeach
Collaborateur / moisHeures Coût imputéTaux horaire
{{ $ligne['nom'] }}@unless ($ligne['mode_heures']) — {{ $ligne['mode'] }}@endunless {{ $ligne['mode_heures'] || $ligne['heures'] > 0 ? number_format($ligne['heures'], 2, ',', ' ').' h' : $ligne['mode'] }} {{ $ligne['cout'] > 0 ? Money::format($ligne['cout']) : 'Aucune ventilation calculée' }} {{ $ligne['taux_horaire'] !== null ? Money::format($ligne['taux_horaire']).' /h' : '—' }}
   {{ $mois['label'] }} {{ number_format($mois['heures'], 2, ',', ' ') }} h {{ Money::format($mois['cout']) }}
Sous-total — {{ $coutHumain->count() }} collaborateur(s) {{ number_format($coutHumainTotaux['heures'], 2, ',', ' ') }} h {{ Money::format($coutHumainTotaux['cout']) }} {{ $coutHumainTotaux['heures'] > 0 ? Money::format(round($coutHumainTotaux['cout'] / $coutHumainTotaux['heures'], 2)).' /h' : '—' }}
@endif {{-- ─── 6. Dépenses ────────────────────────────────────────────────────────────────────── --}}

7. Dépenses

7.1 — Ventilation par catégorie

@if ($depenses->isEmpty())

Aucune dépense validée rattachée à ce marché.

@else @if ($depenses->contains(fn (array $l): bool => $l['parasite']))
Une ou plusieurs « catégories » portent ici le nom du marché : la reprise des données a créé une catégorie par marché au lieu de classer par nature. La ventilation ci-dessous ne dit donc pas encore de quoi ces dépenses sont faites — le reclassement se fait par reprise:reclasser-categories-marche.
@endif @foreach ($depenses as $ligne) @endforeach
CatégorieMontant HTOpérationsPart
{{ $ligne['libelle'] }} {{ Money::format($ligne['montant']) }} {{ $ligne['nb'] }} {{ number_format($ligne['part'], 1, ',', ' ') }} %
Sous-total — dépenses engagées {{ Money::format($depenses->sum('montant')) }} {{ $depenses->sum('nb') }} 100 %
@endif

7.2 — Détail, ligne par ligne

@if ($depensesDetail->isEmpty())

Aucune opération à détailler.

@else @foreach ($depensesDetail as $ligne) @endforeach
DateLibelléCatégorieMontant HTJustificatif
{{ $ligne['date'] }} {{ $ligne['libelle'] }} {{ $ligne['categorie'] }} {{ Money::format($ligne['montant']) }} {{ $ligne['justificatif'] ? 'Oui' : 'Non' }}
Sous-total — {{ $depensesDetail->count() }} opération(s), dont {{ $depensesDetail->where('justificatif', false)->count() }} sans justificatif {{ Money::format($depensesDetail->sum('montant')) }}
@endif {{-- ─── 7. Structure allouée ───────────────────────────────────────────────────────────── --}}

8. Structure allouée

Ce n'est pas une dépense du marché : c'est une quote-part calculée des charges de fonctionnement de la société. Personne n'a payé cette somme « pour ce marché ». Clé employée : {{ $structureCle }}

@if ($structure->isEmpty())

Aucune quote-part de structure calculée sur ce marché.

@else @foreach ($structure as $mois) @endforeach
MoisDétail par sourceMontant
{{ $mois['label'] }} @foreach ($mois['sources'] as $source) {{ $source['libelle'] }} {{ Money::format($source['montant']) }}@unless ($loop->last) · @endunless @endforeach {{ Money::format($mois['montant']) }}
Sous-total — structure allouée ({{ $structure->count() }} mois) {{ Money::format($structureTotal) }}
Total général — dépenses engagées + structure allouée {{ Money::format(round($depenses->sum('montant') + $structureTotal, 2)) }}
@endif {{-- ─── 8 bis. Coût complet — détail (v4.32.0 §E) ──────────────────────────────────────── --}}

8 bis. Coût complet — détail

Les cinq blocs dont la somme est le coût complet : {{ Money::format($coutComplet['total']) }}.

@foreach (['directes' => 'Dépenses directes (détail en 7.2, notes de frais et primes comprises)', 'equipe' => 'Salaires de l\'équipe', 'associes' => 'Salaires des associés', 'societe' => 'Charges de la société', 'amortissements' => 'Amortissements'] as $cle => $titre) @endforeach
BlocPartMontant
{{ $titre }} {{ number_format($coutComplet['blocs'][$cle]['part'], 1, ',', ' ') }} % {{ Money::format($coutComplet['blocs'][$cle]['total']) }}
Coût complet {{ Money::format($coutComplet['total']) }}
@foreach ([ 'equipe' => ['Salaires de l\'équipe — par collaborateur et par mois', fn ($l) => $l['nom'].' · '.$l['mois'], fn ($l) => $l['heures'] !== null && $l['cout_horaire'] !== null ? rtrim(rtrim(number_format($l['heures'], 2, ',', ' '), '0'), ',').' h × '.Money::format($l['cout_horaire']).' / h' : (string) $l['methode']], 'associes' => ['Salaires des associés — par associé et par mois', fn ($l) => $l['nom'].' · '.$l['mois'], fn ($l) => $l['cle'].($l['cout_du_mois'] !== null ? ' × coût du mois '.Money::format($l['cout_du_mois']) : '')], 'societe' => ['Charges de la société — par catégorie', fn ($l) => $l['categorie'].' · '.$l['mois'], fn ($l) => $l['cle']], 'amortissements' => ['Amortissements — par équipement', fn ($l) => $l['equipement'].' · '.$l['mois'], fn ($l) => 'dotation '.Money::format($l['dotation']).' · '.$l['cle']], ] as $cle => [$titre, $quoi, $comment]) @if ($coutComplet['blocs'][$cle]['lignes'] !== [])

{{ $titre }}

@foreach ($coutComplet['blocs'][$cle]['lignes'] as $l) @endforeach
LigneCléMontant
{{ $quoi($l) }}{{ $comment($l) }}{{ Money::format($l['montant']) }}
Sous-total {{ Money::format($coutComplet['blocs'][$cle]['total']) }}
@endif @endforeach {{-- ─── 8. Marge ───────────────────────────────────────────────────────────────────────── --}}

9. Marge

{{-- v3.7 §B1 — LES DEUX MARGES DU MARCHÉ, AVANT LE DÉTAIL DU CALCUL. La marge dégagée se mesure sur le facturé, la réalisée sur l'encaissé. Le rapport ne connaissait que la seconde : un marché facturé et pas encore payé y ressortait déficitaire. --}}

9.1 — Dégagée et réalisée

@if ($position['marge_lisible'])
Marge dégagée — sur le facturé
{{ Money::format($position['marge_degagee']) }}
{{ number_format($position['marge_degagee_pct'], 1, ',', ' ') }} % · ce que le marché a produit
Marge réalisée — sur l'encaissé
{{ Money::format($position['marge_realisee']) }}
{{ number_format($position['marge_realisee_pct'], 1, ',', ' ') }} % · ce que la trésorerie en a vu
@if ($position['reste_a_encaisser_ttc'] > 0)

Les deux diffèrent de {{ Money::format($position['reste_a_encaisser_ttc']) }} TTC : c'est exactement ce que le client doit encore. Quand il aura réglé, elles se confondront — l'écart mesure un délai de paiement, pas une perte.

@endif @else
{{ ucfirst($position['etat_libelle']) }}. {{ $position['phrase'] }} Aucune marge n'est calculée ici : ce serait l'opposé des dépenses présenté comme une rentabilité.
@endif

9.2 — Le calcul, poste par poste

Marge brute
{{ Money::format($marge['marge_brute']) }}
{{ number_format($marge['marge_brute_pct'], 1, ',', ' ') }} %
Marge après structure
{{ Money::format($marge['marge_apres_structure']) }}
{{ number_format($marge['marge_apres_structure_pct'], 1, ',', ' ') }} %
PosteMontant
Revenu retenu — {{ $marge['base_revenu'] }}{{ Money::format($marge['revenu']) }}
− Dépenses engagées (HT){{ Money::format($marge['depenses_engagees']) }}
− Notes de frais approuvées{{ Money::format($marge['notes_de_frais']) }}
− Primes liées au marché{{ Money::format($marge['primes']) }}
− Coût humain ventilé (paie){{ Money::format($marge['cout_humain']) }}
= Marge brute (avant structure) {{ Money::format($marge['marge_brute']) }}
− Structure allouée (quote-part calculée){{ Money::format($marge['structure']) }}
= Marge après structure — coût complet {{ Money::format($marge['cout_complet']) }} {{ Money::format($marge['marge_apres_structure']) }}
{{-- ─── 9. Dates clés et échéances ─────────────────────────────────────────────────────── --}}

10. Dates clés et échéances

@foreach ($datesCles as [$cle, $valeur]) @endforeach
{{ $cle }}{{ $valeur }}
@if ($echeances !== [])

10.1 — Compte à rebours

@foreach ($echeances as $echeance) @endforeach
ÉchéanceDateReste
{{ $echeance['label'] }} {{ $echeance['date']->format('d/m/Y') }} {{ $echeance['days'] < 0 ? 'dépassé de '.abs($echeance['days']).' j' : 'J-'.$echeance['days'] }}
@endif {{-- ─── 10. Cautions ───────────────────────────────────────────────────────────────────── --}}

11. Cautions

{{-- v3.7 §B2 — L'IMMOBILISÉ SE LIT AU JOURNAL, pas sur les fiches. Une fiche caution dit ce qu'on a promis ; le journal dit ce qui est réellement sorti du compte et n'est pas revenu. C'est ce second chiffre qui manque à la trésorerie du marché. --}}
Immobilisé au journal (sorties − retours) {{ Money::format($position['caution_immobilisee']) }}
@if ($position['caution_immobilisee'] < 0)
Immobilisé négatif. Une restitution est rattachée à ce marché sans le dépôt qui l'a précédée : c'est une écriture qui manque, ou une écriture rattachée au mauvais marché. À vérifier au journal avant de lire ce bloc.
@endif @if ($cautions->isEmpty())

Aucune fiche caution rattachée à ce marché.

@else @foreach ($cautions as $caution) @endforeach
TypeMontant exigéMontant immobilisé Émise leÉtat
{{ $caution->type?->label() ?? '—' }} {{ Money::format($caution->required_amount) }} {{ Money::format($caution->amount) }} {{ $caution->issued_date?->format('d/m/Y') ?? '—' }} {{ $caution->status?->label() ?? '—' }}
Sous-total — {{ $cautions->count() }} caution(s) {{ Money::format($cautions->sum('required_amount')) }} {{ Money::format($cautions->sum('amount')) }}

Rappel : une caution n'est jamais une charge ni un coût de marché — c'est une somme immobilisée, restituée à la réception. Elle n'entre donc dans aucun total ci-dessus.

@endif @endsection