diff --git a/assets/pasted-20251204-015537-032cd7e6.png b/assets/pasted-20251204-015537-032cd7e6.png new file mode 100644 index 0000000..4e18bd2 Binary files /dev/null and b/assets/pasted-20251204-015537-032cd7e6.png differ diff --git a/assets/pasted-20251204-015710-55b153a5.png b/assets/pasted-20251204-015710-55b153a5.png new file mode 100644 index 0000000..a40fbea Binary files /dev/null and b/assets/pasted-20251204-015710-55b153a5.png differ diff --git a/assets/pasted-20251204-015859-ba057228.png b/assets/pasted-20251204-015859-ba057228.png new file mode 100644 index 0000000..0e6d83e Binary files /dev/null and b/assets/pasted-20251204-015859-ba057228.png differ diff --git a/assets/pasted-20251204-015945-278d0108.png b/assets/pasted-20251204-015945-278d0108.png new file mode 100644 index 0000000..59984be Binary files /dev/null and b/assets/pasted-20251204-015945-278d0108.png differ diff --git a/assets/pasted-20251204-020809-fbe187c3.png b/assets/pasted-20251204-020809-fbe187c3.png new file mode 100644 index 0000000..16c6e2d Binary files /dev/null and b/assets/pasted-20251204-020809-fbe187c3.png differ diff --git a/assets/pasted-20251204-021404-14f90d2d.png b/assets/pasted-20251204-021404-14f90d2d.png new file mode 100644 index 0000000..df41f82 Binary files /dev/null and b/assets/pasted-20251204-021404-14f90d2d.png differ diff --git a/assets/pasted-20251204-021710-6689afd9.png b/assets/pasted-20251204-021710-6689afd9.png new file mode 100644 index 0000000..d5fa782 Binary files /dev/null and b/assets/pasted-20251204-021710-6689afd9.png differ diff --git a/assets/pasted-20251204-022151-982a3976.png b/assets/pasted-20251204-022151-982a3976.png new file mode 100644 index 0000000..8cc1a37 Binary files /dev/null and b/assets/pasted-20251204-022151-982a3976.png differ diff --git a/assets/pasted-20251204-022306-1a04c023.png b/assets/pasted-20251204-022306-1a04c023.png new file mode 100644 index 0000000..06e303d Binary files /dev/null and b/assets/pasted-20251204-022306-1a04c023.png differ diff --git a/assets/pasted-20251204-022406-7fe85bc3.png b/assets/pasted-20251204-022406-7fe85bc3.png new file mode 100644 index 0000000..3ac8a38 Binary files /dev/null and b/assets/pasted-20251204-022406-7fe85bc3.png differ diff --git a/assets/pasted-20251204-022454-940b0a04.png b/assets/pasted-20251204-022454-940b0a04.png new file mode 100644 index 0000000..fe98d95 Binary files /dev/null and b/assets/pasted-20251204-022454-940b0a04.png differ diff --git a/assets/pasted-20251204-022551-bbe9e793.png b/assets/pasted-20251204-022551-bbe9e793.png new file mode 100644 index 0000000..b5d54b8 Binary files /dev/null and b/assets/pasted-20251204-022551-bbe9e793.png differ diff --git a/assets/pasted-20251204-022908-1d2c298f.png b/assets/pasted-20251204-022908-1d2c298f.png new file mode 100644 index 0000000..245ceeb Binary files /dev/null and b/assets/pasted-20251204-022908-1d2c298f.png differ diff --git a/auth.php b/auth.php new file mode 100644 index 0000000..f0691c7 --- /dev/null +++ b/auth.php @@ -0,0 +1,51 @@ +prepare("SELECT u.id, u.username, u.password, r.name as role_name + FROM users u + JOIN roles r ON u.role_id = r.id + WHERE u.username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user && password_verify($password, $user['password'])) { + // Authentication successful + $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']]); + $permissions = $stmt->fetchAll(PDO::FETCH_COLUMN); + + $_SESSION['user'] = [ + 'id' => $user['id'], + 'username' => $user['username'], + 'role' => $user['role_name'], + 'permissions' => $permissions + ]; + unset($_SESSION['error']); + header('Location: index.php'); + exit(); + } else { + // Authentication failed + $_SESSION['error'] = 'Invalid username or password.'; + header('Location: login.php'); + exit(); + } + } catch (PDOException $e) { + $_SESSION['error'] = 'Database error: ' . $e->getMessage(); + header('Location: login.php'); + exit(); + } +} else { + // Redirect if accessed directly + header('Location: login.php'); + exit(); +} \ No newline at end of file diff --git a/db/migrations/001_create_customer_tables.sql b/db/migrations/001_create_customer_tables.sql new file mode 100644 index 0000000..93a1d97 --- /dev/null +++ b/db/migrations/001_create_customer_tables.sql @@ -0,0 +1,39 @@ +CREATE TABLE IF NOT EXISTS `customer_applications` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`application_id` VARCHAR(255) NOT NULL UNIQUE, +`customer_id` VARCHAR(255) NULL, +`company_name` VARCHAR(255) NOT NULL, +`company_website` VARCHAR(255) NULL, +`company_phone` VARCHAR(255) NULL, +`sales_owner` VARCHAR(255) NOT NULL, +`payment_terms` VARCHAR(255) NOT NULL, +`tags` TEXT NULL, +`notes` TEXT NULL, +`status` VARCHAR(50) NOT NULL DEFAULT 'DRAFT', +`created_by` VARCHAR(255) NOT NULL, +`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `customer_contacts` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`customer_application_id` INT NOT NULL, +`name` VARCHAR(255) NOT NULL, +`email` VARCHAR(255) NOT NULL, +`phone` VARCHAR(255) NULL, +`is_primary` BOOLEAN NOT NULL DEFAULT FALSE, +FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `customer_addresses` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`customer_application_id` INT NOT NULL, +`address_type` VARCHAR(50) NOT NULL, -- e.g., 'BILLING', 'SHIPPING' +`address_line_1` VARCHAR(255) NOT NULL, +`address_line_2` VARCHAR(255) NULL, +`city` VARCHAR(255) NOT NULL, +`state` VARCHAR(255) NOT NULL, +`postal_code` VARCHAR(50) NOT NULL, +`country` VARCHAR(100) NOT NULL, +FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/db/migrations/002_create_application_files_table.sql b/db/migrations/002_create_application_files_table.sql new file mode 100644 index 0000000..0cd8572 --- /dev/null +++ b/db/migrations/002_create_application_files_table.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `application_files` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`customer_application_id` INT NOT NULL, +`filename` VARCHAR(255) NOT NULL, +`filepath` VARCHAR(255) NOT NULL, +`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications`(`id`) ON DELETE CASCADE +); \ No newline at end of file diff --git a/db/migrations/003_create_user_management_tables.sql b/db/migrations/003_create_user_management_tables.sql new file mode 100644 index 0000000..09f1dcb --- /dev/null +++ b/db/migrations/003_create_user_management_tables.sql @@ -0,0 +1,58 @@ +CREATE TABLE IF NOT EXISTS `users` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`username` VARCHAR(255) NOT NULL UNIQUE, +`password` VARCHAR(255) NOT NULL, +`role_id` INT NOT NULL, +`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `roles` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`name` VARCHAR(255) NOT NULL UNIQUE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `permissions` ( +`id` INT AUTO_INCREMENT PRIMARY KEY, +`name` VARCHAR(255) NOT NULL UNIQUE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `role_permissions` ( +`role_id` INT NOT NULL, +`permission_id` INT NOT NULL, +PRIMARY KEY (`role_id`, `permission_id`), +FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE, +FOREIGN KEY (`permission_id`) REFERENCES `permissions`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT IGNORE INTO `roles` (`name`) VALUES ('admin'), ('manager'), ('sales'), ('clerk'); + +INSERT IGNORE INTO `permissions` (`name`) VALUES +('create_application'), +('edit_application'), +('approve_application'), +('view_applications'), +('delete_application'), +('manage_users'), +('manage_roles'), +('upload_files'), +('delete_files'); + +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'admin'), id FROM permissions; + +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'manager'), p.id +FROM permissions p +WHERE p.name IN ('create_application', 'edit_application', 'approve_application', 'view_applications', 'delete_application', 'upload_files', 'delete_files'); + +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'sales'), p.id +FROM permissions p +WHERE p.name IN ('create_application', 'edit_application', 'view_applications', 'upload_files', 'delete_files'); + +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'clerk'), p.id +FROM permissions p +WHERE p.name = 'view_applications'; + +INSERT IGNORE INTO `users` (`username`, `password`, `role_id`) VALUES ('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', (SELECT id FROM roles WHERE name = 'admin')); -- password is 'password' \ No newline at end of file diff --git a/db/migrations/004_add_approval_workflow_columns.sql b/db/migrations/004_add_approval_workflow_columns.sql new file mode 100644 index 0000000..4cb9cbd --- /dev/null +++ b/db/migrations/004_add_approval_workflow_columns.sql @@ -0,0 +1,13 @@ +ALTER TABLE customer_applications ADD COLUMN approval_level INT DEFAULT 1; +ALTER TABLE customer_applications ADD COLUMN current_approver_role_id INT; +CREATE TABLE IF NOT EXISTS application_approvals ( + id INT AUTO_INCREMENT PRIMARY KEY, + application_id INT NOT NULL, + approver_id INT NOT NULL, + approval_level INT NOT NULL, + status VARCHAR(255) NOT NULL, + comments TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (application_id) REFERENCES customer_applications(id), + FOREIGN KEY (approver_id) REFERENCES users(id) +); \ No newline at end of file diff --git a/db/migrations/005_create_approver_roles.sql b/db/migrations/005_create_approver_roles.sql new file mode 100644 index 0000000..0a71835 --- /dev/null +++ b/db/migrations/005_create_approver_roles.sql @@ -0,0 +1,45 @@ +INSERT IGNORE INTO `roles` (`name`) VALUES +('Approver Level 1'), +('Approver Level 2'), +('Approver Level 3'), +('Approver Level 4'), +('Approver Level 5'), +('Approver Level 6'), +('Approver Level 7'); + +INSERT IGNORE INTO `permissions` (`name`) VALUES +('approve_level_1'), +('approve_level_2'), +('approve_level_3'), +('approve_level_4'), +('approve_level_5'), +('approve_level_6'), +('approve_level_7'); + +-- Assign approve_level_1 permission to Approver Level 1 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 1'), (SELECT id FROM permissions WHERE name = 'approve_level_1'); + +-- Assign approve_level_2 permission to Approver Level 2 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 2'), (SELECT id FROM permissions WHERE name = 'approve_level_2'); + +-- Assign approve_level_3 permission to Approver Level 3 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 3'), (SELECT id FROM permissions WHERE name = 'approve_level_3'); + +-- Assign approve_level_4 permission to Approver Level 4 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 4'), (SELECT id FROM permissions WHERE name = 'approve_level_4'); + +-- Assign approve_level_5 permission to Approver Level 5 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 5'), (SELECT id FROM permissions WHERE name = 'approve_level_5'); + +-- Assign approve_level_6 permission to Approver Level 6 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 6'), (SELECT id FROM permissions WHERE name = 'approve_level_6'); + +-- Assign approve_level_7 permission to Approver Level 7 role +INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`) +SELECT (SELECT id FROM roles WHERE name = 'Approver Level 7'), (SELECT id FROM permissions WHERE name = 'approve_level_7'); diff --git a/db/migrations/006_add_trade_ref_bank_and_signature.sql b/db/migrations/006_add_trade_ref_bank_and_signature.sql new file mode 100644 index 0000000..4b03904 --- /dev/null +++ b/db/migrations/006_add_trade_ref_bank_and_signature.sql @@ -0,0 +1,31 @@ +-- Add new tables for trade references and bank details +CREATE TABLE IF NOT EXISTS `customer_trade_references` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `customer_application_id` int(11) NOT NULL, + `company_name` varchar(255) NOT NULL, + `contact_person` varchar(255) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, + `phone` varchar(50) DEFAULT NULL, + `address` text, + PRIMARY KEY (`id`), + KEY `customer_application_id` (`customer_application_id`), + CONSTRAINT `customer_trade_references_ibfk_1` FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `customer_bank_details` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `customer_application_id` int(11) NOT NULL, + `bank_name` varchar(255) DEFAULT NULL, + `branch` varchar(255) DEFAULT NULL, + `bsb_number` varchar(50) DEFAULT NULL, + `account_number` varchar(50) DEFAULT NULL, + `account_name` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `customer_application_id` (`customer_application_id`), + CONSTRAINT `customer_bank_details_ibfk_1` FOREIGN KEY (`customer_application_id`) REFERENCES `customer_applications` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- Add columns for declaration and signature to customer_applications +ALTER TABLE `customer_applications` +ADD COLUMN `declaration_text` TEXT, +ADD COLUMN `signature_path` VARCHAR(255); \ No newline at end of file diff --git a/db/migrations/007_add_financial_credit_details.sql b/db/migrations/007_add_financial_credit_details.sql new file mode 100644 index 0000000..3691b43 --- /dev/null +++ b/db/migrations/007_add_financial_credit_details.sql @@ -0,0 +1,20 @@ +ALTER TABLE `customer_applications` +ADD COLUMN `major_product` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `capital` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `capital_currency` VARCHAR(10) DEFAULT NULL, +ADD COLUMN `main_shareholders` TEXT DEFAULT NULL, +ADD COLUMN `num_employees` INT DEFAULT NULL, +ADD COLUMN `payment_terms_ar` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `pl_year` YEAR DEFAULT NULL, +ADD COLUMN `net_sales` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `net_income_margin` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `net_income_margin_ratio` DECIMAL(5, 2) DEFAULT NULL, +ADD COLUMN `sales_target_this_year` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `sales_target_next_year` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `sales_target_after_next` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `credit_rank` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `credit_limit` DECIMAL(20, 2) DEFAULT NULL, +ADD COLUMN `credit_research_status` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `credit_research_reason` TEXT DEFAULT NULL, +ADD COLUMN `tax_rate_area` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `billing_type` VARCHAR(255) DEFAULT NULL; diff --git a/db/migrations/008_add_del_to_info.sql b/db/migrations/008_add_del_to_info.sql new file mode 100644 index 0000000..bc74751 --- /dev/null +++ b/db/migrations/008_add_del_to_info.sql @@ -0,0 +1,21 @@ +-- Add columns for DEL-TO INFORMATIONS +ALTER TABLE `customer_applications` +ADD COLUMN `del_to_code` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `delivery_abbreviation` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_customer_name` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_address_1` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_address_2` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_address_3` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_address_4` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_postcode` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_phone` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_area_code` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_transportation_code` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_stock_point_code` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_recipient_section` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_country_code` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_shipment_flag` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_transport_days` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_shipment_condition_category` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_transport_service_exist` VARCHAR(255) DEFAULT NULL, +ADD COLUMN `del_to_shipment_condition_place` VARCHAR(255) DEFAULT NULL; diff --git a/db/migrations/009_add_sop_details.sql b/db/migrations/009_add_sop_details.sql new file mode 100644 index 0000000..d499329 --- /dev/null +++ b/db/migrations/009_add_sop_details.sql @@ -0,0 +1,29 @@ +ALTER TABLE `customer_applications` +ADD COLUMN `doc_req_do` VARCHAR(255), +ADD COLUMN `doc_req_packing_list` VARCHAR(255), +ADD COLUMN `doc_req_invoice` VARCHAR(255), +ADD COLUMN `doc_req_export_permit` VARCHAR(255), +ADD COLUMN `doc_req_po_do_inv` VARCHAR(255), +ADD COLUMN `doc_req_do_inv` VARCHAR(255), +ADD COLUMN `doc_req_others` TEXT, +ADD COLUMN `pack_req_one_line_carton` VARCHAR(255), +ADD COLUMN `pack_req_one_item_carton` VARCHAR(255), +ADD COLUMN `pack_req_one_item_pocket` VARCHAR(255), +ADD COLUMN `pack_req_thomson_label` VARCHAR(255), +ADD COLUMN `pack_req_contents_label` VARCHAR(255), +ADD COLUMN `pack_req_delivery_schedule` VARCHAR(255), +ADD COLUMN `forwarder_name` VARCHAR(255), +ADD COLUMN `forwarder_code` VARCHAR(255), +ADD COLUMN `forwarder_address` TEXT, +ADD COLUMN `forwarder_contact_person` VARCHAR(255), +ADD COLUMN `forwarder_phone` VARCHAR(255), +ADD COLUMN `forwarder_fax` VARCHAR(255), +ADD COLUMN `forwarder_delivery_method` VARCHAR(255), +ADD COLUMN `forwarder_delivery_timings` VARCHAR(255), +ADD COLUMN `forwarder_delivery_requirements` TEXT, +ADD COLUMN `special_instructions_shipping_mark` VARCHAR(255), +ADD COLUMN `special_instructions_fax_documents` VARCHAR(255), +ADD COLUMN `special_instructions_details` TEXT, +ADD COLUMN `special_instructions_attention_to` VARCHAR(255), +ADD COLUMN `special_instructions_fax_number` VARCHAR(255), +ADD COLUMN `remarks` TEXT; diff --git a/delete_file.php b/delete_file.php new file mode 100644 index 0000000..9e2ccc5 --- /dev/null +++ b/delete_file.php @@ -0,0 +1,59 @@ +prepare("SELECT stored_filename FROM application_files WHERE id = ? AND application_id = ?"); + $stmt->execute([$file_id, $application_id]); + $file = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($file) { + $filepath = __DIR__ . '/uploads/' . $file['stored_filename']; + + // Delete the file from the filesystem + if (file_exists($filepath)) { + unlink($filepath); + } + + // Delete the record from the database + $delete_stmt = $pdo->prepare("DELETE FROM application_files WHERE id = ?"); + $delete_stmt->execute([$file_id]); + + $_SESSION['message'] = 'File deleted successfully.'; + $_SESSION['message_type'] = 'success'; + } else { + $_SESSION['message'] = 'File not found or you do not have permission to delete it.'; + $_SESSION['message_type'] = 'danger'; + } + + } catch (PDOException $e) { + // In a real app, log this error + $_SESSION['message'] = 'Database error while deleting file.'; + $_SESSION['message_type'] = 'danger'; + } + + header('Location: view_application.php?id=' . $application_id); + exit(); + +} else { + header('Location: index.php'); + exit(); +} \ No newline at end of file diff --git a/edit_application.php b/edit_application.php new file mode 100644 index 0000000..0992dcb --- /dev/null +++ b/edit_application.php @@ -0,0 +1,282 @@ +prepare("SELECT * FROM customer_applications WHERE id = ?"); + $stmt->execute([$application_id]); + $customer = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($customer) { + // Fetch contacts + $stmt = $pdo->prepare("SELECT * FROM customer_contacts WHERE customer_application_id = ? ORDER BY is_primary DESC, id ASC"); + $stmt->execute([$application_id]); + $contacts = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Fetch addresses + $stmt = $pdo->prepare("SELECT * FROM customer_addresses WHERE customer_application_id = ? ORDER BY id ASC"); + $stmt->execute([$application_id]); + $addresses = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Fetch files + $stmt = $pdo->prepare("SELECT * FROM application_files WHERE customer_application_id = ? ORDER BY created_at DESC"); + $stmt->execute([$application_id]); + $files = $stmt->fetchAll(PDO::FETCH_ASSOC); + } + +} catch (PDOException $e) { + die("Database error: " . $e->getMessage()); +} + +if (!$customer) { + http_response_code(404); + echo "Application not found."; + exit(); +} +?> + + + + + + Edit Customer Application + + + + + +
+

Edit Customer Application #

+
+ + + +
+
Company Details
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ Contacts + +
+
+ $contact): ?> +
+ + +
+ > + +
+
+
+
+
+
+
+ +
+
+ + +
+
+ Addresses + +
+
+ $address): ?> +
+ + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+
File Uploads
+
+
+ +
+ + +
+
+
+
Uploaded Files
+
    + +
  • No files uploaded yet.
  • + + +
  • + + Delete +
  • + + +
