251 lines
9.6 KiB
PHP
251 lines
9.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\User;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Additional;
|
|
use App\Models\BookingFoto;
|
|
use App\Models\DetailAdditional;
|
|
use App\Models\PaketFoto;
|
|
use App\Models\Pelanggan;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class BookingFotoController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
$foto = PaketFoto::latest()->get();
|
|
|
|
return view('user/booking-foto', compact('foto'));
|
|
}
|
|
public function detail($id)
|
|
{
|
|
$foto = PaketFoto::findOrFail($id);
|
|
|
|
// Jika add-ons disimpan di DB, ambil juga:
|
|
$additionals = Additional::all();
|
|
// Logika Tanggal
|
|
$start = \Carbon\Carbon::now(); // Mulai hari ini
|
|
$end = \Carbon\Carbon::now()->addMonth(); // Maksimal 1 bulan ke depan
|
|
// Untuk navigasi panah
|
|
$prevMonth = $start->copy()->subMonth();
|
|
$nextMonth = $start->copy()->addMonth();
|
|
|
|
$currentMonthLabel = $start->format('F Y');
|
|
return view('user.detail-foto', compact('foto', 'additionals', 'start', 'end', 'currentMonthLabel', 'prevMonth', 'nextMonth'));
|
|
}
|
|
public function loadCalendar(Request $request)
|
|
{
|
|
// Ambil bulan & tahun dari request AJAX, atau default sekarang
|
|
$month = $request->month ?? date('m');
|
|
$year = $request->year ?? date('Y');
|
|
|
|
$start = \Carbon\Carbon::createFromDate($year, $month, 1);
|
|
|
|
// Data Navigasi
|
|
$prevMonth = $start->copy()->subMonth();
|
|
$nextMonth = $start->copy()->addMonth();
|
|
$currentMonthLabel = $start->format('F Y');
|
|
|
|
// Return hanya potongan HTML (Partial), bukan halaman full
|
|
$html = view('user.components.calendar-grid', compact(
|
|
'start',
|
|
'prevMonth',
|
|
'nextMonth',
|
|
'currentMonthLabel'
|
|
))->render();
|
|
|
|
// Return JSON agar JavaScript bisa membacanya sebagai data.html
|
|
return response()->json(['html' => $html]);
|
|
}
|
|
public function cekSlot(Request $request)
|
|
{
|
|
// Cari booking yang statusnya valid (bukan dibatalkan/ditolak)
|
|
// Sesuaikan status dengan logic bisnis kamu
|
|
$booked = BookingFoto::where('tgl_booking', $request->tanggal)
|
|
->whereIn('status_booking', ['menunggu_verifikasi', 'diterima', 'selesai'])
|
|
->get(['jam_mulai']);
|
|
|
|
return response()->json($booked);
|
|
}
|
|
public function formulir(Request $request)
|
|
{
|
|
// 1. Ambil Data Paket
|
|
$foto = PaketFoto::findOrFail($request->id_paket);
|
|
|
|
// 2. Hitung Total Add-ons (Jika ada)
|
|
$addonsDetails = [];
|
|
$totalAddon = 0;
|
|
|
|
if ($request->has('addons')) {
|
|
foreach ($request->addons as $id => $qty) {
|
|
if ($qty > 0) {
|
|
$add = Additional::find($id);
|
|
$subtotal = $add->harga * $qty;
|
|
$totalAddon += $subtotal;
|
|
|
|
$addonsDetails[] = [
|
|
'nama' => $add->nama,
|
|
'qty' => $qty,
|
|
'subtotal' => $subtotal,
|
|
'id' => $id // Simpan ID untuk dikirim lagi nanti
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Hitung Grand Total
|
|
$grandTotal = $foto->harga + $totalAddon;
|
|
// 1. Cek apakah sudah ada deadline di session? Kalau belum, buat baru (2 jam dari sekarang)
|
|
if (!session()->has('payment_deadline')) {
|
|
$deadline = now()->addHours(2);
|
|
session()->put('payment_deadline', $deadline);
|
|
} else {
|
|
$deadline = session('payment_deadline');
|
|
}
|
|
|
|
// 2. Hitung sisa waktu dalam detik
|
|
$sisaWaktu = now()->diffInSeconds($deadline, false); // false = biar bisa negatif kalau lewat
|
|
|
|
// 3. Jika waktu habis (negatif), hapus session dan tendang user
|
|
if ($sisaWaktu <= 0) {
|
|
session()->forget(['payment_deadline', 'addons']); // Bersihkan session
|
|
return redirect()->route('booking.foto')->with('error', 'Waktu pembayaran telah habis. Silakan ulang pemesanan.');
|
|
}
|
|
// 4. Kirim semua data ke View Pembayaran untuk ditampilkan
|
|
return view('user.pembayaran-foto', compact(
|
|
'foto',
|
|
'request', // Kirim request agar tgl & jam bisa diakses di blade
|
|
'addonsDetails',
|
|
'grandTotal',
|
|
'sisaWaktu'
|
|
));
|
|
}
|
|
public function cancelBooking()
|
|
{
|
|
session()->forget(['payment_deadline', 'addons']); // Hapus session timer & data
|
|
return redirect()->route('booking.foto'); // Kembali ke katalog utama
|
|
}
|
|
public function store(Request $request)
|
|
{
|
|
|
|
// 1. Validasi Input
|
|
$request->validate([
|
|
'id_paket' => 'required|exists:paket_fotos,id_paket',
|
|
'tgl_booking' => 'required|date|after_or_equal:today',
|
|
'jam_mulai' => 'required',
|
|
'nama' => 'required|string|max:255',
|
|
'no_wa' => 'required|numeric',
|
|
'bukti_bayar' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
|
]);
|
|
|
|
DB::beginTransaction(); // Mulai Transaksi Database
|
|
|
|
try {
|
|
// 2. Ambil Data Paket & Hitung Waktu
|
|
$paket = PaketFoto::findOrFail($request->id_paket);
|
|
|
|
// Asumsi durasi default 20 menit (atau ambil dari database jika ada kolom durasi)
|
|
$durasiMenit = $paket->durasi;
|
|
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
|
$jamSelesai = $jamMulai->copy()->addMinutes($durasiMenit);
|
|
|
|
// 3. Cek Slot Sekali Lagi (Mencegah Race Condition)
|
|
$isTaken = BookingFoto::where('tgl_booking', $request->tgl_booking)
|
|
->where('jam_mulai', $request->jam_mulai)
|
|
->whereIn('status_booking', ['menunggu_verifikasi', 'diterima', 'selesai'])
|
|
->exists();
|
|
|
|
if ($isTaken) {
|
|
return back()->with('error', 'Mohon maaf, slot waktu ini baru saja diambil orang lain.');
|
|
}
|
|
|
|
// 4. Simpan/Update Data Pelanggan
|
|
$pelanggan = Pelanggan::firstOrCreate(
|
|
['no_wa' => $request->no_wa],
|
|
['nama' => $request->nama]
|
|
);
|
|
|
|
// 5. Upload Bukti Bayar
|
|
$pathBukti = null;
|
|
if ($request->hasFile('bukti_bayar')) {
|
|
$file = $request->file('bukti_bayar');
|
|
$namaFile = 'bukti_' . time() . '_' . Str::random(5) . '.' . $file->getClientOriginalExtension();
|
|
$file->move(public_path('img/payment'), $namaFile);
|
|
$pathBukti = 'img/payment/' . $namaFile;
|
|
}
|
|
|
|
// 6. Hitung Grand Total (Paket + Additional)
|
|
// Kita hitung ulang di server agar aman dari manipulasi inspect element
|
|
$grandTotal = $paket->harga;
|
|
$listAdditional = [];
|
|
|
|
if ($request->has('addons')) {
|
|
foreach ($request->addons as $idAddon => $qty) {
|
|
if ($qty > 0) {
|
|
$add = \App\Models\Additional::find($idAddon);
|
|
if ($add) {
|
|
$subtotal = $add->harga * $qty;
|
|
$grandTotal += $subtotal;
|
|
|
|
$listAdditional[] = [
|
|
'id_additional' => $idAddon,
|
|
'qty' => $qty,
|
|
'subtotal' => $subtotal
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 7. Simpan Booking Utama
|
|
$booking = BookingFoto::create([
|
|
'no_invoice' => 'INV-FOTO-' . strtoupper(Str::random(6)),
|
|
'id_pelanggan' => $pelanggan->id_pelanggan,
|
|
'id_paket' => $paket->id_paket,
|
|
'tgl_booking' => $request->tgl_booking,
|
|
'jam_mulai' => $request->jam_mulai, // Format "09:00"
|
|
'jam_selesai' => $jamSelesai->format('H:i'),
|
|
'total_bayar' => $grandTotal,
|
|
'bukti_bayar' => $pathBukti,
|
|
'status_booking' => 'menunggu_verifikasi'
|
|
]);
|
|
|
|
// 8. Simpan Detail Additional (Jika ada)
|
|
foreach ($listAdditional as $item) {
|
|
DetailAdditional::create([
|
|
'id_booking' => $booking->id_booking,
|
|
'id_additional' => $item['id_additional'],
|
|
'qty' => $item['qty'],
|
|
'subtotal' => $item['subtotal']
|
|
]);
|
|
}
|
|
|
|
DB::commit();
|
|
// 9. Redirect ke WhatsApp atau Halaman Sukses
|
|
$pesan = "Halo Admin Flo.do! Saya pesan foto paket *{$paket->nama}*.\n" .
|
|
"Tanggal: {$request->tgl_booking}\n" .
|
|
"Jam: {$request->jam_mulai}\n" .
|
|
"Total: Rp " . number_format($grandTotal, 0, ',', '.') . "\n" .
|
|
"Mohon diverifikasi ya.";
|
|
|
|
$urlWA = "https://wa.me/6289673668516?text=" . urlencode($pesan);
|
|
session()->forget('payment_deadline');
|
|
|
|
return redirect()->route('booking.foto')->with([
|
|
'success' => 'Pesanan Berhasil Dibuat!',
|
|
'waUrl' => $urlWA
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
// Teks Debugging Lengkap
|
|
$errorMsg = "Error: " . $e->getMessage() . " | Baris: " . $e->getLine() . " | File: " . basename($e->getFile());
|
|
|
|
// Kirim pesan error lengkap ke layar
|
|
return back()->with('error', $errorMsg)->withInput();
|
|
}
|
|
}
|
|
}
|