@php $logoSrc = null; $logoVal = $settings->hotel_logo ?? ''; if (!empty($logoVal)) { if (preg_match('/^https?:\\/\\//i', $logoVal)) { $logoSrc = $logoVal; } else { $diskPath = \Illuminate\Support\Facades\Storage::disk('public')->path($logoVal); if (is_file($diskPath)) { $mime = @mime_content_type($diskPath) ?: 'image/png'; $data = @file_get_contents($diskPath); if ($data !== false) { $logoSrc = 'data:' . $mime . ';base64,' . base64_encode($data); } } } } @endphp @if($logoSrc) @else
{{ strtoupper($settings->hotel_name ?? 'Lumbu Hotel') }}
@endif
@if($settings)
{{ $settings->hotel_address }}
{{ $settings->hotel_phone }} | {{ $settings->hotel_email }}
@endif

INVOICE

# {{ $invoice->invoice_number }}
{{ optional($invoice->issued_at ?? $invoice->created_at)->format('d F Y') }}
@if(($invoice->non_refundable ?? false) || (!empty($invoice->notes) && preg_match('/non[- ]?refund/i', $invoice->notes)))
NON-REFUNDABLE
@endif
Bill To:
{{ $invoice->guest_name }}
@php $companyName = null; $notesSrc = trim((string)($invoice->notes ?? '')); if ($notesSrc !== '' && stripos($notesSrc, 'Perusahaan:') !== false) { $seg = explode('Perusahaan:', $notesSrc)[1] ?? ''; $companyName = trim(explode('|', $seg)[0] ?? ''); } if (!$companyName && $invoice->booking && !empty($invoice->booking->special_requests)) { $sr = (string)$invoice->booking->special_requests; if ($sr !== '' && stripos($sr, 'Perusahaan:') !== false) { $seg = explode('Perusahaan:', $sr)[1] ?? ''; $companyName = trim(explode('|', $seg)[0] ?? ''); } } if (!$companyName && $invoice->booking && $invoice->booking->guest && !empty($invoice->booking->guest->notes)) { $gn = (string)$invoice->booking->guest->notes; if ($gn !== '' && stripos($gn, 'Perusahaan:') !== false) { $seg = explode('Perusahaan:', $gn)[1] ?? ''; $companyName = trim(explode('|', $seg)[0] ?? ''); } } @endphp @if(!empty($companyName))
{{ $companyName }}
@endif
{{ $invoice->guest_email }}
@php $guestAddress = ''; if (!empty($invoice->guest_address ?? '')) { $guestAddress = $invoice->guest_address; } elseif ($invoice->booking && $invoice->booking->guest && !empty($invoice->booking->guest->address)) { $guestAddress = $invoice->booking->guest->address; } elseif (!empty($invoice->notes)) { if (preg_match('/Alamat:\s*([^|]+)/i', (string)$invoice->notes, $am)) { $guestAddress = trim($am[1] ?? ''); } } @endphp @if(!empty($guestAddress))
{{ $guestAddress }}
@endif @if($invoice->guest_phone)
{{ $invoice->guest_phone }}
@endif @php $guestNationality = ''; if (!empty($invoice->guest_nationality ?? '')) { $guestNationality = $invoice->guest_nationality; } elseif ($invoice->booking && $invoice->booking->guest && !empty($invoice->booking->guest->nationality)) { $guestNationality = $invoice->booking->guest->nationality; } @endphp @if(!empty($guestNationality))
{{ $guestNationality }}
@endif
Details:
@php $isWalkinBooking = strtolower(trim((string) optional($invoice->booking)->booking_source)) === 'walkin'; @endphp @if($invoice->booking_id && !$isWalkinBooking)
Booking ID: {{ optional($invoice->booking)->booking_code ?? 'N/A' }}
@endif @php $bookingGuestName = optional(optional($invoice->booking)->guest)->name ?? optional($invoice->booking)->guest_name; @endphp @if($bookingGuestName && ($bookingGuestName !== $invoice->guest_name))
Guest: {{ $bookingGuestName }}
@endif @php $durationNights = (int) ($invoice->nights ?? 0); if ($durationNights <= 0) { $durationStart = optional($invoice->booking)->check_in_date ?? $invoice->check_in_date; $durationEnd = optional($invoice->booking)->check_out_date ?? $invoice->check_out_date; if ($durationStart && $durationEnd) { $durationNights = \Carbon\Carbon::parse($durationStart)->diffInDays(\Carbon\Carbon::parse($durationEnd)); } $durationNights = max(1, (int) $durationNights); } @endphp
Check-in: {{ $invoice->check_in_date->format('d/m/Y') }}
Check-out: {{ $invoice->check_out_date->format('d/m/Y') }}
Duration: {{ $durationNights }} {{ $durationNights > 1 ? 'nights' : 'night' }}
@if($invoice->paymentChannel)
Payment Method: {{ $invoice->paymentChannel->name }}
@endif @if($invoice->paid_at)
Paid At: {{ $invoice->paid_at->format('d/m/Y') }}
@endif
{{ ucfirst($invoice->status ?? 'Draft') }}
@php $hasMerged = isset($mergedSources) && ($mergedSources->count() > 0); @endphp @php $isNonStayInvoice = empty($invoice->booking_id); $notesDiscountPercent = null; $hasOverrideMarker = !empty($invoice->notes) && (stripos((string) $invoice->notes, 'Override Harga:') !== false || stripos((string) $invoice->notes, 'OVERRIDE_PRICE') !== false); if (!empty($invoice->notes) && preg_match('/Diskon\(%\):\s*([0-9]+(?:\.[0-9]+)?)/i', (string) $invoice->notes, $dm)) { $notesDiscountPercent = (float) ($dm[1] ?? 0); } // Do not infer discount from rate/subtotal delta (override price is not a discount). // Only trust explicit discount_amount or explicit marker in notes. $effectiveDiscountAmount = max(0, (int) ($invoice->discount_amount ?? 0)); if ($effectiveDiscountAmount <= 0 && $notesDiscountPercent !== null && $notesDiscountPercent > 0 && $notesDiscountPercent < 100) { // Subtotal in stay invoices is usually after-discount; reverse to derive display-only discount nominal. $subtotalNow = max(0, (float) ($invoice->subtotal ?? 0)); $effectiveDiscountAmount = (int) round($subtotalNow * ($notesDiscountPercent / (100 - $notesDiscountPercent))); } // Fallback legacy inference for stay invoices that applied discount but did not persist discount metadata. if ($effectiveDiscountAmount <= 0 && !$isNonStayInvoice && !$hasOverrideMarker) { $nightsForInference = (int) ($invoice->nights ?? 0); if ($nightsForInference <= 0 && !empty($invoice->check_in_date) && !empty($invoice->check_out_date)) { $nightsForInference = \Carbon\Carbon::parse($invoice->check_in_date)->diffInDays(\Carbon\Carbon::parse($invoice->check_out_date)); } $nightsForInference = max(1, $nightsForInference); $publishedBase = max(0, ((float) ($invoice->room_rate ?? 0)) * $nightsForInference); $subtotalNow = max(0, (float) ($invoice->subtotal ?? 0)); if ($publishedBase > $subtotalNow) { $effectiveDiscountAmount = (int) round($publishedBase - $subtotalNow); } } @endphp @php $invoiceDiscountPercent = 0; if ($isNonStayInvoice) { if ($notesDiscountPercent !== null) { $invoiceDiscountPercent = min(100, max(0, round($notesDiscountPercent, 2))); } else { $nonStayBase = max(1, (int) ($invoice->subtotal ?? 0)); $invoiceDiscountPercent = $effectiveDiscountAmount > 0 ? min(100, max(0, round(($effectiveDiscountAmount / $nonStayBase) * 100, 2))) : 0; } } else { if ($notesDiscountPercent !== null) { $invoiceDiscountPercent = min(100, max(0, round($notesDiscountPercent, 2))); } else { $invoiceDiscountBase = max(1, (int) (($invoice->subtotal ?? 0) + $effectiveDiscountAmount)); $invoiceDiscountPercent = $effectiveDiscountAmount > 0 ? min(100, max(0, round(($effectiveDiscountAmount / $invoiceDiscountBase) * 100, 2))) : 0; } } @endphp
@php $normalizeServiceLineForPdf = function ($service, int $fallbackNights = 1) { $serviceNameRaw = (string) ($service->service_name ?? ''); $displayQty = max(1, (int) ($service->quantity ?? 1)); $displayUnitPrice = (float) ($service->unit_price ?? 0); $displayTotal = (float) ($service->total_price ?? ($displayQty * $displayUnitPrice)); $serviceNights = max(1, (int) ($service->line_nights ?? $fallbackNights)); if (preg_match('/extra\s*bed/i', $serviceNameRaw)) { $displayQty = $serviceNights; $bedCount = max(1, (int) ($service->quantity ?? 1)); $displayUnitPrice = ($serviceNights > 0 && $bedCount > 0) ? ($displayTotal / ($bedCount * $serviceNights)) : $displayUnitPrice; } return [ 'name' => preg_replace('/\s*\(Manual\)\s*$/i', '', $serviceNameRaw), 'qty' => $displayQty, 'unit' => $displayUnitPrice, 'total' => $displayTotal, ]; }; $expandServiceLinesForPdf = function ($service, int $fallbackNights = 1) use ($normalizeServiceLineForPdf) { $serviceNameRaw = (string) ($service->service_name ?? ''); $isExtraBed = preg_match('/extra\s*bed/i', $serviceNameRaw) === 1; $lineQty = max(1, (int) ($service->quantity ?? 1)); $lineTotal = (float) ($service->total_price ?? 0); if (!$isExtraBed || $lineQty <= 1) { return [ $normalizeServiceLineForPdf($service, $fallbackNights) ]; } $perOrderTotal = $lineQty > 0 ? ($lineTotal / $lineQty) : $lineTotal; $rows = []; for ($i = 0; $i < $lineQty; $i++) { $rows[] = $normalizeServiceLineForPdf((object) [ 'service_name' => $service->service_name, 'quantity' => 1, 'unit_price' => $service->unit_price, 'total_price' => $perOrderTotal, 'line_nights' => (int) ($service->line_nights ?? $fallbackNights), ], $fallbackNights); } return $rows; }; @endphp @if($hasMerged) @foreach($mergedSources as $inv) @php $typeLabel = trim((string) ($inv->room_type ?? '')) !== '' ? (string) $inv->room_type : 'Room'; $roomNumberDisp = trim((string) ($inv->room_number ?? '')) !== '' ? (string) $inv->room_number : '-'; $nightsMerged = (int) ($inv->nights ?? 0); if ($nightsMerged <= 0 && !empty($inv->check_in_date) && !empty($inv->check_out_date)) { $nightsMerged = \Carbon\Carbon::parse($inv->check_in_date)->diffInDays(\Carbon\Carbon::parse($inv->check_out_date)) ?: 1; } $combinedSubtotal = (int) ($inv->subtotal ?? 0); $ratePerNightMerged = (int) ($inv->room_rate ?? 0); if ($ratePerNightMerged <= 0 && $nightsMerged > 0) { $ratePerNightMerged = (int) floor($combinedSubtotal / $nightsMerged); } $sourceNotesDiscountPercent = null; $sourceHasOverrideMarker = !empty($inv->notes) && (stripos((string) $inv->notes, 'Override Harga:') !== false || stripos((string) $inv->notes, 'OVERRIDE_PRICE') !== false); if ($sourceHasOverrideMarker && $nightsMerged > 0) { $ratePerNightMerged = (int) round($combinedSubtotal / $nightsMerged); } if (!empty($inv->notes) && preg_match('/Diskon\(%\):\s*([0-9]+(?:\.[0-9]+)?)/i', (string) $inv->notes, $sdm)) { $sourceNotesDiscountPercent = (float) ($sdm[1] ?? 0); } $sourceEffectiveDiscount = max(0, (int) ($inv->discount_amount ?? 0)); if ($sourceEffectiveDiscount <= 0 && $sourceNotesDiscountPercent !== null && $sourceNotesDiscountPercent > 0 && $sourceNotesDiscountPercent < 100) { $sourceEffectiveDiscount = (int) round($combinedSubtotal * ($sourceNotesDiscountPercent / (100 - $sourceNotesDiscountPercent))); } if ($sourceEffectiveDiscount <= 0 && !$sourceHasOverrideMarker) { $publishedBase = max(0, (int) ($ratePerNightMerged * max(1, $nightsMerged))); if ($publishedBase > $combinedSubtotal) { $sourceEffectiveDiscount = (int) round($publishedBase - $combinedSubtotal); } } if ($sourceNotesDiscountPercent !== null) { $lineDiscountPercent = min(100, max(0, round($sourceNotesDiscountPercent, 2))); } else { $sourceDiscountBase = max(1, (int) ($combinedSubtotal + $sourceEffectiveDiscount)); $lineDiscountPercent = $sourceEffectiveDiscount > 0 ? min(100, max(0, round(($sourceEffectiveDiscount / $sourceDiscountBase) * 100, 2))) : round($invoiceDiscountPercent, 0); } @endphp @endforeach @php $mergedServiceLines = collect(); foreach ($mergedSources as $srcInv) { if ($srcInv->booking && $srcInv->booking->roomServices && $srcInv->booking->roomServices->count() > 0) { $sourceNights = (int) ($srcInv->nights ?? 0); if ($sourceNights <= 0 && !empty($srcInv->check_in_date) && !empty($srcInv->check_out_date)) { $sourceNights = \Carbon\Carbon::parse($srcInv->check_in_date)->diffInDays(\Carbon\Carbon::parse($srcInv->check_out_date)); } $sourceNights = max(1, $sourceNights); foreach ($srcInv->booking->roomServices as $service) { $mergedServiceLines->push((object) [ 'service_name' => $service->service_name, 'quantity' => $service->quantity, 'unit_price' => $service->unit_price, 'total_price' => $service->total_price, 'line_nights' => $sourceNights, ]); } } } if ($mergedServiceLines->isEmpty() && $invoice->booking && $invoice->booking->roomServices && $invoice->booking->roomServices->count() > 0) { $mergedServiceLines = $mergedServiceLines->merge($invoice->booking->roomServices); } $mergedManualExtraLines = collect(); if ($mergedServiceLines->isEmpty()) { $extraBedName = (string) \App\Models\Settings::get('extra_bed_name', 'Extra Bed'); $extraBedPrice = (int) \App\Models\Settings::get('extra_bed_price', 150000); $mergedManualEntries = []; foreach ($mergedSources as $srcInv) { if (!empty($srcInv->notes) && preg_match('/Extra\s*Bed\s*Manual:\s*([0-9]+)/i', (string) $srcInv->notes, $ebm)) { $qty = max(0, (int) ($ebm[1] ?? 0)); if ($qty > 0) { $sourceNights = (int) ($srcInv->nights ?? 0); if ($sourceNights <= 0 && !empty($srcInv->check_in_date) && !empty($srcInv->check_out_date)) { $sourceNights = \Carbon\Carbon::parse($srcInv->check_in_date)->diffInDays(\Carbon\Carbon::parse($srcInv->check_out_date)); } $sourceNights = max(1, $sourceNights); $mergedManualEntries[] = [ 'qty' => $qty, 'nights' => $sourceNights, ]; } } } if (!empty($mergedManualEntries)) { $uniqueSignatures = collect($mergedManualEntries) ->map(fn ($entry) => ($entry['qty'] ?? 0) . '|' . ($entry['nights'] ?? 1)) ->unique() ->values(); if ($uniqueSignatures->count() === 1) { $entry = $mergedManualEntries[0]; $qty = max(1, (int) ($entry['qty'] ?? 1)); $nights = max(1, (int) ($entry['nights'] ?? 1)); $mergedManualExtraLines->push((object) [ 'service_name' => $extraBedName . ' (Manual)', 'quantity' => $qty, 'unit_price' => $extraBedPrice, 'total_price' => $qty * $extraBedPrice * $nights, 'line_nights' => $nights, ]); } else { foreach ($mergedManualEntries as $entry) { $qty = max(1, (int) ($entry['qty'] ?? 1)); $nights = max(1, (int) ($entry['nights'] ?? 1)); $mergedManualExtraLines->push((object) [ 'service_name' => $extraBedName . ' (Manual)', 'quantity' => $qty, 'unit_price' => $extraBedPrice, 'total_price' => $qty * $extraBedPrice * $nights, 'line_nights' => $nights, ]); } } } } @endphp @foreach($mergedServiceLines as $service) @foreach($expandServiceLinesForPdf($service, (int) ($durationNights ?? 1)) as $displayService) @endforeach @endforeach @foreach($mergedManualExtraLines as $service) @foreach($expandServiceLinesForPdf($service, (int) ($durationNights ?? 1)) as $displayService) @endforeach @endforeach @else @php $overrideSegments = []; if (!empty($invoice->notes) && preg_match('/Overrides:\s*([^|]+)/i', $invoice->notes, $ovm)) { $ovraw = trim($ovm[1] ?? ''); if ($ovraw !== '') { foreach (explode(';', $ovraw) as $seg) { $seg = trim($seg); if ($seg === '') continue; $parts = array_map('trim', explode(':', $seg)); if (count($parts) >= 4) { $overrideSegments[] = [ 'name' => $parts[0], 'rate' => is_numeric($parts[1]) ? intval($parts[1]) : 0, 'nights' => is_numeric($parts[2]) ? intval($parts[2]) : 0, 'subtotal' => is_numeric($parts[3]) ? intval($parts[3]) : 0, 'start' => $parts[4] ?? null, 'end' => $parts[5] ?? null, ]; } } } } $hasServiceLines = ((int) ($invoice->services_total ?? 0) > 0) || ($invoice->booking && $invoice->booking->roomServices && $invoice->booking->roomServices->count() > 0); $hideRoomLineForServicesOnly = $hasServiceLines && (int) ($invoice->subtotal ?? 0) <= 0 && (int) ($invoice->nights ?? 0) <= 0; @endphp @if(count($overrideSegments) > 0) @php $overrideSubtotalAll = max(1, (int) collect($overrideSegments)->sum('subtotal')); @endphp @foreach($overrideSegments as $item) @php $roomDisplay = '-'; $name = $item['name'] ?? ''; if (strpos($name, ' - ') !== false) { $partsNm = explode(' - ', $name); $roomDisplay = $partsNm[1] ?? '-'; $name = $partsNm[0] ?? $name; } $lineSubtotal = (int) ($item['subtotal'] ?? 0); $lineEstimatedBase = (int) (($item['rate'] ?? 0) * ($item['nights'] ?? 0)); $lineDiscount = $effectiveDiscountAmount > 0 ? (int) round(($lineSubtotal / $overrideSubtotalAll) * $effectiveDiscountAmount) : max(0, $lineEstimatedBase - $lineSubtotal); @endphp @endforeach @else @if(!$hideRoomLineForServicesOnly) @php $ratePerNight = (int) ($invoice->room_rate ?? (((($invoice->nights ?? 1) > 0) ? (int) (($invoice->subtotal ?? 0) / ($invoice->nights ?? 1)) : (int) ($invoice->subtotal ?? 0)))); if ($hasOverrideMarker && (int) ($invoice->nights ?? 0) > 0) { $ratePerNight = (int) round((int) ($invoice->subtotal ?? 0) / (int) $invoice->nights); } $singleEstimatedBase = (int) ($ratePerNight * (int) ($invoice->nights ?? 0)); $singleDiscount = $effectiveDiscountAmount > 0 ? $effectiveDiscountAmount : max(0, $singleEstimatedBase - (int) ($invoice->subtotal ?? 0)); $singleLineAmount = $isNonStayInvoice ? max(0, (int) ($invoice->subtotal ?? 0) - (int) $effectiveDiscountAmount) : (int) ($invoice->subtotal ?? 0); @endphp @endif @endif @if($invoice->booking && $invoice->booking->roomServices && $invoice->booking->roomServices->count() > 0) @foreach($invoice->booking->roomServices as $service) @foreach($expandServiceLinesForPdf($service, (int) ($durationNights ?? 1)) as $displayService) @endforeach @endforeach @elseif(!empty($invoice->notes) && preg_match('/Extra\s*Bed\s*Manual:\s*([0-9]+)/i', (string) $invoice->notes, $ebs)) @php $qtyExtraManual = max(0, (int) ($ebs[1] ?? 0)); $extraBedName = (string) \App\Models\Settings::get('extra_bed_name', 'Extra Bed'); $extraBedPrice = (int) \App\Models\Settings::get('extra_bed_price', 150000); @endphp @if($qtyExtraManual > 0) @php $serviceObj = (object) [ 'service_name' => $extraBedName, 'quantity' => $qtyExtraManual, 'unit_price' => $extraBedPrice, 'total_price' => $qtyExtraManual * $extraBedPrice * max(1, (int) ($durationNights ?? 1)), 'line_nights' => (int) ($durationNights ?? 1), ]; @endphp @foreach($expandServiceLinesForPdf($serviceObj, (int) ($durationNights ?? 1)) as $displayService) @endforeach @endif @endif @endif
Description {{ ($invoice->booking->booking_type ?? 'room') === 'ballroom' ? 'Ballroom' : 'Room' }} {{ ($invoice->booking->booking_type ?? 'room') === 'ballroom' ? 'Days' : 'Nights' }} Rate Discount Amount
{{ $typeLabel }} Room @if(!empty($inv->check_in_date) && !empty($inv->check_out_date))
{{ \Carbon\Carbon::parse($inv->check_in_date)->format('d M Y') }} - {{ \Carbon\Carbon::parse($inv->check_out_date)->format('d M Y') }}
@endif
{{ $roomNumberDisp }} {{ $nightsMerged }} Rp {{ number_format($ratePerNightMerged, 0, ',', '.') }} {{ number_format($lineDiscountPercent, 2, ',', '.') }}% Rp {{ number_format($combinedSubtotal, 0, ',', '.') }}
Room Service - {{ $displayService['name'] }} - {{ $displayService['qty'] }} Rp {{ number_format($displayService['unit'], 0, ',', '.') }} 0% Rp {{ number_format($displayService['total'], 0, ',', '.') }}
Room Service - {{ $displayService['name'] }} - {{ $displayService['qty'] }} Rp {{ number_format($displayService['unit'], 0, ',', '.') }} 0% Rp {{ number_format($displayService['total'], 0, ',', '.') }}
{{ $name }} Room @if(!empty($item['start']) && !empty($item['end']))
{{ \Carbon\Carbon::parse($item['start'])->format('d M Y') }} - {{ \Carbon\Carbon::parse($item['end'])->format('d M Y') }}
@endif
{{ $roomDisplay }} {{ $item['nights'] }} Rp {{ number_format($item['rate'], 0, ',', '.') }} {{ number_format($invoiceDiscountPercent, 0, ',', '.') }}% Rp {{ number_format($item['subtotal'], 0, ',', '.') }}
{{ ($invoice->booking->room->roomType->name ?? $invoice->room_type) }} Room
{{ $invoice->check_in_date->format('d M Y') }} - {{ $invoice->check_out_date->format('d M Y') }}
{{ $invoice->room_number }} {{ $invoice->nights }}Rp {{ number_format($ratePerNight, 0, ',', '.') }} {{ number_format($invoiceDiscountPercent, 0, ',', '.') }}% Rp {{ number_format($singleLineAmount, 0, ',', '.') }}
Room Service - {{ $displayService['name'] }} - {{ $displayService['qty'] }} Rp {{ number_format($displayService['unit'], 0, ',', '.') }} 0% Rp {{ number_format($displayService['total'], 0, ',', '.') }}
Room Service - {{ $displayService['name'] }} - {{ $displayService['qty'] }} Rp {{ number_format($displayService['unit'], 0, ',', '.') }} 0% Rp {{ number_format($displayService['total'], 0, ',', '.') }}
@php $legacyCardCharge = 0; if (!empty($invoice->notes) && preg_match('/Card\s*Charge:\s*Rp\s*([0-9\.,]+)/i', $invoice->notes, $m)) { $raw = $m[1] ?? '0'; $legacyCardCharge = (int) preg_replace('/[^0-9]/', '', $raw); } $channelName = strtolower((string) optional($invoice->paymentChannel)->name); $isCashPayment = str_contains($channelName, 'cash') || str_contains($channelName, 'tunai'); $chargePercent = $isCashPayment ? 0 : (float) optional($invoice->paymentChannel)->charge_percent; $taxRate = (int) \App\Models\Settings::get('tax_rate', 10); $servicesViewTotal = (int) ($invoice->services_total ?? 0); if ($servicesViewTotal <= 0 && $hasMerged && isset($mergedSources) && $mergedSources->count() > 0) { $servicesViewTotal = (int) $mergedSources->sum(function ($srcInv) { if ($srcInv->booking && $srcInv->booking->roomServices) { return (int) $srcInv->booking->roomServices->sum('total_price'); } return 0; }); } if ($servicesViewTotal <= 0 && $invoice->booking && $invoice->booking->roomServices) { $servicesViewTotal = (int) $invoice->booking->roomServices->sum('total_price'); } if ($servicesViewTotal <= 0) { $extraBedPrice = (int) \App\Models\Settings::get('extra_bed_price', 150000); if ($hasMerged && isset($mergedSources) && $mergedSources->count() > 0) { $mergedManualQty = []; foreach ($mergedSources as $srcInv) { if (!empty($srcInv->notes) && preg_match('/Extra\s*Bed\s*Manual:\s*([0-9]+)/i', (string) $srcInv->notes, $ebm)) { $qty = max(0, (int) ($ebm[1] ?? 0)); if ($qty > 0) $mergedManualQty[] = $qty; } } if (!empty($mergedManualQty)) { $uniqueQty = array_values(array_unique($mergedManualQty)); $effectiveQty = count($uniqueQty) === 1 ? max($mergedManualQty) : array_sum($mergedManualQty); $servicesViewTotal = $effectiveQty * $extraBedPrice; } } elseif (!empty($invoice->notes) && preg_match('/Extra\s*Bed\s*Manual:\s*([0-9]+)/i', (string) $invoice->notes, $ebs)) { $qtyExtraManual = max(0, (int) ($ebs[1] ?? 0)); if ($qtyExtraManual > 0) { $servicesViewTotal = $qtyExtraManual * $extraBedPrice; } } } $totalInclude = max(0, (int) ($invoice->subtotal ?? 0) + $servicesViewTotal - ($isNonStayInvoice ? (int) $effectiveDiscountAmount : 0) ); $cardCharge = $chargePercent > 0 ? (int) round($totalInclude * ($chargePercent / 100)) : ($isCashPayment ? 0 : $legacyCardCharge); $rawDPP = $totalInclude / (1 + ($taxRate / 100)); $baseDPP = floor($rawDPP); $dpp = (($rawDPP - $baseDPP) < 0.5) ? $baseDPP : ($baseDPP + 1); $computedTax = max(0, $totalInclude - $dpp); $finalTotal = $totalInclude + $cardCharge; $terbilang = function ($nilai) use (&$terbilang) { $angka = abs((int) $nilai); $huruf = ['', 'satu', 'dua', 'tiga', 'empat', 'lima', 'enam', 'tujuh', 'delapan', 'sembilan', 'sepuluh', 'sebelas']; if ($angka < 12) return ' ' . $huruf[$angka]; if ($angka < 20) return $terbilang($angka - 10) . ' belas'; if ($angka < 100) return $terbilang(intdiv($angka, 10)) . ' puluh' . $terbilang($angka % 10); if ($angka < 200) return ' seratus' . $terbilang($angka - 100); if ($angka < 1000) return $terbilang(intdiv($angka, 100)) . ' ratus' . $terbilang($angka % 100); if ($angka < 2000) return ' seribu' . $terbilang($angka - 1000); if ($angka < 1000000) return $terbilang(intdiv($angka, 1000)) . ' ribu' . $terbilang($angka % 1000); if ($angka < 1000000000) return $terbilang(intdiv($angka, 1000000)) . ' juta' . $terbilang($angka % 1000000); if ($angka < 1000000000000) return $terbilang(intdiv($angka, 1000000000)) . ' miliar' . $terbilang($angka % 1000000000); return $terbilang(intdiv($angka, 1000000000000)) . ' triliun' . $terbilang($angka % 1000000000000); }; $terbilangTotal = ((int) round($finalTotal) === 0) ? 'Nol rupiah' : ucfirst(trim($terbilang((int) round($finalTotal)))) . ' rupiah'; @endphp
Tax Included: Rp {{ number_format($computedTax, 0, ',', '.') }}
@if($effectiveDiscountAmount > 0)
Discount: - Rp {{ number_format($effectiveDiscountAmount, 0, ',', '.') }}
@endif
Total Included Tax: Rp {{ number_format($totalInclude, 0, ',', '.') }}
@if($cardCharge > 0)
Card Charge{{ $chargePercent > 0 ? ' (' . number_format($chargePercent, 0, ',', '.') . '%)' : '' }}: Rp {{ number_format($cardCharge, 0, ',', '.') }}
@endif
Total:
Terbilang: {{ $terbilangTotal }}
Rp {{ number_format($finalTotal, 0, ',', '.') }}
@if($invoice->paymentChannel || $invoice->paid_at) @php $user = auth()->user(); $receptionistName = $user ? ($user->name ?? ($user->email ?? 'Receptionist')) : 'Receptionist'; @endphp
Pembayaran dapat transfer melalui Bank Mandiri a.n PT. Lumbu Hotel Cabang Sangatta No. Rek: 148 001 257 3542
Terimakasih sudah menginap di Hotel Lumbu.
Semoga Anda puas dengan pelayanan kami dan kembali menginap disini.
Syarat dan Ketentuan:
  1. Mematuhi semua syarat & ketentuan yang berlaku di Lumbu Hotel (Flyer).
  2. Waktu Check-In pukul 13.00 WITA & Waktu Check-Out pukul 12.00 WITA.
  3. Jangan menyimpan benda berharga didalam kamar ketika Anda meninggalkan kamar, kami tidak bertanggung jawab atas kehilangan.
  4. Sebelum Anda Check-Out, pastikan tidak ada barang Anda yang tertinggal.
Payment Information:
@if($invoice->paymentChannel)
Payment Method: {{ $invoice->paymentChannel->name }}
@if($invoice->paymentChannel->description)
{{ $invoice->paymentChannel->description }}
@endif @if(is_array($invoice->paymentChannel->settings) && count($invoice->paymentChannel->settings) > 0)
@foreach($invoice->paymentChannel->settings as $key => $val) {{ $key === 'charge_percent' ? 'Card Charge' : ucwords(str_replace('_', ' ', $key)) }}: {{ e($val) }}{{ $key === 'charge_percent' ? '%' : '' }} @if(!$loop->last)  |  @endif @endforeach
@endif @endif @if($invoice->paid_at)
Payment Date: {{ $invoice->paid_at->format('d F Y') }}
✓ PAID
@else
⚠ UNPAID
@endif
Receptionist
{{ $receptionistName }}
Guest
{{ $invoice->guest_name ?? '-' }}
@endif