Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
a0c7d8b641 Auto commit: 2025-10-02T20:43:00.272Z 2025-10-02 20:43:00 +00:00
21 changed files with 1195 additions and 97 deletions

54
customers/create.php Normal file
View File

@ -0,0 +1,54 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$first_name = $data['first_name'] ?? null;
$last_name = $data['last_name'] ?? null;
$email = $data['email'] ?? null;
$phone = $data['phone'] ?? null;
$address = $data['address'] ?? null;
if (!$first_name || !$last_name || !$email) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: first_name, last_name, email']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid email format']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM customers WHERE email = ?");
$stmt->execute([$email]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode(['error' => 'Customer with this email already exists']);
exit;
}
$stmt = $pdo->prepare("INSERT INTO customers (first_name, last_name, email, phone, address) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$first_name, $last_name, $email, $phone, $address]);
$id = $pdo->lastInsertId();
http_response_code(201);
echo json_encode(['success' => 'Customer created successfully', 'id' => $id]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

33
customers/delete.php Normal file
View File

@ -0,0 +1,33 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Customer ID is required']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM customers WHERE id = ?");
$stmt->execute([$id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Customer not found']);
exit;
}
$stmt = $pdo->prepare("DELETE FROM customers WHERE id = ?");
$stmt->execute([$id]);
echo json_encode(['success' => 'Customer deleted successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

31
customers/read.php Normal file
View File

@ -0,0 +1,31 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
try {
$pdo = db();
if ($id) {
$stmt = $pdo->prepare("SELECT * FROM customers WHERE id = ?");
$stmt->execute([$id]);
$customer = $stmt->fetch(PDO::FETCH_ASSOC);
if ($customer) {
echo json_encode($customer);
} else {
http_response_code(404);
echo json_code(['error' => 'Customer not found']);
}
} else {
$stmt = $pdo->query("SELECT * FROM customers");
$customers = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($customers);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

67
customers/update.php Normal file
View File

@ -0,0 +1,67 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { // Should be PUT or POST
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Customer ID is required']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$first_name = $data['first_name'] ?? null;
$last_name = $data['last_name'] ?? null;
$email = $data['email'] ?? null;
$phone = $data['phone'] ?? null;
$address = $data['address'] ?? null;
if (!$first_name || !$last_name || !$email) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: first_name, last_name, email']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid email format']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM customers WHERE id = ?");
$stmt->execute([$id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Customer not found']);
exit;
}
$stmt = $pdo->prepare("SELECT id FROM customers WHERE email = ? AND id != ?");
$stmt->execute([$email, $id]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode(['error' => 'Another customer with this email already exists']);
exit;
}
$stmt = $pdo->prepare("UPDATE customers SET first_name = ?, last_name = ?, email = ?, phone = ?, address = ? WHERE id = ?");
$stmt->execute([$first_name, $last_name, $email, $phone, $address, $id]);
echo json_encode(['success' => 'Customer updated successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

80
db/schema.sql Normal file
View File

@ -0,0 +1,80 @@
-- Initial schema for CheersPOS System
CREATE TABLE IF NOT EXISTS `roles` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL UNIQUE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Insert default roles
INSERT IGNORE INTO `roles` (`name`) VALUES
('Admin'),
('Manager'),
('Inventory Manager'),
('Cashier'),
('HR Manager'),
('Customer Manager');
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(255) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL UNIQUE,
`role_id` INT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `products` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`description` TEXT,
`price` DECIMAL(10, 2) NOT NULL,
`cost` DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
`quantity` INT NOT NULL DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `customers` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`first_name` VARCHAR(255) NOT NULL,
`last_name` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NOT NULL UNIQUE,
`phone` VARCHAR(50),
`address` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sales` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_id` INT,
`subtotal` DECIMAL(10, 2) NOT NULL,
`tax_total` DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
`total_amount` DECIMAL(10, 2) NOT NULL,
`sale_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`customer_id`) REFERENCES `customers`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sale_items` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`sale_id` INT,
`product_id` INT,
`quantity` INT NOT NULL,
`price` DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (`sale_id`) REFERENCES `sales`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`product_id`) REFERENCES `products`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `taxes` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL UNIQUE,
`rate` DECIMAL(5, 2) NOT NULL COMMENT 'Percentage'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sale_taxes` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`sale_id` INT,
`tax_id` INT,
`tax_amount` DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (`sale_id`) REFERENCES `sales`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`tax_id`) REFERENCES `taxes`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

42
db/setup.php Normal file
View File

@ -0,0 +1,42 @@
<?php
require_once __DIR__ . '/config.php';
try {
// Connect to MySQL server without specifying a database
$pdo = new PDO('mysql:host=' . DB_HOST . ';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// Create the database if it doesn't exist
$pdo->exec("CREATE DATABASE IF NOT EXISTS `" . DB_NAME . "`");
// Reconnect to the specific database
$pdo = db();
$sql = file_get_contents(__DIR__ . '/schema.sql');
$pdo->exec($sql);
echo "Database schema created successfully.\n";
// Check if 'cost' column exists in 'products' table and add it if not
$stmt = $pdo->query("SHOW COLUMNS FROM `products` LIKE 'cost'");
$column_exists = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$column_exists) {
$pdo->exec("ALTER TABLE `products` ADD `cost` DECIMAL(10, 2) NOT NULL DEFAULT 0.00 AFTER `price`");
echo "Column 'cost' added to 'products' table successfully.\n";
}
// Check if 'subtotal' column exists in 'sales' table and add columns if not
$stmt = $pdo->query("SHOW COLUMNS FROM `sales` LIKE 'subtotal'");
$column_exists = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$column_exists) {
$pdo->exec("ALTER TABLE `sales` ADD `subtotal` DECIMAL(10, 2) NOT NULL AFTER `customer_id`");
$pdo->exec("ALTER TABLE `sales` ADD `tax_total` DECIMAL(10, 2) NOT NULL DEFAULT 0.00 AFTER `subtotal`");
echo "Columns 'subtotal' and 'tax_total' added to 'sales' table successfully.\n";
}
} catch (PDOException $e) {
die("Database setup failed: " . $e->getMessage() . "\n");
}

55
employees/create.php Normal file
View File

@ -0,0 +1,55 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$first_name = $data['first_name'] ?? null;
$last_name = $data['last_name'] ?? null;
$email = $data['email'] ?? null;
$phone = $data['phone'] ?? null;
$role_id = $data['role_id'] ?? null;
$hire_date = $data['hire_date'] ?? null;
if (!$first_name || !$last_name || !$email || !$role_id) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: first_name, last_name, email, role_id']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid email format']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM employees WHERE email = ?");
$stmt->execute([$email]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode(['error' => 'Employee with this email already exists']);
exit;
}
$stmt = $pdo->prepare("INSERT INTO employees (first_name, last_name, email, phone, role_id, hire_date) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$first_name, $last_name, $email, $phone, $role_id, $hire_date]);
$id = $pdo->lastInsertId();
http_response_code(201);
echo json_encode(['success' => 'Employee created successfully', 'id' => $id]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

33
employees/delete.php Normal file
View File

@ -0,0 +1,33 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Employee ID is required']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM employees WHERE id = ?");
$stmt->execute([$id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Employee not found']);
exit;
}
$stmt = $pdo->prepare("DELETE FROM employees WHERE id = ?");
$stmt->execute([$id]);
echo json_encode(['success' => 'Employee deleted successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

31
employees/read.php Normal file
View File

@ -0,0 +1,31 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
try {
$pdo = db();
if ($id) {
$stmt = $pdo->prepare("SELECT e.*, r.name as role_name FROM employees e LEFT JOIN roles r ON e.role_id = r.id WHERE e.id = ?");
$stmt->execute([$id]);
$employee = $stmt->fetch(PDO::FETCH_ASSOC);
if ($employee) {
echo json_encode($employee);
} else {
http_response_code(404);
echo json_encode(['error' => 'Employee not found']);
}
} else {
$stmt = $pdo->query("SELECT e.*, r.name as role_name FROM employees e LEFT JOIN roles r ON e.role_id = r.id");
$employees = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($employees);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

68
employees/update.php Normal file
View File

@ -0,0 +1,68 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { // Should be PUT or POST
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Employee ID is required']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$first_name = $data['first_name'] ?? null;
$last_name = $data['last_name'] ?? null;
$email = $data['email'] ?? null;
$phone = $data['phone'] ?? null;
$role_id = $data['role_id'] ?? null;
$hire_date = $data['hire_date'] ?? null;
if (!$first_name || !$last_name || !$email || !$role_id) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: first_name, last_name, email, role_id']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid email format']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM employees WHERE id = ?");
$stmt->execute([$id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Employee not found']);
exit;
}
$stmt = $pdo->prepare("SELECT id FROM employees WHERE email = ? AND id != ?");
$stmt->execute([$email, $id]);
if ($stmt->fetch()) {
http_response_code(409);
echo json_encode(['error' => 'Another employee with this email already exists']);
exit;
}
$stmt = $pdo->prepare("UPDATE employees SET first_name = ?, last_name = ?, email = ?, phone = ?, role_id = ?, hire_date = ? WHERE id = ?");
$stmt->execute([$first_name, $last_name, $email, $phone, $role_id, $hire_date, $id]);
echo json_encode(['success' => 'Employee updated successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

316
index.php
View File

@ -1,103 +1,225 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #4B0082;
--bg-color-end: #8A2BE2;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CheersPOS System</title>
<style>
body { font-family: sans-serif; margin: 2em; background-color: #f4f4f9; color: #333; }
h1 { color: #0056b3; }
nav { margin-bottom: 1em; }
nav button { padding: 10px 15px; border: none; background-color: #007bff; color: white; cursor: pointer; margin-right: 10px; border-radius: 5px; }
nav button:hover { background-color: #0056b3; }
#content { background-color: white; padding: 1em; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
table { width: 100%; border-collapse: collapse; margin-top: 1em; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f2f2f2; }
.error { color: red; }
.success { color: green; }
form div { margin-bottom: 10px; }
</style>
</head>
<body>
<main>
<div class="card">
<h1>Welcome!</h1>
<p>This is your new landing page.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<h1>CheersPOS System</h1>
<nav>
<button onclick="loadProducts()">Products</button>
<button onclick="loadCustomers()">Customers</button>
<button onclick="loadEmployees()">Employees</button>
<button onclick="loadTaxes()">Manage Taxes</button>
<button onclick="showSaleForm()">Create Sale</button>
</nav>
<div id="content">
<p>Welcome to the AI Retail Management Solution. Select an option above to get started.</p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<script>
const contentDiv = document.getElementById('content');
async function loadTaxes() {
try {
const response = await fetch('taxes/read.php');
const taxes = await response.json();
let html = `
<h2>Manage Taxes</h2>
<form id="taxForm" onsubmit="createTax(event)">
<input type="text" name="name" placeholder="Tax Name" required>
<input type="number" step="0.01" name="rate" placeholder="Rate (%)" required>
<button type="submit">Add Tax</button>
</form>
<div id="taxResult"></div>
`;
if (taxes.length === 0) {
html += '<p>No taxes found.</p>';
} else {
html += '<table><tr><th>ID</th><th>Name</th><th>Rate (%)</th><th>Action</th></tr>';
taxes.forEach(t => {
html += `<tr><td>${t.id}</td><td>${t.name}</td><td>${t.rate}</td><td><button onclick="deleteTax(${t.id})">Delete</button></td></tr>`;
});
html += '</table>';
}
contentDiv.innerHTML = html;
} catch (error) {
contentDiv.innerHTML = `<p class="error">Error loading taxes: ${error}</p>`;
}
}
async function createTax(event) {
event.preventDefault();
const form = document.getElementById('taxForm');
const resultDiv = document.getElementById('taxResult');
const formData = new FormData(form);
const taxData = Object.fromEntries(formData.entries());
try {
const response = await fetch('taxes/create.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(taxData)
});
const result = await response.json();
if (response.ok) {
resultDiv.innerHTML = `<p class="success">${result.success}</p>`;
loadTaxes(); // Refresh list
} else {
resultDiv.innerHTML = `<p class="error">${result.error}</p>`;
}
} catch (error) {
resultDiv.innerHTML = `<p class="error">Error creating tax: ${error}</p>`;
}
}
async function deleteTax(id) {
if (!confirm('Are you sure you want to delete this tax?')) return;
try {
const response = await fetch(`taxes/delete.php?id=${id}`);
const result = await response.json();
if (response.ok) {
loadTaxes();
} else {
alert(result.error);
}
} catch (error) {
alert(`Error deleting tax: ${error}`);
}
}
async function showSaleForm() {
let taxCheckboxes = '';
try {
const taxResponse = await fetch('taxes/read.php');
const taxes = await taxResponse.json();
taxes.forEach(tax => {
taxCheckboxes += `
<label>
<input type="checkbox" name="tax_ids[]" value="${tax.id}">
${tax.name} (${tax.rate}%)
</label><br>
`;
});
} catch (e) { taxCheckboxes = '<p class="error">Could not load taxes.</p>'; }
contentDiv.innerHTML = `
<h2>Create Sale</h2>
<form id="saleForm" onsubmit="createSale(event)">
<div>
<label for="customer_id">Customer ID (optional):</label>
<input type="number" id="customer_id" name="customer_id">
</div>
<h3>Items</h3>
<div id="saleItems">
<div class="sale-item">
<input type="number" name="product_id[]" placeholder="Product ID" required>
<input type="number" name="quantity[]" placeholder="Quantity" required>
</div>
</div>
<button type="button" onclick="addSaleItem()">Add Item</button>
<h3>Taxes</h3>
<div id="taxItems">
${taxCheckboxes}
</div>
<button type="submit">Submit Sale</button>
</form>
<div id="saleResult"></div>
`;
}
async function loadProducts() {
try {
const response = await fetch('products/read.php');
const products = await response.json();
let html = '<h2>Products</h2>';
if (products.length === 0) {
html += '<p>No products found.</p>';
} else {
html += '<table><tr><th>ID</th><th>Name</th><th>Description</th><th>Price</th><th>Cost</th><th>Quantity</th></tr>';
products.forEach(p => {
html += `<tr><td>${p.id}</td><td>${p.name}</td><td>${p.description}</td><td>${p.price}</td><td>${p.cost}</td><td>${p.quantity}</td></tr>`;
});
html += '</table>';
}
contentDiv.innerHTML = html;
} catch (error) {
contentDiv.innerHTML = `<p class="error">Error loading products: ${error}</p>`;
}
}
async function loadCustomers() {
try {
const response = await fetch('customers/read.php');
const customers = await response.json();
let html = '<h2>Customers</h2>';
if (customers.length === 0) {
html += '<p>No customers found.</p>';
} else {
html += '<table><tr><th>ID</th><th>Name</th><th>Email</th><th>Phone</th><th>Address</th></tr>';
customers.forEach(c => {
html += `<tr><td>${c.id}</td><td>${c.first_name} ${c.last_name}</td><td>${c.email}</td><td>${c.phone}</td><td>${c.address}</td></tr>`;
});
html += '</table>';
}
contentDiv.innerHTML = html;
} catch (error) {
contentDiv.innerHTML = `<p class="error">Error loading customers: ${error}</p>`;
}
}
async function loadEmployees() {
try {
const response = await fetch('employees/read.php');
const employees = await response.json();
let html = '<h2>Employees</h2>';
if (employees.length === 0) {
html += '<p>No employees found.</p>';
} else {
html += '<table><tr><th>ID</th><th>Name</th><th>Email</th><th>Phone</th><th>Role</th><th>Hire Date</th></tr>';
employees.forEach(e => {
html += `<tr><td>${e.id}</td><td>${e.first_name} ${e.last_name}</td><td>${e.email}</td><td>${e.phone}</td><td>${e.role_name}</td><td>${e.hire_date}</td></tr>`;
});
html += '</table>';
}
contentDiv.innerHTML = html;
} catch (error) {
contentDiv.innerHTML = `<p class="error">Error loading employees: ${error}</p>`;
}
}
function addSaleItem() {
const saleItems = document.getElementById('saleItems');
const newItem = document.createElement('div');
newItem.classList.add('sale-item');
newItem.innerHTML = '
<input type="number" name="product_id[]" placeholder="Product ID" required>
<input type="number" name="quantity[]" placeholder="Quantity" required>
';
saleItems.appendChild(newItem);
}
</script>
</body>
</html>
</html>

48
inventory/update.php Normal file
View File

@ -0,0 +1,48 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$product_id = $data['product_id'] ?? null;
$quantity = $data['quantity'] ?? null;
if (!$product_id || !isset($quantity)) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: product_id, quantity']);
exit;
}
if (!is_numeric($quantity) || $quantity < 0) {
http_response_code(400);
echo json_encode(['error' => 'Invalid quantity']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM products WHERE id = ?");
$stmt->execute([$product_id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Product not found']);
exit;
}
$stmt = $pdo->prepare("UPDATE products SET quantity = ? WHERE id = ?");
$stmt->execute([$quantity, $product_id]);
echo json_encode(['success' => 'Inventory updated successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

36
products/create.php Normal file
View File

@ -0,0 +1,36 @@
<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
if (empty($data['name']) || !isset($data['price']) || !isset($data['cost']) || !isset($data['quantity'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: name, price, cost, and quantity']);
exit;
}
$name = $data['name'];
$description = $data['description'] ?? null;
$price = $data['price'];
$cost = $data['cost'];
$quantity = $data['quantity'];
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO products (name, description, price, cost, quantity) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$name, $description, $price, $cost, $quantity]);
http_response_code(201);
echo json_encode(['message' => 'Product created successfully', 'id' => $pdo->lastInsertId()]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

34
products/delete.php Normal file
View File

@ -0,0 +1,34 @@
<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Missing required parameter: id']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("DELETE FROM products WHERE id = ?");
$stmt->execute([$id]);
if ($stmt->rowCount() > 0) {
echo json_encode(['message' => 'Product deleted successfully']);
} else {
http_response_code(404);
echo json_encode(['error' => 'Product not found']);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

28
products/read.php Normal file
View File

@ -0,0 +1,28 @@
<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
try {
$pdo = db();
if ($id) {
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$id]);
$product = $stmt->fetch();
if ($product) {
echo json_encode($product);
} else {
http_response_code(404);
echo json_encode(['error' => 'Product not found']);
}
} else {
$stmt = $pdo->query("SELECT * FROM products");
$products = $stmt->fetchAll();
echo json_encode($products);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

48
products/update.php Normal file
View File

@ -0,0 +1,48 @@
<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
if (empty($data['id'])) {
http_response_code(400);
echo json_encode(['error' => 'Missing required field: id']);
exit;
}
$id = $data['id'];
// Fetch the existing product to see which fields are being updated
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$id]);
$product = $stmt->fetch();
if (!$product) {
http_response_code(404);
echo json_encode(['error' => 'Product not found']);
exit;
}
$name = $data['name'] ?? $product['name'];
$description = $data['description'] ?? $product['description'];
$price = $data['price'] ?? $product['price'];
$cost = $data['cost'] ?? $product['cost'];
$quantity = $data['quantity'] ?? $product['quantity'];
try {
$stmt = $pdo->prepare("UPDATE products SET name = ?, description = ?, price = ?, cost = ?, quantity = ? WHERE id = ?");
$stmt->execute([$name, $description, $price, $cost, $quantity, $id]);
echo json_encode(['message' => 'Product updated successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

106
sales/create.php Normal file
View File

@ -0,0 +1,106 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$customer_id = $data['customer_id'] ?? null;
$items = $data['items'] ?? [];
$tax_ids = $data['tax_ids'] ?? [];
if (empty($items)) {
http_response_code(400);
echo json_encode(['error' => 'Sale must have at least one item']);
exit;
}
try {
$pdo = db();
$pdo->beginTransaction();
// 1. Calculate subtotal and check stock
$subtotal = 0;
foreach ($items as $item) {
$product_id = $item['product_id'] ?? null;
$quantity = $item['quantity'] ?? null;
if (!$product_id || !$quantity || $quantity <= 0) {
throw new Exception('Invalid item data');
}
$stmt = $pdo->prepare("SELECT price, quantity FROM products WHERE id = ? FOR UPDATE");
$stmt->execute([$product_id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
throw new Exception("Product with ID $product_id not found");
}
if ($product['quantity'] < $quantity) {
throw new Exception("Not enough stock for product ID $product_id");
}
$subtotal += $product['price'] * $quantity;
}
// 2. Calculate taxes
$tax_total = 0;
$taxes_to_apply = [];
if (!empty($tax_ids)) {
$sql = "SELECT id, rate FROM taxes WHERE id IN (" . implode(',', array_fill(0, count($tax_ids), '?')) . ")";
$stmt = $pdo->prepare($sql);
$stmt->execute($tax_ids);
$taxes = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($taxes as $tax) {
$tax_amount = ($subtotal * $tax['rate']) / 100;
$tax_total += $tax_amount;
$taxes_to_apply[] = ['id' => $tax['id'], 'amount' => $tax_amount];
}
}
$total_amount = $subtotal + $tax_total;
// 3. Update product quantities
foreach ($items as $item) {
$stmt = $pdo->prepare("UPDATE products SET quantity = quantity - ? WHERE id = ?");
$stmt->execute([$item['quantity'], $item['product_id']]);
}
// 4. Insert into sales table
$stmt = $pdo->prepare("INSERT INTO sales (customer_id, subtotal, tax_total, total_amount) VALUES (?, ?, ?, ?)");
$stmt->execute([$customer_id, $subtotal, $tax_total, $total_amount]);
$sale_id = $pdo->lastInsertId();
// 5. Insert into sale_items
$stmt = $pdo->prepare("INSERT INTO sale_items (sale_id, product_id, quantity, price) VALUES (?, ?, ?, (SELECT price FROM products WHERE id = ?))");
foreach ($items as $item) {
$stmt->execute([$sale_id, $item['product_id'], $item['quantity'], $item['product_id']]);
}
// 6. Insert into sale_taxes
if (!empty($taxes_to_apply)) {
$stmt = $pdo->prepare("INSERT INTO sale_taxes (sale_id, tax_id, tax_amount) VALUES (?, ?, ?)");
foreach ($taxes_to_apply as $tax) {
$stmt->execute([$sale_id, $tax['id'], $tax['amount']]);
}
}
$pdo->commit();
http_response_code(201);
echo json_encode(['success' => 'Sale created successfully', 'sale_id' => $sale_id, 'total_amount' => $total_amount]);
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(400);
echo json_encode(['error' => $e->getMessage()]);
}

48
taxes/create.php Normal file
View File

@ -0,0 +1,48 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$name = $data['name'] ?? null;
$rate = $data['rate'] ?? null;
if (!$name || !isset($rate)) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: name, rate']);
exit;
}
if (!is_numeric($rate) || $rate < 0 || $rate > 100) {
http_response_code(400);
echo json_encode(['error' => 'Invalid rate. Must be a number between 0 and 100.']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO taxes (name, rate) VALUES (?, ?)");
$stmt->execute([$name, $rate]);
$id = $pdo->lastInsertId();
http_response_code(201);
echo json_encode(['success' => 'Tax created successfully', 'id' => $id]);
} catch (PDOException $e) {
if ($e->errorInfo[1] == 1062) { // Duplicate entry
http_response_code(409);
echo json_encode(['error' => 'A tax with this name already exists.']);
} else {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}
}

42
taxes/delete.php Normal file
View File

@ -0,0 +1,42 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Tax ID is required']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM taxes WHERE id = ?");
$stmt->execute([$id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Tax not found']);
exit;
}
// Check if the tax is being used in any sale
$stmt = $pdo->prepare("SELECT id FROM sale_taxes WHERE tax_id = ?");
$stmt->execute([$id]);
if ($stmt->fetch()) {
http_response_code(400);
echo json_encode(['error' => 'Cannot delete tax because it is associated with existing sales.']);
exit;
}
$stmt = $pdo->prepare("DELETE FROM taxes WHERE id = ?");
$stmt->execute([$id]);
echo json_encode(['success' => 'Tax deleted successfully']);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

31
taxes/read.php Normal file
View File

@ -0,0 +1,31 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
$id = $_GET['id'] ?? null;
try {
$pdo = db();
if ($id) {
$stmt = $pdo->prepare("SELECT * FROM taxes WHERE id = ?");
$stmt->execute([$id]);
$tax = $stmt->fetch(PDO::FETCH_ASSOC);
if ($tax) {
echo json_encode($tax);
} else {
http_response_code(404);
echo json_encode(['error' => 'Tax not found']);
}
} else {
$stmt = $pdo->query("SELECT * FROM taxes ORDER BY name");
$taxes = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($taxes);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}

61
taxes/update.php Normal file
View File

@ -0,0 +1,61 @@
<?php
require_once '../db/config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
$id = $_GET['id'] ?? null;
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'Tax ID is required']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$name = $data['name'] ?? null;
$rate = $data['rate'] ?? null;
if (!$name || !isset($rate)) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: name, rate']);
exit;
}
if (!is_numeric($rate) || $rate < 0 || $rate > 100) {
http_response_code(400);
echo json_encode(['error' => 'Invalid rate. Must be a number between 0 and 100.']);
exit;
}
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT id FROM taxes WHERE id = ?");
$stmt->execute([$id]);
if (!$stmt->fetch()) {
http_response_code(404);
echo json_encode(['error' => 'Tax not found']);
exit;
}
$stmt = $pdo->prepare("UPDATE taxes SET name = ?, rate = ? WHERE id = ?");
$stmt->execute([$name, $rate, $id]);
echo json_encode(['success' => 'Tax updated successfully']);
} catch (PDOException $e) {
if ($e->errorInfo[1] == 1062) { // Duplicate entry
http_response_code(409);
echo json_encode(['error' => 'A tax with this name already exists.']);
} else {
http_response_code(500);
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
}
}