booking_manegment.py
Tue May 13 2025 11:12:24 GMT+0000 (Coordinated Universal Time)
Saved by @dev_shubham14
import tkinter as tk from tkinter import ttk, messagebox from datetime import datetime, timedelta from tkcalendar import DateEntry class BookingManagement(ttk.Frame): def __init__(self, parent, db): super().__init__(parent) self.db = db self.pack(fill=tk.BOTH, expand=True) # Create UI elements self._create_widgets() # Populate booking list self._populate_booking_list() def _create_widgets(self): """Create UI widgets for booking management""" # Title title_label = ttk.Label(self, text="Booking Management", font=("Arial", 14, "bold")) title_label.grid(row=0, column=0, columnspan=2, pady=10, sticky=tk.W) # Main content split into two frames left_frame = ttk.Frame(self) left_frame.grid(row=1, column=0, sticky=tk.NSEW, padx=(0, 10)) right_frame = ttk.Frame(self) right_frame.grid(row=1, column=1, sticky=tk.NSEW) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) self.rowconfigure(1, weight=1) # === Left Frame: Booking List === list_label = ttk.Label(left_frame, text="Current Bookings", font=("Arial", 12, "bold")) list_label.pack(anchor=tk.W, pady=(0, 10)) # Booking list with scrollbar list_frame = ttk.Frame(left_frame) list_frame.pack(fill=tk.BOTH, expand=True) self.booking_tree = ttk.Treeview(list_frame, columns=("Guest", "Room", "Check In", "Check Out", "Status"), selectmode="browse", show="headings") self.booking_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Configure columns self.booking_tree.heading("Guest", text="Guest") self.booking_tree.heading("Room", text="Room") self.booking_tree.heading("Check In", text="Check In") self.booking_tree.heading("Check Out", text="Check Out") self.booking_tree.heading("Status", text="Status") self.booking_tree.column("Guest", width=150) self.booking_tree.column("Room", width=70) self.booking_tree.column("Check In", width=100) self.booking_tree.column("Check Out", width=100) self.booking_tree.column("Status", width=100) # Add scrollbar scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.booking_tree.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.booking_tree.configure(yscrollcommand=scrollbar.set) # Bind selection event self.booking_tree.bind("<<TreeviewSelect>>", self._on_booking_select) # Filter controls filter_frame = ttk.Frame(left_frame) filter_frame.pack(fill=tk.X, pady=10) ttk.Label(filter_frame, text="Status Filter:").pack(side=tk.LEFT, padx=5) self.filter_var = tk.StringVar() status_cb = ttk.Combobox(filter_frame, textvariable=self.filter_var, width=15) status_cb['values'] = ("All", "Confirmed", "Checked In", "Checked Out", "Cancelled") status_cb.current(0) status_cb.pack(side=tk.LEFT, padx=5) status_cb.bind("<<ComboboxSelected>>", self._apply_filter) refresh_btn = ttk.Button(filter_frame, text="Refresh", command=self._populate_booking_list) refresh_btn.pack(side=tk.RIGHT, padx=5) # === Right Frame: Booking Details & Actions === # Right frame has two sections: Details and New Booking notebook = ttk.Notebook(right_frame) notebook.pack(fill=tk.BOTH, expand=True) # First tab: Booking Details details_frame = ttk.Frame(notebook) notebook.add(details_frame, text="Booking Details") # Second tab: New Booking new_booking_frame = ttk.Frame(notebook) notebook.add(new_booking_frame, text="New Booking") # === Details Frame === # Selected booking details info_frame = ttk.LabelFrame(details_frame, text="Booking Information") info_frame.pack(fill=tk.X, padx=5, pady=10) # Guest info ttk.Label(info_frame, text="Guest:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=2) self.guest_label = ttk.Label(info_frame, text="-") self.guest_label.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2) # Room info ttk.Label(info_frame, text="Room:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=2) self.room_label = ttk.Label(info_frame, text="-") self.room_label.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2) # Check-in date ttk.Label(info_frame, text="Check-in:").grid(row=2, column=0, sticky=tk.W, padx=5, pady=2) self.check_in_label = ttk.Label(info_frame, text="-") self.check_in_label.grid(row=2, column=1, sticky=tk.W, padx=5, pady=2) # Check-out date ttk.Label(info_frame, text="Check-out:").grid(row=3, column=0, sticky=tk.W, padx=5, pady=2) self.check_out_label = ttk.Label(info_frame, text="-") self.check_out_label.grid(row=3, column=1, sticky=tk.W, padx=5, pady=2) # Total amount ttk.Label(info_frame, text="Total Amount:").grid(row=4, column=0, sticky=tk.W, padx=5, pady=2) self.amount_label = ttk.Label(info_frame, text="-") self.amount_label.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2) # Status ttk.Label(info_frame, text="Status:").grid(row=5, column=0, sticky=tk.W, padx=5, pady=2) self.status_label = ttk.Label(info_frame, text="-") self.status_label.grid(row=5, column=1, sticky=tk.W, padx=5, pady=2) # Booking date ttk.Label(info_frame, text="Booking Date:").grid(row=6, column=0, sticky=tk.W, padx=5, pady=2) self.booking_date_label = ttk.Label(info_frame, text="-") self.booking_date_label.grid(row=6, column=1, sticky=tk.W, padx=5, pady=2) # Configure columns info_frame.columnconfigure(1, weight=1) # Action buttons action_frame = ttk.Frame(details_frame) action_frame.pack(fill=tk.X, padx=5, pady=10) self.checkin_btn = ttk.Button(action_frame, text="Check In", command=self._check_in, state=tk.DISABLED) self.checkin_btn.pack(side=tk.LEFT, padx=5) self.checkout_btn = ttk.Button(action_frame, text="Check Out", command=self._check_out, state=tk.DISABLED) self.checkout_btn.pack(side=tk.LEFT, padx=5) self.cancel_btn = ttk.Button(action_frame, text="Cancel Booking", command=self._cancel_booking, state=tk.DISABLED) self.cancel_btn.pack(side=tk.LEFT, padx=5) # Payment entry payment_frame = ttk.LabelFrame(details_frame, text="Add Payment") payment_frame.pack(fill=tk.X, padx=5, pady=10) ttk.Label(payment_frame, text="Amount:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) self.payment_amount_var = tk.StringVar() self.payment_amount_entry = ttk.Entry(payment_frame, textvariable=self.payment_amount_var, width=15) self.payment_amount_entry.grid(row=0, column=1, sticky=tk.W, padx=5, pady=5) ttk.Label(payment_frame, text="Method:").grid(row=0, column=2, sticky=tk.W, padx=5, pady=5) self.payment_method_var = tk.StringVar() method_cb = ttk.Combobox(payment_frame, textvariable=self.payment_method_var, width=15) method_cb['values'] = ("Cash", "Credit Card", "Debit Card", "Bank Transfer") method_cb.current(0) method_cb.grid(row=0, column=3, sticky=tk.W, padx=5, pady=5) self.add_payment_btn = ttk.Button(payment_frame, text="Add Payment", command=self._add_payment, state=tk.DISABLED) self.add_payment_btn.grid(row=0, column=4, sticky=tk.W, padx=5, pady=5) # === New Booking Frame === form_frame = ttk.Frame(new_booking_frame) form_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=10) # Guest selection ttk.Label(form_frame, text="Select Guest:").grid(row=0, column=0, sticky=tk.W, pady=5) self.guest_id_var = tk.StringVar() self.guest_cb = ttk.Combobox(form_frame, textvariable=self.guest_id_var, width=30) self.guest_cb.grid(row=0, column=1, sticky=tk.EW, pady=5, padx=5) # Room selection ttk.Label(form_frame, text="Select Room:").grid(row=1, column=0, sticky=tk.W, pady=5) self.room_id_var = tk.StringVar() self.room_cb = ttk.Combobox(form_frame, textvariable=self.room_id_var, width=30) self.room_cb.grid(row=1, column=1, sticky=tk.EW, pady=5, padx=5) # Check-in date ttk.Label(form_frame, text="Check-in Date:").grid(row=2, column=0, sticky=tk.W, pady=5) self.check_in_date = DateEntry(form_frame, width=12, background='darkblue', foreground='white', borderwidth=2, date_pattern='yyyy-mm-dd') self.check_in_date.grid(row=2, column=1, sticky=tk.W, pady=5, padx=5) # Check-out date ttk.Label(form_frame, text="Check-out Date:").grid(row=3, column=0, sticky=tk.W, pady=5) self.check_out_date = DateEntry(form_frame, width=12, background='darkblue', foreground='white', borderwidth=2, date_pattern='yyyy-mm-dd') # Set default to check-in + 1 day tomorrow = datetime.now() + timedelta(days=1) self.check_out_date.set_date(tomorrow) self.check_out_date.grid(row=3, column=1, sticky=tk.W, pady=5, padx=5) # Total amount ttk.Label(form_frame, text="Total Amount:").grid(row=4, column=0, sticky=tk.W, pady=5) self.total_amount_var = tk.StringVar() self.total_amount_entry = ttk.Entry(form_frame, textvariable=self.total_amount_var) self.total_amount_entry.grid(row=4, column=1, sticky=tk.EW, pady=5, padx=5) # Calculate button calculate_btn = ttk.Button(form_frame, text="Calculate Total", command=self._calculate_total) calculate_btn.grid(row=4, column=2, sticky=tk.W, pady=5) # Create booking button create_btn = ttk.Button(form_frame, text="Create Booking", command=self._create_booking) create_btn.grid(row=5, column=1, sticky=tk.E, pady=10, padx=5) # Configure grid form_frame.columnconfigure(1, weight=1) # Populate dropdowns self._populate_dropdowns() # Selected booking data (hidden) self.selected_booking_id = None def _populate_booking_list(self): """Fetch bookings from database and populate the treeview""" # Clear the treeview for item in self.booking_tree.get_children(): self.booking_tree.delete(item) # Get filtered bookings status_filter = self.filter_var.get() if status_filter and status_filter != "All": bookings = self.db.get_bookings(status=status_filter) else: bookings = self.db.get_bookings() # Insert bookings into treeview for booking in bookings: booking_id, first_name, last_name, room_number, check_in, check_out, amount, status = booking guest_name = f"{first_name} {last_name}" self.booking_tree.insert("", tk.END, values=(guest_name, room_number, check_in, check_out, status), tags=(booking_id,)) # Clear selection and details self._clear_selection() def _apply_filter(self, event=None): """Apply filter to booking list""" self._populate_booking_list() def _on_booking_select(self, event=None): """Handle booking selection from treeview""" selection = self.booking_tree.selection() if selection: item = selection[0] # Get booking ID from tags booking_id = self.booking_tree.item(item, "tags")[0] self.selected_booking_id = booking_id # Get full booking details from database booking = self.db.get_booking(booking_id) if booking: _, guest_id, first_name, last_name, room_id, room_number, check_in, check_out, booking_date, amount, status = booking # Update detail labels self.guest_label.config(text=f"{first_name} {last_name}") self.room_label.config(text=room_number) self.check_in_label.config(text=check_in) self.check_out_label.config(text=check_out) self.amount_label.config(text=f"₹{amount:.2f}") self.status_label.config(text=status) self.booking_date_label.config(text=booking_date) # Enable/disable action buttons based on status if status == "Confirmed": self.checkin_btn.config(state=tk.NORMAL) self.checkout_btn.config(state=tk.DISABLED) self.cancel_btn.config(state=tk.NORMAL) elif status == "Checked In": self.checkin_btn.config(state=tk.DISABLED) self.checkout_btn.config(state=tk.NORMAL) self.cancel_btn.config(state=tk.NORMAL) else: self.checkin_btn.config(state=tk.DISABLED) self.checkout_btn.config(state=tk.DISABLED) self.cancel_btn.config(state=tk.DISABLED) # Enable payment button self.add_payment_btn.config(state=tk.NORMAL) def _clear_selection(self): """Clear booking selection and details""" # Clear tree selection if self.booking_tree.selection(): self.booking_tree.selection_remove(self.booking_tree.selection()[0]) # Clear detail labels self.guest_label.config(text="-") self.room_label.config(text="-") self.check_in_label.config(text="-") self.check_out_label.config(text="-") self.amount_label.config(text="-") self.status_label.config(text="-") self.booking_date_label.config(text="-") # Disable action buttons self.checkin_btn.config(state=tk.DISABLED) self.checkout_btn.config(state=tk.DISABLED) self.cancel_btn.config(state=tk.DISABLED) self.add_payment_btn.config(state=tk.DISABLED) # Clear payment fields self.payment_amount_var.set("") self.payment_method_var.set("Cash") self.selected_booking_id = None def _populate_dropdowns(self): """Populate guest and room dropdown lists""" # Populate guest dropdown guests = self.db.get_guests() guest_list = [] self.guest_map = {} # Map display names to IDs for guest in guests: guest_id, first_name, last_name = guest[0:3] display_name = f"{first_name} {last_name}" guest_list.append(display_name) self.guest_map[display_name] = guest_id self.guest_cb['values'] = guest_list # Auto-select first guest if available if guest_list: self.guest_cb.current(0) # Populate room dropdown (only available rooms) rooms = self.db.get_rooms(status="Available") room_list = [] self.room_map = {} # Map display names to IDs for room in rooms: room_id, room_number, room_type, rate = room[0:4] display_name = f"{room_number} - {room_type} (₹{rate:.2f}/night)" room_list.append(display_name) self.room_map[display_name] = (room_id, rate) self.room_cb['values'] = room_list # Auto-select first room if available if room_list: self.room_cb.current(0) # If we have both a room and a guest selected, calculate the total automatically if guest_list and room_list: self._calculate_total() def _calculate_total(self): """Calculate total amount based on selected room and dates""" try: # Get selected room room_display = self.room_id_var.get() if not room_display or room_display not in self.room_map: messagebox.showerror("Error", "Please select a room") return room_id, rate = self.room_map[room_display] # Get dates check_in = self.check_in_date.get_date() check_out = self.check_out_date.get_date() # Calculate number of nights nights = (check_out - check_in).days if nights <= 0: messagebox.showerror("Error", "Check-out date must be after check-in date") return # Calculate total total = nights * rate # Update total field self.total_amount_var.set(f"{total:.2f}") except Exception as e: messagebox.showerror("Error", f"Failed to calculate total: {str(e)}") def _create_booking(self): """Create a new booking""" try: # Get selected guest guest_display = self.guest_id_var.get() if not guest_display: messagebox.showerror("Error", "Please select a guest") return # Check if guest is in map (only if we have a selection) if guest_display and guest_display not in self.guest_map: # If no guests exist yet, show a helpful message if not self.guest_map: messagebox.showerror("Error", "No guests available. Please add a guest first in Guest Management.") return messagebox.showerror("Error", "Please select a valid guest from the dropdown") return guest_id = self.guest_map[guest_display] # Get selected room room_display = self.room_id_var.get() if not room_display: messagebox.showerror("Error", "Please select a room") return # Check if room is in map (only if we have a selection) if room_display and room_display not in self.room_map: # If no rooms are available if not self.room_map: messagebox.showerror("Error", "No rooms available for booking.") return messagebox.showerror("Error", "Please select a valid room from the dropdown") return room_id, _ = self.room_map[room_display] # Get dates check_in = self.check_in_date.get_date().strftime("%Y-%m-%d") check_out = self.check_out_date.get_date().strftime("%Y-%m-%d") # Get total amount total_str = self.total_amount_var.get().strip() if not total_str: messagebox.showerror("Error", "Please calculate the total amount") return try: total = float(total_str) except ValueError: messagebox.showerror("Error", "Invalid total amount") return # Create booking booking_id = self.db.add_booking(guest_id, room_id, check_in, check_out, total) if booking_id: messagebox.showinfo("Success", "Booking created successfully") self._populate_booking_list() self._populate_dropdowns() # Refresh room list # Clear form self.guest_id_var.set("") self.room_id_var.set("") self.total_amount_var.set("") else: messagebox.showerror("Error", "Failed to create booking") except Exception as e: messagebox.showerror("Error", f"Failed to create booking: {str(e)}") def _check_in(self): """Check in a guest for selected booking""" if not self.selected_booking_id: return result = self.db.update_booking_status(self.selected_booking_id, "Checked In") if result: messagebox.showinfo("Success", "Guest checked in successfully") self._populate_booking_list() else: messagebox.showerror("Error", "Failed to check in guest") def _check_out(self): """Check out a guest for selected booking""" if not self.selected_booking_id: return result = self.db.update_booking_status(self.selected_booking_id, "Checked Out") if result: messagebox.showinfo("Success", "Guest checked out successfully") self._populate_booking_list() self._populate_dropdowns() # Refresh room list else: messagebox.showerror("Error", "Failed to check out guest") def _cancel_booking(self): """Cancel selected booking""" if not self.selected_booking_id: return # Confirm cancellation if not messagebox.askyesno("Confirm", "Are you sure you want to cancel this booking?"): return result = self.db.update_booking_status(self.selected_booking_id, "Cancelled") if result: messagebox.showinfo("Success", "Booking cancelled successfully") self._populate_booking_list() self._populate_dropdowns() # Refresh room list else: messagebox.showerror("Error", "Failed to cancel booking") def _add_payment(self): """Add payment for selected booking""" if not self.selected_booking_id: return # Validate payment amount amount_str = self.payment_amount_var.get().strip() method = self.payment_method_var.get() if not amount_str: messagebox.showerror("Error", "Please enter payment amount") return try: amount = float(amount_str) if amount <= 0: raise ValueError("Amount must be positive") except ValueError: messagebox.showerror("Error", "Invalid payment amount") return # Add payment result = self.db.add_payment(self.selected_booking_id, amount, method) if result: messagebox.showinfo("Success", f"Payment of ₹{amount:.2f} added successfully") self.payment_amount_var.set("") # Clear payment field else: messagebox.showerror("Error", "Failed to add payment")
Comments