« Back to History
add.php
|
20260722_120325.php
Initial Domain Snapshot
Copy Code
<?php require_once __DIR__ . '/../includes/bootstrap.php'; require_once __DIR__ . '/../includes/header.php'; require_once __DIR__ . '/../includes/sidebar.php'; $message = ''; $errMessage = ''; // Default values for License Configuration $default_license_type = 'Trial'; $default_trial_days = 14; $default_start_date = date('Y-m-d'); $default_end_date = date('Y-m-d', strtotime('+' . $default_trial_days . ' days')); // Fetch active customers from tenant_master try { $tenantStmt = $pdo->query("SELECT tenant_id, tenant_name, tenant_code FROM tenant_master WHERE status = 'ACTIVE' ORDER BY tenant_name ASC"); $tenants = $tenantStmt->fetchAll(); } catch (PDOException $e) { $tenants = []; } // Backend Action Handler if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = $_POST['action'] ?? ''; // Extract Form Fields $tenant_id = (int)($_POST['tenant_id'] ?? 0); $company_name = trim($_POST['company_name'] ?? ''); $company_code = strtoupper(trim($_POST['company_code'] ?? '')); $company_address = trim($_POST['company_address'] ?? ''); $primary_domain = trim($_POST['primary_domain'] ?? ''); $deployment_type = trim($_POST['deployment_type'] ?? 'SHARED'); $db_host = trim($_POST['db_host'] ?? 'localhost'); $db_port = trim($_POST['db_port'] ?? '3306'); $db_name = trim($_POST['db_name'] ?? ''); $db_user = trim($_POST['db_user'] ?? ''); $db_pass = trim($_POST['db_pass'] ?? ''); $lic_type = trim($_POST['license_type'] ?? 'Trial'); $lic_days = (int)($_POST['trial_days'] ?? 14); $lic_start_date = trim($_POST['license_start_date'] ?? date('Y-m-d')); $lic_end_date = date('Y-m-d H:i:s', strtotime($lic_start_date . ' +' . $lic_days . ' days')); if ($action === 'test_connection') { try { if (empty($db_host) || empty($db_name) || empty($db_user)) { throw new Exception("Database connection test requires Host, Name, and Username."); } $dsn = "mysql:host={$db_host};port={$db_port};dbname={$db_name};charset=utf8mb4"; $test_pdo = new PDO($dsn, $db_user, $db_pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::TIMEOUT => 5 ]); $message = "SUCCESS: Connection established successfully. (Database ERP table verification skipped/ignored)."; } catch (Exception $e) { $errMessage = "CONNECTION FAILED: " . $e->getMessage(); } } elseif ($action === 'create_company') { if ($tenant_id > 0 && !empty($company_name) && !empty($company_code) && !empty($company_address) && !empty($primary_domain) && !empty($db_name) && !empty($db_user)) { try { // 1. Standalone Target Database Connection Check try { $dsn = "mysql:host={$db_host};port={$db_port};dbname={$db_name};charset=utf8mb4"; $target_pdo = new PDO($dsn, $db_user, $db_pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5 ]); } catch (PDOException $e) { throw new Exception("Target Database Connection Failed: " . $e->getMessage()); } // --- BEGIN TRANSACTION --- $pdo->beginTransaction(); // 2. Duplicate Company Code Check $checkCode = $pdo->prepare("SELECT id FROM company_master WHERE company_code = ? LIMIT 1"); $checkCode->execute([$company_code]); if ($checkCode->fetch()) { throw new Exception("Provisioning Blocked: Company code already exists in the system index."); } // 3. Duplicate Database Check $checkDb = $pdo->prepare("SELECT id FROM company_master WHERE db_name = ? AND db_host = ? LIMIT 1"); $checkDb->execute([$db_name, $db_host]); if ($checkDb->fetch()) { throw new Exception("Provisioning Blocked: A company profile is already bound to this database target."); } // 4. Duplicate Domain Check $checkDomain = $pdo->prepare("SELECT id FROM company_master WHERE primary_domain = ? LIMIT 1"); $checkDomain->execute([$primary_domain]); if ($checkDomain->fetch()) { throw new Exception("Provisioning Blocked: Target Primary Domain mapping already exists."); } // 5. Insert company_master Profile $stmt = $pdo->prepare("INSERT INTO company_master ( tenant_id, company_name, company_code, company_address, deployment_type, primary_domain, db_host, db_name, db_user, db_pass, setup_status, status ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'COMPLETED', 'ACTIVE')"); $stmt->execute([ $tenant_id, $company_name, $company_code, $company_address, $deployment_type, $primary_domain, $db_host, $db_name, $db_user, $db_pass ]); $new_company_id = $pdo->lastInsertId(); // 6. Create Trial License Record $licStmt = $pdo->prepare("INSERT INTO trial_licenses (company_id, start_date, end_date, status) VALUES (?, ?, ?, 'ACTIVE')"); $licStmt->execute([ $new_company_id, date('Y-m-d H:i:s', strtotime($lic_start_date)), $lic_end_date ]); // ======================================================== // TARGET CLIENT DATABASE STRUCTURAL PROVISIONING ENGINE // ======================================================== // A. Create client 'companies' table if not exists and insert detail $target_pdo->exec("CREATE TABLE IF NOT EXISTS `companies` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `short_name` varchar(100) DEFAULT NULL, `address` text DEFAULT NULL, `location` varchar(255) DEFAULT NULL, `login_code` varchar(50) DEFAULT NULL, `is_active` tinyint(4) DEFAULT 1, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); $targetCompanyStmt = $target_pdo->prepare("INSERT INTO companies (id, name, short_name, address, location, login_code, is_active) VALUES (?, ?, ?, ?, ?, ?, 1)"); $targetCompanyStmt->execute([ $new_company_id, $company_name, $company_code, $company_address, 'Surat', $company_code ]); // B. Create client 'users' table if not exists and insert default Owner user $target_pdo->exec("CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `employee_id` int(11) DEFAULT NULL, `company_id` int(11) DEFAULT NULL, `department_id` int(11) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `username` varchar(150) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `dp_url` varchar(255) DEFAULT NULL, `password_hash` varchar(255) DEFAULT NULL, `password_plain` varchar(100) DEFAULT NULL, `role` varchar(50) DEFAULT NULL, `is_active` tinyint(4) DEFAULT 1, `created_at` datetime DEFAULT current_timestamp(), `mobile` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); // Default Owner details $default_username = strtolower($company_code) . "_owner"; $default_plain_pass = "12345678"; $default_hash_pass = password_hash($default_plain_pass, PASSWORD_BCRYPT); $targetUserStmt = $target_pdo->prepare("INSERT INTO users (company_id, name, username, password_hash, password_plain, role, is_active, created_at) VALUES (?, ?, ?, ?, ?, 'Owner', 1, NOW())"); $targetUserStmt->execute([ $new_company_id, $company_name . " Administrator", $default_username, $default_hash_pass, $default_plain_pass ]); // --- COMMIT TRANSACTION --- $pdo->commit(); $message = "Company Created Successfully.\nClient database records provisioned.\nDefault Owner Account:\nUsername: {$default_username}\nPassword: {$default_plain_pass}"; // Clear post-submit fields for refreshing console state $_POST = []; } catch (Exception $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $errMessage = "Provisioning Aborted: " . $e->getMessage(); } } else { $errMessage = "Validation Failure: All requested parameters must be completely populated."; } } } ?> <div class="container-fluid px-4 py-3"> <!-- Header Block --> <div class="d-flex justify-content-between align-items-center mb-4 border-bottom pb-3"> <div> <h3 class="m-0 fw-bold text-dark tracking-tight">Company Provisioning Console</h3> <small class="text-muted">Mister Manager Cloud ERP Core Orchestrator Infrastructure</small> </div> <div> <a href="/admin/company/list.php" class="btn btn-sm btn-outline-secondary px-3"> <i class="bi bi-list-ul me-1"></i> Company Registry List </a> </div> </div> <!-- Feedback Alerts --> <?php if (!empty($message)): ?> <div class="alert alert-success border-0 shadow-sm d-flex align-items-start small p-3 mb-4" role="alert"> <div class="me-2"> <strong class="d-block fw-bold text-success">PROVISIONING COMPLETE</strong> <span style="white-space: pre-line;"><?= htmlspecialchars($message) ?></span> </div> </div> <?php endif; ?> <?php if (!empty($errMessage)): ?> <div class="alert alert-danger border-0 shadow-sm d-flex align-items-start small p-3 mb-4" role="alert"> <div class="me-2"> <strong class="d-block fw-bold text-danger">PROVISIONING ERROR</strong> <?= htmlspecialchars($errMessage) ?> </div> </div> <?php endif; ?> <form action="add.php" method="POST" id="provisioningForm"> <input type="hidden" name="action" id="formAction" value="create_company"> <div class="row g-4"> <!-- Left Orchestrator Layout Form Blocks --> <div class="col-xl-8 col-lg-7"> <!-- Section 1: Customer Linkage --> <div class="card border-0 shadow-sm mb-4"> <div class="card-header bg-white py-3 border-0"> <span class="badge bg-secondary mb-1">SECTION 01</span> <h5 class="m-0 fw-bold text-dark">Customer/Tenant Linkage</h5> </div> <div class="card-body pt-0"> <div class="row"> <div class="col-md-12"> <label class="form-label small fw-semibold text-secondary">Target Active Account / Parent Node *</label> <select name="tenant_id" class="form-select form-select-sm" required> <option value="">-- Bind Active Customer Profile --</option> <?php foreach ($tenants as $t): ?> <option value="<?= $t['tenant_id'] ?>" <?= (($_POST['tenant_id'] ?? '') == $t['tenant_id']) ? 'selected' : '' ?>> <?= htmlspecialchars($t['tenant_name']) ?> [<?= htmlspecialchars($t['tenant_code']) ?>] </option> <?php endforeach; ?> </select> </div> </div> </div> </div> <!-- Section 2: Profile Metrics --> <div class="card border-0 shadow-sm mb-4"> <div class="card-header bg-white py-3 border-0"> <span class="badge bg-secondary mb-1">SECTION 02</span> <h5 class="m-0 fw-bold text-dark">Company Information Matrix</h5> </div> <div class="card-body pt-0"> <div class="row g-3"> <div class="col-md-6"> <label class="form-label small fw-semibold text-secondary">Corporate Entities Legal Name *</label> <input type="text" name="company_name" id="company_name" value="<?= htmlspecialchars($_POST['company_name'] ?? '') ?>" class="form-control form-control-sm" required> </div> <div class="col-md-6"> <label class="form-label small fw-semibold text-secondary">System Index Identifier Code *</label> <input type="text" name="company_code" id="company_code" placeholder="e.g. MSR01" value="<?= htmlspecialchars($_POST['company_code'] ?? '') ?>" class="form-control form-control-sm text-uppercase" required> </div> <div class="col-md-8"> <label class="form-label small fw-semibold text-secondary">Primary Subdomain / Target URL Mapping *</label> <input type="text" name="primary_domain" id="primary_domain" placeholder="tenant.mistermanager.in" value="<?= htmlspecialchars($_POST['primary_domain'] ?? '') ?>" class="form-control form-control-sm" required> </div> <div class="col-md-4"> <label class="form-label small fw-semibold text-secondary">Deployment Type Meta *</label> <select name="deployment_type" class="form-select form-select-sm" required> <option value="SHARED" <?= (($_POST['deployment_type'] ?? '') === 'SHARED') ? 'selected' : '' ?>>SHARED ENGINE</option> <option value="DEDICATED" <?= (($_POST['deployment_type'] ?? '') === 'DEDICATED') ? 'selected' : '' ?>>DEDICATED INSTANCE</option> </select> </div> <div class="col-md-12"> <label class="form-label small fw-semibold text-secondary">Registered Corporate Headquarters Address *</label> <textarea name="company_address" class="form-control form-control-sm" rows="2" required><?= htmlspecialchars($_POST['company_address'] ?? '') ?></textarea> </div> </div> </div> </div> <!-- Section 3: Data Layer Target --> <div class="card border-0 shadow-sm mb-4"> <div class="card-header bg-white py-3 border-0"> <span class="badge bg-secondary mb-1">SECTION 03</span> <h5 class="m-0 fw-bold text-dark">Isolated Database Topology</h5> </div> <div class="card-body pt-0"> <div class="row g-3"> <div class="col-md-9"> <label class="form-label small fw-semibold text-secondary">Target Database Host Cluster *</label> <input type="text" name="db_host" id="db_host" value="<?= htmlspecialchars($_POST['db_host'] ?? 'localhost') ?>" class="form-control form-control-sm" required> </div> <div class="col-md-3"> <label class="form-label small fw-semibold text-secondary">Port *</label> <input type="number" name="db_port" id="db_port" value="<?= htmlspecialchars($_POST['db_port'] ?? '3306') ?>" class="form-control form-control-sm" required> </div> <div class="col-md-12"> <label class="form-label small fw-semibold text-secondary">Isolated Target Database Name *</label> <input type="text" name="db_name" id="db_name" value="<?= htmlspecialchars($_POST['db_name'] ?? '') ?>" class="form-control form-control-sm" required> </div> <div class="col-md-6"> <label class="form-label small fw-semibold text-secondary">Data Layer Username Access Key *</label> <input type="text" name="db_user" id="db_user" value="<?= htmlspecialchars($_POST['db_user'] ?? '') ?>" class="form-control form-control-sm" required> </div> <div class="col-md-6"> <label class="form-label small fw-semibold text-secondary">Data Layer Cryptographic Password Key</label> <input type="password" name="db_pass" id="db_pass" value="<?= htmlspecialchars($_POST['db_pass'] ?? '') ?>" class="form-control form-control-sm"> </div> </div> <div class="mt-3 bg-light p-2 rounded d-flex justify-content-between align-items-center"> <span class="small text-muted">Test target server connectivity before saving company credentials.</span> <button type="button" id="btnTestConnection" class="btn btn-xs btn-dark font-monospace px-3 py-1 text-uppercase" style="font-size: 0.75rem;">Test Database Connection</button> </div> </div> </div> <!-- Section 4: License Block --> <div class="card border-0 shadow-sm mb-4"> <div class="card-header bg-white py-3 border-0"> <span class="badge bg-secondary mb-1">SECTION 04</span> <h5 class="m-0 fw-bold text-dark">License Configuration Matrix</h5> </div> <div class="card-body pt-0"> <div class="row g-3"> <div class="col-md-3"> <label class="form-label small fw-semibold text-secondary">License Frame *</label> <input type="text" name="license_type" class="form-control form-control-sm bg-light fw-bold" value="<?= htmlspecialchars($_POST['license_type'] ?? $default_license_type) ?>" readonly> </div> <div class="col-md-3"> <label class="form-label small fw-semibold text-secondary">Window Length (Days) *</label> <input type="number" name="trial_days" id="trial_days" value="<?= htmlspecialchars($_POST['trial_days'] ?? $default_trial_days) ?>" class="form-control form-control-sm" required> </div> <div class="col-md-3"> <label class="form-label small fw-semibold text-secondary">Activation Epoch *</label> <input type="date" name="license_start_date" id="license_start_date" value="<?= htmlspecialchars($_POST['license_start_date'] ?? $default_start_date) ?>" class="form-control form-control-sm" required> </div> <div class="col-md-3"> <label class="form-label small fw-semibold text-secondary">Expiration Matrix Point</label> <input type="text" id="license_end_date_display" class="form-control form-control-sm bg-light" value="<?= $default_end_date ?>" readonly> </div> </div> </div> </div> </div> <!-- Right Monitoring Columns --> <div class="col-xl-4 col-lg-5"> <!-- Section 5: Real-time Runtime Feedback Panels --> <div class="card border-0 shadow-sm bg-dark text-white mb-4 position-sticky" style="top: 20px;"> <div class="card-header bg-transparent border-secondary py-3 text-white-50"> <span class="badge bg-danger mb-1">SECTION 05</span> <h5 class="m-0 fw-bold text-white tracking-wider text-uppercase" style="font-size:0.9rem;">Live Validation Panel</h5> </div> <div class="card-body pt-0"> <ul class="list-group list-group-flush border-0 bg-transparent text-white small" id="liveValidationList"> <li class="list-group-item bg-transparent text-white border-secondary px-0 d-flex justify-content-between align-items-center"> <span><i class="bi bi-circle me-2 text-warning" id="icon_code"></i> Company Code Configured</span> <span class="badge bg-secondary font-monospace" id="badge_code">PENDING</span> </li> <li class="list-group-item bg-transparent text-white border-secondary px-0 d-flex justify-content-between align-items-center"> <span><i class="bi bi-circle me-2 text-warning" id="icon_domain"></i> Domain Target Mapped</span> <span class="badge bg-secondary font-monospace" id="badge_domain">PENDING</span> </li> <li class="list-group-item bg-transparent text-white border-secondary px-0 d-flex justify-content-between align-items-center"> <span><i class="bi bi-circle me-2 text-warning" id="icon_conn"></i> Database Connectivity Link</span> <span class="badge bg-secondary font-monospace" id="badge_conn">UNTESTED</span> </li> <li class="list-group-item bg-transparent text-white border-secondary px-0 d-flex justify-content-between align-items-center"> <span><i class="bi bi-circle me-2 text-muted" id="icon_tables"></i> Structure (Auto-Sync)</span> <span class="badge bg-secondary font-monospace" id="badge_tables">LIVE</span> </li> </ul> </div> <!-- Section 6: Pipeline Context Summary --> <div class="card-footer bg-transparent border-secondary py-3"> <span class="badge bg-primary mb-1">SECTION 06</span> <h6 class="m-0 fw-bold text-white mb-2" style="font-size:0.85rem;">PROVISIONING RUNTIME SUMMARY</h6> <div class="bg-secondary bg-opacity-25 rounded p-2 text-white-50 font-monospace" style="font-size: 0.75rem;"> <div>CUSTOMER : <span class="text-white" id="summary_cust">--</span></div> <div>COMPANY : <span class="text-white" id="summary_comp">--</span></div> <div>DATABASE : <span class="text-white" id="summary_db">--</span></div> <div>LICENSE : <span class="text-white" id="summary_lic">14 Days Trial</span></div> <hr class="my-1 border-secondary"> <div class="d-flex justify-content-between align-items-center"> <span>STATUS INTERFACE</span> <span class="badge bg-warning text-dark px-2 py-0.5" id="summary_status">READY TO TEST</span> </div> </div> </div> <!-- Pipeline Engine Control Switches --> <div class="card-body border-top border-secondary bg-black bg-opacity-25 text-center"> <div class="row gx-2"> <div class="col-6"> <button type="button" id="btnResetConsole" class="btn btn-sm btn-outline-secondary w-100 py-2">Reset Console</button> </div> <div class="col-6"> <button type="submit" id="btnExecuteProvision" class="btn btn-sm btn-success w-100 py-2 fw-bold text-uppercase">Create Company</button> </div> </div> </div> </div> </div> </div> </form> </div> <!-- Runtime Sync Mechanics Script Interceptor --> <script> document.addEventListener('DOMContentLoaded', function() { const form = document.getElementById('provisioningForm'); const formAction = document.getElementById('formAction'); const btnTestConnection = document.getElementById('btnTestConnection'); const btnResetConsole = document.getElementById('btnResetConsole'); // Form field variables to sync summaries const companyNameInput = document.getElementById('company_name'); const companyCodeInput = document.getElementById('company_code'); const domainInput = document.getElementById('primary_domain'); const dbNameInput = document.getElementById('db_name'); const tenantSelect = document.querySelector('select[name="tenant_id"]'); const trialDaysInput = document.getElementById('trial_days'); const startDateInput = document.getElementById('license_start_date'); const endDateDisplay = document.getElementById('license_end_date_display'); // Panel UI variables const summaryCust = document.getElementById('summary_cust'); const summaryComp = document.getElementById('summary_comp'); const summaryDb = document.getElementById('summary_db'); const summaryLic = document.getElementById('summary_lic'); const summaryStatus = document.getElementById('summary_status'); const iconCode = document.getElementById('icon_code'); const badgeCode = document.getElementById('badge_code'); const iconDomain = document.getElementById('icon_domain'); const badgeDomain = document.getElementById('badge_domain'); // Calculation function for End Date Window Frames function calculateEndDate() { const days = parseInt(trialDaysInput.value) || 0; const start = startDateInput.value; if(start) { const dateObj = new Date(start); dateObj.setDate(dateObj.getDate() + days); const yyyy = dateObj.getFullYear(); const mm = String(dateObj.getMonth() + 1).padStart(2, '0'); const dd = String(dateObj.getDate()).padStart(2, '0'); endDateDisplay.value = `${yyyy}-${mm}-${dd}`; summaryLic.innerText = `${days} Days Trial (${endDateDisplay.value})`; } } // Interactive Summary Reflector Engine function syncConsoleSummaryData() { if(tenantSelect.selectedIndex > 0) { summaryCust.innerText = tenantSelect.options[tenantSelect.selectedIndex].text.split('[')[0].trim(); } else { summaryCust.innerText = "--"; } summaryComp.innerText = (companyNameInput.value || "--") + " (" + (companyCodeInput.value || "--") + ")"; summaryDb.innerText = dbNameInput.value || "--"; if (companyCodeInput.value.trim() !== "") { iconCode.className = "bi bi-check-circle-fill text-success"; badgeCode.className = "badge bg-success"; badgeCode.innerText = "CONFIGURED"; } else { iconCode.className = "bi bi-circle text-warning"; badgeCode.className = "badge bg-secondary"; badgeCode.innerText = "PENDING"; } if (domainInput.value.trim() !== "") { iconDomain.className = "bi bi-check-circle-fill text-success"; badgeDomain.className = "badge bg-success"; badgeDomain.innerText = "MAPPED"; } else { iconDomain.className = "bi bi-circle text-warning"; badgeDomain.className = "badge bg-secondary"; badgeDomain.innerText = "PENDING"; } } // Event attachments companyNameInput.addEventListener('input', syncConsoleSummaryData); companyCodeInput.addEventListener('input', syncConsoleSummaryData); domainInput.addEventListener('input', syncConsoleSummaryData); dbNameInput.addEventListener('input', syncConsoleSummaryData); tenantSelect.addEventListener('change', syncConsoleSummaryData); trialDaysInput.addEventListener('input', calculateEndDate); startDateInput.addEventListener('change', calculateEndDate); // Form submission route modifiers btnTestConnection.addEventListener('click', function() { formAction.value = 'test_connection'; form.removeAttribute('novalidate'); form.submit(); }); btnResetConsole.addEventListener('click', function() { form.reset(); window.location.href = window.location.pathname; }); // Handle post-execution feedback states inside panel graphics <?php if ($action === 'test_connection' && empty($errMessage)): ?> document.getElementById('icon_conn').className = "bi bi-check-circle-fill text-success"; document.getElementById('badge_conn').className = "badge bg-success"; document.getElementById('badge_conn').innerText = "SUCCESS"; summaryStatus.className = "badge bg-success text-white px-2 py-0.5"; summaryStatus.innerText = "VALID CONTEXT"; <?php elseif ($action === 'test_connection' && !empty($errMessage)): ?> document.getElementById('icon_conn').className = "bi bi-x-circle-fill text-danger"; document.getElementById('badge_conn').className = "badge bg-danger"; document.getElementById('badge_conn').innerText = "FAILED"; summaryStatus.className = "badge bg-danger text-white px-2 py-0.5"; summaryStatus.innerText = "LINK MISMATCH"; <?php endif; ?> // Initialize display values once on frame lifecycle start syncConsoleSummaryData(); calculateEndDate(); }); </script> <?php require_once __DIR__ . '/../includes/footer.php'; ?>