+
+
+ + + Cancel +
+
+ + + + diff --git a/includes/auth_helpers.php b/includes/auth_helpers.php new file mode 100644 index 0000000..4fda5fc --- /dev/null +++ b/includes/auth_helpers.php @@ -0,0 +1,27 @@ + 'danger', + 'message' => 'You do not have permission to access this page.' + ]; + header('Location: index.php'); + exit(); + } +} diff --git a/index.php b/index.php index 7205f3d..eb3c6bd 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,217 @@ - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + + Dashboard - <?php echo htmlspecialchars($projectName); ?> + + + + + + + + + + + + + + + + + + + + + -
-
-

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

-
-
- + + +
+ + + + + +
+

Dashboard

+ + New Customer Application + +
+ +

Your dashboard is ready. From here you can manage customer applications.

+ +
+
+
Submitted Applications
+
+
+
+
+ +
+
+ +
+
+ +
+
+ ' : ' '; + } + return ''; + } + + function getStatusBadgeClass($status) { + switch ($status) { + case 'pending_approval': + return 'bg-warning'; + case 'approved': + return 'bg-success'; + case 'rejected': + return 'bg-danger'; + case 'reverted': + return 'bg-info'; + case 'draft': + default: + return 'bg-secondary'; + } + } + + $sort_column = $_GET['sort'] ?? 'created_at'; + $sort_order = $_GET['order'] ?? 'DESC'; + $valid_columns = ['id', 'application_id', 'company_name', 'status', 'created_at']; + if (!in_array($sort_column, $valid_columns)) { + $sort_column = 'created_at'; + } + + $status_filter = $_GET['status'] ?? ''; + $sql = "SELECT id, application_id, company_name, status, created_at FROM customer_applications"; + $params = []; + + if ($status_filter) { + $sql .= " WHERE status = ?"; + $params[] = $status_filter; + } + + $sql .= " ORDER BY $sort_column $sort_order"; + + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + $applications = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (count($applications) > 0) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + foreach ($applications as $app) { $badgeClass = getStatusBadgeClass($app['status']); + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo ''; + echo '
Application ID' . getSortIcon('application_id', $sort_column, $sort_order) . 'Company Name' . getSortIcon('company_name', $sort_column, $sort_order) . 'Status' . getSortIcon('status', $sort_column, $sort_order) . 'Date Submitted' . getSortIcon('created_at', $sort_column, $sort_order) . '
' . htmlspecialchars($app['application_id']) . '' . htmlspecialchars($app['company_name']) . '' . htmlspecialchars(ucfirst(str_replace('_', ' ', $app['status']))) . '' . htmlspecialchars(date("Y-m-d H:i", strtotime($app['created_at']))) . '
'; + } else { + echo '

