@php // $preview = true renders the same template for the on-screen preview // (served as HTML rather than dompdf). The only differences: the logo is // loaded by URL instead of base64 (dompdf needs the bytes inlined; a browser // does not), so the shared markup/CSS below stays the single source of truth. $preview = $preview ?? false; // When true, the totals box shows the refunded amount and the adjusted net // total, and a refund-history table is printed below the payment history. $showRefunds = ($showRefunds ?? false) && (float) $invoice->refunded_total > 0; $netTotal = round((float) $invoice->total - (float) $invoice->refunded_total, 2); $primary = $setting?->primary_color ?: '#3B82F6'; $headerColor = $setting?->table_header_color ?: $primary; $symbol = $currencySymbol ?: ''; $code = $invoice->currency_code ?: ''; $money = function ($value) use ($symbol, $code) { $formatted = number_format((float) $value, 2); return ($symbol !== '' ? $symbol . ' ' : ($code !== '' ? $code . ' ' : '')) . $formatted; }; // The badge says whether it is paid (Unpaid / Partially paid / Paid / // Void), never the internal send state: a customer has no use for "SENT". $statusKey = $invoice->isOverdue() ? 'OVERDUE' : $invoice->displayStatus(); $statusColors = [ 'UNPAID' => '#f59e0b', 'PARTIAL' => '#3b82f6', 'PAID' => '#10b981', 'OVERDUE' => '#ef4444', 'VOID' => '#6b7280', ]; $statusColor = $statusColors[$statusKey] ?? '#6b7280'; $statusText = $statusKey === 'OVERDUE' ? __('Unpaid, overdue') : $invoice->displayLabel(); $logoData = null; if ($setting?->logo_path) { try { if ($preview) { $logoData = \Illuminate\Support\Facades\Storage::disk('public')->url($setting->logo_path); } else { $logoFull = \Illuminate\Support\Facades\Storage::disk('public')->path($setting->logo_path); if (is_file($logoFull)) { $logoData = 'data:' . (mime_content_type($logoFull) ?: 'image/png') . ';base64,' . base64_encode(file_get_contents($logoFull)); } } } catch (\Throwable $e) { $logoData = null; } } // Banking / payment details are only useful while money is still owed. // Once an invoice is fully paid (or voided) we don't attach them. $needsPayment = $invoice->status !== 'VOID' && (float) $invoice->balance_due > 0.01; // Short link a customer can type off the printed page. Only on an unpaid // invoice, a settled one should not invite another payment. $payLink = $needsPayment ? $invoice->shortPayUrl() : null; $payLinkPlain = $payLink ? preg_replace('#^https?://#', '', $payLink) : null; // How this invoice can be paid, resolved for ITS currency: a USD account // never appears on a ZAR invoice. Resolved here rather than in the // controller because this template is rendered from eight places (preview, // download, portal, email attachment, combined letters) and every one of // them has to agree. // // The payment method goes in because the bank details are conditional on // it: they print only when the customer said they were paying by transfer, // which is the only case where this total has the transfer charge in it. $banking = \App\Support\DocumentBanking::for( $invoice->team_id, $invoice->currency_code, $setting, $invoice->paymentMethod ); // The printed page carries bank details and the pay link only, so it is // those two that decide whether there is a page at all. $hasBankingPage = $needsPayment && ($banking['showsBanking'] || $payLink); @endphp {{ $invoice->invoice_number }}
@if ($logoData)
@endif

{{ $setting?->company_name ?: $invoice->team?->name }}

@if ($setting?->address)

{{ __('Address') }}: {!! nl2br(e($setting->address)) !!}

@endif @if ($setting?->email)

{{ __('Email') }}: {{ $setting->email }}

@endif @if ($setting?->website)

{{ __('Web') }}: {{ $setting->website }}

@endif @if ($setting?->phone)

{{ __('Phone') }}: {{ $setting->phone }}

@endif @if ($setting?->tin_number)

{{ __('TIN') }}: {{ $setting->tin_number }}

@endif

{{ __('Invoice') }}

{{ __('Invoice #') }}: {{ $invoice->invoice_number }}
{{ __('Date') }}: {{ $invoice->created_at?->format('d/m/Y') }}
@if ($invoice->due_date)
{{ __('Due Date') }}: {{ $invoice->due_date->format('d/m/Y') }}
@endif {{ $statusText }}

