feat: add Devis and BesoinInformatique modules
- Devis: CRUD complet, génération PDF (dompdf), paramètres (DevisSetting) - BesoinInformatique: CRUD complet avec gestion matériels/licences - Migrations pour les 3 nouvelles tables - Navigation mise à jour dans AuthenticatedLayout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
030d76af53
commit
7312c7640b
@@ -0,0 +1,259 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Devis {{ $devis->number }}</title>
|
||||
<style>
|
||||
@page { margin: 0; size: A4; }
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: 'DejaVu Sans', sans-serif;
|
||||
color: #334155;
|
||||
font-size: 8.5pt;
|
||||
line-height: 1.5;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/* ─── PAGE WRAPPER ─────────────────────────────────── */
|
||||
.page { padding: 32px 40px; }
|
||||
|
||||
/* ─── SECTION SPACER ────────────────────────────────── */
|
||||
.section-gap { height: 22px; }
|
||||
.section-gap-sm { height: 12px; }
|
||||
|
||||
/* ─── HEADER ────────────────────────────────────────── */
|
||||
.header-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
.header-table td { border: none; vertical-align: middle; padding: 0 0 14px 0; }
|
||||
.header-logo { height: 70px; width: auto; vertical-align: middle; margin-right: 14px; }
|
||||
.header-entity-name {
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.5px;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.deco-squares { margin-top: 3px; }
|
||||
.deco-sq { display: inline-block; width: 13px; height: 11px; margin-right: 3px; }
|
||||
.header-right {
|
||||
text-align: right;
|
||||
font-size: 7.5pt;
|
||||
color: #64748b;
|
||||
line-height: 1.65;
|
||||
font-weight: 600;
|
||||
}
|
||||
.header-right .entity-sub { font-weight: 800; color: #1e293b; display: block; margin-bottom: 2px; }
|
||||
|
||||
/* ─── INFO CARDS ────────────────────────────────────── */
|
||||
.cards-table { width: 100%; border-collapse: collapse; }
|
||||
.cards-table td { border: none; padding: 0; vertical-align: top; }
|
||||
.card { border: 2px solid #0f172a; border-radius: 24px; padding: 20px 24px; }
|
||||
.card-title {
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.3px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.card-body { font-size: 8pt; color: #334155; line-height: 1.7; }
|
||||
.card-label { font-weight: bold; color: #0f172a; }
|
||||
.card-dest-name { font-weight: bold; color: #0f172a; font-size: 9pt; display: block; margin-bottom: 3px; }
|
||||
.card-dest-type { font-weight: 700; color: #64748b; font-size: 7.5pt; display: block; margin-bottom: 7px; }
|
||||
.card-referent-sep { border-top: 1px solid #e2e8f0; margin-top: 7px; padding-top: 7px; }
|
||||
|
||||
/* ─── ITEMS TABLE ───────────────────────────────────── */
|
||||
.items-table { width: 100%; border-collapse: collapse; }
|
||||
.items-table thead tr { background-color: #f1f5f9; }
|
||||
.items-table th {
|
||||
font-size: 7.5pt;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
color: #0f172a;
|
||||
letter-spacing: 0.4px;
|
||||
padding: 10px 20px;
|
||||
text-align: left;
|
||||
border: none;
|
||||
}
|
||||
.items-table td {
|
||||
padding: 13px 20px;
|
||||
border-bottom: 2px dotted #cbd5e1;
|
||||
vertical-align: middle;
|
||||
border-top: none; border-left: none; border-right: none;
|
||||
}
|
||||
.item-designation { font-weight: bold; color: #0f172a; font-size: 9pt; margin-bottom: 2px; }
|
||||
.item-type { font-weight: bold; color: #94a3b8; font-size: 7pt; text-transform: uppercase; letter-spacing: 0.4px; }
|
||||
.col-price { text-align: right; font-size: 8.5pt; }
|
||||
.col-qty { text-align: center; font-weight: bold; color: #0f172a; font-size: 8.5pt; }
|
||||
.col-total { text-align: right; font-weight: bold; color: #0f172a; font-size: 9pt; }
|
||||
|
||||
/* ─── TOTAUX ────────────────────────────────────────── */
|
||||
.sig-label {
|
||||
font-size: 7pt;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 0.4px;
|
||||
display: block;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.sig-line { border-bottom: 1px dashed #94a3b8; width: 200px; height: 8px; margin-bottom: 5px; }
|
||||
.sig-note { font-size: 6.5pt; color: #94a3b8; font-style: italic; }
|
||||
|
||||
/* ─── FOOTER ────────────────────────────────────────── */
|
||||
.footer-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer-table td { border: none; padding: 0; vertical-align: top; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
|
||||
{{-- ─── HEADER ─────────────────────────────────────────── --}}
|
||||
<table class="header-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 60%;">
|
||||
<table style="border-collapse: collapse; width: 100%;"><tbody><tr>
|
||||
<td style="border: none; padding: 0; vertical-align: middle; width: 1%;">
|
||||
@if(file_exists(public_path('logo.png')))
|
||||
<img src="data:image/png;base64,{{ base64_encode(file_get_contents(public_path('logo.png'))) }}"
|
||||
class="header-logo" alt="Logo">
|
||||
@endif
|
||||
</td>
|
||||
<td style="border: none; padding: 0; vertical-align: middle; padding-left: 20px;">
|
||||
<span class="header-entity-name">{{ $settings->entity_name ?? "Direction des Systèmes d'Information" }}</span>
|
||||
<div class="deco-squares">
|
||||
<span class="deco-sq" style="background-color:#047857;"></span>
|
||||
<span class="deco-sq" style="background-color:#14b8a6;"></span>
|
||||
<span class="deco-sq" style="background-color:#f59e0b;"></span>
|
||||
<span class="deco-sq" style="background-color:#f43f5e;"></span>
|
||||
<span class="deco-sq" style="background-color:#f472b6;"></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr></tbody></table>
|
||||
</td>
|
||||
<td style="width: 40%; text-align: right;" class="header-right">
|
||||
<span class="entity-sub">{{ $settings->entity_sub ?? "Communauté d'Agglomération Béziers Méditerranée" }}</span>
|
||||
@if(!empty($settings->telephone))<span>Téléphone : {{ $settings->telephone }}</span><br>@endif
|
||||
@if(!empty($settings->email))<span>E-mail : {{ $settings->email }}</span><br>@endif
|
||||
@if(!empty($settings->adresse))<span>Adresse : {{ $settings->adresse }}</span>@endif
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="section-gap"></div>
|
||||
|
||||
{{-- ─── INFO CARDS ──────────────────────────────────────── --}}
|
||||
<table class="cards-table">
|
||||
<tbody><tr>
|
||||
<td style="width: 48%;">
|
||||
<div class="card">
|
||||
<div class="card-title">DEVIS N° : {{ $devis->number }}</div>
|
||||
<div class="card-body">
|
||||
<span class="card-label">DATE :</span> {{ \Carbon\Carbon::parse($devis->date_devis)->format('d/m/Y') }}<br>
|
||||
<span class="card-label">OBJET :</span> {{ $devis->title }}
|
||||
@if(!empty($devis->notes))
|
||||
<br><span class="card-label">CONDITIONS :</span> {{ $devis->notes }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 4%;"></td>
|
||||
<td style="width: 48%;">
|
||||
<div class="card">
|
||||
<div class="card-title">DESTINATAIRE :</div>
|
||||
<div class="card-body">
|
||||
<span class="card-dest-name">{{ $devis->destinataire_nom }}</span>
|
||||
<span class="card-dest-type">
|
||||
{{ $devis->destinataire_type === 'commune' ? 'Commune / Collectivité' : 'Service Interne' }}
|
||||
</span>
|
||||
@if(!empty($devis->referent))
|
||||
<div class="card-referent-sep">
|
||||
<span class="card-label">À L'ATTENTION DE :</span> {{ $devis->referent }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
<div class="section-gap"></div>
|
||||
|
||||
{{-- ─── TABLEAU LIGNES ──────────────────────────────────── --}}
|
||||
<table class="items-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Description :</th>
|
||||
<th style="text-align: right;">Prix Unitaire :</th>
|
||||
<th style="text-align: center;">Quantité :</th>
|
||||
<th style="text-align: right;">Total :</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($devis->items as $item)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="item-designation">{{ $item['designation'] ?? '' }}</div>
|
||||
<div class="item-type">{{ $item['type'] ?? '' }}</div>
|
||||
</td>
|
||||
<td class="col-price">{{ number_format($item['prix_unitaire'] ?? 0, 2, ',', "\u{202F}") }} €</td>
|
||||
<td class="col-qty">{{ $item['quantite'] ?? 0 }}</td>
|
||||
<td class="col-total">{{ number_format(($item['quantite'] ?? 0) * ($item['prix_unitaire'] ?? 0), 2, ',', "\u{202F}") }} €</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="section-gap-sm"></div>
|
||||
|
||||
{{-- ─── TOTAUX ───────────────────────────────────────────── --}}
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tbody><tr>
|
||||
<td style="border: none; padding: 0;"></td>
|
||||
<td style="border: none; padding: 0; width: 280px; text-align: right;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border: none; padding: 4px 0; font-weight: bold; font-size: 8.5pt; color: #0f172a; text-align: left;">TOTAL HT :</td>
|
||||
<td style="border: none; padding: 4px 0; font-weight: bold; font-size: 8.5pt; color: #0f172a; text-align: right;">{{ number_format($devis->total_ht, 2, ',', "\u{202F}") }} €</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border: none; padding: 4px 0; font-weight: bold; font-size: 8.5pt; color: #0f172a; text-align: left;">TVA ({{ $devis->tva_rate }}%) :</td>
|
||||
<td style="border: none; padding: 4px 0; font-weight: bold; font-size: 8.5pt; color: #0f172a; text-align: right;">{{ number_format($devis->total_ttc - $devis->total_ht, 2, ',', "\u{202F}") }} €</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border: none; border-top: 2px solid #0f172a; padding: 9px 0 4px 0; font-weight: bold; font-size: 10.5pt; color: #0f172a; text-align: left;">TOTAL TTC :</td>
|
||||
<td style="border: none; border-top: 2px solid #0f172a; padding: 9px 0 4px 0; font-weight: 800; font-size: 10.5pt; color: #0284c7; text-align: right;">{{ number_format($devis->total_ttc, 2, ',', "\u{202F}") }} €</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
{{-- ─── FOOTER : SIGNATURE DESTINATAIRE ────────────────── --}}
|
||||
<table class="footer-table">
|
||||
<tbody><tr>
|
||||
<td style="vertical-align: top; padding-top: 22px;">
|
||||
<span class="sig-label">Pour le Destinataire (Bon pour accord)</span>
|
||||
<div class="sig-line"></div>
|
||||
<span class="sig-note">Nom, Date et Signature</span>
|
||||
</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user