No customer applications found.

'; + } + } catch (PDOException $e) { + echo '
Error: Could not fetch applications.
'; + // Optional: log error to a file + // error_log($e->getMessage()); + } + ?> +
+
+
+ + - + \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 0000000..eb70ae6 --- /dev/null +++ b/login.php @@ -0,0 +1,65 @@ + + + + + + + Login - Customer Master + + + + + +
+
+

Customer Master

+
Please sign in
+ + + + + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+ + + diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..49263e1 --- /dev/null +++ b/logout.php @@ -0,0 +1,6 @@ +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' + ]; + } + } else { + $_SESSION['flash_message'] = [ + 'type' => 'danger', + 'message' => 'Error uploading file.' + ]; + } + header('Location: manage_users.php'); + exit(); +} +?> + + + + + + Manage Users - <?php echo htmlspecialchars($projectName); ?> + + + + + + +
+ + + + + +

Manage Users

+
+
+
Import Users from CSV
+
+
+

Upload a CSV file with the following columns: username, password, role.

+
+
+ + +
+ +
+
+
+
+ + + + diff --git a/new_application.php b/new_application.php new file mode 100644 index 0000000..16f9cb7 --- /dev/null +++ b/new_application.php @@ -0,0 +1,780 @@ + + + + + + + New Customer Application + + + + + + +
+

