- Increased icon sizes in service items, service orders, users, and technician management for better visibility. - Added custom loading indicators with appropriate icons in search fields for vehicles, work orders, and technicians. - Introduced invoice management routes for better organization and access control. - Created a new test for the estimate PDF functionality to ensure proper rendering and data integrity.
513 lines
16 KiB
PHP
513 lines
16 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Invoice #{{ $invoice->invoice_number }}</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'DejaVu Sans', Arial, sans-serif;
|
|
font-size: 11px;
|
|
line-height: 1.4;
|
|
color: #333;
|
|
}
|
|
|
|
.header {
|
|
border-bottom: 3px solid #2563eb;
|
|
padding-bottom: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.header-content {
|
|
display: table;
|
|
width: 100%;
|
|
}
|
|
|
|
.company-info {
|
|
display: table-cell;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.invoice-info {
|
|
display: table-cell;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
text-align: right;
|
|
}
|
|
|
|
.company-name {
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
color: #2563eb;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.invoice-title {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.invoice-number {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 25px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
color: #1f2937;
|
|
margin-bottom: 10px;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
padding-bottom: 5px;
|
|
}
|
|
|
|
.info-grid {
|
|
display: table;
|
|
width: 100%;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.info-column {
|
|
display: table-cell;
|
|
width: 50%;
|
|
vertical-align: top;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.info-row {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.info-label {
|
|
font-weight: bold;
|
|
display: inline-block;
|
|
width: 100px;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-size: 10px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.status-draft {
|
|
background-color: #f3f4f6;
|
|
color: #374151;
|
|
}
|
|
|
|
.status-sent {
|
|
background-color: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.status-paid {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
}
|
|
|
|
.status-overdue {
|
|
background-color: #fee2e2;
|
|
color: #991b1b;
|
|
}
|
|
|
|
.status-cancelled {
|
|
background-color: #f3f4f6;
|
|
color: #6b7280;
|
|
}
|
|
|
|
.items-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 15px 0;
|
|
}
|
|
|
|
.items-table th,
|
|
.items-table td {
|
|
border: 1px solid #e5e7eb;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
.items-table th {
|
|
background-color: #f9fafb;
|
|
font-weight: bold;
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.items-table td {
|
|
font-size: 11px;
|
|
}
|
|
|
|
.text-right {
|
|
text-align: right;
|
|
}
|
|
|
|
.text-center {
|
|
text-align: center;
|
|
}
|
|
|
|
.type-badge {
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
font-size: 9px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.type-labour {
|
|
background-color: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.type-parts {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
}
|
|
|
|
.type-miscellaneous {
|
|
background-color: #f3f4f6;
|
|
color: #374151;
|
|
}
|
|
|
|
.summary-table {
|
|
width: 300px;
|
|
margin-left: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.summary-table td {
|
|
padding: 5px 10px;
|
|
border: none;
|
|
}
|
|
|
|
.summary-row {
|
|
border-bottom: 1px solid #e5e7eb;
|
|
}
|
|
|
|
.total-row {
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
border-top: 2px solid #2563eb;
|
|
background-color: #eff6ff;
|
|
}
|
|
|
|
.payment-section {
|
|
margin-top: 30px;
|
|
padding: 15px;
|
|
background-color: #f9fafb;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.notes-section {
|
|
margin-top: 20px;
|
|
padding: 15px;
|
|
background-color: #f9fafb;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.terms-section {
|
|
margin-top: 20px;
|
|
font-size: 10px;
|
|
color: #6b7280;
|
|
border-top: 1px solid #e5e7eb;
|
|
padding-top: 15px;
|
|
}
|
|
|
|
.footer {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
text-align: center;
|
|
font-size: 9px;
|
|
color: #9ca3af;
|
|
border-top: 1px solid #e5e7eb;
|
|
padding: 10px 0;
|
|
background-color: white;
|
|
}
|
|
|
|
.page-break {
|
|
page-break-before: always;
|
|
}
|
|
|
|
.paid-watermark {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%) rotate(-45deg);
|
|
font-size: 72px;
|
|
font-weight: bold;
|
|
color: rgba(34, 197, 94, 0.1);
|
|
z-index: -1;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
@if($invoice->status === 'paid')
|
|
<div class="paid-watermark">PAID</div>
|
|
@endif
|
|
|
|
<!-- Header -->
|
|
<div class="header">
|
|
<div class="header-content">
|
|
<div class="company-info">
|
|
<div class="company-name">{{ app(\App\Settings\GeneralSettings::class)->shop_name ?? 'Car Repairs Shop' }}</div>
|
|
<div>{{ app(\App\Settings\GeneralSettings::class)->shop_address ?? 'Shop Address' }}</div>
|
|
<div>{{ app(\App\Settings\GeneralSettings::class)->shop_phone ?? 'Phone Number' }}</div>
|
|
<div>{{ app(\App\Settings\GeneralSettings::class)->shop_email ?? 'Email Address' }}</div>
|
|
</div>
|
|
<div class="invoice-info">
|
|
<div class="invoice-title">INVOICE</div>
|
|
<div class="invoice-number">#{{ $invoice->invoice_number }}</div>
|
|
<div>Date: {{ $invoice->invoice_date->format('M j, Y') }}</div>
|
|
<div>Due: {{ $invoice->due_date->format('M j, Y') }}</div>
|
|
@if($invoice->isPaid())
|
|
<div>Paid: {{ $invoice->paid_at->format('M j, Y') }}</div>
|
|
@endif
|
|
<div style="margin-top: 10px;">
|
|
<span class="status-badge status-{{ $invoice->status }}">{{ ucfirst($invoice->status) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Customer Information -->
|
|
<div class="section">
|
|
<div class="section-title">Bill To</div>
|
|
<div class="info-grid">
|
|
<div class="info-column">
|
|
<div class="info-row">
|
|
<span class="info-label">Name:</span>
|
|
{{ $invoice->customer->name }}
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Email:</span>
|
|
{{ $invoice->customer->email }}
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">Phone:</span>
|
|
{{ $invoice->customer->phone }}
|
|
</div>
|
|
</div>
|
|
<div class="info-column">
|
|
@if($invoice->customer->address)
|
|
<div class="info-row">
|
|
<span class="info-label">Address:</span>
|
|
{{ $invoice->customer->address }}
|
|
</div>
|
|
@endif
|
|
@if($invoice->customer->city)
|
|
<div class="info-row">
|
|
<span class="info-label">City:</span>
|
|
{{ $invoice->customer->city }}, {{ $invoice->customer->state ?? '' }} {{ $invoice->customer->zip_code ?? '' }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vehicle Information (if applicable) -->
|
|
@php
|
|
$vehicle = $invoice->serviceOrder?->vehicle ?? $invoice->jobCard?->vehicle;
|
|
@endphp
|
|
@if($vehicle)
|
|
<div class="section">
|
|
<div class="section-title">Vehicle Information</div>
|
|
<div class="info-grid">
|
|
<div class="info-column">
|
|
<div class="info-row">
|
|
<span class="info-label">Vehicle:</span>
|
|
{{ $vehicle->year }} {{ $vehicle->make }} {{ $vehicle->model }}
|
|
</div>
|
|
<div class="info-row">
|
|
<span class="info-label">License:</span>
|
|
{{ $vehicle->license_plate }}
|
|
</div>
|
|
</div>
|
|
<div class="info-column">
|
|
@if($vehicle->vin)
|
|
<div class="info-row">
|
|
<span class="info-label">VIN:</span>
|
|
{{ $vehicle->vin }}
|
|
</div>
|
|
@endif
|
|
@if($vehicle->mileage)
|
|
<div class="info-row">
|
|
<span class="info-label">Mileage:</span>
|
|
{{ number_format($vehicle->mileage) }} miles
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Service Information (if applicable) -->
|
|
@if($invoice->serviceOrder || $invoice->jobCard || $invoice->estimate)
|
|
<div class="section">
|
|
<div class="section-title">Service Information</div>
|
|
@if($invoice->serviceOrder)
|
|
<div class="info-row">
|
|
<span class="info-label">Service Order:</span>
|
|
#{{ $invoice->serviceOrder->order_number }}
|
|
</div>
|
|
@endif
|
|
@if($invoice->jobCard)
|
|
<div class="info-row">
|
|
<span class="info-label">Job Card:</span>
|
|
#{{ $invoice->jobCard->job_number }}
|
|
</div>
|
|
@endif
|
|
@if($invoice->estimate)
|
|
<div class="info-row">
|
|
<span class="info-label">Estimate:</span>
|
|
#{{ $invoice->estimate->estimate_number }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Line Items -->
|
|
<div class="section">
|
|
<div class="section-title">Services & Parts</div>
|
|
@if($invoice->lineItems->count() > 0)
|
|
<table class="items-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Type</th>
|
|
<th>Description</th>
|
|
<th class="text-center">Qty</th>
|
|
<th class="text-right">Unit Price</th>
|
|
<th class="text-right">Total</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($invoice->lineItems as $item)
|
|
<tr>
|
|
<td>
|
|
<span class="type-badge type-{{ $item->type }}">
|
|
{{ ucfirst($item->type) }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{{ $item->description }}
|
|
@if($item->part)
|
|
<br><small style="color: #6b7280;">Part #: {{ $item->part->part_number }}</small>
|
|
@endif
|
|
@if($item->part_number)
|
|
<br><small style="color: #6b7280;">Part #: {{ $item->part_number }}</small>
|
|
@endif
|
|
@if($item->technical_notes)
|
|
<br><small style="color: #6b7280;">{{ $item->technical_notes }}</small>
|
|
@endif
|
|
</td>
|
|
<td class="text-center">{{ $item->quantity }}</td>
|
|
<td class="text-right">${{ number_format($item->unit_price, 2) }}</td>
|
|
<td class="text-right">${{ number_format($item->total_amount, 2) }}</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
@else
|
|
<div style="text-align: center; padding: 20px; color: #6b7280;">
|
|
No line items available
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Summary -->
|
|
<table class="summary-table">
|
|
<tr class="summary-row">
|
|
<td>Subtotal:</td>
|
|
<td class="text-right">${{ number_format($invoice->subtotal, 2) }}</td>
|
|
</tr>
|
|
@if($invoice->discount_amount > 0)
|
|
<tr class="summary-row">
|
|
<td>Discount:</td>
|
|
<td class="text-right" style="color: #dc2626;">-${{ number_format($invoice->discount_amount, 2) }}</td>
|
|
</tr>
|
|
@endif
|
|
<tr class="summary-row">
|
|
<td>Tax ({{ $invoice->tax_rate }}%):</td>
|
|
<td class="text-right">${{ number_format($invoice->tax_amount, 2) }}</td>
|
|
</tr>
|
|
<tr class="total-row">
|
|
<td><strong>Total:</strong></td>
|
|
<td class="text-right"><strong>${{ number_format($invoice->total_amount, 2) }}</strong></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<!-- Payment Information -->
|
|
@if($invoice->isPaid())
|
|
<div class="payment-section">
|
|
<div class="section-title">Payment Information</div>
|
|
<div>
|
|
<p><strong>Payment Date:</strong> {{ $invoice->paid_at->format('M j, Y g:i A') }}</p>
|
|
@if($invoice->payment_method)
|
|
<p><strong>Payment Method:</strong> {{ ucfirst(str_replace('_', ' ', $invoice->payment_method)) }}</p>
|
|
@endif
|
|
@if($invoice->payment_reference)
|
|
<p><strong>Reference:</strong> {{ $invoice->payment_reference }}</p>
|
|
@endif
|
|
@if($invoice->payment_notes)
|
|
<p><strong>Notes:</strong> {{ $invoice->payment_notes }}</p>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="payment-section">
|
|
<div class="section-title">Payment Information</div>
|
|
<div>
|
|
<p>Payment is due by {{ $invoice->due_date->format('M j, Y') }}.</p>
|
|
<p>Please include invoice number {{ $invoice->invoice_number }} with your payment.</p>
|
|
<p class="mt-2">
|
|
<strong>Payment Methods:</strong> Cash, Credit Card, Check, Bank Transfer
|
|
</p>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Customer Notes -->
|
|
@if($invoice->notes)
|
|
<div class="notes-section">
|
|
<div class="section-title">Notes</div>
|
|
<div style="white-space: pre-wrap;">{{ $invoice->notes }}</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Terms & Conditions -->
|
|
@if($invoice->terms_and_conditions)
|
|
<div class="terms-section">
|
|
<div style="font-weight: bold; margin-bottom: 10px;">Terms & Conditions</div>
|
|
<div style="white-space: pre-wrap;">{{ $invoice->terms_and_conditions }}</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Footer -->
|
|
<div class="footer">
|
|
<div>Invoice generated on {{ now()->format('M j, Y g:i A') }}</div>
|
|
@if($invoice->createdBy)
|
|
<div>Prepared by: {{ $invoice->createdBy->name }}</div>
|
|
@endif
|
|
</div>
|
|
</body>
|
|
</html>
|