feat(multi-tenant): configurar las 3 empresas oficiales (Telcotronics, XSYSTEM, DOMUS) y selector rapido de entorno
This commit is contained in:
+36
-20
@@ -27,23 +27,37 @@ async function setup() {
|
||||
)
|
||||
`);
|
||||
|
||||
// Ensure demo company exists FIRST (before migrations reference it)
|
||||
const [existing] = await conn.query('SELECT id FROM companies WHERE slug = ?', ['demo']);
|
||||
let companyId;
|
||||
if (existing.length === 0) {
|
||||
// Ensure official ecosystem companies exist: Telcotronics, XSYSTEM, DOMUS
|
||||
// Migrate legacy demo / testcorp / nuevaemp if present
|
||||
await conn.query(`UPDATE companies SET name = 'Telcotronics', slug = 'telcotronics', plan = 'enterprise', max_users = 100, max_tickets = 10000 WHERE slug = 'demo' OR id = 1`);
|
||||
await conn.query(`UPDATE companies SET name = 'XSYSTEM', slug = 'xsystem', plan = 'enterprise', max_users = 100, max_tickets = 10000 WHERE slug = 'testcorp' OR id = 2`);
|
||||
await conn.query(`UPDATE companies SET name = 'DOMUS', slug = 'domus', plan = 'enterprise', max_users = 100, max_tickets = 10000 WHERE slug = 'nuevaemp' OR id = 3`);
|
||||
|
||||
const ecosystemCompanies = [
|
||||
{ id: 1, name: 'Telcotronics', slug: 'telcotronics', plan: 'enterprise', max_users: 100, max_tickets: 10000 },
|
||||
{ id: 2, name: 'XSYSTEM', slug: 'xsystem', plan: 'enterprise', max_users: 100, max_tickets: 10000 },
|
||||
{ id: 3, name: 'DOMUS', slug: 'domus', plan: 'enterprise', max_users: 100, max_tickets: 10000 }
|
||||
];
|
||||
|
||||
for (const ec of ecosystemCompanies) {
|
||||
const [found] = await conn.query('SELECT id FROM companies WHERE slug = ? OR id = ?', [ec.slug, ec.id]);
|
||||
if (found.length === 0) {
|
||||
await conn.query(
|
||||
'INSERT INTO companies (name, slug, plan, max_users, max_tickets) VALUES (?, ?, ?, ?, ?)',
|
||||
['Demo Company', 'demo', 'enterprise', 100, 10000]
|
||||
'INSERT INTO companies (id, name, slug, plan, max_users, max_tickets) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[ec.id, ec.name, ec.slug, ec.plan, ec.max_users, ec.max_tickets]
|
||||
);
|
||||
companyId = 1;
|
||||
console.log('+ Empresa demo creada');
|
||||
console.log(`+ Empresa ${ec.name} creada (id=${ec.id})`);
|
||||
} else {
|
||||
companyId = existing[0].id;
|
||||
console.log('~ Empresa demo ya existe (id=' + companyId + ')');
|
||||
await conn.query(
|
||||
'UPDATE companies SET name = ?, slug = ?, plan = ?, max_users = ?, max_tickets = ? WHERE id = ?',
|
||||
[ec.name, ec.slug, ec.plan, ec.max_users, ec.max_tickets, found[0].id]
|
||||
);
|
||||
console.log(`~ Empresa ${ec.name} sincronizada (id=${found[0].id})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure company_id default value references existing company
|
||||
const defCompanyId = companyId;
|
||||
const companyId = 1;
|
||||
const defCompanyId = 1;
|
||||
|
||||
// Migrate existing tables
|
||||
const migrateColumn = async (table, column, definition) => {
|
||||
@@ -129,21 +143,23 @@ async function setup() {
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
`);
|
||||
|
||||
// Seed departments
|
||||
const [depts] = await conn.query('SELECT COUNT(*) as c FROM departments WHERE company_id = ?', [companyId]);
|
||||
// Seed departments for all ecosystem companies
|
||||
for (const ec of ecosystemCompanies) {
|
||||
const [depts] = await conn.query('SELECT COUNT(*) as c FROM departments WHERE company_id = ?', [ec.id]);
|
||||
if (depts[0].c === 0) {
|
||||
await conn.query(
|
||||
'INSERT INTO departments (company_id, nombre, descripcion) VALUES ?',
|
||||
[[
|
||||
[companyId, 'Soporte Técnico', 'Problemas técnicos y de software'],
|
||||
[companyId, 'Facturación', 'Problemas de facturación y pagos'],
|
||||
[companyId, 'Ventas', 'Consultas sobre ventas y productos'],
|
||||
[companyId, 'General', 'Consultas generales']
|
||||
[ec.id, 'Soporte Técnico', 'Problemas técnicos y de software'],
|
||||
[ec.id, 'Facturación', 'Problemas de facturación y pagos'],
|
||||
[ec.id, 'Ventas', 'Consultas sobre ventas y productos'],
|
||||
[ec.id, 'General', 'Consultas generales']
|
||||
]]
|
||||
);
|
||||
console.log('+ Departamentos seed insertados');
|
||||
console.log(`+ Departamentos seed insertados para ${ec.name}`);
|
||||
} else {
|
||||
console.log('~ Departamentos ya existen');
|
||||
console.log(`~ Departamentos ya existen para ${ec.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Seed users
|
||||
|
||||
@@ -25,6 +25,9 @@ module.exports = {
|
||||
if (req.session.user && req.session.user.company_id) {
|
||||
const company = await Company.findById(req.session.user.company_id);
|
||||
res.locals.company = company;
|
||||
if (req.session.user.rol === 'superadmin') {
|
||||
res.locals.allCompanies = await Company.findAll();
|
||||
}
|
||||
}
|
||||
next();
|
||||
},
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ const db = require('../config/db');
|
||||
|
||||
const Company = {
|
||||
findAll: async () => {
|
||||
const [rows] = await db.query('SELECT * FROM companies ORDER BY created_at DESC');
|
||||
const [rows] = await db.query('SELECT * FROM companies ORDER BY id ASC');
|
||||
return rows;
|
||||
},
|
||||
findById: async (id) => {
|
||||
|
||||
@@ -60,6 +60,20 @@ body {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.company-nav-pill.dropdown-toggle::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.company-nav-pill.dropdown-toggle {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.company-nav-pill.dropdown-toggle:hover {
|
||||
background: rgba(255, 255, 255, 0.14) !important;
|
||||
border-color: rgba(255, 255, 255, 0.28) !important;
|
||||
}
|
||||
|
||||
.company-plan-badge {
|
||||
background: linear-gradient(135deg, #0284c7 0%, #38bdf8 100%);
|
||||
color: #ffffff;
|
||||
|
||||
@@ -69,4 +69,32 @@ router.post('/companies/update/:id', isSuperAdmin, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/switch/:id', isSuperAdmin, async (req, res) => {
|
||||
try {
|
||||
const targetId = parseInt(req.params.id);
|
||||
const company = await Company.findById(targetId);
|
||||
if (!company) {
|
||||
req.flash('error_msg', 'Empresa no encontrada');
|
||||
return res.redirect('/saas/companies');
|
||||
}
|
||||
|
||||
req.session.user.company_id = company.id;
|
||||
if (req.session.user.id) {
|
||||
await User.update(req.session.user.id, { company_id: company.id });
|
||||
}
|
||||
|
||||
req.flash('success_msg', `Cambiaste de entorno a: ${company.name}`);
|
||||
const referrer = req.get('Referrer');
|
||||
// Redirigir de forma segura al referrer o al dashboard
|
||||
if (referrer && !referrer.includes('/login') && !referrer.includes('/logout')) {
|
||||
return res.redirect(referrer);
|
||||
}
|
||||
res.redirect('/admin/dashboard');
|
||||
} catch (error) {
|
||||
console.error('Error switching company:', error);
|
||||
req.flash('error_msg', 'Error al cambiar de empresa');
|
||||
res.redirect('/saas/companies');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -15,12 +15,44 @@
|
||||
</div>
|
||||
</a>
|
||||
<% if (typeof company !== 'undefined' && company) { %>
|
||||
<% if (typeof user !== 'undefined' && user && user.rol === 'superadmin' && typeof allCompanies !== 'undefined' && allCompanies.length > 1) { %>
|
||||
<div class="dropdown ms-2 d-none d-lg-inline-block">
|
||||
<button class="company-nav-pill dropdown-toggle d-flex align-items-center gap-1.5 border-0 bg-transparent text-white" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Cambiar Empresa (Superadmin)">
|
||||
<i class="fas fa-building text-info" style="font-size: 0.75rem;"></i>
|
||||
<span class="company-name-text fw-semibold"><%= company.name %></span>
|
||||
<span class="badge company-plan-badge"><%= company.plan %></span>
|
||||
<i class="fas fa-chevron-down ms-1 text-muted" style="font-size: 0.62rem;"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-dark shadow-lg border-secondary mt-1 py-1" style="font-size: 0.85rem; min-width: 220px;">
|
||||
<li class="dropdown-header text-uppercase text-muted fw-bold" style="font-size: 0.65rem; letter-spacing: 0.5px;">
|
||||
<i class="fas fa-exchange-alt me-1 text-info"></i> Entornos / Empresas
|
||||
</li>
|
||||
<% allCompanies.forEach(c => { %>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex align-items-center justify-content-between py-2 <%= c.id === company.id ? 'active' : '' %>" href="/saas/switch/<%= c.id %>">
|
||||
<span class="fw-medium"><%= c.name %></span>
|
||||
<span class="badge <%= c.id === company.id ? 'bg-info text-dark' : 'bg-dark text-muted border border-secondary' %>" style="font-size: 0.65rem;">
|
||||
<%= c.slug %>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<% }) %>
|
||||
<li><hr class="dropdown-divider my-1 border-secondary"></li>
|
||||
<li>
|
||||
<a class="dropdown-item py-1.5 text-warning small d-flex align-items-center gap-1.5" href="/saas/companies">
|
||||
<i class="fas fa-sliders-h"></i> Gestionar Empresas
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<div class="company-nav-pill ms-2 d-none d-lg-flex align-items-center gap-1.5">
|
||||
<i class="fas fa-building text-info" style="font-size: 0.75rem;"></i>
|
||||
<span class="company-name-text"><%= company.name %></span>
|
||||
<span class="badge company-plan-badge"><%= company.plan %></span>
|
||||
</div>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<% if (user) { %>
|
||||
|
||||
@@ -102,9 +102,14 @@
|
||||
<% } %>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="d-inline-flex align-items-center gap-1">
|
||||
<a href="/saas/switch/<%= c.id %>" class="btn btn-sm <%= typeof company !== 'undefined' && company && company.id === c.id ? 'btn-info text-dark fw-bold disabled' : 'btn-outline-primary' %>" title="<%= typeof company !== 'undefined' && company && company.id === c.id ? 'Empresa activa actualmente' : 'Cambiar y administrar esta empresa' %>">
|
||||
<i class="fas <%= typeof company !== 'undefined' && company && company.id === c.id ? 'fa-check' : 'fa-exchange-alt' %>"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#editModal<%= c.id %>" title="Editar cuotas y plan">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
|
||||
Reference in New Issue
Block a user