New Customer Application

+
+ +
+
+
Company Details
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
Financial & Credit Details
+
+
+
+ + +
+
+ +
+ + +
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
Customer's P/L Information
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
ROHM Sales Target (KUS$)
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
Credit Information
+
+
+ + +
+
+ + +
+
+
+
Credit Research
+
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
Del-To Informations
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+ + +
+
+
SOP
+
+
+
+
Document Requirements
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
Packing Requirements
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
Forwarder Information
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
Special Instructions
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+ Contacts + +
+
+ +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ Addresses + +
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+ Trade References + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
Bank Details
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
Declaration
+
+
+ + +
+
+ +
+ +
+ + +
+
+
+
+ +
+ + + +
+
+
+ + + + + \ No newline at end of file diff --git a/permissions.php b/permissions.php new file mode 100644 index 0000000..efbcdac --- /dev/null +++ b/permissions.php @@ -0,0 +1,7 @@ +prepare('SELECT u.email FROM users u JOIN user_roles ur ON u.id = ur.user_id JOIN roles r ON ur.role_id = r.id WHERE r.name = ?'); + $stmt->execute([$role_name]); + return $stmt->fetchAll(PDO::FETCH_COLUMN); +} + +// Fetch application +$stmt = $pdo->prepare('SELECT * FROM customer_applications WHERE id = ?'); +$stmt->execute([$application_id]); +$application = $stmt->fetch(); + +if (!$application) { + die('Application not found.'); +} + +// Check permission +$approval_level = $application['approval_level']; +$permission_needed = 'approve_level_' . $approval_level; +if (!hasPermission($permission_needed)) { + $_SESSION['flash_message'] = [ + 'type' => 'danger', + 'message' => 'You do not have permission to perform this action.' + ]; + header('Location: view_application.php?id=' . $application_id); + exit(); +} + +// Get applicant email +$stmt_applicant = $pdo->prepare('SELECT email FROM customer_contacts WHERE customer_application_id = ? AND is_primary = 1'); +$stmt_applicant->execute([$application_id]); +$applicant_email = $stmt_applicant->fetchColumn(); + +try { + if ($action === 'approve') { + $next_approval_level = $approval_level + 1; + $next_approver_role_name = 'Approver Level ' . $next_approval_level; + + $stmt_role = $pdo->prepare("SELECT id FROM roles WHERE name = ?"); + $stmt_role->execute([$next_approver_role_name]); + $next_approver_role = $stmt_role->fetch(); + + if ($next_approver_role) { + // Move to next approval level + $stmt = $pdo->prepare('UPDATE customer_applications SET approval_level = ?, current_approver_role_id = ? WHERE id = ?'); + $stmt->execute([$next_approval_level, $next_approver_role['id'], $application_id]); + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Application approved and moved to the next level.']; + + // Notify next approvers + $next_approver_emails = get_user_emails_by_role($next_approver_role_name, $pdo); + if (!empty($next_approver_emails)) { + // Get Sales Rep name + $stmt_sales_rep = $pdo->prepare('SELECT name FROM users WHERE id = ?'); + $stmt_sales_rep->execute([$application['created_by_user_id']]); + $sales_rep_name = $stmt_sales_rep->fetchColumn(); + + // Get Credit Amount + $stmt_credit = $pdo->prepare('SELECT requested_credit_limit FROM financial_credit_details WHERE customer_application_id = ?'); + $stmt_credit->execute([$application_id]); + $credit_amount = $stmt_credit->fetchColumn(); + + $subject = 'Credit Application - ' . $application['company_name']; + $submission_date = date('Y-m-d'); + $body = " +

A new credit application requires your approval.

+

Customer Name: {$application['company_name']}

+

Sales Rep: {$sales_rep_name}

+

Credit Amount: $" . number_format($credit_amount, 2) . "

+

Submission Date: {$submission_date}

+

View Application

+ "; + MailService::sendMail($next_approver_emails, $subject, $body); + } + } else { + // Final approval + $stmt = $pdo->prepare("UPDATE customer_applications SET status = 'APPROVED', approval_level = NULL, current_approver_role_id = NULL WHERE id = ?"); + $stmt->execute([$application_id]); + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Application approved.']; + + // Notify applicant + if ($applicant_email) { + $subject = 'Your Application has been Approved: ' . $application['application_id']; + $body = "

Congratulations! Your customer application ({$application['application_id']}) has been approved.

"; + MailService::sendMail($applicant_email, $subject, $body); + } + } + } elseif ($action === 'reject') { + $stmt = $pdo->prepare("UPDATE customer_applications SET status = 'REJECTED' WHERE id = ?"); + $stmt->execute([$application_id]); + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Application rejected.']; + + // Notify applicant + if ($applicant_email) { + $subject = 'Your Application has been Rejected: ' . $application['application_id']; + $body = "

We regret to inform you that your customer application ({$application['application_id']}) has been rejected.

"; + MailService::sendMail($applicant_email, $subject, $body); + } + } + +} catch (PDOException $e) { + error_log('Approval processing failed: ' . $e->getMessage()); + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'An error occurred. Please try again.']; +} + +header('Location: view_application.php?id=' . $application_id); +exit(); diff --git a/profile.php b/profile.php new file mode 100644 index 0000000..6d7630a --- /dev/null +++ b/profile.php @@ -0,0 +1,127 @@ + + + + + + + + User Profile - <?php echo htmlspecialchars($projectName); ?> + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+

