diff --git a/assets/pasted-20251211-060813-1bfd24b7.png b/assets/pasted-20251211-060813-1bfd24b7.png new file mode 100644 index 0000000..c686c74 Binary files /dev/null and b/assets/pasted-20251211-060813-1bfd24b7.png differ diff --git a/assets/pasted-20251211-061003-c6ec9aea.png b/assets/pasted-20251211-061003-c6ec9aea.png new file mode 100644 index 0000000..7436ce3 Binary files /dev/null and b/assets/pasted-20251211-061003-c6ec9aea.png differ diff --git a/assets/pasted-20251211-061109-702062e6.png b/assets/pasted-20251211-061109-702062e6.png new file mode 100644 index 0000000..10838fe Binary files /dev/null and b/assets/pasted-20251211-061109-702062e6.png differ diff --git a/assets/pasted-20251211-061208-2fdbda92.png b/assets/pasted-20251211-061208-2fdbda92.png new file mode 100644 index 0000000..192792f Binary files /dev/null and b/assets/pasted-20251211-061208-2fdbda92.png differ diff --git a/assets/pasted-20251211-061245-39ca0406.png b/assets/pasted-20251211-061245-39ca0406.png new file mode 100644 index 0000000..63ebb8e Binary files /dev/null and b/assets/pasted-20251211-061245-39ca0406.png differ diff --git a/assets/pasted-20251211-061329-25a6f76d.png b/assets/pasted-20251211-061329-25a6f76d.png new file mode 100644 index 0000000..5eb2755 Binary files /dev/null and b/assets/pasted-20251211-061329-25a6f76d.png differ diff --git a/auth.php b/auth.php index f0691c7..c97b415 100644 --- a/auth.php +++ b/auth.php @@ -8,7 +8,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { $pdo = db(); - $stmt = $pdo->prepare("SELECT u.id, u.username, u.password, r.name as role_name + $stmt = $pdo->prepare("SELECT u.id, u.username, u.password, u.role_id, r.name as role_name FROM users u JOIN roles r ON u.role_id = r.id WHERE u.username = ?"); @@ -20,14 +20,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $stmt = $pdo->prepare("SELECT p.name FROM permissions p JOIN role_permissions rp ON p.id = rp.permission_id - WHERE rp.role_id = (SELECT id FROM roles WHERE name = ?)"); - $stmt->execute([$user['role_name']]); + WHERE rp.role_id = ?"); + $stmt->execute([$user['role_id']]); $permissions = $stmt->fetchAll(PDO::FETCH_COLUMN); $_SESSION['user'] = [ 'id' => $user['id'], 'username' => $user['username'], 'role' => $user['role_name'], + 'role_id' => $user['role_id'], 'permissions' => $permissions ]; unset($_SESSION['error']); diff --git a/db/migrations/010_add_new_approval_roles.sql b/db/migrations/010_add_new_approval_roles.sql new file mode 100644 index 0000000..a2137a1 --- /dev/null +++ b/db/migrations/010_add_new_approval_roles.sql @@ -0,0 +1,6 @@ +INSERT IGNORE INTO `roles` (`name`) VALUES +('Sales Manager'), +('General Manager'), +('Managing Director'), +('Accounts'), +('IT'); diff --git a/db/migrations/012_add_full_application_fields_split.sql b/db/migrations/012_add_full_application_fields_split.sql new file mode 100644 index 0000000..7e23af0 --- /dev/null +++ b/db/migrations/012_add_full_application_fields_split.sql @@ -0,0 +1,52 @@ +-- New fields for customer_applications from Page 1 +ALTER TABLE `customer_applications` ADD COLUMN `fax` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `gst_reg_no` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `company_reg_no` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `date_of_incorporation` DATE DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `country_of_incorporation` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `contact_person_designation` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `credit_terms` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `account_setup_ar_statement` BOOLEAN DEFAULT FALSE; +ALTER TABLE `customer_applications` ADD COLUMN `account_setup_dunning_letter` BOOLEAN DEFAULT FALSE; +ALTER TABLE `customer_applications` ADD COLUMN `account_setup_ap_payment` BOOLEAN DEFAULT FALSE; + +-- New table for shareholder/director information from Page 2 +CREATE TABLE IF NOT EXISTS `shareholder_director_information` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `application_id` INT NOT NULL, + `name` VARCHAR(255) DEFAULT NULL, + `address` TEXT DEFAULT NULL, + `perc_of_shareholding` VARCHAR(255) DEFAULT NULL, + `contact_no` VARCHAR(255) DEFAULT NULL, + FOREIGN KEY (`application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE +); + +-- New fields for customer_trade_references from Page 2 +ALTER TABLE `customer_trade_references` ADD COLUMN `telephone_no` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_trade_references` ADD COLUMN `fax_no` VARCHAR(255) DEFAULT NULL; + +-- New fields for banker's information from Page 3 +ALTER TABLE `customer_bank_details` ADD COLUMN `address` TEXT DEFAULT NULL; +ALTER TABLE `customer_bank_details` ADD COLUMN `swift_code` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_bank_details` ADD COLUMN `contact_person` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_bank_details` ADD COLUMN `telephone_no` VARCHAR(255) DEFAULT NULL; +ALTER TABLE `customer_bank_details` ADD COLUMN `fax_no` VARCHAR(255) DEFAULT NULL; + + +-- New table for financial information from Page 3 +CREATE TABLE IF NOT EXISTS `customer_financial_information` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `customer_application_id` INT NOT NULL, + `latest_audited_financial_year` VARCHAR(255) DEFAULT NULL, + `shareholder_equity` VARCHAR(255) DEFAULT NULL, + `paid_up_capital` VARCHAR(255) DEFAULT NULL, + `annual_turnover` VARCHAR(255) DEFAULT NULL, + `net_profit_loss` VARCHAR(255) DEFAULT NULL, + `currency` VARCHAR(50) DEFAULT NULL, + FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE +); + +-- Declaration and Authorisation from Page 3 +ALTER TABLE `customer_applications` ADD COLUMN `declaration_name` TEXT DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `declaration_designation` TEXT DEFAULT NULL; +ALTER TABLE `customer_applications` ADD COLUMN `declaration_date` DATE DEFAULT NULL; diff --git a/db/migrations/013_add_nature_of_business.sql b/db/migrations/013_add_nature_of_business.sql new file mode 100644 index 0000000..dc5b898 --- /dev/null +++ b/db/migrations/013_add_nature_of_business.sql @@ -0,0 +1,7 @@ +ALTER TABLE `customer_applications` MODIFY `company_name` TEXT; +ALTER TABLE `customer_applications` MODIFY `company_website` TEXT; +ALTER TABLE `customer_applications` MODIFY `sales_owner` TEXT; +ALTER TABLE `customer_applications` MODIFY `major_product` TEXT; +ALTER TABLE `customer_applications` MODIFY `credit_rank` TEXT; +ALTER TABLE `customer_applications` MODIFY `del_to_customer_name` TEXT; +ALTER TABLE `customer_applications` ADD COLUMN `nature_of_business` VARCHAR(255) DEFAULT NULL; \ No newline at end of file diff --git a/db/migrations/014_add_credit_terms_requested.sql b/db/migrations/014_add_credit_terms_requested.sql new file mode 100644 index 0000000..f8b6c3a --- /dev/null +++ b/db/migrations/014_add_credit_terms_requested.sql @@ -0,0 +1 @@ +ALTER TABLE `customer_applications` ADD COLUMN `credit_terms_requested` VARCHAR(255) DEFAULT NULL; \ No newline at end of file diff --git a/db/migrations/015_add_credit_limit_requested.sql b/db/migrations/015_add_credit_limit_requested.sql new file mode 100644 index 0000000..0106f79 --- /dev/null +++ b/db/migrations/015_add_credit_limit_requested.sql @@ -0,0 +1 @@ +ALTER TABLE `customer_applications` ADD COLUMN `credit_limit_requested` DECIMAL(15, 2) DEFAULT NULL; \ No newline at end of file diff --git a/db/migrations/016_add_nric_to_shareholders.sql b/db/migrations/016_add_nric_to_shareholders.sql new file mode 100644 index 0000000..5557e7a --- /dev/null +++ b/db/migrations/016_add_nric_to_shareholders.sql @@ -0,0 +1 @@ +ALTER TABLE `shareholder_director_information` ADD COLUMN `nric_fin` VARCHAR(255) DEFAULT NULL; \ No newline at end of file diff --git a/db/migrations/017_create_customer_principals_table.sql b/db/migrations/017_create_customer_principals_table.sql new file mode 100644 index 0000000..b28e26c --- /dev/null +++ b/db/migrations/017_create_customer_principals_table.sql @@ -0,0 +1,10 @@ +-- Create customer_principals table +CREATE TABLE IF NOT EXISTS `customer_principals` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `customer_application_id` INT NOT NULL, + `name` VARCHAR(255) NOT NULL, + `designation` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/includes/auth_helpers.php b/includes/auth_helpers.php index 4fda5fc..bef1259 100644 --- a/includes/auth_helpers.php +++ b/includes/auth_helpers.php @@ -7,6 +7,13 @@ function hasPermission($permission_name) { return false; } +function get_user_role_id() { + if (isset($_SESSION['user']['role_id'])) { + return $_SESSION['user']['role_id']; + } + return null; +} + function redirect_if_not_authenticated() { if (!isset($_SESSION['user'])) { header('Location: login.php'); diff --git a/manage_users.php b/manage_users.php index bbc752b..e764bbe 100644 --- a/manage_users.php +++ b/manage_users.php @@ -3,183 +3,262 @@ session_start(); require_once 'includes/auth_helpers.php'; require_once 'db/config.php'; -// Protect route: check if user is logged in and has permission redirect_if_not_authenticated(); redirect_if_no_permission('manage_users'); +$pdo = db(); $user = $_SESSION['user']; -// Dynamic project data from environment -$projectName = $_SERVER['PROJECT_NAME'] ?? 'Customer Master'; -$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Customer Master Registration & Maintenance'; -$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; +// Handle POST requests for Create, Update, Delete +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? ''; -// Handle file upload -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['userCsv'])) { - $file = $_FILES['userCsv']; + try { + if ($action === 'create_user') { + $username = $_POST['username']; + $password = $_POST['password']; + $role_id = $_POST['role_id']; + $hashed_password = password_hash($password, PASSWORD_DEFAULT); - if ($file['error'] === UPLOAD_ERR_OK) { - $csvData = array_map('str_getcsv', file($file['tmp_name'])); - $header = array_shift($csvData); - $expectedHeader = ['username', 'password', 'role']; - - if ($header === $expectedHeader) { - $pdo = db(); - $pdo->beginTransaction(); - $createdCount = 0; - $errorCount = 0; - $errors = []; - - // Get all roles from the database - $stmt = $pdo->query("SELECT id, name FROM roles"); - $roles = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); - - foreach ($csvData as $rowIndex => $row) { - $username = $row[0] ?? null; - $password = $row[1] ?? null; - $roleName = $row[2] ?? null; - - if (empty($username) || empty($password) || empty($roleName)) { - $errorCount++; - $errors[] = "Row " . ($rowIndex + 2) . ": Invalid data."; - continue; - } - - if (!in_array($roleName, $roles)) { - $errorCount++; - $errors[] = "Row " . ($rowIndex + 2) . ": Role '".htmlspecialchars($roleName)."' does not exist."; - continue; - } - - $roleId = array_search($roleName, $roles); - - try { - $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?"); - $stmt->execute([$username]); - if ($stmt->fetch()) { - $errorCount++; - $errors[] = "Row " . ($rowIndex + 2) . ": User '".htmlspecialchars($username)."' already exists."; - continue; - } - - $hashedPassword = password_hash($password, PASSWORD_DEFAULT); - $stmt = $pdo->prepare("INSERT INTO users (username, password, role_id) VALUES (?, ?, ?)"); - $stmt->execute([$username, $hashedPassword, $roleId]); - $createdCount++; - } catch (PDOException $e) { - $errorCount++; - $errors[] = "Row " . ($rowIndex + 2) . ": Database error."; - } - } - - if ($errorCount > 0) { - $pdo->rollBack(); - $_SESSION['flash_message'] = [ - 'type' => 'danger', - 'message' => "User import failed with {$errorCount} errors.", - 'errors' => $errors - ]; - } else { - $pdo->commit(); - $_SESSION['flash_message'] = [ - 'type' => 'success', - 'message' => "Successfully created {$createdCount} users." - ]; - } - } else { - $_SESSION['flash_message'] = [ - 'type' => 'danger', - 'message' => 'Invalid CSV header. Expected: username,password,role' - ]; + $stmt = $pdo->prepare("INSERT INTO users (username, password, role_id) VALUES (?, ?, ?)"); + $stmt->execute([$username, $hashed_password, $role_id]); + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'User created successfully.']; } - } else { - $_SESSION['flash_message'] = [ - 'type' => 'danger', - 'message' => 'Error uploading file.' - ]; + + if ($action === 'update_role') { + $user_id = $_POST['user_id']; + $role_id = $_POST['role_id']; + + $stmt = $pdo->prepare("UPDATE users SET role_id = ? WHERE id = ?"); + $stmt->execute([$role_id, $user_id]); + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'User role updated successfully.']; + } + + if ($action === 'delete_user') { + $user_id = $_POST['user_id']; + + // Prevent admin from deleting themselves + if ($user_id == get_user_id()) { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'You cannot delete your own account.']; + } else { + $stmt = $pdo->prepare("DELETE FROM users WHERE id = ?"); + $stmt->execute([$user_id]); + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'User deleted successfully.']; + } + } + } catch (PDOException $e) { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Database error: ' . $e->getMessage()]; } + header('Location: manage_users.php'); exit(); } + +// Fetch all users and roles +$stmt_users = $pdo->query("SELECT u.id, u.username, r.name as role_name, u.created_at FROM users u JOIN roles r ON u.role_id = r.id ORDER BY u.created_at DESC"); +$users_list = $stmt_users->fetchAll(); + +$stmt_roles = $pdo->query("SELECT id, name FROM roles ORDER BY name"); +$roles = $stmt_roles->fetchAll(); + ?>
-