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.
+
+
+
+
+ Name
+ Owner
+ Contact
+ Location
+
+
+
+
+
+ No bunks found. Add one to get started.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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;
+}
+
+?>
+
+
+
+
+
+
+
+
+
+
Existing Customers
+
+
+
+ Name
+ Contact
+ Address
+ Credit Limit
+ Vehicles
+
+
+
+
+
+ No customers found.
+
+
+
+
+
+
+
+
+
+ ';
+ }
+ } else {
+ echo 'No vehicles';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
Add Vehicle to Customer
+
+
+
+ Customer
+
+ Select a Customer
+
+
+
+
+
+
+ Vehicle Number
+
+
+ Add Vehicle
+
+
+
+
+
+
+
+
+
+
+
\ 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();
+}
+
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
New Sale Entry
+
+
+ Date
+
+
+
+ Bunk
+
+ Select Bunk
+
+
+
+
+
+
+ Customer
+
+ Select Customer
+
+
+
+
+
+
+
+
+ Fuel Type
+
+ Select Fuel Type
+
+
+
+
+
+
+ Quantity (Ltr)
+
+
+
+ Rate
+
+
+ Record Sale
+
+
+
+
Recent Credit Sales
+
+
+
+
+ Date
+ Customer
+ Bunk
+ Fuel
+ Qty
+ Rate
+ Amount
+
+
+
+
+
+ No credit sales recorded yet.
+
+
+
+
+
+
+
+
+
+ .php echo htmlspecialchars(number_format($sale['rate'], 2)); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Date:
+
+
+ Load Readings
+
+
+
+
+
+
+ $fuel_nozzles): ?>
+
+
+
+
+ Save Readings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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();
+}
+
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Recent Fuel Receipts
+
+
+
+
+ Date
+ Bunk
+ Supplier
+ Invoice #
+ Fuel
+ Qty
+ Rate
+ Amount
+
+
+
+
+
+ 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 Name
+ Actions
+
+
+
+
+
+ No fuel types found. Add one to get started.
+
+
+
+
+
+
+ Edit
+ Delete
+
+
+
+
+
+
+
+
+
+
+
Add New Fuel Type
+
+
+
+ Fuel Name
+
+
+ Add 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 @@
+
+
+
+