{{ __('Bill To') }}

{{ $invoice->payerName() ?: '—' }}

@if ($invoice->customer) @if ($invoice->customer->address)

{{ __('Address') }}: {!! nl2br(e($invoice->customer->address)) !!}

@endif @if ($invoice->customer->email)

{{ __('Email') }}: {{ $invoice->customer->email }}

@endif @if ($invoice->customer->phone)

{{ __('Phone') }}: {{ $invoice->customer->phone }}

@endif @if ($invoice->customer->tin_number)

{{ __('TIN') }}: {{ $invoice->customer->tin_number }}

@endif @elseif ($invoice->tenant) @if ($invoice->tenant->space)

{{ __('Space') }}: {{ $invoice->tenant->space }}

@endif @if ($invoice->tenant->email)

{{ __('Email') }}: {{ $invoice->tenant->email }}

@endif @if ($invoice->tenant->phone)

{{ __('Phone') }}: {{ $invoice->tenant->phone }}

@endif @endif
@forelse ($invoice->lineItems as $i => $line) @empty @endforelse
# {{ __('Item Description') }} {{ __('Qty') }} {{ __('Rate') }} {{ __('Discount') }} {{ __('Total') }}
{{ $i + 1 }}
{{ $line->name }}
@if ($line->description)
{!! nl2br(e($line->description)) !!}
@endif
{{ number_format((float) $line->qty, 2) }} {{ $money($line->unit_price) }} {{ $money($line->discount) }} {{ $money($line->line_total) }}
{{ __('No items.') }}
@if ((float) $invoice->discount > 0) @endif @if ((float) $invoice->surcharge > 0) @endif @if ((float) $invoice->amount_paid > 0) @endif @if ($showRefunds) @endif
{{ __('Sub Total') }}: {{ $money($invoice->subtotal) }}
{{ __('VAT') }}: {{ $money($invoice->vat) }}
{{ __('Discount') }}: - {{ $money($invoice->discount) }}
{{ \App\Models\PaymentMethod::feeLabel() }}: {{ $money($invoice->surcharge) }}
{{ __('TOTAL') }}: {{ $money($invoice->total) }}
{{ __('Amount Paid') }}: {{ $money($invoice->amount_paid) }}
{{ __('Balance Due') }}: {{ $money($invoice->balance_due) }}
{{ __('Refunded') }}: - {{ $money($invoice->refunded_total) }}
{{ __('Net Total') }}: {{ $money($netTotal) }}
@if ($preview && $invoice->payments->isNotEmpty())
{{ __('Payment History') }}
@foreach ($invoice->payments->sortBy('paid_at')->values() as $i => $payment) @endforeach
# {{ __('Date') }} {{ __('Method') }} {{ __('Reference') }} {{ __('Amount') }}
{{ $i + 1 }} {{ optional($payment->paid_at)->format('d/m/Y') }} {{ $payment->channel }} @if ((float) $payment->surcharge > 0) (+{{ $money($payment->surcharge) }} {{ __('fee') }}) @endif {{ $payment->reference ?: '—' }} {{ $money($payment->amount) }}
{{ __('Total Paid') }}: {{ $money($invoice->amount_paid) }}
@endif @if ($invoice->notes)

{{ __('Notes') }}

{{ $invoice->notes }}

@endif @php $invoiceTerms = \App\Support\DocumentTerms::forInvoice($setting, $invoice); @endphp @if (trim($invoiceTerms) !== '')

{{ __('Terms & Conditions') }}

{!! \App\Support\Html::display($invoiceTerms) !!}
@endif @unless ($hasBankingPage)
@endunless @if ($hasBankingPage)
@if ($banking['showsBanking']) @include('pdf.partials.banking', [ 'accounts' => $banking['accounts'], 'currencyCode' => $invoice->currency_code, 'reference' => __('invoice number'), ]) @endif @if ($payLink)

{{ __('Pay Online') }}

{{-- One sentence, the link sitting in it. A label column would push the address away from the words that introduce it and read as two separate things. --}}

{{ __('Make a secure online payment through Paynow:') }} {{ $payLinkPlain }}

@endif
@endif