User Profile

+
+
+

Username:

+

Role:

+ +
+ +
Change Password
+ + + + + + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+ + + + diff --git a/submit_application.php b/submit_application.php new file mode 100644 index 0000000..489e402 --- /dev/null +++ b/submit_application.php @@ -0,0 +1,221 @@ +prepare("SELECT id FROM roles WHERE name = 'Approver Level 1'"); + $stmt_role->execute(); + $approver_role = $stmt_role->fetch(PDO::FETCH_ASSOC); + $approver_role_id = $approver_role ? $approver_role['id'] : null; + + $pdo->beginTransaction(); + + // 1. Insert into customer_applications + $application_id = 'APP-' . strtoupper(uniqid()); + $created_by = $_SESSION['user']['username'] ?? 'system'; + + $stmt = $db->prepare( + 'INSERT INTO customer_applications (user_id, company_name, company_website, company_phone, sales_owner, payment_terms, tags, notes, declaration_text, signature_path, major_product, capital, capital_currency, main_shareholders, num_employees, payment_terms_ar, pl_year, net_sales, net_income_margin, net_income_margin_ratio, sales_target_this_year, sales_target_next_year, sales_target_after_next, credit_rank, credit_limit, credit_research_status, credit_research_reason, tax_rate_area, billing_type, del_to_code, delivery_abbreviation, del_to_customer_name, del_to_address_1, del_to_address_2, del_to_address_3, del_to_address_4, del_to_postcode, del_to_phone, del_to_area_code, del_to_transportation_code, del_to_stock_point_code, del_to_recipient_section, del_to_country_code, del_to_shipment_flag, del_to_transport_days, del_to_shipment_condition_category, del_to_transport_service_exist, del_to_shipment_condition_place, doc_req_do, doc_req_packing_list, doc_req_invoice, doc_req_export_permit, doc_req_po_do_inv, doc_req_do_inv, doc_req_others, pack_req_one_line_carton, pack_req_one_item_carton, pack_req_one_item_pocket, pack_req_thomson_label, pack_req_contents_label, pack_req_delivery_schedule, forwarder_name, forwarder_code, forwarder_address, forwarder_contact_person, forwarder_phone, forwarder_fax, forwarder_delivery_method, forwarder_delivery_timings, forwarder_delivery_requirements, special_instructions_shipping_mark, special_instructions_fax_documents, special_instructions_details, special_instructions_attention_to, special_instructions_fax_number, remarks) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' + ); + $stmt->execute([ + $_SESSION['user_id'], + $_POST['company_name'], + $_POST['company_website'], + $_POST['company_phone'], + $_POST['sales_owner'], + $_POST['payment_terms'], + $_POST['tags'], + $_POST['notes'], + $_POST['declaration_text'], + $signature_path, + $_POST['major_product'] ?? null, + $_POST['capital'] ?? null, + $_POST['capital_currency'] ?? null, + $_POST['main_shareholders'] ?? null, + $_POST['num_employees'] ?? null, + $_POST['payment_terms_ar'] ?? null, + $_POST['pl_year'] ?? null, + $_POST['net_sales'] ?? null, + $_POST['net_income_margin'] ?? null, + $_POST['net_income_margin_ratio'] ?? null, + $_POST['sales_target_this_year'] ?? null, + $_POST['sales_target_next_year'] ?? null, + $_POST['sales_target_after_next'] ?? null, + $_POST['credit_rank'] ?? null, + $_POST['credit_limit'] ?? null, + $_POST['credit_research_status'] ?? null, + $_POST['credit_research_reason'] ?? null, + $_POST['tax_rate_area'] ?? null, + $_POST['billing_type'] ?? null, + $_POST['del_to_code'] ?? null, + $_POST['delivery_abbreviation'] ?? null, + $_POST['del_to_customer_name'] ?? null, + $_POST['del_to_address_1'] ?? null, + $_POST['del_to_address_2'] ?? null, + $_POST['del_to_address_3'] ?? null, + $_POST['del_to_address_4'] ?? null, + $_POST['del_to_postcode'] ?? null, + $_POST['del_to_phone'] ?? null, + $_POST['del_to_area_code'] ?? null, + $_POST['del_to_transportation_code'] ?? null, + $_POST['del_to_stock_point_code'] ?? null, + $_POST['del_to_recipient_section'] ?? null, + $_POST['del_to_country_code'] ?? null, + $_POST['del_to_shipment_flag'] ?? null, + $_POST['del_to_transport_days'] ?? null, + $_POST['del_to_shipment_condition_category'] ?? null, + isset($_POST['del_to_transport_service_exist']) ? implode(',', $_POST['del_to_transport_service_exist']) : null, + $_POST['del_to_shipment_condition_place'] ?? null, + $_POST['doc_req_do'] ?? null, + $_POST['doc_req_packing_list'] ?? null, + $_POST['doc_req_invoice'] ?? null, + $_POST['doc_req_export_permit'] ?? null, + $_POST['doc_req_po_do_inv'] ?? null, + $_POST['doc_req_do_inv'] ?? null, + $_POST['doc_req_others'] ?? null, + $_POST['pack_req_one_line_carton'] ?? null, + $_POST['pack_req_one_item_carton'] ?? null, + $_POST['pack_req_one_item_pocket'] ?? null, + $_POST['pack_req_thomson_label'] ?? null, + $_POST['pack_req_contents_label'] ?? null, + $_POST['pack_req_delivery_schedule'] ?? null, + $_POST['forwarder_name'] ?? null, + $_POST['forwarder_code'] ?? null, + $_POST['forwarder_address'] ?? null, + $_POST['forwarder_contact_person'] ?? null, + $_POST['forwarder_phone'] ?? null, + $_POST['forwarder_fax'] ?? null, + $_POST['forwarder_delivery_method'] ?? null, + $_POST['forwarder_delivery_timings'] ?? null, + $_POST['forwarder_delivery_requirements'] ?? null, + $_POST['special_instructions_shipping_mark'] ?? null, + $_POST['special_instructions_fax_documents'] ?? null, + $_POST['special_instructions_details'] ?? null, + $_POST['special_instructions_attention_to'] ?? null, + $_POST['special_instructions_fax_number'] ?? null, + $_POST['remarks'] ?? null + ]); + $customer_application_id = $pdo->lastInsertId(); + + // 2. Insert into customer_contacts + if (isset($_POST['contact']) && is_array($_POST['contact'])) { + $stmt_contact = $pdo->prepare( + 'INSERT INTO customer_contacts (customer_application_id, name, email, phone, is_primary) VALUES (?, ?, ?, ?, ?)' + ); + foreach ($_POST['contact'] as $index => $contact) { + $is_primary = (isset($contact['is_primary']) && $contact['is_primary'] == '1'); + $stmt_contact->execute([ + $customer_application_id, + $contact['name'], + $contact['email'], + $contact['phone'], + $is_primary ? 1 : 0 + ]); + } + } + + // 3. Insert into customer_addresses + if (isset($_POST['address']) && is_array($_POST['address'])) { + $stmt_address = $pdo->prepare( + 'INSERT INTO customer_addresses (customer_application_id, address_type, address_line_1, address_line_2, city, state, postal_code, country) VALUES (?, ?, ?, ?, ?, ?, ?, ?)' + ); + foreach ($_POST['address'] as $address) { + $stmt_address->execute([ + $customer_application_id, + $address['type'], + $address['line1'], + $address['line2'], + $address['city'], + $address['state'], + $address['postal_code'], + $address['country'] + ]); + } + } + + // 4. Insert into customer_trade_references + if (isset($_POST['trade_reference']) && is_array($_POST['trade_reference'])) { + $stmt_trade_ref = $pdo->prepare( + 'INSERT INTO customer_trade_references (customer_application_id, company_name, contact_person, email, phone, address) VALUES (?, ?, ?, ?, ?, ?)' + ); + foreach ($_POST['trade_reference'] as $trade_ref) { + $stmt_trade_ref->execute([ + $customer_application_id, + $trade_ref['company_name'], + $trade_ref['contact_person'], + $trade_ref['email'], + $trade_ref['phone'], + $trade_ref['address'] + ]); + } + } + + // 5. Insert into customer_bank_details + if (isset($_POST['bank_name'])) { + $stmt_bank = $pdo->prepare( + 'INSERT INTO customer_bank_details (customer_application_id, bank_name, branch, bsb_number, account_number, account_name) VALUES (?, ?, ?, ?, ?, ?)' + ); + $stmt_bank->execute([ + $customer_application_id, + $_POST['bank_name'], + $_POST['branch'], + $_POST['bsb_number'], + $_POST['account_number'], + $_POST['account_name'] + ]); + } + + // 6. Handle Signature and Declaration + $signature_path = null; + if (isset($_POST['signature']) && !empty($_POST['signature'])) { + $signature_data = $_POST['signature']; + list($type, $data) = explode(';', $signature_data); + list(, $data) = explode(',', $data); + $data = base64_decode($data); + $signature_filename = 'signature_' . $application_id . '_' . time() . '.png'; + $signature_path = 'uploads/' . $signature_filename; + file_put_contents($signature_path, $data); + } + + $stmt_declar = $pdo->prepare('UPDATE customer_applications SET declaration_text = ?, signature_path = ? WHERE id = ?'); + $stmt_declar->execute([ + $_POST['declaration_text'], + $signature_path, + $customer_application_id + ]); + + $pdo->commit(); + + // Redirect to dashboard with success message + $_SESSION['flash_message'] = [ + 'type' => 'success', + 'message' => 'Customer application (' . $application_id . ') submitted successfully!' + ]; + header('Location: index.php'); + exit(); + +} catch (PDOException $e) { + $pdo->rollBack(); + error_log('Application submission failed: ' . $e->getMessage()); + + // Redirect back to form with error message + $_SESSION['flash_message'] = [ + 'type' => 'danger', + 'message' => 'There was an error submitting your application. Please try again. ' . $e->getMessage() + ]; + header('Location: new_application.php'); + exit(); +} diff --git a/update_application.php b/update_application.php new file mode 100644 index 0000000..c9f4d22 --- /dev/null +++ b/update_application.php @@ -0,0 +1,179 @@ +prepare($sql); + $stmt->execute([$customer_id, $file_name, $unique_file_name]); + $_SESSION['success_message'] = "File uploaded successfully."; + } catch (PDOException $e) { + $_SESSION['error_message'] = "Error saving file info: " . $e->getMessage(); + } + } else { + $_SESSION['error_message'] = "Error moving uploaded file."; + } + } else { + $_SESSION['error_message'] = "Error uploading file: " . $_FILES['file_upload']['error']; + } + header("Location: edit_application.php?id=" . $customer_id); + exit(); +} + +// Handle Form Submission +if (isset($_POST['save_changes'])) { + try { + $pdo->beginTransaction(); + + // 1. Update Customer Table + $sql = "UPDATE customer_applications SET + company_name = ?, + company_website = ?, + company_phone = ?, + sales_owner = ?, + payment_terms = ?, + tags = ?, + notes = ? + WHERE id = ?"; + $stmt = $pdo->prepare($sql); + $stmt->execute([ + $_POST['company_name'], + $_POST['company_website'], + $_POST['company_phone'], + $_POST['sales_owner'], + $_POST['payment_terms'], + $_POST['tags'], + $_POST['notes'], + $customer_id + ]); + + // 2. Process Contacts + $submitted_contact_ids = []; + if (isset($_POST['contact']) && is_array($_POST['contact'])) { + // Reset primary contact + $reset_primary_stmt = $pdo->prepare("UPDATE customer_contacts SET is_primary = 0 WHERE customer_application_id = ?"); + $reset_primary_stmt->execute([$customer_id]); + + foreach ($_POST['contact'] as $index => $contact_data) { + $contact_id = $contact_data['id'] ?? null; + $is_primary = (isset($contact_data['is_primary']) && $contact_data['is_primary'] == '1') ? 1 : 0; + + if ($contact_id) { // Existing contact + $sql = "UPDATE customer_contacts SET name = ?, email = ?, phone = ?, is_primary = ? WHERE id = ? AND customer_application_id = ?"; + $stmt = $pdo->prepare($sql); + $stmt->execute([$contact_data['name'], $contact_data['email'], $contact_data['phone'], $is_primary, $contact_id, $customer_id]); + $submitted_contact_ids[] = $contact_id; + } else { // New contact + $sql = "INSERT INTO customer_contacts (customer_application_id, name, email, phone, is_primary) VALUES (?, ?, ?, ?, ?)"; + $stmt = $pdo->prepare($sql); + $stmt->execute([$customer_id, $contact_data['name'], $contact_data['email'], $contact_data['phone'], $is_primary]); + $submitted_contact_ids[] = $pdo->lastInsertId(); + } + } + } + + // 3. Delete Removed Contacts + $stmt = $pdo->prepare("SELECT id FROM customer_contacts WHERE customer_application_id = ?"); + $stmt->execute([$customer_id]); + $existing_contact_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + $contacts_to_delete = array_diff($existing_contact_ids, $submitted_contact_ids); + + if (!empty($contacts_to_delete)) { + $sql = "DELETE FROM customer_contacts WHERE id IN (" . implode(',', array_fill(0, count($contacts_to_delete), '?')) . ") AND customer_application_id = ?"; + $stmt = $pdo->prepare($sql); + $params = array_merge(array_values($contacts_to_delete), [$customer_id]); + $stmt->execute($params); + } + + // 4. Process Addresses + $submitted_address_ids = []; + if (isset($_POST['address']) && is_array($_POST['address'])) { + foreach ($_POST['address'] as $address_data) { + $address_id = $address_data['id'] ?? null; + + if ($address_id) { // Existing address + $sql = "UPDATE customer_addresses SET address_type = ?, address_line_1 = ?, address_line_2 = ?, city = ?, state = ?, postal_code = ?, country = ? WHERE id = ? AND customer_application_id = ?"; + $stmt = $pdo->prepare($sql); + $stmt->execute([$address_data['type'], $address_data['line1'], $address_data['line2'], $address_data['city'], $address_data['state'], $address_data['postal_code'], $address_data['country'], $address_id, $customer_id]); + $submitted_address_ids[] = $address_id; + } else { // New address + $sql = "INSERT INTO customer_addresses (customer_application_id, address_type, address_line_1, address_line_2, city, state, postal_code, country) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + $stmt = $pdo->prepare($sql); + $stmt->execute([$customer_id, $address_data['type'], $address_data['line1'], $address_data['line2'], $address_data['city'], $address_data['state'], $address_data['postal_code'], $address_data['country']]); + $submitted_address_ids[] = $pdo->lastInsertId(); + } + } + } + + // 5. Delete Removed Addresses + $stmt = $pdo->prepare("SELECT id FROM customer_addresses WHERE customer_application_id = ?"); + $stmt->execute([$customer_id]); + $existing_address_ids = $stmt->fetchAll(PDO::FETCH_COLUMN); + $addresses_to_delete = array_diff($existing_address_ids, $submitted_address_ids); + + if (!empty($addresses_to_delete)) { + $sql = "DELETE FROM customer_addresses WHERE id IN (" . implode(',', array_fill(0, count($addresses_to_delete), '?')) . ") AND customer_application_id = ?"; + $stmt = $pdo->prepare($sql); + $params = array_merge(array_values($addresses_to_delete), [$customer_id]); + $stmt->execute($params); + } + + // Check if the application was reverted and resubmit it + $stmt_status = $pdo->prepare("SELECT status FROM customer_applications WHERE id = ?"); + $stmt_status->execute([$customer_id]); + $current_status = $stmt_status->fetchColumn(); + + if ($current_status === 'REVERTED') { + $stmt_resubmit = $pdo->prepare("UPDATE customer_applications SET status = 'pending_approval' WHERE id = ?"); + $stmt_resubmit->execute([$customer_id]); + $_SESSION['message'] = "Application resubmitted for approval."; + $_SESSION['message_type'] = 'success'; + } else { + $_SESSION['message'] = "Application #{$customer_id} updated successfully."; + $_SESSION['message_type'] = 'success'; + } + + $pdo->commit(); + + } catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + $_SESSION['message'] = "Error updating application: " . $e->getMessage(); + $_SESSION['message_type'] = 'danger'; + } + + header("Location: view_application.php?id=" . $customer_id); + exit(); +} + +header("Location: index.php"); +exit(); \ No newline at end of file diff --git a/update_profile.php b/update_profile.php new file mode 100644 index 0000000..a2a040a --- /dev/null +++ b/update_profile.php @@ -0,0 +1,67 @@ + 'danger', 'message' => 'All fields are required.']; + header('Location: profile.php'); + exit(); + } + + if ($new_password !== $confirm_new_password) { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'New passwords do not match.']; + header('Location: profile.php'); + exit(); + } + + try { + $pdo = db(); + + // 2. Fetch current user from DB to verify current password + $stmt = $pdo->prepare("SELECT password FROM users WHERE id = ?"); + $stmt->execute([$_SESSION['user']['id']]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user || !password_verify($current_password, $user['password'])) { + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'Incorrect current password.']; + header('Location: profile.php'); + exit(); + } + + // 3. Hash new password + $new_password_hash = password_hash($new_password, PASSWORD_DEFAULT); + + // 4. Update password in the database + $stmt = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?"); + $stmt->execute([$new_password_hash, $_SESSION['user']['id']]); + + $_SESSION['flash_message'] = ['type' => 'success', 'message' => 'Password updated successfully.']; + header('Location: profile.php'); + exit(); + + } catch (PDOException $e) { + // Log error and show a generic message + error_log("Password update failed: " . $e->getMessage()); + $_SESSION['flash_message'] = ['type' => 'danger', 'message' => 'An error occurred. Please try again.']; + header('Location: profile.php'); + exit(); + } + +} else { + // Redirect if not a POST request + header('Location: profile.php'); + exit(); +} diff --git a/update_status.php b/update_status.php new file mode 100644 index 0000000..b112239 --- /dev/null +++ b/update_status.php @@ -0,0 +1,105 @@ +prepare("SELECT approval_level, current_approver_role_id FROM customer_applications WHERE id = ?"); + $stmt_app->execute([$application_id]); + $application = $stmt_app->fetch(PDO::FETCH_ASSOC); + + if (!$application) { + $_SESSION['message'] = 'Application not found.'; + $_SESSION['message_type'] = 'danger'; + header('Location: index.php'); + exit(); + } + + $current_level = $application['approval_level']; + $current_approver_role_id = $application['current_approver_role_id']; + $user_role_id = $_SESSION['user']['role_id']; + + // Check if the user has permission to approve at this level + if ($user_role_id != $current_approver_role_id) { + $_SESSION['message'] = 'You do not have permission to approve this application at the current level.'; + $_SESSION['message_type'] = 'danger'; + header('Location: view_application.php?id=' . $application_id); + exit(); + } + + $pdo->beginTransaction(); + + // Insert into application_approvals + $stmt_approval = $pdo->prepare( + 'INSERT INTO application_approvals (application_id, approver_id, approval_level, status, comments) VALUES (?, ?, ?, ?, ?)' + ); + $stmt_approval->execute([$application_id, $_SESSION['user']['id'], $current_level, $new_status, $comments]); + + if ($new_status === 'approved') { + $next_level = $current_level + 1; + if ($next_level <= 7) { + // Get the role ID for the next approval level + $stmt_role = $pdo->prepare("SELECT id FROM roles WHERE name = ?"); + $stmt_role->execute(['Approver Level ' . $next_level]); + $next_approver_role = $stmt_role->fetch(PDO::FETCH_ASSOC); + $next_approver_role_id = $next_approver_role ? $next_approver_role['id'] : null; + + $stmt_update = $pdo->prepare( + 'UPDATE customer_applications SET approval_level = ?, current_approver_role_id = ? WHERE id = ?' + ); + $stmt_update->execute([$next_level, $next_approver_role_id, $application_id]); + $_SESSION['message'] = "Application approved and moved to the next level."; + + } else { + $stmt_update = $pdo->prepare( + "UPDATE customer_applications SET status = 'APPROVED', approval_level = 7, current_approver_role_id = NULL WHERE id = ?" + ); + $stmt_update->execute([$application_id]); + $_SESSION['message'] = "Application approved."; + } + } elseif ($new_status === 'reverted') { // Reverted + $stmt_update = $pdo->prepare( + "UPDATE customer_applications SET status = 'REVERTED' WHERE id = ?" + ); + $stmt_update->execute([$application_id]); + $_SESSION['message'] = "Application reverted to the applicant for amendments."; + } else { // Rejected + $stmt_update = $pdo->prepare( + "UPDATE customer_applications SET status = 'REJECTED', current_approver_role_id = NULL WHERE id = ?" + ); + $stmt_update->execute([$application_id]); + $_SESSION['message'] = "Application rejected."; + } + + $pdo->commit(); + $_SESSION['message_type'] = 'success'; + +} catch (PDOException $e) { + $pdo->rollBack(); + $_SESSION['message'] = 'Database error: ' . $e->getMessage(); + $_SESSION['message_type'] = 'danger'; +} + +header('Location: view_application.php?id=' . $application_id); +exit(); diff --git a/upload_file.php b/upload_file.php new file mode 100644 index 0000000..8e10607 --- /dev/null +++ b/upload_file.php @@ -0,0 +1,72 @@ + 5 * 1024 * 1024) { // 5MB limit + $_SESSION['message'] = 'File is too large. Maximum size is 5MB.'; + $_SESSION['message_type'] = 'danger'; + header('Location: view_application.php?id=' . $application_id); + exit(); + } + + if (move_uploaded_file($file['tmp_name'], $upload_path)) { + try { + $pdo = db(); + $stmt = $pdo->prepare("INSERT INTO application_files (application_id, original_filename, stored_filename) VALUES (?, ?, ?)"); + $stmt->execute([$application_id, $original_filename, $stored_filename]); + + $_SESSION['message'] = 'File uploaded successfully.'; + $_SESSION['message_type'] = 'success'; + } catch (PDOException $e) { + // In a real app, log this error + $_SESSION['message'] = 'Database error while saving file information.'; + $_SESSION['message_type'] = 'danger'; + // Optionally, delete the uploaded file if DB insert fails + unlink($upload_path); + } + } else { + $_SESSION['message'] = 'Failed to move uploaded file.'; + $_SESSION['message_type'] = 'danger'; + } + } else { + $_SESSION['message'] = 'File upload error. Please try again.'; + $_SESSION['message_type'] = 'danger'; + } + + header('Location: view_application.php?id=' . $application_id); + exit(); + +} else { + header('Location: index.php'); + exit(); +} diff --git a/uploads/.htaccess b/uploads/.htaccess new file mode 100644 index 0000000..3418e55 --- /dev/null +++ b/uploads/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/view_application.php b/view_application.php new file mode 100644 index 0000000..98a0842 --- /dev/null +++ b/view_application.php @@ -0,0 +1,408 @@ +prepare('SELECT * FROM customer_applications WHERE id = ?'); +$stmt->execute([$application_id]); +$application = $stmt->fetch(); + +if (!$application) { + die('Application not found.'); +} + +// Fetch contacts +$stmt = $pdo->prepare('SELECT * FROM customer_contacts WHERE customer_application_id = ?'); +$stmt->execute([$application_id]); +$contacts = $stmt->fetchAll(); + +// Fetch addresses +$stmt = $pdo->prepare('SELECT * FROM customer_addresses WHERE customer_application_id = ?'); +$stmt->execute([$application_id]); +$addresses = $stmt->fetchAll(); + +// Fetch trade references +$stmt = $pdo->prepare('SELECT * FROM customer_trade_references WHERE customer_application_id = ?'); +$stmt->execute([$application_id]); +$trade_references = $stmt->fetchAll(); + +// Fetch bank details +$stmt = $pdo->prepare('SELECT * FROM customer_bank_details WHERE customer_application_id = ?'); +$stmt->execute([$application_id]); +$bank_details = $stmt->fetch(); + +// Fetch principals +$stmt = $pdo->prepare('SELECT * FROM customer_principals WHERE customer_application_id = ?'); +$stmt->execute([$application_id]); +$principals = $stmt->fetchAll(); + +?> + + + + + + View Application - <?= htmlspecialchars($application['application_id']) ?> + + + + +
+

