diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..c136b54 --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,51 @@ + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #F8F9FA; +} + +.navbar-brand { + font-weight: 600; +} + +.hero-section { + padding: 4rem 0; + background: linear-gradient(45deg, #0D6EFD, #4D8BFD); + color: white; +} + +.hero-section h1 { + font-weight: 700; + font-size: 2.5rem; +} + +.hero-section p { + font-size: 1.1rem; + opacity: 0.9; +} + +.login-section { + padding: 4rem 0; +} + +.login-card { + border: none; + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1); +} + +.card-title { + font-weight: 600; +} + +.btn-primary { + background-color: #0D6EFD; + border-color: #0D6EFD; +} + +.footer { + padding: 2rem 0; + background-color: #FFFFFF; + border-top: 1px solid #DEE2E6; + font-size: 0.9rem; + color: #6C757D; +} diff --git a/auth.php b/auth.php new file mode 100644 index 0000000..478b9e9 --- /dev/null +++ b/auth.php @@ -0,0 +1,7 @@ +prepare("INSERT INTO bunks (name, owner, contact, location) VALUES (?, ?, ?, ?)"); + $stmt->execute([$name, $owner, $contact, $location]); + $message = '
Bunk added successfully!
'; + } catch (PDOException $e) { + $message = '
Error: ' . $e->getMessage() . '
'; + } + } else { + $message = '
Please fill in all fields.
'; + } +} + +// Fetch all bunks to display +$stmt = $pdo->query("SELECT * FROM bunks ORDER BY name"); +$bunks = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + +
+
+

Manage Bunks

+

