add database management and fix some visual glitch

This commit is contained in:
Suherdy Yacob 2025-08-17 22:24:55 +07:00
parent 38cf8c2cfc
commit 8747d112f2
8 changed files with 465 additions and 58 deletions

Binary file not shown.

View File

@ -339,7 +339,7 @@ class AuthManager:
},
{
'name': 'Inventory Manager',
'permissions': ['view_dashboard', 'view_inventory', 'adjust_inventory', 'view_stock_movements']
'permissions': ['view_dashboard', 'view_inventory', 'manage_products', 'adjust_inventory', 'view_stock_movements']
},
{
'name': 'Reports Viewer',

View File

@ -125,6 +125,7 @@ class DatabaseManager:
description TEXT,
unit_price REAL DEFAULT 0,
sku TEXT,
min_stock INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
@ -251,24 +252,13 @@ class DatabaseManager:
)
''')
# Create stock movements table
cursor.execute('''
CREATE TABLE IF NOT EXISTS stock_movements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER,
movement_type TEXT, -- 'IN' for receipts, 'OUT' for issues
quantity INTEGER,
reference_type TEXT, -- 'PO', 'SO', 'MO', 'ADJUSTMENT'
reference_id INTEGER, -- ID of the related document
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products (id)
)
''')
self.connection.commit()
return True
except Exception as e:
print(f"Database initialization error: {e}")
if self.connection:
self.connection.rollback()
return False
finally:
self.disconnect()
@ -283,8 +273,25 @@ class DatabaseManager:
if self.db_type == 'sqlite':
shutil.copy2(self.db_name, backup_path)
return backup_path
elif self.db_type == 'postgresql':
# For PostgreSQL, we can use pg_dump
import subprocess
try:
subprocess.run([
'pg_dump',
'-h', self.db_host,
'-p', str(self.db_port),
'-U', self.db_user,
'-d', self.db_name,
'-f', backup_path
], check=True, env={'PGPASSWORD': self.db_password})
return backup_path
except subprocess.CalledProcessError as e:
print(f"PostgreSQL backup error: {e}")
return None
else:
# For other databases, implement appropriate backup logic
# For other databases, return None for now
print(f"Backup not implemented for database type: {self.db_type}")
return None
except Exception as e:
print(f"Database backup error: {e}")
@ -296,8 +303,59 @@ class DatabaseManager:
if self.db_type == 'sqlite':
shutil.copy2(backup_path, self.db_name)
return True
elif self.db_type == 'postgresql':
# For PostgreSQL, we can use pg_restore or psql
import subprocess
try:
# First, disconnect all users from the database
disconnect_sql = f"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '{self.db_name}';"
# Drop and recreate the database
drop_sql = f"DROP DATABASE IF EXISTS {self.db_name};"
create_sql = f"CREATE DATABASE {self.db_name};"
# Execute the commands
subprocess.run([
'psql',
'-h', self.db_host,
'-p', str(self.db_port),
'-U', self.db_user,
'-c', disconnect_sql
], check=True, env={'PGPASSWORD': self.db_password})
subprocess.run([
'psql',
'-h', self.db_host,
'-p', str(self.db_port),
'-U', self.db_user,
'-c', drop_sql
], check=True, env={'PGPASSWORD': self.db_password})
subprocess.run([
'psql',
'-h', self.db_host,
'-p', str(self.db_port),
'-U', self.db_user,
'-c', create_sql
], check=True, env={'PGPASSWORD': self.db_password})
# Restore the backup
subprocess.run([
'psql',
'-h', self.db_host,
'-p', str(self.db_port),
'-U', self.db_user,
'-d', self.db_name,
'-f', backup_path
], check=True, env={'PGPASSWORD': self.db_password})
return True
except subprocess.CalledProcessError as e:
print(f"PostgreSQL restore error: {e}")
return False
else:
# For other databases, implement appropriate restore logic
# For other databases, return False for now
print(f"Restore not implemented for database type: {self.db_type}")
return False
except Exception as e:
print(f"Database restore error: {e}")
@ -323,3 +381,24 @@ class DatabaseManager:
elif self.db_type == 'postgresql':
return f"postgresql://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}"
return ""
def is_database_initialized(self):
"""Check if the database is properly initialized"""
if not self.connect():
return False
try:
cursor = self.connection.cursor()
# Check if the users table exists
if self.db_type == 'sqlite':
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='users'")
elif self.db_type == 'postgresql':
cursor.execute("SELECT tablename FROM pg_tables WHERE tablename='users'")
result = cursor.fetchone()
return result is not None
except Exception as e:
print(f"Database initialization check error: {e}")
return False
finally:
self.disconnect()

View File

@ -0,0 +1,148 @@
import customtkinter as ctk
from tkinter import messagebox, filedialog
import os
import sys
# Add the project root to the path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from src.database import DatabaseManager
class DatabaseManagementDialog:
def __init__(self, parent, db_manager):
self.db_manager = db_manager
# Create the dialog window
self.dialog = ctk.CTkToplevel(parent)
self.dialog.title("Database Management")
self.dialog.geometry("500x400")
self.dialog.transient(parent)
self.dialog.grab_set()
# Center the dialog
self.dialog.update_idletasks()
width = self.dialog.winfo_width()
height = self.dialog.winfo_height()
x = (self.dialog.winfo_screenwidth() // 2) - (width // 2)
y = (self.dialog.winfo_screenheight() // 2) - (height // 2)
self.dialog.geometry(f'{width}x{height}+{x}+{y}')
# Create UI elements
self.create_widgets()
def create_widgets(self):
"""Create the UI elements for the dialog"""
# Title
title_label = ctk.CTkLabel(self.dialog, text="Database Management",
font=("Arial", 20, "bold"))
title_label.pack(pady=20)
# Main frame
main_frame = ctk.CTkFrame(self.dialog)
main_frame.pack(fill="both", expand=True, padx=20, pady=10)
# Database info
info_frame = ctk.CTkFrame(main_frame)
info_frame.pack(fill="x", padx=10, pady=10)
ctk.CTkLabel(info_frame, text="Database Information",
font=("Arial", 14, "bold")).pack(anchor="w", padx=10, pady=5)
ctk.CTkLabel(info_frame, text=f"Database Type: {self.db_manager.db_type}").pack(anchor="w", padx=20, pady=2)
ctk.CTkLabel(info_frame, text=f"Database Name: {self.db_manager.db_name}").pack(anchor="w", padx=20, pady=2)
# Actions frame
actions_frame = ctk.CTkFrame(main_frame)
actions_frame.pack(fill="x", padx=10, pady=10)
ctk.CTkLabel(actions_frame, text="Database Actions",
font=("Arial", 14, "bold")).pack(anchor="w", padx=10, pady=5)
# Buttons
button_frame = ctk.CTkFrame(actions_frame, fg_color="transparent")
button_frame.pack(fill="x", padx=20, pady=10)
ctk.CTkButton(button_frame, text="Backup Database",
command=self.backup_database, width=150, height=32).pack(pady=5)
ctk.CTkButton(button_frame, text="Restore Database",
command=self.restore_database, width=150, height=32).pack(pady=5)
ctk.CTkButton(button_frame, text="Initialize Database",
command=self.initialize_database, width=150, height=32).pack(pady=5)
ctk.CTkButton(button_frame, text="Check Database Status",
command=self.check_database_status, width=150, height=32).pack(pady=5)
# Close button
close_button = ctk.CTkButton(self.dialog, text="Close",
command=self.dialog.destroy, width=100, height=32)
close_button.pack(pady=20)
def backup_database(self):
"""Backup the database"""
try:
# Ask user for backup location
backup_path = filedialog.asksaveasfilename(
defaultextension=".db",
filetypes=[("Database files", "*.db"), ("All files", "*.*")],
title="Save Database Backup As"
)
if backup_path:
result = self.db_manager.backup_database(backup_path)
if result:
messagebox.showinfo("Success", f"Database backed up successfully to {result}")
else:
messagebox.showerror("Error", "Failed to backup database")
except Exception as e:
messagebox.showerror("Error", f"Failed to backup database: {str(e)}")
def restore_database(self):
"""Restore the database from a backup"""
try:
# Ask user for backup file to restore
backup_path = filedialog.askopenfilename(
filetypes=[("Database files", "*.db"), ("All files", "*.*")],
title="Select Database Backup File"
)
if backup_path:
# Confirm with user before restoring
if messagebox.askyesno("Confirm Restore",
"Are you sure you want to restore the database? "
"This will overwrite the current database."):
result = self.db_manager.restore_database(backup_path)
if result:
messagebox.showinfo("Success", "Database restored successfully")
else:
messagebox.showerror("Error", "Failed to restore database")
except Exception as e:
messagebox.showerror("Error", f"Failed to restore database: {str(e)}")
def initialize_database(self):
"""Initialize the database"""
try:
# Confirm with user before initializing
if messagebox.askyesno("Confirm Initialization",
"Are you sure you want to initialize the database? "
"This will create all necessary tables."):
result = self.db_manager.initialize_database()
if result:
messagebox.showinfo("Success", "Database initialized successfully")
else:
messagebox.showerror("Error", "Failed to initialize database")
except Exception as e:
messagebox.showerror("Error", f"Failed to initialize database: {str(e)}")
def check_database_status(self):
"""Check the database status"""
try:
is_initialized = self.db_manager.is_database_initialized()
if is_initialized:
messagebox.showinfo("Database Status", "Database is properly initialized")
else:
messagebox.showwarning("Database Status", "Database is not initialized")
except Exception as e:
messagebox.showerror("Error", f"Failed to check database status: {str(e)}")

View File

@ -17,6 +17,7 @@ from src.ui.manufacturing_order_dialog import ManufacturingOrderDialog
from src.ui.sales_order_dialog import SalesOrderDialog
from src.ui.customer_dialog import CustomerDialog
from src.ui.supplier_dialog import SupplierDialog
from src.ui.database_management_dialog import DatabaseManagementDialog
class MainWindow:
def __init__(self, root, user, db_manager, app=None):
@ -67,19 +68,27 @@ class MainWindow:
menubar.add_cascade(label="Modules", menu=modules_menu)
modules_menu.add_command(label="Dashboard", command=self.show_dashboard)
modules_menu.add_separator()
modules_menu.add_command(label="Purchase", command=self.show_purchase)
modules_menu.add_command(label="Manufacture", command=self.show_manufacturing)
modules_menu.add_command(label="Sales", command=self.show_sales)
modules_menu.add_command(label="Inventory", command=self.show_inventory)
if self.auth_manager.user_has_permission(self.user['id'], 'view_purchase_orders') or self.user['is_admin']:
modules_menu.add_command(label="Purchase", command=self.show_purchase)
if self.auth_manager.user_has_permission(self.user['id'], 'view_manufacturing_orders') or self.user['is_admin']:
modules_menu.add_command(label="Manufacture", command=self.show_manufacturing)
if self.auth_manager.user_has_permission(self.user['id'], 'view_sales_orders') or self.user['is_admin']:
modules_menu.add_command(label="Sales", command=self.show_sales)
if self.auth_manager.user_has_permission(self.user['id'], 'view_inventory') or self.user['is_admin']:
modules_menu.add_command(label="Inventory", command=self.show_inventory)
modules_menu.add_separator()
modules_menu.add_command(label="Suppliers", command=self.show_supplier_list)
modules_menu.add_command(label="Customers", command=self.show_customer_list)
if self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']:
modules_menu.add_command(label="Suppliers", command=self.show_supplier_list)
if self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']:
modules_menu.add_command(label="Customers", command=self.show_customer_list)
# Configuration menu (admin only)
if self.user['is_admin']:
config_menu = tk.Menu(menubar, tearoff=0, bg="#f0f0f0", fg="black")
menubar.add_cascade(label="Configuration", menu=config_menu)
config_menu.add_command(label="User Management", command=self.manage_users)
config_menu.add_separator()
config_menu.add_command(label="Database Management", command=self.manage_database)
# Reports menu
has_report_permissions = (
@ -114,14 +123,18 @@ class MainWindow:
ctk.CTkButton(button_frame, text="Dashboard", command=self.show_dashboard,
width=100, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Purchase", command=self.show_purchase,
width=100, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Manufacture", command=self.show_manufacturing,
width=100, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Sales", command=self.show_sales,
width=100, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Inventory", command=self.show_inventory,
width=100, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'view_purchase_orders') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Purchase", command=self.show_purchase,
width=100, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'view_manufacturing_orders') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Manufacture", command=self.show_manufacturing,
width=100, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'view_sales_orders') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Sales", command=self.show_sales,
width=100, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'view_inventory') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Inventory", command=self.show_inventory,
width=100, height=32).pack(side="left", padx=5)
# User info
user_frame = ctk.CTkFrame(toolbar, fg_color="transparent")
@ -269,6 +282,11 @@ class MainWindow:
def show_purchase(self):
"""Show purchase management"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_purchase_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to access the purchase module")
return
self.clear_content()
self.status_bar.configure(text="Purchase Management")
@ -289,14 +307,20 @@ class MainWindow:
command=self.new_purchase_order, width=150, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="View Purchase Orders",
command=self.view_purchase_orders, width=150, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Manage Suppliers",
command=self.show_supplier_list, width=150, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Manage Suppliers",
command=self.show_supplier_list, width=150, height=32).pack(side="left", padx=5)
# Show purchase orders by default
self.show_purchase_orders()
def show_manufacturing(self):
"""Show manufacturing management"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_manufacturing_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to access the manufacturing module")
return
self.clear_content()
self.status_bar.configure(text="Manufacturing Management")
@ -325,6 +349,11 @@ class MainWindow:
def show_sales(self):
"""Show sales management"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_sales_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to access the sales module")
return
self.clear_content()
self.status_bar.configure(text="Sales Management")
@ -345,14 +374,20 @@ class MainWindow:
command=self.new_sales_order, width=150, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="View Sales Orders",
command=self.view_sales_orders, width=150, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Manage Customers",
command=self.show_customer_list, width=150, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Manage Customers",
command=self.show_customer_list, width=150, height=32).pack(side="left", padx=5)
# Show sales orders by default
self.show_sales_orders()
def show_inventory(self):
"""Show inventory management"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_inventory') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to access the inventory module")
return
self.clear_content()
self.status_bar.configure(text="Inventory Management")
@ -371,8 +406,9 @@ class MainWindow:
ctk.CTkButton(button_frame, text="View Inventory",
command=self.view_inventory, width=150, height=32).pack(side="left", padx=5)
ctk.CTkButton(button_frame, text="Stock Adjustment",
command=self.stock_adjustment, width=150, height=32).pack(side="left", padx=5)
if self.auth_manager.user_has_permission(self.user['id'], 'adjust_inventory') or self.user['is_admin']:
ctk.CTkButton(button_frame, text="Stock Adjustment",
command=self.stock_adjustment, width=150, height=32).pack(side="left", padx=5)
# Show inventory by default
self.show_inventory_management()
@ -416,6 +452,11 @@ class MainWindow:
# Module command methods
def new_purchase_order(self):
"""Create a new purchase order"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'edit_purchase_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to create purchase orders")
return
dialog = PurchaseOrderDialog(self.root, self.purchase_service, self.product_service, self.supplier_service)
dialog.grab_set()
@ -425,11 +466,21 @@ class MainWindow:
def manage_suppliers(self):
"""Manage suppliers"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to manage suppliers")
return
dialog = SupplierDialog(self.root, self.supplier_service)
dialog.grab_set()
def new_manufacturing_order(self):
"""Create a new manufacturing order"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'edit_manufacturing_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to create manufacturing orders")
return
dialog = ManufacturingOrderDialog(self.root, self.manufacturing_service, self.product_service, self.inventory_service)
dialog.grab_set()
@ -443,6 +494,11 @@ class MainWindow:
def new_sales_order(self):
"""Create a new sales order"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'edit_sales_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to create sales orders")
return
dialog = SalesOrderDialog(self.root, self.sales_service, self.product_service, self.customer_service)
dialog.grab_set()
@ -452,6 +508,11 @@ class MainWindow:
def manage_customers(self):
"""Manage customers"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to manage customers")
return
dialog = CustomerDialog(self.root, self.customer_service)
dialog.grab_set()
@ -517,7 +578,7 @@ class MainWindow:
for idx, order in enumerate(orders):
row_frame = ctk.CTkFrame(scroll_frame, height=24)
row_frame.pack(fill="x", padx=5, pady=2)
row_frame.pack_propagate(False) # Prevent frame from shrinking
#row_frame.pack_propagate(False) # Prevent frame from shrinking
ctk.CTkLabel(row_frame, text=f"PO-{order.id}").grid(
row=0, column=0, padx=5, pady=5, sticky="w")
@ -535,11 +596,13 @@ class MainWindow:
# Actions
actions_frame = ctk.CTkFrame(row_frame, fg_color="transparent")
actions_frame.grid(row=0, column=5, padx=5, pady=5, sticky="w")
ctk.CTkButton(actions_frame, text="",
command=None,
width=1, height=24).pack(side="left", padx=2)
# Add status change button for pending orders
if order.status == "pending":
ctk.CTkButton(actions_frame, text="Complete",
command=lambda o=order: self.complete_purchase_order(o),
command=None,
width=70, height=24, fg_color="green").pack(side="left", padx=2)
def show_manufacturing_orders(self):
@ -604,7 +667,7 @@ class MainWindow:
for idx, order in enumerate(orders):
row_frame = ctk.CTkFrame(scroll_frame, height=24)
row_frame.pack(fill="x", padx=5, pady=2)
row_frame.pack_propagate(False) # Prevent frame from shrinking
#row_frame.pack_propagate(False) # Prevent frame from shrinking
ctk.CTkLabel(row_frame, text=f"MO-{order.id}").grid(
row=0, column=0, padx=5, pady=5, sticky="w")
@ -622,6 +685,9 @@ class MainWindow:
# Actions
actions_frame = ctk.CTkFrame(row_frame, fg_color="transparent")
actions_frame.grid(row=0, column=5, padx=5, pady=5, sticky="w")
ctk.CTkButton(actions_frame, text="",
command=None,
width=1, height=24).pack(side="left", padx=2)
# Add status change button for pending orders
if order.status == "pending":
@ -691,7 +757,7 @@ class MainWindow:
for idx, order in enumerate(orders):
row_frame = ctk.CTkFrame(scroll_frame, height=24)
row_frame.pack(fill="x", padx=5, pady=2)
row_frame.pack_propagate(False) # Prevent frame from shrinking
#row_frame.pack_propagate(False) # Prevent frame from shrinking
ctk.CTkLabel(row_frame, text=f"SO-{order.id}").grid(
row=0, column=0, padx=5, pady=5, sticky="w")
@ -709,6 +775,9 @@ class MainWindow:
# Actions
actions_frame = ctk.CTkFrame(row_frame, fg_color="transparent")
actions_frame.grid(row=0, column=5, padx=5, pady=5, sticky="w")
ctk.CTkButton(actions_frame, text="",
command=None,
width=1, height=24).pack(side="left", padx=2)
# Add status change button for pending orders
if order.status == "pending":
@ -734,6 +803,11 @@ class MainWindow:
def stock_adjustment(self):
"""Adjust stock levels"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'adjust_inventory') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to adjust inventory")
return
self.show_inventory_management()
def show_inventory_management(self):
@ -796,7 +870,7 @@ class MainWindow:
for idx, product in enumerate(products):
row_frame = ctk.CTkFrame(scroll_frame, height=24)
row_frame.pack(fill="x", padx=5, pady=2)
row_frame.pack_propagate(False) # Prevent frame from shrinking
#row_frame.pack_propagate(False) # Prevent frame from shrinking
# Product info
ctk.CTkLabel(row_frame, text=product.name).grid(
@ -831,6 +905,11 @@ class MainWindow:
def manage_products(self):
"""Open product management dialog"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_products') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to manage products")
return
dialog = ProductDialog(self.root, self.product_service)
dialog.grab_set()
@ -840,11 +919,21 @@ class MainWindow:
def edit_product(self, product):
"""Edit a specific product"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_products') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to edit products")
return
dialog = ProductDialog(self.root, self.product_service, product)
dialog.grab_set()
def adjust_product_stock(self, product):
"""Adjust stock for a specific product"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'adjust_inventory') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to adjust inventory")
return
# Create a simple stock adjustment dialog
dialog = ctk.CTkToplevel(self.root)
dialog.title(f"Adjust Stock - {product.name}")
@ -879,8 +968,19 @@ class MainWindow:
self.root.wait_window(dialog)
def manage_database(self):
"""Database management (admin only)"""
dialog = DatabaseManagementDialog(self.root, self.db_manager)
self.root.wait_window(dialog)
def show_inventory_report(self):
"""Show inventory report"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_inventory_report') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to view inventory reports")
return
try:
filename = self.inventory_service.export_inventory_report()
messagebox.showinfo("Success", f"Inventory report exported to {filename}")
@ -889,6 +989,11 @@ class MainWindow:
def show_sales_report(self):
"""Show sales report with date range selection"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_reports') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to view sales reports")
return
from src.ui.date_range_dialog import DateRangeDialog
dialog = DateRangeDialog(self.root, "Select Date Range for Sales Report", "Export")
self.root.wait_window(dialog)
@ -903,6 +1008,11 @@ class MainWindow:
def show_purchase_report(self):
"""Show purchase report with date range selection"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_reports') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to view purchase reports")
return
from src.ui.date_range_dialog import DateRangeDialog
dialog = DateRangeDialog(self.root, "Select Date Range for Purchase Report", "Export")
self.root.wait_window(dialog)
@ -917,6 +1027,11 @@ class MainWindow:
def show_manufacturing_report(self):
"""Show manufacturing report with date range selection"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_reports') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to view manufacturing reports")
return
from src.ui.date_range_dialog import DateRangeDialog
dialog = DateRangeDialog(self.root, "Select Date Range for Manufacturing Report", "Export")
self.root.wait_window(dialog)
@ -931,6 +1046,11 @@ class MainWindow:
def show_stock_movement_report(self):
"""Show stock movement report"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'view_stock_movements') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to view stock movement reports")
return
try:
filename = self.inventory_service.export_stock_movement_report()
messagebox.showinfo("Success", f"Stock movement report exported to {filename}")
@ -939,6 +1059,11 @@ class MainWindow:
def complete_purchase_order(self, order):
"""Complete a purchase order"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'receive_purchase_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to complete purchase orders")
return
if messagebox.askyesno("Confirm", f"Are you sure you want to complete purchase order #{order.id}?"):
if self.purchase_service.receive_purchase_order(order.id):
messagebox.showinfo("Success", f"Purchase order #{order.id} completed successfully")
@ -948,6 +1073,11 @@ class MainWindow:
def complete_manufacturing_order(self, order):
"""Complete a manufacturing order"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'complete_manufacturing_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to complete manufacturing orders")
return
if messagebox.askyesno("Confirm", f"Are you sure you want to complete manufacturing order #{order.id}?"):
if self.manufacturing_service.complete_manufacturing_order(order.id):
messagebox.showinfo("Success", f"Manufacturing order #{order.id} completed successfully")
@ -957,6 +1087,11 @@ class MainWindow:
def complete_sales_order(self, order):
"""Complete a sales order"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'deliver_sales_orders') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to complete sales orders")
return
if messagebox.askyesno("Confirm", f"Are you sure you want to complete sales order #{order.id}?"):
if self.sales_service.deliver_sales_order(order.id):
messagebox.showinfo("Success", f"Sales order #{order.id} completed successfully")
@ -978,6 +1113,11 @@ class MainWindow:
def show_supplier_list(self):
"""Show supplier management interface"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to manage suppliers")
return
self.clear_content()
self.status_bar.configure(text="Supplier Management")
@ -1036,7 +1176,7 @@ class MainWindow:
for idx, supplier in enumerate(suppliers):
row_frame = ctk.CTkFrame(scroll_frame, height=24)
row_frame.pack(fill="x", padx=5, pady=2)
row_frame.pack_propagate(False) # Prevent frame from shrinking
#row_frame.pack_propagate(False) # Prevent frame from shrinking
ctk.CTkLabel(row_frame, text=supplier.name).grid(
row=0, column=0, padx=5, pady=5, sticky="w")
@ -1060,6 +1200,11 @@ class MainWindow:
def add_supplier(self):
"""Add a new supplier"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to add suppliers")
return
from src.ui.supplier_dialog import SupplierDialog
dialog = SupplierDialog(self.root, self.supplier_service)
self.root.wait_window(dialog)
@ -1067,6 +1212,11 @@ class MainWindow:
def edit_supplier(self, supplier):
"""Edit a supplier"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to edit suppliers")
return
from src.ui.supplier_dialog import SupplierDialog
dialog = SupplierDialog(self.root, self.supplier_service, supplier)
self.root.wait_window(dialog)
@ -1074,6 +1224,11 @@ class MainWindow:
def delete_supplier(self, supplier):
"""Delete a supplier"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_suppliers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to delete suppliers")
return
if messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete supplier '{supplier.name}'?"):
if self.supplier_service.delete_supplier(supplier.id):
messagebox.showinfo("Success", f"Supplier '{supplier.name}' deleted successfully")
@ -1083,6 +1238,11 @@ class MainWindow:
def show_customer_list(self):
"""Show customer management interface"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to manage customers")
return
self.clear_content()
self.status_bar.configure(text="Customer Management")
@ -1141,7 +1301,7 @@ class MainWindow:
for idx, customer in enumerate(customers):
row_frame = ctk.CTkFrame(scroll_frame, height=24)
row_frame.pack(fill="x", padx=5, pady=2)
row_frame.pack_propagate(False) # Prevent frame from shrinking
#row_frame.pack_propagate(False) # Prevent frame from shrinking
ctk.CTkLabel(row_frame, text=customer.name).grid(
row=0, column=0, padx=5, pady=5, sticky="w")
@ -1165,6 +1325,11 @@ class MainWindow:
def add_customer(self):
"""Add a new customer"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to add customers")
return
from src.ui.customer_dialog import CustomerDialog
dialog = CustomerDialog(self.root, self.customer_service)
self.root.wait_window(dialog)
@ -1172,6 +1337,11 @@ class MainWindow:
def edit_customer(self, customer):
"""Edit a customer"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to edit customers")
return
from src.ui.customer_dialog import CustomerDialog
dialog = CustomerDialog(self.root, self.customer_service, customer)
self.root.wait_window(dialog)
@ -1179,6 +1349,11 @@ class MainWindow:
def delete_customer(self, customer):
"""Delete a customer"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_customers') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to delete customers")
return
if messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete customer '{customer.name}'?"):
if self.customer_service.delete_customer(customer.id):
messagebox.showinfo("Success", f"Customer '{customer.name}' deleted successfully")
@ -1212,6 +1387,11 @@ class MainWindow:
def add_product(self):
"""Add a new product"""
# Check permissions
if not (self.auth_manager.user_has_permission(self.user['id'], 'manage_products') or self.user['is_admin']):
messagebox.showerror("Access Denied", "You don't have permission to add products")
return
from src.ui.product_dialog import ProductDialog
dialog = ProductDialog(self.root, self.product_service)
self.root.wait_window(dialog)