Application Details:

+ + +
+
Company Details
+
+

Company Name:

+

Company Website:

+

Company Phone:

+

Sales Owner:

+

Payment Terms:

+

Tags:

+

Notes:

+
+
+ + +
+
Financial & Credit Details
+
+

Major Product:

+

Capital:

+

Main Shareholders:

+

Number of Employees:

+

Payment Terms AR:

+
+
P/L Information
+

Year:

+

Net Sales:

+

Net Income Margin:

+

Net Income Margin Ratio: %

+
+
ROHM Sales Target (KUS$)
+

This Year:

+

Next Year:

+

After the Next:

+
+
Credit Information
+

Rank:

+

Limit: KUS$

+
+
Credit Research
+

Status:

+

Reason:

+
+

Tax Rate/Area (GST TYPE):

+

Billing Type:

+
+
+ + +
+
Del-To Informations
+
+

Del-To Code:

+

Abbreviation of Delivery-To:

+

Customer Name:

+

Address:
+
+ +
+ + +
+ + +
+ +

+

Postcode (Zip Code):

+

Phone Number:

+

Area Code:

+

Transportation Code:

+

Stock Point Code:

+

Recipient Section:

+

Country Code:

+

Shipment Flag:

+

Number of Date for Transport:

+

Shipment Condition: Category:

