@extends('layouts.app') @section('content') @php $__roundOnlyAmount = function ($amount) { $amount = max((float) $amount, 0); $roundTo = 1000; if ($amount <= 0) { return 0; } return (float) (ceil($amount / $roundTo) * $roundTo); }; $__meterMinRoundAmount = function ($amount) use ($__roundOnlyAmount) { $amount = max((float) $amount, 0); $minimumOrder = 10000; if ($amount <= 0) { return 0; } if ($amount < $minimumOrder) { $amount = $minimumOrder; } return $__roundOnlyAmount($amount); }; $__normalizeUnit = function ($unit) { $unit = strtolower(trim((string) $unit)); $unit = str_replace(['²', '�'], ['2', '2'], $unit); if (in_array($unit, ['m2', 'meter2', 'meter persegi', 'per meter persegi'], true)) { return 'm2'; } if (in_array($unit, ['meter', 'm', 'per meter'], true)) { return 'meter'; } return $unit ?: 'lembar'; }; $__parseM2FromDescription = function ($description) { $description = strtolower(trim((string) $description)); if ($description === '') { return 0; } $description = str_replace(['²', '�', '*'], ['2', '2', 'x'], $description); if (preg_match('/([0-9]+(?:[\.,][0-9]+)?)\s*[x×]\s*([0-9]+(?:[\.,][0-9]+)?)/i', $description, $matches)) { $width = (float) str_replace(',', '.', $matches[1]); $height = (float) str_replace(',', '.', $matches[2]); if ($width > 0 && $height > 0) { if ($width >= 10 || $height >= 10) { $width = $width / 100; $height = $height / 100; } return round($width * $height, 4); } } return 0; }; $__itemDisplaySubtotal = function ($item) use ($__roundOnlyAmount, $__meterMinRoundAmount, $__normalizeUnit, $__parseM2FromDescription) { $unit = $__normalizeUnit($item->size_unit ?? $item->unit ?? 'lembar'); $storedSubtotal = max((float) ($item->subtotal ?? $item->total_price ?? 0), 0); if ($storedSubtotal > 0) { return in_array($unit, ['m2', 'meter'], true) ? $__meterMinRoundAmount($storedSubtotal) : $__roundOnlyAmount($storedSubtotal); } $qty = max((float) ($item->qty ?? 1), 0); $basePrice = max((float) ($item->base_price ?? $item->calculated_price ?? $item->price ?? 0), 0); $sizeValue = max((float) ($item->size_value ?? 0), 0); if ($unit === 'm2') { if ($sizeValue <= 0) { $sizeValue = $__parseM2FromDescription($item->size_description ?? $item->size ?? ''); } if ($sizeValue <= 0) { $sizeValue = 1; } return $__meterMinRoundAmount($basePrice * $sizeValue * $qty); } if ($unit === 'meter') { $sizeValue = $sizeValue > 0 ? $sizeValue : 1; return $__meterMinRoundAmount($basePrice * $sizeValue * $qty); } return $__roundOnlyAmount($basePrice * $qty); }; $__orderDisplaySubtotalBeforePpn = function ($order) use ($__itemDisplaySubtotal, $__roundOnlyAmount) { $items = method_exists($order, 'items') ? ($order->relationLoaded('items') ? $order->items : $order->items()->get()) : collect(); $activeItems = $items->filter(function ($item) { if (method_exists($item, 'isCancelled')) { return !$item->isCancelled(); } return !(bool) ($item->is_cancelled ?? false); }); if ($activeItems->isNotEmpty()) { return (float) $activeItems->sum(fn ($item) => $__itemDisplaySubtotal($item)); } $subtotalBeforePpn = (float) ($order->subtotal_before_ppn ?? 0); if ($subtotalBeforePpn > 0) { return $__roundOnlyAmount($subtotalBeforePpn); } $total = (float) ($order->total_price ?? 0); $ppn = (float) ($order->ppn_amount ?? 0); if ((bool) ($order->use_ppn ?? false) && $ppn > 0 && $total > $ppn) { return $__roundOnlyAmount($total - $ppn); } return $__roundOnlyAmount($total); }; $__orderDisplayTotal = function ($order) use ($__orderDisplaySubtotalBeforePpn) { $subtotal = $__orderDisplaySubtotalBeforePpn($order); $ppn = ((bool) ($order->use_ppn ?? false) || (float) ($order->ppn_amount ?? 0) > 0) ? (float) ($order->ppn_amount ?? 0) : 0; return (float) ($subtotal + $ppn); }; $__orderDisplayRemaining = function ($order) use ($__orderDisplayTotal) { $paid = method_exists($order, 'getTotalPaid') ? (float) $order->getTotalPaid() : (float) ($order->paid_amount ?? 0); return max((float) $__orderDisplayTotal($order) - $paid, 0); }; $normalizeUnit = function ($unit) { $unit = strtolower(trim((string) $unit)); return match ($unit) { 'm2', 'm�' => 'm2', 'meter', 'm' => 'meter', 'pcs', 'pc', 'piece', 'lembar' => 'lembar', default => $unit !== '' ? $unit : 'lembar', }; }; $formatQty = function ($value, $decimals = 2) { $formatted = number_format((float) $value, $decimals, ',', '.'); $formatted = rtrim($formatted, '0'); $formatted = rtrim($formatted, ','); return $formatted === '' ? '0' : $formatted; }; $isItemCancelled = function ($item) { if (method_exists($item, 'isCancelled')) { return $item->isCancelled(); } return (bool) ($item->is_cancelled ?? false); }; $parseM2FromDescription = function ($description) { $description = strtolower(trim((string) $description)); if ($description === '') { return 1; } $description = str_replace(['�', '*'], 'x', $description); if (!preg_match('/^\s*(\d+(?:\.\d+)?)\s*x\s*(\d+(?:\.\d+)?)\s*$/', $description, $matches)) { return 1; } $a = (float) $matches[1]; $b = (float) $matches[2]; if ($a >= 10 || $b >= 10) { $a = $a / 100; $b = $b / 100; } return round($a * $b, 4); }; $calculateItemSubtotal = function ($item) use ($__itemDisplaySubtotal) { return round($__itemDisplaySubtotal($item), 2); }; @endphp {{-- AI Guidance --}}