List of all petrol bunks registered in the system.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameOwnerContactLocation
No bunks found. Add one to get started.
+
+
+
+
+
Add New Bunk
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ + diff --git a/credit_customers.php b/credit_customers.php new file mode 100644 index 0000000..38f6a9b --- /dev/null +++ b/credit_customers.php @@ -0,0 +1,168 @@ +prepare("INSERT INTO credit_customers (name, contact, address, credit_limit) VALUES (?, ?, ?, ?)"); + $stmt->execute([$name, $contact, $address, $credit_limit]); + $customer_message = '
Customer added successfully!
'; + } catch (PDOException $e) { + $customer_message = '
Error: ' . $e->getMessage() . '
'; + } + } else { + $customer_message = '
Customer name is required.
'; + } +} + +// Handle form submission for adding a new vehicle +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_vehicle'])) { + $customer_id = $_POST['customer_id']; + $vehicle_number = trim($_POST['vehicle_number']); + + if (!empty($customer_id) && !empty($vehicle_number)) { + try { + $stmt = $pdo->prepare("INSERT INTO vehicles (customer_id, vehicle_number) VALUES (?, ?)"); + $stmt->execute([$customer_id, $vehicle_number]); + $vehicle_message = '
Vehicle added successfully!
'; + } catch (PDOException $e) { + $vehicle_message = '
Error: ' . $e->getMessage() . '
'; + } + } else { + $vehicle_message = '
Vehicle number is required.
'; + } +} + + +// Fetch all customers +$customers_stmt = $pdo->query("SELECT * FROM credit_customers ORDER BY name"); +$customers = $customers_stmt->fetchAll(PDO::FETCH_ASSOC); + +// Fetch all vehicles and group by customer +$vehicles_stmt = $pdo->query("SELECT * FROM vehicles ORDER BY vehicle_number"); +$all_vehicles = $vehicles_stmt->fetchAll(PDO::FETCH_ASSOC); +$vehicles_by_customer = []; +foreach ($all_vehicles as $vehicle) { + $vehicles_by_customer[$vehicle['customer_id']][] = $vehicle; +} + +?> + +
+ +
+
+
+

Manage Credit Customers

+
+
+
+
+
Existing Customers
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameContactAddressCredit LimitVehicles
No customers found.
+ '; + } + } else { + echo 'No vehicles'; + } + ?> +
+
+
+
+
+
Add New Customer
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
Add Vehicle to Customer
+ +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/credit_sales.php b/credit_sales.php new file mode 100644 index 0000000..67787d9 --- /dev/null +++ b/credit_sales.php @@ -0,0 +1,213 @@ +beginTransaction(); + + // Insert into credit_sales + $sql = "INSERT INTO credit_sales (bunk_id, customer_id, date, fuel_type_id, quantity, rate, amount) VALUES (:bunk_id, :customer_id, :date, :fuel_type_id, :quantity, :rate, :amount)"; + $stmt = $pdoconn->prepare($sql); + $stmt->execute([ + ':bunk_id' => $bunk_id, + ':customer_id' => $customer_id, + ':date' => $date, + ':fuel_type_id' => $fuel_type_id, + ':quantity' => $quantity, + ':rate' => $rate, + ':amount' => $amount + ]); + + // Update customer's outstanding balance + $update_sql = "UPDATE credit_customers SET outstanding_balance = outstanding_balance + :amount WHERE id = :customer_id"; + $update_stmt = $pdoconn->prepare($update_sql); + $update_stmt->execute([':amount' => $amount, ':customer_id' => $customer_id]); + + $pdoconn->commit(); + $success_message = "Credit sale recorded successfully!"; + } catch (PDOException $e) { + $pdoconn->rollBack(); + $error_message = "Error recording sale: " . $e->getMessage(); + } + } else { + $error_message = "All fields are required."; + } +} + +// Fetch master data for dropdowns +try { + $bunks_stmt = $pdoconn->query("SELECT id, name FROM bunks ORDER BY name"); + $bunks = $bunks_stmt->fetchAll(PDO::FETCH_ASSOC); + + $customers_stmt = $pdoconn->query("SELECT id, name, bunk_id FROM credit_customers ORDER BY name"); + $customers = $customers_stmt->fetchAll(PDO::FETCH_ASSOC); + + $fuel_types_stmt = $pdoconn->query("SELECT id, name FROM fuel_types ORDER BY name"); + $fuel_types = $fuel_types_stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $bunks = []; + $customers = []; + $fuel_types = []; + $page_error = "Error fetching master data: " . $e->getMessage(); +} + +// Fetch recent credit sales +try { + $sales_stmt = $pdoconn->query(" + SELECT cs.*, c.name as customer_name, b.name as bunk_name, ft.name as fuel_type_name + FROM credit_sales cs + JOIN credit_customers c ON cs.customer_id = c.id + JOIN bunks b ON cs.bunk_id = b.id + JOIN fuel_types ft ON cs.fuel_type_id = ft.id + ORDER BY cs.date DESC, cs.id DESC + LIMIT 20 + "); + $sales = $sales_stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $sales = []; + $page_error = "Error fetching sales: " . $e->getMessage(); +} + +?> + +
+
+
+

Record Credit Sale

+
+
+ +
+ + +
+ + +
+ + +
+
+
New Sale Entry
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
Recent Credit Sales
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateCustomerBunkFuelQtyRateAmount
No credit sales recorded yet.
+
+
+
+
+
+
+ + + + diff --git a/dashboard.php b/dashboard.php new file mode 100644 index 0000000..196e3e6 --- /dev/null +++ b/dashboard.php @@ -0,0 +1,11 @@ + + +
+

Welcome, !

+
+

This is your central dashboard. From here, you can manage all aspects of your petrol bunk operations. Use the navigation bar above to get started.

+

Your role is:

+
+
+ + diff --git a/daybook.php b/daybook.php new file mode 100644 index 0000000..2bf259b --- /dev/null +++ b/daybook.php @@ -0,0 +1,253 @@ +beginTransaction(); + + try { + $stmt = $pdo->prepare( + "INSERT INTO daybook_readings (date, nozzle_id, opening_reading, closing_reading, price) + VALUES (?, ?, ?, ?, ?)" + ); + + // Fetch fuel types for nozzles + $nozzle_fuel_types = []; + $nozzles_stmt = $pdo->query(" + SELECT n.id, ft.fuel_name + FROM nozzles n + JOIN pumps p ON n.pump_id = p.id + JOIN tanks t ON p.tank_id = t.id + JOIN fuel_types ft ON t.fuel_type_id = ft.id + "); + while ($row = $nozzles_stmt->fetch(PDO::FETCH_ASSOC)) { + $nozzle_fuel_types[$row['id']] = $row['fuel_name']; + } + + foreach ($readings as $reading) { + $nozzle_id = $reading['nozzle_id']; + $opening = $reading['opening']; + $closing = $reading['closing']; + + if ($closing < $opening) { + throw new Exception("Closing reading cannot be less than opening reading for one of the nozzles."); + } + + $fuel_name = $nozzle_fuel_types[$nozzle_id] ?? null; + if (!$fuel_name || !isset($prices[$fuel_name])) { + throw new Exception("Could not determine price for nozzle ID: $nozzle_id"); + } + $price = $prices[$fuel_name]; + + $stmt->execute([$date, $nozzle_id, $opening, $closing, $price]); + } + + $pdo->commit(); + $message = "Daybook readings saved successfully!"; + } catch (PDOException $e) { + $pdo->rollBack(); + if ($e->errorInfo[1] == 1062) { // Duplicate entry + $error = "Error: Daybook readings for this date and nozzle already exist."; + } else { + $error = "Database error: " . $e->getMessage(); + } + } catch (Exception $e) { + $pdo->rollBack(); + $error = "Error: " . $e->getMessage(); + } +} + + +// Fetch previous day's closing readings to suggest as opening readings +$previous_day_readings = []; +if (isset($_GET['date'])) { + $current_date = $_GET['date']; + $previous_date = date('Y-m-d', strtotime($current_date . ' -1 day')); + $stmt = db()->prepare("SELECT nozzle_id, closing_reading FROM daybook_readings WHERE date = ?"); + $stmt->execute([$previous_date]); + $readings = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($readings as $reading) { + $previous_day_readings[$reading['nozzle_id']] = $reading['closing_reading']; + } +} + +// Fetch nozzles and their associated fuel types +$nozzles_stmt = db()->query(" + SELECT n.id, n.nozzle_number, ft.fuel_name + FROM nozzles n + JOIN pumps p ON n.pump_id = p.id + JOIN tanks t ON p.tank_id = t.id + JOIN fuel_types ft ON t.fuel_type_id = ft.id + ORDER BY n.id +"); +$nozzles = $nozzles_stmt->fetchAll(PDO::FETCH_ASSOC); + +// Group nozzles by fuel type +$nozzles_by_fuel = []; +foreach ($nozzles as $nozzle) { + $nozzles_by_fuel[$nozzle['fuel_name']][] = $nozzle; +} + +?> + +
+

Daily Daybook

+ + +
+ + +
+ + +
+
+
Enter Readings
+
+
+
+
+ + +
+ +
+ + +
+ + + $fuel_nozzles): ?> +
+
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + +
NozzleOpening ReadingClosing Reading
+ + + + +
+
+ + +
+ +
+ + +
+
+
+ + + + + + \ No newline at end of file diff --git a/db/migrate.php b/db/migrate.php new file mode 100644 index 0000000..1cba85f --- /dev/null +++ b/db/migrate.php @@ -0,0 +1,41 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Create migrations table if it doesn't exist + $pdo->exec("CREATE TABLE IF NOT EXISTS `migrations` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `migration` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP + );"); + + // Get all executed migrations + $executed_migrations = $pdo->query("SELECT migration FROM migrations")->fetchAll(PDO::FETCH_COLUMN); + + // Get all migration files + $migration_files = glob(__DIR__ . '/migrations/*.sql'); + + foreach ($migration_files as $file) { + $migration_name = basename($file); + if (!in_array($migration_name, $executed_migrations)) { + // Execute the migration + $sql = file_get_contents($file); + $pdo->exec($sql); + + // Record the migration + $stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)"); + $stmt->execute([$migration_name]); + + echo "Executed migration: {$migration_name}\n"; + } + } + + echo "Database migrations completed successfully!\n"; + +} catch (PDOException $e) { + die("Database migration failed: " . $e->getMessage() . "\n"); +} + diff --git a/db/migrations/001_initial_schema.sql b/db/migrations/001_initial_schema.sql new file mode 100644 index 0000000..1de4e46 --- /dev/null +++ b/db/migrations/001_initial_schema.sql @@ -0,0 +1,188 @@ +-- Initial Schema for Petrol Bunk Management System + +-- Users and Roles +CREATE TABLE IF NOT EXISTS `users` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(100) NOT NULL, + `email` VARCHAR(100) NOT NULL UNIQUE, + `password` VARCHAR(255) NOT NULL, + `role` ENUM('Superadmin', 'Manager', 'Attendant', 'Accountant') NOT NULL, + `bunk_id` INT, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Master Data +CREATE TABLE IF NOT EXISTS `bunks` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `owner` VARCHAR(255), + `contact` VARCHAR(50), + `location` TEXT, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS `fuel_types` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(50) NOT NULL UNIQUE, + `unit` VARCHAR(20) DEFAULT 'Litre' +); + +CREATE TABLE IF NOT EXISTS `tanks` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(100) NOT NULL, + `bunk_id` INT NOT NULL, + `fuel_type_id` INT NOT NULL, + `capacity` DECIMAL(10, 2) NOT NULL, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`fuel_type_id`) REFERENCES `fuel_types`(`id`) +); + +CREATE TABLE IF NOT EXISTS `pumps` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(100) NOT NULL, + `bunk_id` INT NOT NULL, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS `nozzles` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(100) NOT NULL, + `pump_id` INT NOT NULL, + `tank_id` INT NOT NULL, + FOREIGN KEY (`pump_id`) REFERENCES `pumps`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`tank_id`) REFERENCES `tanks`(`id`) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS `price_history` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `fuel_type_id` INT NOT NULL, + `bunk_id` INT NOT NULL, + `price` DECIMAL(10, 2) NOT NULL, + `date` DATE NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY `bunk_fuel_date` (`bunk_id`, `fuel_type_id`, `date`), + FOREIGN KEY (`fuel_type_id`) REFERENCES `fuel_types`(`id`), + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +-- Daily Operations +CREATE TABLE IF NOT EXISTS `daybook` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `bunk_id` INT NOT NULL, + `nozzle_id` INT NOT NULL, + `date` DATE NOT NULL, + `opening_reading` DECIMAL(10, 2) NOT NULL, + `closing_reading` DECIMAL(10, 2), + `testing` DECIMAL(10, 2) DEFAULT 0, + `sale` DECIMAL(10, 2) GENERATED ALWAYS AS (closing_reading - opening_reading - testing) STORED, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE KEY `bunk_nozzle_date` (`bunk_id`, `nozzle_id`, `date`), + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`nozzle_id`) REFERENCES `nozzles`(`id`) ON DELETE CASCADE +); + +-- Credit Sales +CREATE TABLE IF NOT EXISTS `credit_customers` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `contact` VARCHAR(50), + `bunk_id` INT NOT NULL, + `credit_limit` DECIMAL(10, 2) DEFAULT 0, + `outstanding_balance` DECIMAL(10, 2) DEFAULT 0, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS `credit_sales` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `bunk_id` INT NOT NULL, + `customer_id` INT NOT NULL, + `date` DATE NOT NULL, + `fuel_type_id` INT NOT NULL, + `quantity` DECIMAL(10, 2) NOT NULL, + `rate` DECIMAL(10, 2) NOT NULL, + `amount` DECIMAL(10, 2) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`customer_id`) REFERENCES `credit_customers`(`id`), + FOREIGN KEY (`fuel_type_id`) REFERENCES `fuel_types`(`id`) +); + +-- Receipts and Expenses +CREATE TABLE IF NOT EXISTS `fuel_receipts` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `bunk_id` INT NOT NULL, + `supplier` VARCHAR(255), + `invoice_number` VARCHAR(100), + `fuel_type_id` INT NOT NULL, + `quantity` DECIMAL(10, 2) NOT NULL, + `rate` DECIMAL(10, 2) NOT NULL, + `amount` DECIMAL(10, 2) NOT NULL, + `date` DATE NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`fuel_type_id`) REFERENCES `fuel_types`(`id`) +); + +CREATE TABLE IF NOT EXISTS `expenses` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `bunk_id` INT NOT NULL, + `category` VARCHAR(100) NOT NULL, + `amount` DECIMAL(10, 2) NOT NULL, + `description` TEXT, + `payment_mode` VARCHAR(50), + `date` DATE NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +-- Product & Stock +CREATE TABLE IF NOT EXISTS `products` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `category` VARCHAR(100), + `price` DECIMAL(10, 2) NOT NULL, + `bunk_id` INT NOT NULL, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS `stock` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `product_id` INT NOT NULL, + `date` DATE NOT NULL, + `opening_stock` INT NOT NULL, + `sales` INT DEFAULT 0, + `closing_stock` INT GENERATED ALWAYS AS (opening_stock - sales) STORED, + `bunk_id` INT NOT NULL, + UNIQUE KEY `bunk_product_date` (`bunk_id`, `product_id`, `date`), + FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +-- Accounts +CREATE TABLE IF NOT EXISTS `accounts` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `type` ENUM('Owner', 'Bank', 'OD', 'Supplier', 'Customer', 'Cash') NOT NULL, + `bunk_id` INT NOT NULL, + `balance` DECIMAL(12, 2) DEFAULT 0, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS `transactions` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `bunk_id` INT NOT NULL, + `date` DATE NOT NULL, + `account_id` INT NOT NULL, + `type` ENUM('Debit', 'Credit') NOT NULL, + `amount` DECIMAL(10, 2) NOT NULL, + `description` TEXT, + `related_entity_type` VARCHAR(50), -- e.g., 'credit_sale', 'expense', 'fuel_receipt' + `related_entity_id` INT, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`bunk_id`) REFERENCES `bunks`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) +); + +-- Add a superadmin user +INSERT IGNORE INTO `users` (`name`, `email`, `password`, `role`) VALUES +('Superadmin', 'admin@example.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'Superadmin'); -- password is "password" diff --git a/db/migrations/002_add_vehicle_to_customers.sql b/db/migrations/002_add_vehicle_to_customers.sql new file mode 100644 index 0000000..ab3ab4c --- /dev/null +++ b/db/migrations/002_add_vehicle_to_customers.sql @@ -0,0 +1 @@ +ALTER TABLE `credit_customers` ADD `vehicle_mapping` TEXT COMMENT 'JSON encoded array of vehicle numbers'; diff --git a/db/migrations/003_add_fields_to_bunks.sql b/db/migrations/003_add_fields_to_bunks.sql new file mode 100644 index 0000000..315d11a --- /dev/null +++ b/db/migrations/003_add_fields_to_bunks.sql @@ -0,0 +1,4 @@ +ALTER TABLE `bunks` +ADD COLUMN `owner` VARCHAR(255) NULL, +ADD COLUMN `location` VARCHAR(255) NULL, +ADD COLUMN `contact` VARCHAR(255) NULL; diff --git a/db/migrations/004_create_daybook_table.sql b/db/migrations/004_create_daybook_table.sql new file mode 100644 index 0000000..4e8cc3d --- /dev/null +++ b/db/migrations/004_create_daybook_table.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS `daybook_readings` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `date` DATE NOT NULL, + `nozzle_id` INT NOT NULL, + `opening_reading` DECIMAL(10, 2) NOT NULL, + `closing_reading` DECIMAL(10, 2) NOT NULL, + `price` DECIMAL(10, 2) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`nozzle_id`) REFERENCES `nozzles`(`id`), + UNIQUE KEY `date_nozzle` (`date`, `nozzle_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/db/migrations/005_add_tank_to_pumps.sql b/db/migrations/005_add_tank_to_pumps.sql new file mode 100644 index 0000000..18cb080 --- /dev/null +++ b/db/migrations/005_add_tank_to_pumps.sql @@ -0,0 +1 @@ +ALTER TABLE `pumps` ADD COLUMN `tank_id` INT NULL, ADD FOREIGN KEY (`tank_id`) REFERENCES `tanks`(`id`); diff --git a/db/migrations/006_create_credit_management_tables.sql b/db/migrations/006_create_credit_management_tables.sql new file mode 100644 index 0000000..35f7492 --- /dev/null +++ b/db/migrations/006_create_credit_management_tables.sql @@ -0,0 +1,35 @@ +CREATE TABLE IF NOT EXISTS `credit_customers` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `contact` VARCHAR(255) NULL, + `address` TEXT NULL, + `credit_limit` DECIMAL(10, 2) DEFAULT 0.00, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `vehicles` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `customer_id` INT NOT NULL, + `vehicle_number` VARCHAR(255) NOT NULL, + `make` VARCHAR(255) NULL, + `model` VARCHAR(255) NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`customer_id`) REFERENCES `credit_customers`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `credit_sales` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `date` DATE NOT NULL, + `customer_id` INT NOT NULL, + `vehicle_id` INT NULL, + `fuel_type_id` INT NOT NULL, + `quantity` DECIMAL(10, 2) NOT NULL, + `rate` DECIMAL(10, 2) NOT NULL, + `amount` DECIMAL(10, 2) NOT NULL, + `is_settled` BOOLEAN DEFAULT FALSE, + `settled_at` DATETIME NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (`customer_id`) REFERENCES `credit_customers`(`id`), + FOREIGN KEY (`vehicle_id`) REFERENCES `vehicles`(`id`), + FOREIGN KEY (`fuel_type_id`) REFERENCES `fuel_types`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/fuel_receipts.php b/fuel_receipts.php new file mode 100644 index 0000000..fa15aa8 --- /dev/null +++ b/fuel_receipts.php @@ -0,0 +1,177 @@ +prepare($sql); + $stmt->execute([ + ':bunk_id' => $bunk_id, + ':supplier' => $supplier, + ':invoice_number' => $invoice_number, + ':fuel_type_id' => $fuel_type_id, + ':quantity' => $quantity, + ':rate' => $rate, + ':amount' => $amount, + ':date' => $date + ]); + $success_message = "Fuel receipt recorded successfully!"; + } catch (PDOException $e) { + $error_message = "Error recording receipt: " . $e->getMessage(); + } + } else { + $error_message = "Bunk, fuel type, quantity, rate, and date are required."; + } +} + +// Fetch master data for dropdowns +try { + $bunks_stmt = $pdoconn->query("SELECT id, name FROM bunks ORDER BY name"); + $bunks = $bunks_stmt->fetchAll(PDO::FETCH_ASSOC); + + $fuel_types_stmt = $pdoconn->query("SELECT id, name FROM fuel_types ORDER BY name"); + $fuel_types = $fuel_types_stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $bunks = []; + $fuel_types = []; + $page_error = "Error fetching master data: " . $e->getMessage(); +} + +// Fetch recent fuel receipts +try { + $receipts_stmt = $pdoconn->query(" + SELECT fr.*, b.name as bunk_name, ft.name as fuel_type_name + FROM fuel_receipts fr + JOIN bunks b ON fr.bunk_id = b.id + JOIN fuel_types ft ON fr.fuel_type_id = ft.id + ORDER BY fr.date DESC, fr.id DESC + LIMIT 20 + "); + $receipts = $receipts_stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $receipts = []; + $page_error = "Error fetching receipts: " . $e->getMessage(); +} + +?> + +
+
+
+

Record Fuel Receipt

+
+
+ +
+ + +
+ + +
+ + +
+
+
New Receipt Entry
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
Recent Fuel Receipts
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateBunkSupplierInvoice #FuelQtyRateAmount
No fuel receipts recorded yet.
+
+
+
+
+
+
+ + diff --git a/fuel_types.php b/fuel_types.php new file mode 100644 index 0000000..911eed0 --- /dev/null +++ b/fuel_types.php @@ -0,0 +1,82 @@ +prepare("INSERT INTO fuel_types (name) VALUES (?)"); + $stmt->execute([$name]); + $message = '
Fuel type added successfully!
'; + } catch (PDOException $e) { + if ($e->errorInfo[1] == 1062) { // Duplicate entry + $message = '
Error: A fuel type with this name already exists.
'; + } else { + $message = '
Error: ' . $e->getMessage() . '
'; + } + } + } else { + $message = '
Please enter a fuel type name.
'; + } +} + +// Fetch all fuel types to display +$stmt = $pdo->query("SELECT * FROM fuel_types ORDER BY name"); +$fuel_types = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + +
+
+

Manage Fuel Types

+

List of all fuel types available in the system (e.g., MS, HSD, Power).

+ + + + + + + + + + + + + + + + + + + + + + +
Fuel Type NameActions
No fuel types found. Add one to get started.
+ Edit + Delete +
+
+
+
+
+
Add New Fuel Type
+ +
+
+ + +
+ +
+
+
+
+
+ + diff --git a/includes/footer.php b/includes/footer.php new file mode 100644 index 0000000..12acf11 --- /dev/null +++ b/includes/footer.php @@ -0,0 +1,5 @@ + + + + + diff --git a/includes/header.php b/includes/header.php new file mode 100644 index 0000000..6522ace --- /dev/null +++ b/includes/header.php @@ -0,0 +1,62 @@ + + + + + + + Petrol Bunk Management + + + + + + + +
diff --git a/index.php b/index.php index 7205f3d..57534b0 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,81 @@ - - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + Petrol Bunk Management + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- + + + + + +
+
+

Efficient Fuel Management

+

The all-in-one solution for managing your petrol bunk operations seamlessly.

+
+
+ + +
+
+
+
+ +
+
+
+
+ + + + + - + \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 0000000..7a20662 --- /dev/null +++ b/login.php @@ -0,0 +1,34 @@ +prepare('SELECT * FROM users WHERE email = ?'); + $stmt->execute([$email]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['user_name'] = $user['name']; + $_SESSION['user_role'] = $user['role']; + header('Location: dashboard.php'); + exit; + } else { + header('Location: index.php?error=Invalid email or password'); + exit; + } + } catch (PDOException $e) { + // In a real app, log this error instead of showing it to the user + die('Database error: ' . $e->getMessage()); + } +} diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..3e55907 --- /dev/null +++ b/logout.php @@ -0,0 +1,6 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $sql = "INSERT INTO nozzles (nozzle_name, pump_id) VALUES (:nozzle_name, :pump_id)"; + $stmt = $pdoconn->prepare($sql); + $stmt->bindParam(':nozzle_name', $nozzle_name); + $stmt->bindParam(':pump_id', $pump_id); + $stmt->execute(); + $success_message = "Nozzle added successfully!"; + } catch (PDOException $e) { + $error_message = "Error adding nozzle: " . $e->getMessage(); + } + } else { + $error_message = "Nozzle name and pump are required."; + } +} + +// Fetch pumps for the dropdown +try { + $pdoconn = db(); + $pumps_stmt = $pdoconn->query("SELECT id, pump_number FROM pumps ORDER BY pump_number"); + $pumps = $pumps_stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $pumps = []; + $page_error = "Error fetching pumps: " . $e->getMessage(); +} + +// Fetch existing nozzles with their pump and bunk +try { + $pdoconn = db(); + $nozzles_stmt = $pdoconn->query(" + SELECT n.id, n.nozzle_name, p.pump_number, b.name as bunk_name + FROM nozzles n + JOIN pumps p ON n.pump_id = p.id + JOIN bunks b ON p.bunk_id = b.id + ORDER BY b.name, p.pump_number, n.nozzle_name + "); + $nozzles = $nozzles_stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + $nozzles = []; + $page_error = "Error fetching nozzles: " . $e->getMessage(); +} + +?> + +
+
+
+
+
+

Manage Nozzles

+
+
+ +
+ + +
+ + +
+ + +
+
+
Add New Nozzle
+
+
+ + +
+
+ + +
+ +
+
+
+
Existing Nozzles
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
IDNozzle NamePumpBunk
No nozzles found.
+
+
+
+
+
+
+
+
+ + diff --git a/price_history.php b/price_history.php new file mode 100644 index 0000000..35e6417 --- /dev/null +++ b/price_history.php @@ -0,0 +1,100 @@ +prepare("INSERT INTO price_history (fuel_type_id, price, date) VALUES (?, ?, ?)"); + $stmt->execute([$fuel_type_id, $price, $date]); + $message = '
Price added successfully!
'; + } catch (PDOException $e) { + $message = '
Error: ' . $e->getMessage() . '
'; + } + } else { + $message = '
Please fill in all fields.
'; + } +} + +// Fetch related data for forms +$fuel_types = $pdo->query("SELECT id, name FROM fuel_types ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); + +// Fetch all price history with fuel type name +$stmt = $pdo->query(" + SELECT ph.id, ph.price, ph.date, ft.name AS fuel_type_name + FROM price_history ph + JOIN fuel_types ft ON ph.fuel_type_id = ft.id + ORDER BY ph.date DESC, ft.name +"); +$price_history = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + +
+
+

Price History

+

History of fuel prices.

+ + + + + + + + + + + + + + + + + + + + + + + + +
DateFuel TypePrice
No price history found. Add one to get started.
+
+
+
+
+
Add New Price
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/pumps.php b/pumps.php new file mode 100644 index 0000000..9c8f2a7 --- /dev/null +++ b/pumps.php @@ -0,0 +1,117 @@ +prepare("INSERT INTO pumps (bunk_id, tank_id, pump_number) VALUES (?, ?, ?)"); + $stmt->execute([$bunk_id, $tank_id, $pump_number]); + $message = '
Pump added successfully!
'; + } catch (PDOException $e) { + $message = '
Error: ' . $e->getMessage() . '
'; + } + } else { + $message = '
Please fill in all fields.
'; + } +} + +// Fetch related data for forms +$bunks = $pdo->query("SELECT id, name FROM bunks ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); +$tanks = $pdo->query(" + SELECT t.id, t.name as tank_name, b.name as bunk_name, ft.name as fuel_type_name + FROM tanks t + JOIN bunks b ON t.bunk_id = b.id + JOIN fuel_types ft ON t.fuel_type_id = ft.id + ORDER BY b.name, t.name +")->fetchAll(PDO::FETCH_ASSOC); + + +// Fetch all pumps with their bunk and tank name +$stmt = $pdo->query(" + SELECT p.id, p.pump_number, b.name AS bunk_name, t.name as tank_name, ft.name as fuel_type_name + FROM pumps p + JOIN bunks b ON p.bunk_id = b.id + LEFT JOIN tanks t ON p.tank_id = t.id + LEFT JOIN fuel_types ft ON t.fuel_type_id = ft.id + ORDER BY b.name, p.pump_number +"); +$pumps = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + +
+
+

Manage Pumps

+

List of all fuel pumps and their assigned tanks.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Pump NumberBunkTankFuel Type
No pumps found. Add one to get started.
+
+
+
+
+
Add New Pump
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/tanks.php b/tanks.php new file mode 100644 index 0000000..f075f57 --- /dev/null +++ b/tanks.php @@ -0,0 +1,119 @@ +prepare("INSERT INTO tanks (bunk_id, fuel_type_id, name, capacity) VALUES (?, ?, ?, ?)"); + $stmt->execute([$bunk_id, $fuel_type_id, $name, $capacity]); + $message = '
Tank added successfully!
'; + } catch (PDOException $e) { + $message = '
Error: ' . $e->getMessage() . '
'; + } + } else { + $message = '
Please fill in all fields.
'; + } +} + +// Fetch related data for forms and listing +$bunks = $pdo->query("SELECT id, name FROM bunks ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); +$fuel_types = $pdo->query("SELECT id, name FROM fuel_types ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); + +// Fetch all tanks with their related bunk and fuel type names +$stmt = $pdo->query(" + SELECT + t.id, + t.name AS tank_name, + t.capacity, + b.name AS bunk_name, + ft.name AS fuel_type_name + FROM tanks t + JOIN bunks b ON t.bunk_id = b.id + JOIN fuel_types ft ON t.fuel_type_id = ft.id + ORDER BY b.name, t.name +"); +$tanks = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + +
+
+

Manage Tanks

+

List of all fuel tanks, associated with a bunk and fuel type.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Tank NameBunkFuel TypeCapacity (L)
No tanks found. Add one to get started.
+
+
+
+
+
Add New Tank
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ +