19a7afac27
- barryvdh/laravel-dompdf instalat - WorkOrderPdfService: încarcă WO cu toate relațiile (works/parts/payments), embed-ează logo ca data URI, foloseste theme_color din settings - Blade template /resources/views/pdf/work-order.blade.php: - Header cu logo + date companie + nr fișă + data - Box-uri client + auto (kilometraj/VIN/plate) - Plângere + diagnostic - Tabel manopere (h, preț/h, total) cu maistru pe fiecare rând - Tabel piese (cod, brand, qty, preț, total) - Box total cu discount + plăți efectuate + rest de achitat - Block recomandări cu fundal galben (warning) - Linii semnătură client + maistru - Footer cu timestamp generare - Action 'PDF' (icon descărcare) pe rând în lista de WO - Action 'Descarcă PDF' în header-ul paginii Edit WO
296 lines
11 KiB
PHP
296 lines
11 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="ro">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>{{ $wo->number }} — Fișă lucru</title>
|
|
<style>
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
font-family: DejaVu Sans, sans-serif;
|
|
font-size: 11px;
|
|
color: #1f2937;
|
|
margin: 0; padding: 24px;
|
|
}
|
|
h1, h2, h3 { margin: 0; }
|
|
.row { display: table; width: 100%; }
|
|
.col { display: table-cell; vertical-align: top; }
|
|
.col-half { width: 50%; }
|
|
.col-third { width: 33.33%; }
|
|
|
|
.header { border-bottom: 2px solid {{ $themeColor }}; padding-bottom: 12px; margin-bottom: 16px; }
|
|
.brand { font-size: 18px; font-weight: 700; color: {{ $themeColor }}; }
|
|
.brand-sub { font-size: 11px; color: #6b7280; margin-top: 4px; }
|
|
.doc-title {
|
|
text-align: right;
|
|
font-size: 22px; font-weight: 700;
|
|
color: {{ $themeColor }};
|
|
}
|
|
.doc-meta {
|
|
text-align: right;
|
|
font-size: 11px; color: #6b7280; margin-top: 4px;
|
|
}
|
|
|
|
.box {
|
|
border: 1px solid #e5e7eb; border-radius: 6px;
|
|
padding: 12px; margin-bottom: 12px;
|
|
}
|
|
.box h3 {
|
|
font-size: 12px; text-transform: uppercase;
|
|
color: #6b7280; margin-bottom: 8px;
|
|
border-bottom: 1px solid #f3f4f6; padding-bottom: 4px;
|
|
}
|
|
.kv { font-size: 11px; line-height: 1.6; }
|
|
.kv b { display: inline-block; min-width: 90px; color: #6b7280; font-weight: 500; }
|
|
|
|
table.items {
|
|
width: 100%; border-collapse: collapse;
|
|
margin: 8px 0 12px;
|
|
}
|
|
table.items th {
|
|
background: #f9fafb;
|
|
text-align: left; padding: 6px 8px;
|
|
font-size: 10px; text-transform: uppercase;
|
|
color: #6b7280;
|
|
border-bottom: 2px solid #e5e7eb;
|
|
}
|
|
table.items th.r, table.items td.r { text-align: right; }
|
|
table.items td {
|
|
padding: 6px 8px;
|
|
border-bottom: 1px solid #f3f4f6;
|
|
font-size: 11px;
|
|
}
|
|
table.items tfoot td {
|
|
font-weight: 700;
|
|
border-top: 2px solid #e5e7eb;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.total-box {
|
|
margin-top: 16px;
|
|
padding: 12px;
|
|
background: #f9fafb;
|
|
border-radius: 6px;
|
|
text-align: right;
|
|
}
|
|
.total-line { font-size: 11px; color: #6b7280; padding: 2px 0; }
|
|
.total-line.grand {
|
|
font-size: 16px; font-weight: 700;
|
|
color: {{ $themeColor }};
|
|
border-top: 1px solid #e5e7eb;
|
|
margin-top: 6px; padding-top: 8px;
|
|
}
|
|
|
|
.recommendations {
|
|
background: #fef3c7; border-left: 4px solid #f59e0b;
|
|
padding: 10px 12px; margin: 12px 0;
|
|
font-size: 11px;
|
|
}
|
|
.recommendations h3 {
|
|
color: #92400e; font-size: 11px;
|
|
margin-bottom: 4px; text-transform: uppercase;
|
|
}
|
|
|
|
.signatures {
|
|
margin-top: 24px;
|
|
}
|
|
.sig-cell {
|
|
width: 50%;
|
|
padding: 8px 16px;
|
|
font-size: 10px;
|
|
color: #6b7280;
|
|
}
|
|
.sig-line {
|
|
border-bottom: 1px solid #1f2937;
|
|
height: 28px;
|
|
}
|
|
|
|
.footer {
|
|
margin-top: 16px; padding-top: 8px;
|
|
border-top: 1px solid #f3f4f6;
|
|
font-size: 9px; color: #9ca3af;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<div class="row">
|
|
<div class="col col-half">
|
|
@if (!empty($logoData))
|
|
<img src="{{ $logoData }}" style="max-height: 50px; max-width: 200px;" alt="logo">
|
|
<div class="brand-sub">{{ $company->display_name ?? $company->name }}</div>
|
|
@else
|
|
<div class="brand">{{ $company->display_name ?? $company->name }}</div>
|
|
@endif
|
|
@if ($company->city)
|
|
<div class="brand-sub">{{ $company->city }}</div>
|
|
@endif
|
|
@if ($company->phone)
|
|
<div class="brand-sub">📞 {{ $company->phone }}</div>
|
|
@endif
|
|
@if ($company->email)
|
|
<div class="brand-sub">✉ {{ $company->email }}</div>
|
|
@endif
|
|
</div>
|
|
<div class="col col-half">
|
|
<div class="doc-title">FIȘĂ DE LUCRU</div>
|
|
<div class="doc-meta">Nr.: <b>{{ $wo->number }}</b></div>
|
|
<div class="doc-meta">Data deschiderii: <b>{{ $wo->opened_at?->format('d.m.Y') }}</b></div>
|
|
@if ($wo->closed_at)
|
|
<div class="doc-meta">Data închiderii: <b>{{ $wo->closed_at->format('d.m.Y') }}</b></div>
|
|
@endif
|
|
<div class="doc-meta">
|
|
Status: <b>{{ \App\Models\Tenant\WorkOrder::STATUSES[$wo->status] ?? $wo->status }}</b>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col col-half" style="padding-right: 8px;">
|
|
<div class="box">
|
|
<h3>Client</h3>
|
|
<div class="kv">
|
|
<div><b>Nume:</b> {{ $wo->client?->name ?? '—' }}</div>
|
|
<div><b>Telefon:</b> {{ $wo->client?->phone ?? '—' }}</div>
|
|
@if ($wo->client?->email)
|
|
<div><b>Email:</b> {{ $wo->client->email }}</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col col-half" style="padding-left: 8px;">
|
|
<div class="box">
|
|
<h3>Auto</h3>
|
|
<div class="kv">
|
|
<div><b>Marca/Model:</b> {{ $wo->vehicle?->make }} {{ $wo->vehicle?->model }} {{ $wo->vehicle?->year }}</div>
|
|
<div><b>Nr.:</b> {{ $wo->vehicle?->plate ?? '—' }}</div>
|
|
@if ($wo->vehicle?->vin)
|
|
<div><b>VIN:</b> {{ $wo->vehicle->vin }}</div>
|
|
@endif
|
|
<div><b>Km la intrare:</b> {{ $wo->mileage_in ? number_format($wo->mileage_in, 0, '.', ' ') : '—' }}</div>
|
|
@if ($wo->mileage_out)
|
|
<div><b>Km la ieșire:</b> {{ number_format($wo->mileage_out, 0, '.', ' ') }}</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if ($wo->complaint || $wo->diagnosis)
|
|
<div class="box">
|
|
@if ($wo->complaint)
|
|
<h3>Plângere client</h3>
|
|
<div style="margin-bottom: 8px;">{{ $wo->complaint }}</div>
|
|
@endif
|
|
@if ($wo->diagnosis)
|
|
<h3>Diagnostic</h3>
|
|
<div>{{ $wo->diagnosis }}</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
@if ($wo->works->isNotEmpty())
|
|
<h3 style="font-size: 12px; margin: 12px 0 4px; text-transform: uppercase; color: {{ $themeColor }};">Manopere</h3>
|
|
<table class="items">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 50%;">Denumire</th>
|
|
<th class="r" style="width: 12%;">Ore</th>
|
|
<th class="r" style="width: 18%;">Preț/h</th>
|
|
<th class="r" style="width: 20%;">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($wo->works as $w)
|
|
<tr>
|
|
<td>{{ $w->name }}@if ($w->master?->name) <span style="color:#9ca3af;">— {{ $w->master->name }}</span>@endif</td>
|
|
<td class="r">{{ number_format((float) $w->hours, 2) }}</td>
|
|
<td class="r">{{ number_format((float) $w->price_per_hour, 2, '.', ' ') }}</td>
|
|
<td class="r"><b>{{ number_format((float) $w->total, 2, '.', ' ') }}</b></td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="3" class="r">Subtotal manopere:</td>
|
|
<td class="r">{{ number_format((float) $worksTotal, 2, '.', ' ') }} {{ $currency }}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
@endif
|
|
|
|
@if ($wo->parts->isNotEmpty())
|
|
<h3 style="font-size: 12px; margin: 12px 0 4px; text-transform: uppercase; color: {{ $themeColor }};">Piese</h3>
|
|
<table class="items">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 40%;">Denumire</th>
|
|
<th style="width: 18%;">Cod</th>
|
|
<th class="r" style="width: 10%;">Cant.</th>
|
|
<th class="r" style="width: 14%;">Preț</th>
|
|
<th class="r" style="width: 18%;">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($wo->parts as $p)
|
|
<tr>
|
|
<td>{{ $p->name }}@if ($p->brand) <span style="color:#9ca3af;">({{ $p->brand }})</span>@endif</td>
|
|
<td>{{ $p->article ?? '—' }}</td>
|
|
<td class="r">{{ number_format((float) $p->qty, 2) }} {{ $p->unit }}</td>
|
|
<td class="r">{{ number_format((float) $p->sell_price, 2, '.', ' ') }}</td>
|
|
<td class="r"><b>{{ number_format((float) $p->total, 2, '.', ' ') }}</b></td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="4" class="r">Subtotal piese:</td>
|
|
<td class="r">{{ number_format((float) $partsTotal, 2, '.', ' ') }} {{ $currency }}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
@endif
|
|
|
|
<div class="total-box">
|
|
@if ($wo->discount_pct > 0)
|
|
<div class="total-line">Subtotal: {{ number_format((float) ($worksTotal + $partsTotal), 2, '.', ' ') }} {{ $currency }}</div>
|
|
<div class="total-line">Discount: -{{ $wo->discount_pct }}%</div>
|
|
@endif
|
|
<div class="total-line grand">
|
|
TOTAL DE PLATĂ: {{ number_format((float) $wo->total, 2, '.', ' ') }} {{ $currency }}
|
|
</div>
|
|
@if ($paid > 0)
|
|
<div class="total-line">Achitat: {{ number_format($paid, 2, '.', ' ') }} {{ $currency }}</div>
|
|
<div class="total-line"><b>Rest de achitat: {{ number_format($balance, 2, '.', ' ') }} {{ $currency }}</b></div>
|
|
@endif
|
|
</div>
|
|
|
|
@if ($wo->recommendations)
|
|
<div class="recommendations">
|
|
<h3>⚠ Recomandări</h3>
|
|
<div>{{ $wo->recommendations }}</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="signatures">
|
|
<table style="width: 100%;">
|
|
<tr>
|
|
<td class="sig-cell">
|
|
<div>Client (nume, semnătură):</div>
|
|
<div class="sig-line"></div>
|
|
</td>
|
|
<td class="sig-cell">
|
|
<div>Maistru / Recepție:</div>
|
|
<div class="sig-line"></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
Document generat automat — {{ now()->format('d.m.Y H:i') }} — {{ $company->display_name ?? $company->name }}
|
|
</div>
|
|
</body>
|
|
</html>
|