Kasir Guidance

Mode: Riwayat Pembayaran

Halaman ini dipakai untuk melihat order yang sudah dibayar, membuka bukti transaksi, memperbaiki data pembayaran, melakukan refund, atau mencetak beberapa nota sekaligus dengan sistem checklist.

Checklist Invoice
Centang invoice yang ingin digabung. Bisa juga klik nama customer untuk memilih semua invoice customer itu.
Cetak Gabungan
Setelah memilih beberapa invoice, klik Cetak Gabungan untuk membuat satu nota print.
Refund / Edit
Tetap bisa digunakan untuk edit pembayaran atau refund seperti sebelumnya.
Perhatian: Cetak gabungan hanya menggabungkan invoice yang sedang dicentang.
@php $allOrdersJson = $orders->map(function ($order) use ($calculateItemSubtotal, $isItemCancelled) { $orderItems = method_exists($order, 'items') ? ($order->relationLoaded('items') ? $order->items : $order->items()->get()) : collect(); $activeItems = $orderItems->filter(fn ($item) => !$isItemCancelled($item))->values(); $displayTotal = $activeItems->isNotEmpty() ? $activeItems->sum(fn ($item) => $calculateItemSubtotal($item)) : (float) $__orderDisplayTotal($order); return [ 'id' => $order->id, 'invoice' => $order->invoice_number, 'customer' => $order->customer_name, 'total' => (float) $displayTotal, ]; })->values(); @endphp

Riwayat Pembayaran

Daftar order yang sudah pernah dibayar. Gunakan checklist untuk cetak nota gabungan.

Tagihan Pending
@forelse($orders as $order) @php $orderItems = method_exists($order, 'items') ? ($order->relationLoaded('items') ? $order->items : $order->items()->get()) : collect(); $activeItems = $orderItems->filter(fn ($item) => !$isItemCancelled($item))->values(); $summaryItem = $activeItems->first() ?? $orderItems->first(); $summaryFileName = $summaryItem->file_name ?? $order->file_name ?? '-'; $summaryQty = $summaryItem->qty ?? $order->qty ?? null; $summaryUnit = $normalizeUnit($summaryItem->size_unit ?? ($order->size_unit ?? '')); $summarySizeDescription = $summaryItem->size_description ?? $order->size ?? null; $summarySizeValue = $summaryItem->size_value ?? $order->size_value ?? null; $displayTotal = $activeItems->isNotEmpty() ? $activeItems->sum(fn ($item) => $calculateItemSubtotal($item)) : (float) $__orderDisplayTotal($order); $hasM2Item = $activeItems->contains(function ($item) use ($normalizeUnit) { return $normalizeUnit($item->size_unit ?? '') === 'm2'; }); $summaryText = '-'; if ($summaryItem) { $unitLabel = $summaryUnit === 'm2' ? 'm2' : $summaryUnit; $summaryText = trim($summaryFileName); if (!empty($summarySizeDescription)) { $summaryText .= ' - ' . $summarySizeDescription; } if (!is_null($summaryQty) && $summaryQty !== '') { $summaryText .= ' - ' . $formatQty($summaryQty) . ' ' . $unitLabel; } if (($summaryUnit === 'm2' || $summaryUnit === 'meter') && !empty($summarySizeValue)) { $summaryText .= ' - nilai ' . $formatQty($summarySizeValue); } } $paymentsJson = optional($order->payments)->map(function ($p) { return [ 'id' => $p->id, 'date' => optional($p->created_at)->format('d/m/Y H:i'), 'amount' => (float) ($p->amount ?? 0), 'method' => (string) ($p->payment_method ?? ''), 'notes' => (string) ($p->notes ?? ''), 'proof' => $p->proof_path ? asset('storage/' . $p->proof_path) : null, 'cashier' => (string) ($p->cashier->name ?? '-'), ]; })->values()->toJson(); @endphp @empty @endforelse
Invoice Pelanggan Total Kasir Waktu Bayar Aksi
{{ $order->invoice_number }}
{{ $summaryText }}
Rp {{ number_format($displayTotal, 0, ',', '.') }}
@if($hasM2Item)
Total dihitung dari ukuran x harga/m2
@endif
{{ $order->cashier->name ?? '-' }} {{ $order->paid_at?->format('d M Y H:i') ?? '-' }}
@if(\Illuminate\Support\Facades\Route::has('orders.invoice-print')) Cetak A4 Cetak A5 21,5×16,5 @endif @if(\Illuminate\Support\Facades\Route::has('kasir.receipt')) POS 80 @elseif(\Illuminate\Support\Facades\Route::has('print.nota')) POS 80 @endif
Tidak ada data
@if($orders->hasPages())
{{ $orders->withQueryString()->links() }}
@endif
{{-- MODAL: PAYMENT MANAGER --}} {{-- MODAL: EDIT PAYMENT --}} @endsection @push('scripts') @endpush