Deploy Ladill Care / deploy (push) Successful in 1m0s
Clinics on Pro/Enterprise can connect their own gateway in Integrations and collect card or MoMo on bills with 0% Ladill fee, while cash payments and balance totals stay correct for pending checkouts.
137 lines
9.5 KiB
PHP
137 lines
9.5 KiB
PHP
@php $money = fn ($minor) => config('care.billing.currency').' '.number_format($minor / 100, 2); @endphp
|
|
<x-app-layout :title="$bill->invoice_number">
|
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
|
<div>
|
|
<p class="text-xs uppercase text-slate-500">{{ $statuses[$bill->status] ?? $bill->status }}</p>
|
|
<h1 class="text-2xl font-semibold text-slate-900">{{ $bill->invoice_number }}</h1>
|
|
<p class="mt-1 text-sm text-slate-500">{{ $bill->patient->fullName() }} · {{ $bill->branch?->name }}</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<a href="{{ route('care.bills.print', $bill) }}" target="_blank" class="rounded-lg border border-slate-200 px-4 py-2 text-sm">Print</a>
|
|
@if ($canManage && ! in_array($bill->status, [\App\Models\Bill::STATUS_PAID, \App\Models\Bill::STATUS_VOID]))
|
|
<x-confirm-dialog
|
|
:name="'void-bill-'.$bill->id"
|
|
title="Void this bill?"
|
|
message="The invoice will be voided and can no longer be paid."
|
|
:action="route('care.bills.void', $bill)"
|
|
confirm-label="Void bill"
|
|
>
|
|
<x-slot:trigger>
|
|
<x-btn type="button" variant="danger">Void</x-btn>
|
|
</x-slot:trigger>
|
|
</x-confirm-dialog>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-6 grid gap-6 lg:grid-cols-3">
|
|
<section class="rounded-2xl border border-slate-200 bg-white p-6 lg:col-span-2">
|
|
<h2 class="text-sm font-semibold uppercase text-slate-500">Line items</h2>
|
|
<table class="mt-4 min-w-full text-sm">
|
|
<thead class="text-left text-xs uppercase text-slate-500"><tr><th class="py-2">Description</th><th>Qty</th><th>Unit</th><th>Total</th></tr></thead>
|
|
<tbody class="divide-y divide-slate-50">
|
|
@foreach ($bill->lineItems as $item)
|
|
<tr>
|
|
<td class="py-2">{{ $item->description }} <span class="text-slate-400">({{ $lineTypes[$item->type] ?? $item->type }})</span></td>
|
|
<td>{{ $item->quantity }}</td>
|
|
<td>{{ $money($item->unit_price_minor) }}</td>
|
|
<td>{{ $money($item->total_minor) }}</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
<dl class="mt-4 space-y-1 border-t border-slate-100 pt-4 text-sm">
|
|
<div class="flex justify-between"><dt class="text-slate-500">Subtotal</dt><dd>{{ $money($bill->subtotal_minor) }}</dd></div>
|
|
@if ($bill->discount_minor)<div class="flex justify-between"><dt class="text-slate-500">Discount</dt><dd>-{{ $money($bill->discount_minor) }}</dd></div>@endif
|
|
@if ($bill->tax_minor)<div class="flex justify-between"><dt class="text-slate-500">Tax</dt><dd>{{ $money($bill->tax_minor) }}</dd></div>@endif
|
|
<div class="flex justify-between font-semibold"><dt>Total</dt><dd>{{ $money($bill->total_minor) }}</dd></div>
|
|
<div class="flex justify-between text-amber-700"><dt>Balance</dt><dd>{{ $money($bill->balance_minor) }}</dd></div>
|
|
</dl>
|
|
</section>
|
|
|
|
<div class="space-y-6">
|
|
@if ($canManage && ! in_array($bill->status, [\App\Models\Bill::STATUS_PAID, \App\Models\Bill::STATUS_VOID]))
|
|
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
|
<h2 class="text-sm font-semibold uppercase text-slate-500">Add line item</h2>
|
|
<form method="POST" action="{{ route('care.bills.line-items.store', $bill) }}" class="mt-4 space-y-3">
|
|
@csrf
|
|
<select name="type" class="w-full rounded-lg border-slate-300 text-sm">@foreach ($lineTypes as $k => $v)<option value="{{ $k }}">{{ $v }}</option>@endforeach</select>
|
|
<input type="text" name="description" required placeholder="Description" class="w-full rounded-lg border-slate-300 text-sm">
|
|
<div class="grid grid-cols-2 gap-2">
|
|
<input type="number" name="quantity" value="1" min="1" class="rounded-lg border-slate-300 text-sm">
|
|
<input type="number" name="unit_price_minor" placeholder="Price (minor)" min="0" class="rounded-lg border-slate-300 text-sm">
|
|
</div>
|
|
<button type="submit" class="btn-primary w-full">Add</button>
|
|
</form>
|
|
</section>
|
|
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
|
<h2 class="text-sm font-semibold uppercase text-slate-500">Discount & tax</h2>
|
|
<form method="POST" action="{{ route('care.bills.adjustments', $bill) }}" class="mt-4 space-y-3">
|
|
@csrf
|
|
<input type="number" name="discount_minor" value="{{ $bill->discount_minor }}" min="0" placeholder="Discount (minor)" class="w-full rounded-lg border-slate-300 text-sm">
|
|
<input type="number" name="tax_minor" value="{{ $bill->tax_minor }}" min="0" placeholder="Tax (minor)" class="w-full rounded-lg border-slate-300 text-sm">
|
|
<button type="submit" class="btn-primary w-full">Update</button>
|
|
</form>
|
|
</section>
|
|
@endif
|
|
|
|
@if ($canPay && $bill->balance_minor > 0 && $bill->status !== \App\Models\Bill::STATUS_VOID)
|
|
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
|
<h2 class="text-sm font-semibold uppercase text-slate-500">Record payment</h2>
|
|
<form method="POST" action="{{ route('care.bills.payments.store', $bill) }}" class="mt-4 space-y-3">
|
|
@csrf
|
|
<input type="number" name="amount_minor" value="{{ $bill->balance_minor }}" min="1" required class="w-full rounded-lg border-slate-300 text-sm">
|
|
<select name="method" class="w-full rounded-lg border-slate-300 text-sm">@foreach ($manualPaymentMethods as $k => $v)<option value="{{ $k }}">{{ $v }}</option>@endforeach</select>
|
|
<input type="text" name="reference" placeholder="Reference" class="w-full rounded-lg border-slate-300 text-sm">
|
|
<button type="submit" class="btn-primary w-full">Record payment</button>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
|
<h2 class="text-sm font-semibold uppercase text-slate-500">Collect online</h2>
|
|
<p class="mt-1 text-xs text-slate-500">
|
|
Card or mobile money via your connected gateway
|
|
@if ($gatewayConfigured && $gatewayProvider)
|
|
({{ $gatewayLabels[$gatewayProvider] ?? ucfirst($gatewayProvider) }})
|
|
@endif
|
|
— 0% Ladill fee.
|
|
</p>
|
|
@if ($gatewayConfigured)
|
|
<form method="POST" action="{{ route('care.bills.payments.gateway', $bill) }}" class="mt-4 space-y-3">
|
|
@csrf
|
|
<input type="number" name="amount_minor" value="{{ $bill->balance_minor }}" min="1" max="{{ $bill->balance_minor }}" required class="w-full rounded-lg border-slate-300 text-sm" aria-label="Amount (minor units)">
|
|
<button type="submit" class="btn-primary w-full">Open checkout</button>
|
|
</form>
|
|
@else
|
|
<p class="mt-3 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800">
|
|
Connect Paystack, Flutterwave, or Hubtel in
|
|
<a href="{{ route('care.integrations') }}" class="font-medium underline">Integrations</a>
|
|
to collect online payments.
|
|
</p>
|
|
@endif
|
|
</section>
|
|
@endif
|
|
|
|
<section class="rounded-2xl border border-slate-200 bg-white p-6">
|
|
<h2 class="text-sm font-semibold uppercase text-slate-500">Payments</h2>
|
|
@forelse ($bill->payments as $payment)
|
|
@php
|
|
$methodLabel = $payment->method === \App\Models\Payment::METHOD_GATEWAY
|
|
? ($gatewayLabels[$payment->gateway_provider] ?? 'Online')
|
|
: ($paymentMethods[$payment->method] ?? $payment->method);
|
|
$statusSuffix = $payment->status === \App\Models\Payment::STATUS_PAID
|
|
? ''
|
|
: ' · '.ucfirst($payment->status);
|
|
$when = $payment->isPaid()
|
|
? ($payment->paid_at?->format('d M Y') ?? $payment->created_at->format('d M Y'))
|
|
: $payment->created_at->format('d M Y');
|
|
@endphp
|
|
<p class="mt-2 text-sm">{{ $money($payment->amount_minor) }} · {{ $methodLabel }}{{ $statusSuffix }} · {{ $when }}@if($payment->reference)<span class="text-slate-400"> · {{ $payment->reference }}</span>@endif</p>
|
|
@empty
|
|
<p class="mt-2 text-sm text-slate-400">No payments recorded.</p>
|
|
@endforelse
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</x-app-layout>
|