+

Transport Service Exist:

+

Shipment Condition: Place:

+
+
+ + +
+
SOP
+
+
+
+
Document Requirements
+

D/O*:

+

Packing List*:

+

Invoice*:

+

Export Permit:

+

1 PO/1 DO/1 INV:

+

1 DO/1 INV:

+

Others:

+
+
+
Packing Requirements
+

One Line One Carton:

+

One Item One Carton:

+

One Item One Pocket:

+

Special Thomson Label:

+

Special Contents Label:

+

Delivery schedule:

+
+
+
+
Forwarder Information
+

Forwarder Name:

+

Forwarder Code:

+

Forwarder Address:

+

Contact Person:

+

Phone:

+

Fax:

+

Delivery Method:

+

Delivery Timings:

+

Delivery Requirements:

+
+
Special Instructions
+

Shipping Mark:

+

Fax Documents:

+

Details:

+

Attention To:

+

Fax Number:

+

Remarks:

+
+
+ + +
+
Contacts
+
+ +
+

Name:

+

Email:

+

Phone:

+
+ +
+
+ + +
+
Addresses
+
+ +
+

Type:

+

Address:
+
+ +
+ + ,
+ +

+
+ +
+
+ + +
+
Trade References
+
+ +
+

Company Name:

+

Contact Person:

