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, 'specialization' => $employee->specialization, ]; }); 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) { $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, $request) { $invoiceNumber = Transaction::generateInvoiceNumber(); $procedure = Procedure::findOrFail($validated['procedure_id']); $discountAmount = $validated['discount_amount'] ?? 0; $insuranceCoveredAmount = $validated['insurance_covered_amount'] ?? 0; $patientResponsibility = $validated['grand_total'] - $insuranceCoveredAmount; $changeAmount = 0; if ($validated['paid_amount'] > $patientResponsibility) { $changeAmount = $validated['paid_amount'] - $patientResponsibility; } $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, ]); 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, ]); 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, 'specialization' => $employee->specialization, ]; }); $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) { $procedure = Procedure::findOrFail($validated['procedure_id']); $discountAmount = $validated['discount_amount'] ?? 0; $insuranceCoveredAmount = $validated['insurance_covered_amount'] ?? 0; $patientResponsibility = $validated['grand_total'] - $insuranceCoveredAmount; $changeAmount = 0; if ($validated['paid_amount'] > $patientResponsibility) { $changeAmount = $validated['paid_amount'] - $patientResponsibility; } $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'], ]); $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); } $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'); }); } }