# KAYAL STORE SALES - REPOSITORY
================================================================================
Project Name: Kayal Store sales
Created: 2025-09-27 02:02:08
Last Updated: 2025-09-27 02:02:14
Source ZIP: public_html.zip
Total Files: 17
Total Folders: 5
================================================================================
## FILE STRUCTURE
================================================================================
Kayal Store sales/
├── assets
├── auth.php
├── config/
│ └── database.php
├── css/
│ └── style.css
├── default.php
├── expenses.php
├── includes/
│ ├── footer.php
│ └── header.php
├── index.php
├── investments.php
├── js/
│ └── script.js
├── loans.php
├── login.php
├── logout.php
├── sales.php
├── settings.php
├── setup.php
└── users.php
================================================================================
## FILE CONTENTS
================================================================================
### FILE 1: auth.php
- Type: PHP
- Size: 3.83 KB
- Path: .
- Name: auth.php
------------------------------------------------------------
pdo = $pdo;
}
public function login($username, $password) {
try {
$stmt = $this->pdo->prepare("SELECT id, full_name, phone, username, password, role FROM users WHERE username = ? AND status = 'active'");
$stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['full_name'] = $user['full_name'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
$_SESSION['phone'] = $user['phone'];
// Create session token for additional security
$token = bin2hex(random_bytes(32));
$_SESSION['token'] = $token;
// Store session in database
$expires = date('Y-m-d H:i:s', strtotime('+24 hours'));
$stmt = $this->pdo->prepare("INSERT INTO user_sessions (user_id, session_token, expires_at) VALUES (?, ?, ?)");
$stmt->execute([$user['id'], $token, $expires]);
return true;
}
return false;
} catch (PDOException $e) {
return false;
}
}
public function logout() {
if (isset($_SESSION['user_id']) && isset($_SESSION['token'])) {
// Remove session from database
$stmt = $this->pdo->prepare("DELETE FROM user_sessions WHERE user_id = ? AND session_token = ?");
$stmt->execute([$_SESSION['user_id'], $_SESSION['token']]);
}
session_destroy();
header("Location: login.php");
exit();
}
public function isLoggedIn() {
return isset($_SESSION['user_id']) && isset($_SESSION['username']);
}
public function requireLogin() {
if (!$this->isLoggedIn()) {
header("Location: login.php");
exit();
}
}
public function requireAdmin() {
$this->requireLogin();
if ($_SESSION['role'] !== 'admin') {
header("Location: index.php");
exit();
}
}
public function isSetupCompleted() {
try {
$stmt = $this->pdo->prepare("SELECT setting_value FROM app_settings WHERE setting_key = 'setup_completed'");
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result && $result['setting_value'] == '1';
} catch (PDOException $e) {
return false;
}
}
public function createFirstAdmin($full_name, $phone, $username, $password) {
try {
// Check if setup is already completed
if ($this->isSetupCompleted()) {
return false;
}
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $this->pdo->prepare("INSERT INTO users (full_name, phone, username, password, role) VALUES (?, ?, ?, ?, 'admin')");
$result = $stmt->execute([$full_name, $phone, $username, $hashed_password]);
if ($result) {
// Mark setup as completed
$stmt = $this->pdo->prepare("UPDATE app_settings SET setting_value = '1' WHERE setting_key = 'setup_completed'");
$stmt->execute();
return true;
}
return false;
} catch (PDOException $e) {
return false;
}
}
public function getUserRole() {
return $_SESSION['role'] ?? null;
}
public function getUserName() {
return $_SESSION['full_name'] ?? '';
}
}
$auth = new Auth($pdo);
?>
-------------------- END OF FILE --------------------
### FILE 2: default.php
- Type: PHP
- Size: 15.99 KB
- Path: .
- Name: default.php
------------------------------------------------------------
Default page
You Are All Set to Go!
All you have to do now is upload your website files and start your journey. Check out how to do that below:
-------------------- END OF FILE --------------------
### FILE 3: expenses.php
- Type: PHP
- Size: 1.26 KB
- Path: .
- Name: expenses.php
------------------------------------------------------------
requireLogin();
$page_title = 'Expenses Management';
include 'includes/header.php';
?>
Expenses Module Coming Soon
This module will help you track all business expenses including feed costs, equipment purchases, maintenance, labor, and other operational expenses.
Add New Expense
View Expense Report
Expense Categories
No Expense Records
Start recording your business expenses to see them here.
-------------------- END OF FILE --------------------
### FILE 4: index.php
- Type: PHP
- Size: 6.06 KB
- Path: .
- Name: index.php
------------------------------------------------------------
requireLogin();
$page_title = 'Dashboard';
// Fetch real statistics
try {
// Sales statistics
$stmt = $pdo->prepare("
SELECT
COUNT(*) as total_sales_count,
COALESCE(SUM(total_amount), 0) as total_sales_amount,
COALESCE(SUM(CASE WHEN payment_status = 'pending' THEN total_amount ELSE 0 END), 0) as pending_amount
FROM sales
");
$stmt->execute();
$sales_stats = $stmt->fetch(PDO::FETCH_ASSOC);
// Products/Services count
$stmt = $pdo->prepare("SELECT COUNT(*) as count FROM products_services WHERE status = 'active'");
$stmt->execute();
$products_count = $stmt->fetchColumn();
// Recent sales for activity - now showing bills with their items
$stmt = $pdo->prepare("
SELECT s.*,
(SELECT GROUP_CONCAT(CONCAT(ps.name, ' (', si.quantity, ' ', u.unit_symbol, ')') SEPARATOR ', ')
FROM sales_items si
JOIN products_services ps ON si.product_service_id = ps.id
JOIN units u ON ps.unit_id = u.id
WHERE si.sale_id = s.id) as items_summary
FROM sales s
ORDER BY s.created_at DESC
LIMIT 5
");
$stmt->execute();
$recent_sales = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$sales_stats = ['total_sales_count' => 0, 'total_sales_amount' => 0, 'pending_amount' => 0];
$products_count = 0;
$recent_sales = [];
}
include 'includes/header.php';
?>
| Bill# |
Date |
Items |
Amount |
Customer |
|
|
|
₹ |
-
|
| User Role |
getUserRole())); ?> |
| Login Time |
|
| System Status |
Active |
-------------------- END OF FILE --------------------
### FILE 5: investments.php
- Type: PHP
- Size: 1.28 KB
- Path: .
- Name: investments.php
------------------------------------------------------------
requireLogin();
$page_title = 'Investments Management';
include 'includes/header.php';
?>
Investments Module Coming Soon
This module will help you track investments in ponds, equipment, infrastructure, breeding stock, and other capital investments for your fish business.
Add New Investment
View Investment Portfolio
ROI Analysis
No Investment Records
Start tracking your business investments to see them here.
-------------------- END OF FILE --------------------
### FILE 6: loans.php
- Type: PHP
- Size: 1.22 KB
- Path: .
- Name: loans.php
------------------------------------------------------------
requireLogin();
$page_title = 'Loans Management';
include 'includes/header.php';
?>
Loans Module Coming Soon
This module will help you track business loans, including loan details, repayment schedules, interest calculations, and outstanding balances.
Add New Loan
Record Payment
View Repayment Schedule
No Loan Records
Start tracking your business loans to see them here.
-------------------- END OF FILE --------------------
### FILE 7: login.php
- Type: PHP
- Size: 2.57 KB
- Path: .
- Name: login.php
------------------------------------------------------------
isSetupCompleted()) {
header("Location: setup.php");
exit();
}
// Redirect if already logged in
if ($auth->isLoggedIn()) {
header("Location: index.php");
exit();
}
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username) || empty($password)) {
$error_message = 'Please enter both username and password.';
} else {
if ($auth->login($username, $password)) {
header("Location: index.php");
exit();
} else {
$error_message = 'Invalid username or password.';
}
}
}
$page_title = 'Login';
?>
- Kayal Aqua Management
Kayal Aqua
Fish Business Management System
© Kayal Aqua. All rights reserved.
-------------------- END OF FILE --------------------
### FILE 8: logout.php
- Type: PHP
- Size: 50 B
- Path: .
- Name: logout.php
------------------------------------------------------------
logout();
?>
-------------------- END OF FILE --------------------
### FILE 9: sales.php
- Type: PHP
- Size: 21.84 KB
- Path: .
- Name: sales.php
------------------------------------------------------------
requireLogin();
$page_title = 'Sales Management';
$success_message = '';
$error_message = '';
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['action'])) {
if ($_POST['action'] == 'record_sale') {
$sale_date = $_POST['sale_date'] ?? '';
$customer_name = trim($_POST['customer_name'] ?? '');
$customer_phone = trim($_POST['customer_phone'] ?? '');
$payment_status = $_POST['payment_status'] ?? 'paid';
$payment_method = $_POST['payment_method'] ?? 'cash';
$notes = trim($_POST['notes'] ?? '');
// Get line items from individual form fields
$products = $_POST['product_id'] ?? [];
$quantities = $_POST['quantity'] ?? [];
$unit_prices = $_POST['unit_price'] ?? [];
$line_totals = $_POST['line_total'] ?? [];
// Validation
if (empty($sale_date)) {
$error_message = 'Sale date is required.';
} elseif (empty($products) || !is_array($products)) {
$error_message = 'At least one product/service must be added.';
} else {
// Build line items array
$line_items = [];
$total_amount = 0;
for ($i = 0; $i < count($products); $i++) {
if (!empty($products[$i]) && !empty($quantities[$i]) && isset($unit_prices[$i]) && isset($line_totals[$i])) {
$product_id = intval($products[$i]);
$quantity = floatval($quantities[$i]);
$unit_price = floatval($unit_prices[$i]);
$line_total = floatval($line_totals[$i]);
if ($product_id > 0 && $quantity > 0 && $unit_price >= 0 && $line_total >= 0) {
$line_items[] = [
'product_id' => $product_id,
'quantity' => $quantity,
'unit_price' => $unit_price,
'line_total' => $line_total
];
$total_amount += $line_total;
}
}
}
if (empty($line_items)) {
$error_message = 'At least one valid product/service must be added.';
} elseif ($total_amount <= 0) {
$error_message = 'Total amount must be greater than zero.';
} else {
try {
// Generate bill number
$bill_number = 'BILL' . date('YmdHis') . rand(100, 999);
// Start transaction
$pdo->beginTransaction();
// Insert sale
$stmt = $pdo->prepare("
INSERT INTO sales (bill_number, sale_date, customer_name, customer_phone,
total_amount, payment_status, payment_method, notes, created_by)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$bill_number, $sale_date, $customer_name, $customer_phone,
$total_amount, $payment_status, $payment_method, $notes, $_SESSION['user_id']
]);
$sale_id = $pdo->lastInsertId();
// Insert line items
$stmt = $pdo->prepare("
INSERT INTO sales_items (sale_id, product_service_id, quantity, unit_price, line_total)
VALUES (?, ?, ?, ?, ?)
");
foreach ($line_items as $item) {
$stmt->execute([
$sale_id, $item['product_id'], $item['quantity'],
$item['unit_price'], $item['line_total']
]);
}
$pdo->commit();
$_SESSION['success_message'] = 'Sale recorded successfully! Bill: ' . $bill_number;
// Redirect to prevent form resubmission
header("Location: sales.php");
exit();
} catch (Exception $e) {
$pdo->rollBack();
$error_message = 'Error recording sale: ' . $e->getMessage();
}
}
}
}
elseif ($_POST['action'] == 'delete_sale') {
$sale_id = intval($_POST['sale_id'] ?? 0);
if ($sale_id > 0) {
try {
$pdo->beginTransaction();
// Delete sales items first
$stmt = $pdo->prepare("DELETE FROM sales_items WHERE sale_id = ?");
$stmt->execute([$sale_id]);
// Delete sale
$stmt = $pdo->prepare("DELETE FROM sales WHERE id = ?");
$result = $stmt->execute([$sale_id]);
if ($result) {
$pdo->commit();
$_SESSION['success_message'] = 'Sale deleted successfully.';
} else {
$pdo->rollBack();
$_SESSION['error_message'] = 'Failed to delete sale.';
}
} catch (Exception $e) {
$pdo->rollBack();
$_SESSION['error_message'] = 'Error deleting sale: ' . $e->getMessage();
}
}
header("Location: sales.php");
exit();
}
}
// Fetch products/services
try {
$stmt = $pdo->prepare("
SELECT ps.*, u.unit_name, u.unit_symbol
FROM products_services ps
JOIN units u ON ps.unit_id = u.id
WHERE ps.status = 'active'
ORDER BY ps.name
");
$stmt->execute();
$products_services = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$products_services = [];
}
// Fetch sales with filters
$filter_date_from = $_GET['filter_date_from'] ?? '';
$filter_date_to = $_GET['filter_date_to'] ?? '';
$where_clause = "1=1";
$params = [];
if (!empty($filter_date_from)) {
$where_clause .= " AND s.sale_date >= ?";
$params[] = $filter_date_from;
}
if (!empty($filter_date_to)) {
$where_clause .= " AND s.sale_date <= ?";
$params[] = $filter_date_to;
}
try {
// Get sales stats
$stmt = $pdo->prepare("
SELECT
COUNT(*) as total_sales,
COALESCE(SUM(total_amount), 0) as total_revenue,
COALESCE(SUM(CASE WHEN payment_status = 'paid' THEN total_amount ELSE 0 END), 0) as paid_amount
FROM sales s WHERE $where_clause
");
$stmt->execute($params);
$stats = $stmt->fetch(PDO::FETCH_ASSOC);
// Get sales records
$stmt = $pdo->prepare("
SELECT s.*, u.full_name as created_by_name
FROM sales s
JOIN users u ON s.created_by = u.id
WHERE $where_clause
ORDER BY s.created_at DESC
LIMIT 50
");
$stmt->execute($params);
$sales = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Get line items for each sale
foreach ($sales as &$sale) {
$stmt = $pdo->prepare("
SELECT si.*, ps.name as product_name, u.unit_symbol
FROM sales_items si
JOIN products_services ps ON si.product_service_id = ps.id
JOIN units u ON ps.unit_id = u.id
WHERE si.sale_id = ?
");
$stmt->execute([$sale['id']]);
$sale['items'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
} catch (PDOException $e) {
$stats = ['total_sales' => 0, 'total_revenue' => 0, 'paid_amount' => 0];
$sales = [];
$error_message = 'Error fetching sales data.';
}
include 'includes/header.php';
?>
No Sales Found
Record your first sale using the form above.
| Bill# |
Date |
Items |
Total |
Customer |
Payment |
Action |
|
|
:
@ ₹ =
₹
|
₹ |
-
|
|
|
|
Notes:
|
-------------------- END OF FILE --------------------
### FILE 10: settings.php
- Type: PHP
- Size: 42.85 KB
- Path: .
- Name: settings.php
------------------------------------------------------------
requireLogin();
$page_title = 'Settings';
$success_message = '';
$error_message = '';
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
try {
switch ($_POST['action']) {
case 'create_unit':
$unit_name = trim($_POST['unit_name'] ?? '');
$unit_symbol = trim($_POST['unit_symbol'] ?? '');
$unit_type = $_POST['unit_type'] ?? '';
if (empty($unit_name) || empty($unit_symbol) || empty($unit_type)) {
$error_message = 'All fields are required for unit creation.';
} else {
$stmt = $pdo->prepare("SELECT id FROM units WHERE unit_name = ? OR unit_symbol = ?");
$stmt->execute([$unit_name, $unit_symbol]);
if ($stmt->fetch()) {
$error_message = 'Unit name or symbol already exists.';
} else {
$stmt = $pdo->prepare("INSERT INTO units (unit_name, unit_symbol, unit_type, created_by) VALUES (?, ?, ?, ?)");
if ($stmt->execute([$unit_name, $unit_symbol, $unit_type, $_SESSION['user_id']])) {
$_SESSION['success_message'] = 'Unit created successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to create unit.';
}
}
}
break;
case 'edit_unit':
$unit_id = $_POST['unit_id'] ?? 0;
$unit_name = trim($_POST['unit_name'] ?? '');
$unit_symbol = trim($_POST['unit_symbol'] ?? '');
$unit_type = $_POST['unit_type'] ?? '';
if (empty($unit_name) || empty($unit_symbol) || empty($unit_type)) {
$error_message = 'All fields are required for unit update.';
} else {
$stmt = $pdo->prepare("SELECT id FROM units WHERE (unit_name = ? OR unit_symbol = ?) AND id != ?");
$stmt->execute([$unit_name, $unit_symbol, $unit_id]);
if ($stmt->fetch()) {
$error_message = 'Unit name or symbol already exists.';
} else {
$stmt = $pdo->prepare("UPDATE units SET unit_name = ?, unit_symbol = ?, unit_type = ? WHERE id = ?");
if ($stmt->execute([$unit_name, $unit_symbol, $unit_type, $unit_id])) {
$_SESSION['success_message'] = 'Unit updated successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to update unit.';
}
}
}
break;
case 'delete_unit':
$unit_id = $_POST['unit_id'] ?? 0;
// Check if unit is being used by any products/services
$stmt = $pdo->prepare("SELECT COUNT(*) FROM products_services WHERE unit_id = ?");
$stmt->execute([$unit_id]);
$usage_count = $stmt->fetchColumn();
if ($usage_count > 0) {
$error_message = 'Cannot delete unit. It is being used by ' . $usage_count . ' product(s)/service(s).';
} else {
$stmt = $pdo->prepare("DELETE FROM units WHERE id = ?");
if ($stmt->execute([$unit_id])) {
$_SESSION['success_message'] = 'Unit deleted successfully.';
} else {
$_SESSION['error_message'] = 'Failed to delete unit.';
}
header("Location: settings.php");
exit();
}
break;
case 'create_expense_category':
$category_name = trim($_POST['category_name'] ?? '');
$description = trim($_POST['description'] ?? '');
if (empty($category_name)) {
$error_message = 'Category name is required.';
} else {
$stmt = $pdo->prepare("SELECT id FROM expense_categories WHERE category_name = ?");
$stmt->execute([$category_name]);
if ($stmt->fetch()) {
$error_message = 'Category already exists.';
} else {
$stmt = $pdo->prepare("INSERT INTO expense_categories (category_name, description, created_by) VALUES (?, ?, ?)");
if ($stmt->execute([$category_name, $description, $_SESSION['user_id']])) {
$_SESSION['success_message'] = 'Expense category created successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to create category.';
}
}
}
break;
case 'edit_expense_category':
$category_id = $_POST['category_id'] ?? 0;
$category_name = trim($_POST['category_name'] ?? '');
$description = trim($_POST['description'] ?? '');
if (empty($category_name)) {
$error_message = 'Category name is required.';
} else {
$stmt = $pdo->prepare("SELECT id FROM expense_categories WHERE category_name = ? AND id != ?");
$stmt->execute([$category_name, $category_id]);
if ($stmt->fetch()) {
$error_message = 'Category name already exists.';
} else {
$stmt = $pdo->prepare("UPDATE expense_categories SET category_name = ?, description = ? WHERE id = ?");
if ($stmt->execute([$category_name, $description, $category_id])) {
$_SESSION['success_message'] = 'Category updated successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to update category.';
}
}
}
break;
case 'delete_expense_category':
$category_id = $_POST['category_id'] ?? 0;
// Check if category has subcategories
$stmt = $pdo->prepare("SELECT COUNT(*) FROM expense_subcategories WHERE category_id = ?");
$stmt->execute([$category_id]);
$subcategory_count = $stmt->fetchColumn();
if ($subcategory_count > 0) {
$error_message = 'Cannot delete category. It has ' . $subcategory_count . ' subcategory/subcategories.';
} else {
$stmt = $pdo->prepare("DELETE FROM expense_categories WHERE id = ?");
if ($stmt->execute([$category_id])) {
$_SESSION['success_message'] = 'Category deleted successfully.';
} else {
$_SESSION['error_message'] = 'Failed to delete category.';
}
header("Location: settings.php");
exit();
}
break;
case 'create_expense_subcategory':
$category_id = $_POST['category_id'] ?? 0;
$subcategory_name = trim($_POST['subcategory_name'] ?? '');
$description = trim($_POST['description'] ?? '');
if (empty($subcategory_name) || empty($category_id)) {
$error_message = 'Subcategory name and category are required.';
} else {
$stmt = $pdo->prepare("SELECT id FROM expense_subcategories WHERE category_id = ? AND subcategory_name = ?");
$stmt->execute([$category_id, $subcategory_name]);
if ($stmt->fetch()) {
$error_message = 'Subcategory already exists in this category.';
} else {
$stmt = $pdo->prepare("INSERT INTO expense_subcategories (category_id, subcategory_name, description, created_by) VALUES (?, ?, ?, ?)");
if ($stmt->execute([$category_id, $subcategory_name, $description, $_SESSION['user_id']])) {
$_SESSION['success_message'] = 'Expense subcategory created successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to create subcategory.';
}
}
}
break;
case 'edit_expense_subcategory':
$subcategory_id = $_POST['subcategory_id'] ?? 0;
$category_id = $_POST['category_id'] ?? 0;
$subcategory_name = trim($_POST['subcategory_name'] ?? '');
$description = trim($_POST['description'] ?? '');
if (empty($subcategory_name) || empty($category_id)) {
$error_message = 'Subcategory name and category are required.';
} else {
$stmt = $pdo->prepare("SELECT id FROM expense_subcategories WHERE category_id = ? AND subcategory_name = ? AND id != ?");
$stmt->execute([$category_id, $subcategory_name, $subcategory_id]);
if ($stmt->fetch()) {
$error_message = 'Subcategory already exists in this category.';
} else {
$stmt = $pdo->prepare("UPDATE expense_subcategories SET category_id = ?, subcategory_name = ?, description = ? WHERE id = ?");
if ($stmt->execute([$category_id, $subcategory_name, $description, $subcategory_id])) {
$_SESSION['success_message'] = 'Subcategory updated successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to update subcategory.';
}
}
}
break;
case 'delete_expense_subcategory':
$subcategory_id = $_POST['subcategory_id'] ?? 0;
$stmt = $pdo->prepare("DELETE FROM expense_subcategories WHERE id = ?");
if ($stmt->execute([$subcategory_id])) {
$_SESSION['success_message'] = 'Subcategory deleted successfully.';
} else {
$_SESSION['error_message'] = 'Failed to delete subcategory.';
}
header("Location: settings.php");
exit();
break;
case 'create_product_service':
$name = trim($_POST['name'] ?? '');
$type = $_POST['type'] ?? '';
$description = trim($_POST['description'] ?? '');
$unit_id = $_POST['unit_id'] ?? 0;
$unit_rate = $_POST['unit_rate'] ?? 0;
if (empty($name) || empty($type) || empty($unit_id)) {
$error_message = 'Name, type, and unit are required.';
} elseif (!is_numeric($unit_rate) || $unit_rate < 0) {
$error_message = 'Please enter a valid unit rate.';
} else {
$stmt = $pdo->prepare("SELECT id FROM products_services WHERE name = ?");
$stmt->execute([$name]);
if ($stmt->fetch()) {
$error_message = 'Product/Service name already exists.';
} else {
$stmt = $pdo->prepare("INSERT INTO products_services (name, type, description, unit_id, unit_rate, created_by) VALUES (?, ?, ?, ?, ?, ?)");
if ($stmt->execute([$name, $type, $description, $unit_id, $unit_rate, $_SESSION['user_id']])) {
$_SESSION['success_message'] = ucfirst($type) . ' created successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to create ' . $type . '.';
}
}
}
break;
case 'edit_product_service':
$ps_id = $_POST['ps_id'] ?? 0;
$name = trim($_POST['name'] ?? '');
$type = $_POST['type'] ?? '';
$description = trim($_POST['description'] ?? '');
$unit_id = $_POST['unit_id'] ?? 0;
$unit_rate = $_POST['unit_rate'] ?? 0;
if (empty($name) || empty($type) || empty($unit_id)) {
$error_message = 'Name, type, and unit are required.';
} elseif (!is_numeric($unit_rate) || $unit_rate < 0) {
$error_message = 'Please enter a valid unit rate.';
} else {
$stmt = $pdo->prepare("SELECT id FROM products_services WHERE name = ? AND id != ?");
$stmt->execute([$name, $ps_id]);
if ($stmt->fetch()) {
$error_message = 'Product/Service name already exists.';
} else {
$stmt = $pdo->prepare("UPDATE products_services SET name = ?, type = ?, description = ?, unit_id = ?, unit_rate = ? WHERE id = ?");
if ($stmt->execute([$name, $type, $description, $unit_id, $unit_rate, $ps_id])) {
$_SESSION['success_message'] = 'Product/Service updated successfully.';
header("Location: settings.php");
exit();
} else {
$error_message = 'Failed to update product/service.';
}
}
}
break;
case 'delete_product_service':
$ps_id = $_POST['ps_id'] ?? 0;
$stmt = $pdo->prepare("DELETE FROM products_services WHERE id = ?");
if ($stmt->execute([$ps_id])) {
$_SESSION['success_message'] = 'Product/Service deleted successfully.';
} else {
$_SESSION['error_message'] = 'Failed to delete product/service.';
}
header("Location: settings.php");
exit();
break;
case 'toggle_unit_status':
$unit_id = $_POST['unit_id'] ?? 0;
$status = $_POST['status'] ?? 'active';
$stmt = $pdo->prepare("UPDATE units SET status = ? WHERE id = ?");
if ($stmt->execute([$status, $unit_id])) {
$_SESSION['success_message'] = 'Unit status updated successfully.';
} else {
$_SESSION['error_message'] = 'Failed to update unit status.';
}
header("Location: settings.php");
exit();
break;
case 'toggle_category_status':
$category_id = $_POST['category_id'] ?? 0;
$status = $_POST['status'] ?? 'active';
$stmt = $pdo->prepare("UPDATE expense_categories SET status = ? WHERE id = ?");
if ($stmt->execute([$status, $category_id])) {
$_SESSION['success_message'] = 'Category status updated successfully.';
} else {
$_SESSION['error_message'] = 'Failed to update category status.';
}
header("Location: settings.php");
exit();
break;
case 'toggle_product_status':
$product_id = $_POST['product_id'] ?? 0;
$status = $_POST['status'] ?? 'active';
$stmt = $pdo->prepare("UPDATE products_services SET status = ? WHERE id = ?");
if ($stmt->execute([$status, $product_id])) {
$_SESSION['success_message'] = 'Product/Service status updated successfully.';
} else {
$_SESSION['error_message'] = 'Failed to update status.';
}
header("Location: settings.php");
exit();
break;
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
// Get edit data if editing
$edit_unit = null;
$edit_category = null;
$edit_subcategory = null;
$edit_ps = null;
if (isset($_GET['edit_unit'])) {
$stmt = $pdo->prepare("SELECT * FROM units WHERE id = ?");
$stmt->execute([$_GET['edit_unit']]);
$edit_unit = $stmt->fetch(PDO::FETCH_ASSOC);
}
if (isset($_GET['edit_category'])) {
$stmt = $pdo->prepare("SELECT * FROM expense_categories WHERE id = ?");
$stmt->execute([$_GET['edit_category']]);
$edit_category = $stmt->fetch(PDO::FETCH_ASSOC);
}
if (isset($_GET['edit_subcategory'])) {
$stmt = $pdo->prepare("SELECT * FROM expense_subcategories WHERE id = ?");
$stmt->execute([$_GET['edit_subcategory']]);
$edit_subcategory = $stmt->fetch(PDO::FETCH_ASSOC);
}
if (isset($_GET['edit_ps'])) {
$stmt = $pdo->prepare("SELECT * FROM products_services WHERE id = ?");
$stmt->execute([$_GET['edit_ps']]);
$edit_ps = $stmt->fetch(PDO::FETCH_ASSOC);
}
// Fetch data for display
try {
// Fetch units
$stmt = $pdo->prepare("SELECT * FROM units ORDER BY unit_type, unit_name");
$stmt->execute();
$units = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch expense categories
$stmt = $pdo->prepare("SELECT * FROM expense_categories ORDER BY category_name");
$stmt->execute();
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch expense subcategories with category names
$stmt = $pdo->prepare("
SELECT es.*, ec.category_name
FROM expense_subcategories es
JOIN expense_categories ec ON es.category_id = ec.id
ORDER BY ec.category_name, es.subcategory_name
");
$stmt->execute();
$subcategories = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch products/services with unit info
$stmt = $pdo->prepare("
SELECT ps.*, u.unit_name, u.unit_symbol
FROM products_services ps
JOIN units u ON ps.unit_id = u.id
ORDER BY ps.type, ps.name
");
$stmt->execute();
$products_services = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error_message = 'Failed to fetch data: ' . $e->getMessage();
$units = $categories = $subcategories = $products_services = [];
}
include 'includes/header.php';
?>
| Name |
Symbol |
Type |
Status |
Actions |
|
|
|
|
|
| Category Name |
Description |
Status |
Actions |
|
|
|
|
| Main Category |
Subcategory |
Description |
Status |
Actions |
|
|
|
|
|
| Name |
Type |
Unit |
Rate (₹) |
Description |
Status |
Actions |
|
|
|
₹ |
|
|
|
-------------------- END OF FILE --------------------
### FILE 11: setup.php
- Type: PHP
- Size: 5 KB
- Path: .
- Name: setup.php
------------------------------------------------------------
isSetupCompleted()) {
header("Location: login.php");
exit();
}
$error_message = '';
$success_message = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$full_name = trim($_POST['full_name'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$confirm_password = $_POST['confirm_password'] ?? '';
// Validation
if (empty($full_name) || empty($phone) || empty($username) || empty($password)) {
$error_message = 'All fields are required.';
} elseif ($password !== $confirm_password) {
$error_message = 'Passwords do not match.';
} elseif (strlen($password) < 6) {
$error_message = 'Password must be at least 6 characters long.';
} elseif (!preg_match('/^[0-9+\-\s]{10,15}$/', $phone)) {
$error_message = 'Please enter a valid phone number.';
} elseif (!preg_match('/^[a-zA-Z0-9_]{3,}$/', $username)) {
$error_message = 'Username must be at least 3 characters and contain only letters, numbers, and underscore.';
} else {
// Create first admin user
if ($auth->createFirstAdmin($full_name, $phone, $username, $password)) {
$success_message = 'Admin user created successfully! You can now login.';
} else {
$error_message = 'Failed to create admin user. Please try again.';
}
}
}
$page_title = 'Initial Setup';
?>
- Kayal Aqua Management
-------------------- END OF FILE --------------------
### FILE 12: users.php
- Type: PHP
- Size: 9.41 KB
- Path: .
- Name: users.php
------------------------------------------------------------
requireAdmin(); // Only admin can access this page
$page_title = 'Users Management';
$success_message = '';
$error_message = '';
// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'create_user':
$full_name = trim($_POST['full_name'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$role = $_POST['role'] ?? 'manager';
// Validation
if (empty($full_name) || empty($phone) || empty($username) || empty($password)) {
$error_message = 'All fields are required.';
} elseif (strlen($password) < 6) {
$error_message = 'Password must be at least 6 characters long.';
} elseif (!preg_match('/^[0-9+\-\s]{10,15}$/', $phone)) {
$error_message = 'Please enter a valid phone number.';
} elseif (!preg_match('/^[a-zA-Z0-9_]{3,}$/', $username)) {
$error_message = 'Username must be at least 3 characters and contain only letters, numbers, and underscore.';
} else {
try {
// Check if username already exists
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
$stmt->execute([$username]);
if ($stmt->fetch()) {
$error_message = 'Username already exists.';
} else {
// Create new user
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (full_name, phone, username, password, role) VALUES (?, ?, ?, ?, ?)");
if ($stmt->execute([$full_name, $phone, $username, $hashed_password, $role])) {
$_SESSION['success_message'] = 'User created successfully.';
header("Location: users.php");
exit();
} else {
$error_message = 'Failed to create user.';
}
}
} catch (PDOException $e) {
$error_message = 'Database error: ' . $e->getMessage();
}
}
break;
case 'toggle_status':
$user_id = $_POST['user_id'] ?? 0;
$new_status = $_POST['status'] ?? 'active';
try {
$stmt = $pdo->prepare("UPDATE users SET status = ? WHERE id = ? AND id != ?");
if ($stmt->execute([$new_status, $user_id, $_SESSION['user_id']])) {
$_SESSION['success_message'] = 'User status updated successfully.';
} else {
$_SESSION['error_message'] = 'Failed to update user status.';
}
} catch (PDOException $e) {
$_SESSION['error_message'] = 'Database error.';
}
header("Location: users.php");
exit();
break;
}
}
}
// Fetch all users
try {
$stmt = $pdo->prepare("SELECT id, full_name, phone, username, role, status, created_at FROM users ORDER BY created_at DESC");
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$users = [];
$error_message = 'Failed to fetch users.';
}
include 'includes/header.php';
?>
No Users Found
Create your first user using the form above.
| Name |
Username |
Phone |
Role |
Status |
Created |
Actions |
|
|
|
|
|
|
Current User
|
-------------------- END OF FILE --------------------
### FILE 13: config/database.php
- Type: PHP
- Size: 967 B
- Path: config
- Name: database.php
------------------------------------------------------------
conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->db_name . ";charset=utf8mb4",
$this->username,
$this->password,
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);
} catch(PDOException $exception) {
die("Connection error: " . $exception->getMessage());
}
return $this->conn;
}
}
// Create database instance
$database = new Database();
$pdo = $database->getConnection();
?>
-------------------- END OF FILE --------------------
### FILE 14: css/style.css
- Type: CSS
- Size: 10.79 KB
- Path: css
- Name: style.css
------------------------------------------------------------
/* CSS Variables for Colors */
:root {
--primary-yellow: #FFD700;
--dark-navy: #1a237e;
--light-navy: #3949ab;
--white: #ffffff;
--light-gray: #f5f5f5;
--dark-gray: #666666;
--success: #4caf50;
--error: #f44336;
--warning: #ff9800;
--shadow: rgba(26, 35, 126, 0.1);
}
/* Utility Classes */
.text-center { text-align: center; }
.text-right { text-align: right; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.d-none { display: none; }
.d-block { display: block; }
.d-flex { display: flex; }
.justify-between { justify-content: space-between; }
.align-center { align-items: center; }
.gap-1 { gap: 0.5rem; }
.gap-2 { gap: 1rem; }
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
line-height: 1.6;
color: var(--dark-navy);
background-color: var(--light-gray);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header Styles */
.header {
background: linear-gradient(135deg, var(--dark-navy) 0%, var(--light-navy) 100%);
color: var(--white);
padding: 1rem;
box-shadow: 0 2px 10px var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-yellow);
text-decoration: none;
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
}
.user-name {
font-weight: 500;
font-size: 0.9rem;
}
.logout-btn {
background: var(--primary-yellow);
color: var(--dark-navy);
border: none;
padding: 0.5rem 1rem;
border-radius: 5px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
font-size: 0.8rem;
}
.logout-btn:hover {
background: #ffed4e;
transform: translateY(-1px);
}
/* Navigation */
.nav-container {
background: var(--white);
padding: 1rem;
box-shadow: 0 2px 5px var(--shadow);
margin-bottom: 1rem;
}
.nav-menu {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
max-width: 1200px;
margin: 0 auto;
}
.nav-item {
display: block;
text-align: center;
padding: 0.8rem 0.5rem;
background: var(--light-gray);
color: var(--dark-navy);
text-decoration: none;
border-radius: 8px;
font-weight: 500;
font-size: 0.85rem;
transition: all 0.3s ease;
border: 2px solid transparent;
}
.nav-item:hover,
.nav-item.active {
background: var(--primary-yellow);
color: var(--dark-navy);
border-color: var(--dark-navy);
transform: translateY(-2px);
}
/* Main Content */
.main-content {
flex: 1;
max-width: 1200px;
margin: 0 auto;
width: 100%;
padding: 0 1rem;
}
.page-header {
margin-bottom: 2rem;
text-align: center;
}
.page-title {
font-size: 2rem;
color: var(--dark-navy);
margin-bottom: 0.5rem;
}
.page-subtitle {
color: var(--dark-gray);
font-size: 1rem;
}
/* Cards */
.card {
background: var(--white);
border-radius: 10px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 4px 15px var(--shadow);
border: 1px solid #e0e0e0;
}
.card-header {
border-bottom: 2px solid var(--primary-yellow);
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
.card-title {
color: var(--dark-navy);
font-size: 1.2rem;
font-weight: 600;
}
/* Forms */
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: var(--dark-navy);
font-size: 0.9rem;
}
.form-input,
.form-select,
.form-textarea {
width: 100%;
padding: 0.8rem;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 1rem;
transition: all 0.3s ease;
background: var(--white);
}
.form-input:focus,
.form-select:focus,
.form-textarea:focus {
outline: none;
border-color: var(--primary-yellow);
box-shadow: 0 0 0 3px rgba(255, 215, 0, 0.1);
}
.form-textarea {
resize: vertical;
min-height: 100px;
}
/* Buttons */
.btn {
display: inline-block;
padding: 0.8rem 1.5rem;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
font-size: 0.9rem;
text-align: center;
min-width: 100px;
}
.btn-primary {
background: var(--dark-navy);
color: var(--white);
}
.btn-primary:hover {
background: var(--light-navy);
transform: translateY(-1px);
}
.btn-secondary {
background: var(--primary-yellow);
color: var(--dark-navy);
}
.btn-secondary:hover {
background: #ffed4e;
transform: translateY(-1px);
}
.btn-success {
background: var(--success);
color: var(--white);
}
.btn-danger {
background: var(--error);
color: var(--white);
}
.btn-full {
width: 100%;
}
/* Tables */
.table-container {
overflow-x: auto;
margin-bottom: 1rem;
}
.table {
width: 100%;
border-collapse: collapse;
background: var(--white);
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px var(--shadow);
}
.table th,
.table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
.table th {
background: var(--dark-navy);
color: var(--white);
font-weight: 600;
font-size: 0.9rem;
}
.table tr:hover {
background: #f9f9f9;
}
/* Alerts */
.alert {
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
font-weight: 500;
border-left: 4px solid;
}
.alert-success {
background: #e8f5e8;
color: #2d5932;
border-left-color: var(--success);
}
.alert-error {
background: #fdeaea;
color: #721c24;
border-left-color: var(--error);
}
.alert-warning {
background: #fff8e1;
color: #663c00;
border-left-color: var(--warning);
}
/* Loading Spinner */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid var(--primary-yellow);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Footer */
.footer {
background: var(--dark-navy);
color: var(--white);
text-align: center;
padding: 1rem;
margin-top: auto;
}
/* Login Page Specific */
.login-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, var(--dark-navy) 0%, var(--light-navy) 100%);
padding: 1rem;
}
.login-card {
background: var(--white);
padding: 2rem;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 400px;
}
.login-logo {
text-align: center;
margin-bottom: 2rem;
}
.login-logo h1 {
color: var(--dark-navy);
font-size: 2rem;
margin-bottom: 0.5rem;
}
.login-logo p {
color: var(--dark-gray);
font-size: 0.9rem;
}
/* Dashboard Grid */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: linear-gradient(135deg, var(--primary-yellow) 0%, #ffed4e 100%);
color: var(--dark-navy);
padding: 1.5rem;
border-radius: 15px;
text-align: center;
box-shadow: 0 5px 15px rgba(255, 215, 0, 0.3);
transition: transform 0.3s ease;
}
.stat-card:hover {
transform: translateY(-2px);
}
.stat-number {
font-size: 2rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.stat-label {
font-weight: 600;
opacity: 0.8;
}
/* Empty State */
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: var(--dark-gray);
}
.empty-state h3 {
margin-bottom: 1rem;
color: var(--dark-navy);
}
/* Mobile Responsive */
@media (max-width: 768px) {
.header-content {
flex-direction: column;
gap: 0.5rem;
}
.user-info {
justify-content: center;
}
.nav-menu {
grid-template-columns: repeat(2, 1fr);
}
.page-title {
font-size: 1.5rem;
}
.card {
padding: 1rem;
}
.table th,
.table td {
padding: 0.5rem;
font-size: 0.8rem;
}
.btn {
padding: 0.7rem 1rem;
font-size: 0.8rem;
}
}
@media (max-width: 480px) {
.nav-menu {
grid-template-columns: 1fr;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
}
/* Sales Line Items Styling */
.line-item-form {
background: var(--light-gray);
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
}
.line-items-table {
margin-top: 1rem;
}
.line-items-table .table tfoot td {
background: linear-gradient(135deg, var(--primary-yellow) 0%, #ffed4e 100%);
color: var(--dark-navy);
font-weight: 700;
border-top: 3px solid var(--dark-navy);
}
.bill-section-divider {
border-bottom: 2px solid var(--light-gray);
padding-bottom: 1rem;
margin-bottom: 2rem;
}
.payment-section-divider {
border-top: 2px solid var(--light-gray);
padding-top: 1rem;
margin-top: 2rem;
}
/* Editable line total inputs */
.line-total-input {
width: 100px;
display: inline-block;
padding: 0.4rem;
font-size: 0.9rem;
border: 2px solid var(--primary-yellow);
border-radius: 4px;
font-weight: bold;
}
.line-total-input:focus {
outline: none;
border-color: var(--dark-navy);
box-shadow: 0 0 0 2px rgba(26, 35, 126, 0.1);
}
/* Sales history improvements */
.sales-item-details {
font-size: 0.85rem;
line-height: 1.3;
}
.sales-item-details > div {
margin-bottom: 0.25rem;
padding: 0.25rem 0;
border-bottom: 1px dotted #e0e0e0;
}
.sales-item-details > div:last-child {
border-bottom: none;
margin-bottom: 0;
}
/* Button group for actions */
.btn-group {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
@media (max-width: 768px) {
.btn-group {
flex-direction: column;
}
.line-total-input {
width: 80px;
font-size: 0.8rem;
}
.sales-item-details {
font-size: 0.8rem;
}
}
.text-right { text-align: right; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.d-none { display: none; }
.d-block { display: block; }
.d-flex { display: flex; }
.justify-between { justify-content: space-between; }
.align-center { align-items: center; }
.gap-1 { gap: 0.5rem; }
.gap-2 { gap: 1rem; }
-------------------- END OF FILE --------------------
### FILE 15: includes/footer.php
- Type: PHP
- Size: 230 B
- Path: includes
- Name: footer.php
------------------------------------------------------------