+

Email:

+

Phone:

+

Address:

+
+ +
+
+ + +
+
Bank Details
+
+

Bank Name:

+

Branch:

+

BSB Number:

+

Account Number:

+

Account Name:

+
+
+ + +
+
Declaration and Signature
+
+

Declaration:

+ +

Signature:

+ Signature + +

No signature provided.

+ +
+
+ + +
+
Application Summary
+
+
Company Details
+
+
+

Customer Name:

+

Major Product of Customer:

+

Capital:

+

Main Shareholders:

+

Number of Employee:

+

Payment Terms AR:

+
+
+
Customer's P/L Information
+

Year:

+

Net Sales:

+

Net Income Margin:

+

Net Income Margin Ratio(%):

+
+
+
+
+
+
ROHM Sales Target
+

This Year: KUS$

+

Next Year: KUS$

+

After the Next: KUS$

+
+
+
Credit Information
+

Rank:

+

Limit (KUS$):

+
+
+
+
+
+
Credit Research
+

Status:

+

Reason:

+
+
+
Other Details
+

Tax Rate/Area (GST TYPE):

+

Billing Type:

+
+
+
+
+ +
+
Principals Summary
+
+ +
+

Name:

+

Title:

+

Ownership %:

+
+ +
+
+ +
+
Contacts Summary
+
+ +
+

Name:

+

Email:

+

Phone:

+
+ +
+
+ +
+
Trade References Summary
+
+ +
+

Company Name:

+

Contact Person:

+

Email:

+

Phone:

+
+ +
+
+ +
+
Bank Reference Summary
+
+

Bank Name:

+

Branch:

+

Account Number:

+

Account Name:

+
+
+ + + +
+
Approval Action
+
+
+ + + +
+
+
+ + +
+ + \ No newline at end of file diff --git a/view_applications.php b/view_applications.php new file mode 100644 index 0000000..91d55e8 --- /dev/null +++ b/view_applications.php @@ -0,0 +1,71 @@ +query('SELECT id, application_id, company_name, status, created_at FROM customer_applications ORDER BY created_at DESC'); +$applications = $stmt->fetchAll(); + +?> + + + + + + View Applications + + + + +
+

Submitted Applications

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Application IDCompany NameStatusSubmitted AtAction
No applications found.
+ View +
+
+ +