450 lines
19 KiB
PHP
450 lines
19 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Employee;
|
|
use App\Models\Insurance;
|
|
use App\Models\Patient;
|
|
use App\Models\Procedure;
|
|
use App\Models\Registration;
|
|
use App\Models\Transaction;
|
|
use App\Models\TransactionDetail;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Inertia\Inertia;
|
|
|
|
class TransactionController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of the resource.
|
|
*/
|
|
public function index()
|
|
{
|
|
$transactions = Transaction::with([
|
|
'patient:id,name',
|
|
'registration:id,registration_number',
|
|
'insurance:id,name',
|
|
'cashier:id,name'
|
|
])
|
|
->orderBy('transaction_datetime', 'desc')
|
|
->select([
|
|
'id',
|
|
'invoice_number',
|
|
'registration_id',
|
|
'patient_id',
|
|
'insurance_id',
|
|
'cashier_id',
|
|
'transaction_datetime',
|
|
'payment_datetime',
|
|
'grand_total',
|
|
'paid_amount',
|
|
'insurance_covered_amount',
|
|
'patient_responsibility',
|
|
'payment_method',
|
|
'status',
|
|
'created_at'
|
|
])
|
|
->paginate(10)
|
|
->through(function ($transaction) {
|
|
return [
|
|
'id' => $transaction->id,
|
|
'invoice_number' => $transaction->invoice_number,
|
|
'registration_number' => $transaction->registration->registration_number,
|
|
'patient_name' => $transaction->patient->name,
|
|
'insurance_name' => $transaction->insurance?->name,
|
|
'cashier_name' => $transaction->cashier?->name,
|
|
'transaction_datetime' => $transaction->transaction_datetime->format('Y-m-d H:i'),
|
|
'payment_datetime' => $transaction->payment_datetime?->format('Y-m-d H:i'),
|
|
'grand_total' => number_format($transaction->grand_total, 2),
|
|
'paid_amount' => number_format($transaction->paid_amount, 2),
|
|
'insurance_covered_amount' => number_format($transaction->insurance_covered_amount, 2),
|
|
'patient_responsibility' => number_format($transaction->patient_responsibility, 2),
|
|
'payment_method' => $transaction->payment_method,
|
|
'status' => $transaction->status,
|
|
'created_at' => $transaction->created_at->format('Y-m-d H:i:s'),
|
|
];
|
|
});
|
|
|
|
return Inertia::render('transactions/index', [
|
|
'transactions' => $transactions,
|
|
'status' => session('status'),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new resource.
|
|
*/
|
|
public function create()
|
|
{
|
|
$patients = Patient::select(['id', 'name'])->get();
|
|
$registrations = Registration::with(['patient:id,name'])
|
|
->whereDoesntHave('transaction')
|
|
->where('status', '!=', 'cancelled')
|
|
->select(['id', 'registration_number', 'patient_id'])
|
|
->get()
|
|
->map(function ($registration) {
|
|
return [
|
|
'id' => $registration->id,
|
|
'registration_number' => $registration->registration_number,
|
|
'patient_name' => $registration->patient->name,
|
|
];
|
|
});
|
|
$insurances = Insurance::select(['id', 'name'])->get();
|
|
$procedures = Procedure::select(['id', 'code', 'name', 'base_price', 'tax_percentage', 'is_taxable'])->get();
|
|
$employees = Employee::with('user:id,name')
|
|
->whereHas('user')
|
|
->get()
|
|
->map(function ($employee) {
|
|
return [
|
|
'id' => $employee->id,
|
|
'name' => $employee->user->name,
|
|
];
|
|
});
|
|
|
|
return Inertia::render('transactions/form', [
|
|
'mode' => 'create',
|
|
'patients' => $patients,
|
|
'registrations' => $registrations,
|
|
'insurances' => $insurances,
|
|
'procedures' => $procedures,
|
|
'employees' => $employees,
|
|
'paymentMethods' => [
|
|
'cash' => 'Tunai',
|
|
'debit_card' => 'Kartu Debit',
|
|
'credit_card' => 'Kartu Kredit',
|
|
'transfer' => 'Transfer Bank',
|
|
'insurance' => 'Asuransi',
|
|
'other' => 'Lainnya',
|
|
],
|
|
'statusOptions' => [
|
|
'pending' => 'Menunggu Pembayaran',
|
|
'paid' => 'Lunas',
|
|
'partially_paid' => 'Bayar Sebagian',
|
|
'cancelled' => 'Dibatalkan',
|
|
'refunded' => 'Dikembalikan',
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Store a newly created resource in storage.
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
// Validate the main transaction data
|
|
$validated = $request->validate([
|
|
'registration_id' => 'required|exists:t_registration,id',
|
|
'patient_id' => 'required|exists:m_patient,id',
|
|
'insurance_id' => 'nullable|exists:m_insurance,id',
|
|
'procedure_id' => 'required|exists:m_procedure,id',
|
|
'employee_id' => 'nullable|exists:m_employee,id',
|
|
'service_name' => 'required|string|max:100',
|
|
'transaction_datetime' => 'required|date',
|
|
'payment_datetime' => 'nullable|date',
|
|
'due_date' => 'nullable|date',
|
|
'subtotal' => 'required|numeric|min:0',
|
|
'tax_amount' => 'required|numeric|min:0',
|
|
'discount_amount' => 'nullable|numeric|min:0',
|
|
'grand_total' => 'required|numeric|min:0',
|
|
'paid_amount' => 'required|numeric|min:0',
|
|
'insurance_covered_amount' => 'nullable|numeric|min:0',
|
|
'payment_method' => 'required|in:cash,debit_card,credit_card,transfer,insurance,other',
|
|
'payment_reference' => 'nullable|string|max:100',
|
|
'status' => 'required|in:pending,paid,partially_paid,cancelled,refunded',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
// Start database transaction
|
|
return DB::transaction(function () use ($validated, $request) {
|
|
// Generate invoice number
|
|
$invoiceNumber = Transaction::generateInvoiceNumber();
|
|
|
|
// Get procedure details
|
|
$procedure = Procedure::findOrFail($validated['procedure_id']);
|
|
|
|
// Set default values for optional fields
|
|
$discountAmount = $validated['discount_amount'] ?? 0;
|
|
$insuranceCoveredAmount = $validated['insurance_covered_amount'] ?? 0;
|
|
$patientResponsibility = $validated['grand_total'] - $insuranceCoveredAmount;
|
|
|
|
// Calculate change amount if payment made
|
|
$changeAmount = 0;
|
|
if ($validated['paid_amount'] > $patientResponsibility) {
|
|
$changeAmount = $validated['paid_amount'] - $patientResponsibility;
|
|
}
|
|
|
|
// Create transaction
|
|
$transaction = Transaction::create([
|
|
'invoice_number' => $invoiceNumber,
|
|
'registration_id' => $validated['registration_id'],
|
|
'patient_id' => $validated['patient_id'],
|
|
'insurance_id' => $validated['insurance_id'],
|
|
'cashier_id' => auth()->id(),
|
|
'transaction_datetime' => $validated['transaction_datetime'],
|
|
'payment_datetime' => $validated['payment_datetime'],
|
|
'due_date' => $validated['due_date'] ?? null,
|
|
'subtotal' => $validated['subtotal'],
|
|
'tax_amount' => $validated['tax_amount'],
|
|
'discount_amount' => $discountAmount,
|
|
'grand_total' => $validated['grand_total'],
|
|
'paid_amount' => $validated['paid_amount'],
|
|
'change_amount' => $changeAmount,
|
|
'insurance_covered_amount' => $insuranceCoveredAmount,
|
|
'patient_responsibility' => $patientResponsibility,
|
|
'payment_method' => $validated['payment_method'],
|
|
'payment_reference' => $validated['payment_reference'] ?? null,
|
|
'status' => $validated['status'],
|
|
'notes' => $validated['notes'] ?? null,
|
|
]);
|
|
|
|
// Create transaction detail
|
|
TransactionDetail::create([
|
|
'transaction_id' => $transaction->id,
|
|
'procedure_id' => $validated['procedure_id'],
|
|
'performed_by' => $validated['employee_id'],
|
|
'procedure_code' => $procedure->code,
|
|
'procedure_name' => $validated['service_name'],
|
|
'quantity' => 1, // Default quantity to 1
|
|
'unit_price' => $validated['subtotal'],
|
|
'discount_amount' => $discountAmount,
|
|
'tax_amount' => $validated['tax_amount'],
|
|
'subtotal' => $validated['subtotal'],
|
|
'notes' => $validated['notes'] ?? null,
|
|
]);
|
|
|
|
// Update registration payment status if needed
|
|
if ($validated['status'] === 'paid') {
|
|
$registration = Registration::findOrFail($validated['registration_id']);
|
|
$registration->update(['payment_status' => 'paid']);
|
|
} elseif ($validated['status'] === 'partially_paid') {
|
|
$registration = Registration::findOrFail($validated['registration_id']);
|
|
$registration->update(['payment_status' => 'partial']);
|
|
}
|
|
|
|
return redirect()->route('transactions.index')
|
|
->with('status', 'Transaksi berhasil dibuat');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Display the specified resource.
|
|
*/
|
|
public function show(Transaction $transaction)
|
|
{
|
|
//
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified resource.
|
|
*/
|
|
public function edit(Transaction $transaction)
|
|
{
|
|
$transaction->load([
|
|
'details',
|
|
'registration',
|
|
'patient',
|
|
'insurance',
|
|
]);
|
|
|
|
$patients = Patient::select(['id', 'name'])->get();
|
|
$registrations = Registration::with(['patient:id,name'])
|
|
->where(function ($query) use ($transaction) {
|
|
$query->where('status', '!=', 'cancelled')
|
|
->where('payment_status', '!=', 'paid')
|
|
->orWhere('id', $transaction->registration_id);
|
|
})
|
|
->select(['id', 'registration_number', 'patient_id'])
|
|
->get()
|
|
->map(function ($registration) {
|
|
return [
|
|
'id' => $registration->id,
|
|
'registration_number' => $registration->registration_number,
|
|
'patient_name' => $registration->patient->name,
|
|
];
|
|
});
|
|
$insurances = Insurance::select(['id', 'name'])->get();
|
|
$procedures = Procedure::select(['id', 'code', 'name'])->get();
|
|
$employees = Employee::with('user:id,name')
|
|
->whereHas('user')
|
|
->get()
|
|
->map(function ($employee) {
|
|
return [
|
|
'id' => $employee->id,
|
|
'name' => $employee->user->name,
|
|
];
|
|
});
|
|
|
|
// Get the first detail for the form
|
|
$firstDetail = $transaction->details->first();
|
|
|
|
// Hitung ulang patient_responsibility jika diperlukan
|
|
$patientResponsibility = $transaction->patient_responsibility;
|
|
if ($transaction->payment_method === 'insurance') {
|
|
$patientResponsibility = $transaction->grand_total - $transaction->insurance_covered_amount;
|
|
}
|
|
|
|
return Inertia::render('transactions/form', [
|
|
'mode' => 'edit',
|
|
'transaction' => [
|
|
'id' => $transaction->id,
|
|
'invoice_number' => $transaction->invoice_number,
|
|
'registration_id' => $transaction->registration_id,
|
|
'patient_id' => $transaction->patient_id,
|
|
'insurance_id' => $transaction->insurance_id,
|
|
'procedure_id' => $firstDetail ? $firstDetail->procedure_id : null,
|
|
'employee_id' => $firstDetail ? $firstDetail->performed_by : null,
|
|
'service_name' => $firstDetail ? $firstDetail->procedure_name : '',
|
|
'transaction_datetime' => $transaction->transaction_datetime->format('Y-m-d\TH:i'),
|
|
'payment_datetime' => $transaction->payment_datetime?->format('Y-m-d\TH:i'),
|
|
'due_date' => $transaction->due_date?->format('Y-m-d'),
|
|
'subtotal' => (float) $transaction->subtotal,
|
|
'tax_amount' => (float) $transaction->tax_amount,
|
|
'discount_amount' => (float) $transaction->discount_amount,
|
|
'discount_reason' => $transaction->discount_reason,
|
|
'grand_total' => (float) $transaction->grand_total,
|
|
'paid_amount' => (float) $transaction->paid_amount,
|
|
'change_amount' => (float) $transaction->change_amount,
|
|
'insurance_covered_amount' => (float) $transaction->insurance_covered_amount,
|
|
'patient_responsibility' => (float) $patientResponsibility,
|
|
'payment_method' => $transaction->payment_method,
|
|
'payment_reference' => $transaction->payment_reference,
|
|
'status' => $transaction->status,
|
|
'notes' => $transaction->notes,
|
|
],
|
|
'patients' => $patients,
|
|
'registrations' => $registrations,
|
|
'insurances' => $insurances,
|
|
'procedures' => $procedures,
|
|
'employees' => $employees,
|
|
'paymentMethods' => [
|
|
'cash' => 'Tunai',
|
|
'debit_card' => 'Kartu Debit',
|
|
'credit_card' => 'Kartu Kredit',
|
|
'transfer' => 'Transfer Bank',
|
|
'insurance' => 'Asuransi',
|
|
'other' => 'Lainnya',
|
|
],
|
|
'statusOptions' => [
|
|
'pending' => 'Menunggu Pembayaran',
|
|
'paid' => 'Lunas',
|
|
'partially_paid' => 'Bayar Sebagian',
|
|
'cancelled' => 'Dibatalkan',
|
|
'refunded' => 'Dikembalikan',
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Update the specified resource in storage.
|
|
*/
|
|
public function update(Request $request, Transaction $transaction)
|
|
{
|
|
$validated = $request->validate([
|
|
'registration_id' => 'required|exists:t_registration,id',
|
|
'patient_id' => 'required|exists:m_patient,id',
|
|
'insurance_id' => 'nullable|exists:m_insurance,id',
|
|
'procedure_id' => 'required|exists:m_procedure,id',
|
|
'employee_id' => 'nullable|exists:m_employee,id',
|
|
'service_name' => 'required|string|max:100',
|
|
'transaction_datetime' => 'required|date',
|
|
'payment_datetime' => 'nullable|date',
|
|
'due_date' => 'nullable|date',
|
|
'subtotal' => 'required|numeric|min:0',
|
|
'tax_amount' => 'required|numeric|min:0',
|
|
'discount_amount' => 'nullable|numeric|min:0',
|
|
'grand_total' => 'required|numeric|min:0',
|
|
'paid_amount' => 'required|numeric|min:0',
|
|
'insurance_covered_amount' => 'nullable|numeric|min:0',
|
|
'payment_method' => 'required|in:cash,debit_card,credit_card,transfer,insurance,other',
|
|
'payment_reference' => 'nullable|string|max:100',
|
|
'status' => 'required|in:pending,paid,partially_paid,cancelled,refunded',
|
|
'notes' => 'nullable|string',
|
|
]);
|
|
|
|
return DB::transaction(function () use ($validated, $transaction) {
|
|
// Get procedure details
|
|
$procedure = Procedure::findOrFail($validated['procedure_id']);
|
|
|
|
// Set default values for optional fields
|
|
$discountAmount = $validated['discount_amount'] ?? 0;
|
|
$insuranceCoveredAmount = $validated['insurance_covered_amount'] ?? 0;
|
|
$patientResponsibility = $validated['grand_total'] - $insuranceCoveredAmount;
|
|
|
|
// Calculate change amount if payment made
|
|
$changeAmount = 0;
|
|
if ($validated['paid_amount'] > $patientResponsibility) {
|
|
$changeAmount = $validated['paid_amount'] - $patientResponsibility;
|
|
}
|
|
|
|
// Update transaction
|
|
$transaction->update([
|
|
'registration_id' => $validated['registration_id'],
|
|
'patient_id' => $validated['patient_id'],
|
|
'insurance_id' => $validated['insurance_id'],
|
|
'transaction_datetime' => $validated['transaction_datetime'],
|
|
'payment_datetime' => $validated['payment_datetime'],
|
|
'due_date' => $validated['due_date'],
|
|
'subtotal' => $validated['subtotal'],
|
|
'tax_amount' => $validated['tax_amount'],
|
|
'discount_amount' => $discountAmount,
|
|
'grand_total' => $validated['grand_total'],
|
|
'paid_amount' => $validated['paid_amount'],
|
|
'change_amount' => $changeAmount,
|
|
'insurance_covered_amount' => $insuranceCoveredAmount,
|
|
'patient_responsibility' => $patientResponsibility,
|
|
'payment_method' => $validated['payment_method'],
|
|
'payment_reference' => $validated['payment_reference'],
|
|
'status' => $validated['status'],
|
|
'notes' => $validated['notes'],
|
|
]);
|
|
|
|
// Update the first transaction detail or create a new one
|
|
$detail = TransactionDetail::where('transaction_id', $transaction->id)->first();
|
|
$detailData = [
|
|
'transaction_id' => $transaction->id,
|
|
'procedure_id' => $validated['procedure_id'],
|
|
'performed_by' => $validated['employee_id'],
|
|
'procedure_code' => $procedure->code,
|
|
'procedure_name' => $validated['service_name'],
|
|
'quantity' => 1, // Default quantity to 1
|
|
'unit_price' => $validated['subtotal'],
|
|
'discount_amount' => $discountAmount,
|
|
'tax_amount' => $validated['tax_amount'],
|
|
'subtotal' => $validated['subtotal'],
|
|
'notes' => $validated['notes'],
|
|
];
|
|
|
|
if ($detail) {
|
|
$detail->update($detailData);
|
|
} else {
|
|
TransactionDetail::create($detailData);
|
|
}
|
|
|
|
// Update registration payment status if needed
|
|
$registration = Registration::findOrFail($validated['registration_id']);
|
|
if ($validated['status'] === 'paid') {
|
|
$registration->update(['payment_status' => 'paid']);
|
|
} elseif ($validated['status'] === 'partially_paid') {
|
|
$registration->update(['payment_status' => 'partial']);
|
|
}
|
|
|
|
return redirect()->route('transactions.index')
|
|
->with('status', 'Data transaksi berhasil diperbarui');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove the specified resource from storage.
|
|
*/
|
|
public function destroy(Transaction $transaction)
|
|
{
|
|
return DB::transaction(function () use ($transaction) {
|
|
TransactionDetail::where('transaction_id', $transaction->id)->delete();
|
|
$transaction->delete();
|
|
|
|
return redirect()->route('transactions.index')
|
|
->with('status', 'Data transaksi berhasil dihapus');
|
|
});
|
|
}
|
|
}
|