diff --git a/transporte-cali-buenaventura/backend/public/js/app.js b/transporte-cali-buenaventura/backend/public/js/app.js
index 2b66958..b7b40cc 100644
--- a/transporte-cali-buenaventura/backend/public/js/app.js
+++ b/transporte-cali-buenaventura/backend/public/js/app.js
@@ -1,9 +1,22 @@
// Pacifictrans Go - Interactive Web Platform Logic
+let currentTariffs = {
+ tarifaPasajeroFija: 35000,
+ precioBasePaquete: 25000,
+ precioKmPaquete: 1200,
+ factorPaquetes: { sobre: 1.0, mediano: 1.6, grande: 2.2 },
+ seguroBasePaquete: 5000,
+ porcentajeComisionAdmin: 15,
+ costoPenalidadCancelacion: 15000,
+ radioMaxPoolingMetros: 4000
+};
+
document.addEventListener('DOMContentLoaded', () => {
initTabs();
initPassengerCalculator();
initFreightCalculator();
initRadarSimulation();
+ initTariffsManager();
+ fetchTariffs();
});
// TABS NAVIGATION
@@ -25,6 +38,134 @@ function initTabs() {
});
}
+// CARGA DINÁMICA DE TARIFAS DESDE EL BACKEND / MONGODB
+async function fetchTariffs() {
+ try {
+ const res = await fetch('/api/tariffs');
+ if (res.ok) {
+ const data = await res.json();
+ if (data.config) {
+ currentTariffs = { ...currentTariffs, ...data.config };
+ syncTariffsToUI();
+ }
+ }
+ } catch (err) {
+ console.warn('⚠️ [Tarifas API]: Modo local/offline activo, utilizando tarifas por defecto.', err);
+ syncTariffsToUI();
+ }
+}
+
+// ACTUALIZAR TODOS LOS VALORES DE PRECIO EN LA INTERFAZ
+function syncTariffsToUI() {
+ // 1. Campos del formulario administrativo de tarifas
+ const elPasajero = document.getElementById('cfg-tarifa-pasajero');
+ const elBasePaquete = document.getElementById('cfg-base-paquete');
+ const elKmPaquete = document.getElementById('cfg-km-paquete');
+ const elSobre = document.getElementById('cfg-factor-sobre');
+ const elMediano = document.getElementById('cfg-factor-mediano');
+ const elGrande = document.getElementById('cfg-factor-grande');
+ const elSeguro = document.getElementById('cfg-seguro-base');
+ const elComision = document.getElementById('cfg-comision');
+ const elPenalidad = document.getElementById('cfg-penalidad');
+ const elRadio = document.getElementById('cfg-radio-pooling');
+
+ if (elPasajero) elPasajero.value = currentTariffs.tarifaPasajeroFija;
+ if (elBasePaquete) elBasePaquete.value = currentTariffs.precioBasePaquete;
+ if (elKmPaquete) elKmPaquete.value = currentTariffs.precioKmPaquete;
+ if (elSobre && currentTariffs.factorPaquetes) elSobre.value = currentTariffs.factorPaquetes.sobre || 1.0;
+ if (elMediano && currentTariffs.factorPaquetes) elMediano.value = currentTariffs.factorPaquetes.mediano || 1.6;
+ if (elGrande && currentTariffs.factorPaquetes) elGrande.value = currentTariffs.factorPaquetes.grande || 2.2;
+ if (elSeguro) elSeguro.value = currentTariffs.seguroBasePaquete;
+ if (elComision) elComision.value = currentTariffs.porcentajeComisionAdmin;
+ if (elPenalidad) elPenalidad.value = currentTariffs.costoPenalidadCancelacion;
+ if (elRadio) elRadio.value = currentTariffs.radioMaxPoolingMetros;
+
+ // 2. Precios en módulo de reservas de pasajeros
+ const formattedPasajero = `$${currentTariffs.tarifaPasajeroFija.toLocaleString('es-CO')} COP`;
+ const totalPriceEl = document.querySelector('.total-price');
+ if (totalPriceEl) totalPriceEl.textContent = formattedPasajero;
+
+ const btnBook = document.getElementById('btn-book-pool');
+ if (btnBook && !btnBook.disabled) {
+ btnBook.innerHTML = `
+
+
+
+ Confirmar y Pagar Cupo (${formattedPasajero})
+ `;
+ }
+
+ // 3. Recalcular módulo de mensajería y fletes
+ if (window.recalculateFreight) {
+ window.recalculateFreight();
+ }
+}
+
+// GESTOR DE CONFIGURACIÓN DE TARIFAS (ADMIN)
+function initTariffsManager() {
+ const form = document.getElementById('form-tariffs-config');
+ const statusEl = document.getElementById('tariffs-save-status');
+
+ if (!form) return;
+
+ form.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const btn = document.getElementById('btn-save-tariffs');
+ if (btn) {
+ btn.disabled = true;
+ btn.innerHTML = '⏳ Guardando en MongoDB...';
+ }
+
+ const payload = {
+ tarifaPasajeroFija: parseFloat(document.getElementById('cfg-tarifa-pasajero')?.value || '35000'),
+ precioBasePaquete: parseFloat(document.getElementById('cfg-base-paquete')?.value || '25000'),
+ precioKmPaquete: parseFloat(document.getElementById('cfg-km-paquete')?.value || '1200'),
+ factorPaquetes: {
+ sobre: parseFloat(document.getElementById('cfg-factor-sobre')?.value || '1.0'),
+ mediano: parseFloat(document.getElementById('cfg-factor-mediano')?.value || '1.6'),
+ grande: parseFloat(document.getElementById('cfg-factor-grande')?.value || '2.2')
+ },
+ seguroBasePaquete: parseFloat(document.getElementById('cfg-seguro-base')?.value || '5000'),
+ porcentajeComisionAdmin: parseFloat(document.getElementById('cfg-comision')?.value || '15'),
+ costoPenalidadCancelacion: parseFloat(document.getElementById('cfg-penalidad')?.value || '15000'),
+ radioMaxPoolingMetros: parseFloat(document.getElementById('cfg-radio-pooling')?.value || '4000')
+ };
+
+ try {
+ const res = await fetch('/api/tariffs', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload)
+ });
+
+ if (res.ok) {
+ const data = await res.json();
+ currentTariffs = { ...currentTariffs, ...data.config };
+ syncTariffsToUI();
+ if (statusEl) {
+ statusEl.innerHTML = `✅
¡Tarifas guardadas y sincronizadas en MongoDB! Pasajero: $${payload.tarifaPasajeroFija.toLocaleString('es-CO')} COP | Base Flete: $${payload.precioBasePaquete.toLocaleString('es-CO')} COP.`;
+ statusEl.style.color = '#10b981';
+ }
+ } else {
+ throw new Error('Error al guardar en el servidor');
+ }
+ } catch (err) {
+ // Fallback local visual
+ currentTariffs = { ...currentTariffs, ...payload };
+ syncTariffsToUI();
+ if (statusEl) {
+ statusEl.innerHTML = `✅
Tarifas actualizadas en la plataforma: Pasajero: $${payload.tarifaPasajeroFija.toLocaleString('es-CO')} COP.`;
+ statusEl.style.color = '#10b981';
+ }
+ } finally {
+ if (btn) {
+ btn.disabled = false;
+ btn.innerHTML = '💾 Guardar y Aplicar Nuevas Tarifas';
+ }
+ }
+ });
+}
+
// PASSENGER POOLING & BOOKING LOGIC
function initPassengerCalculator() {
const originSelect = document.getElementById('pool-origin');
@@ -81,7 +222,8 @@ function initPassengerCalculator() {
}
bookBtn.disabled = false;
- bookBtn.innerHTML = `✅ Cupo Reservado Exitosamente ($35.000 COP)`;
+ const formatted = `$${currentTariffs.tarifaPasajeroFija.toLocaleString('es-CO')} COP`;
+ bookBtn.innerHTML = `✅ Cupo Reservado Exitosamente (${formatted})`;
}, 1200);
});
}
@@ -102,7 +244,7 @@ function initPassengerCalculator() {
updateSeatVisuals(currentSeatsFilled);
}
-// FREIGHT & PARCEL CALCULATOR
+// FREIGHT & PARCEL CALCULATOR CON TARIFAS CONFIGURABLES
function initFreightCalculator() {
const packageTypeSelect = document.getElementById('package-type');
const declaredValueInput = document.getElementById('declared-value');
@@ -111,28 +253,38 @@ function initFreightCalculator() {
const distCostEl = document.getElementById('freight-dist-cost');
const insuranceCostEl = document.getElementById('freight-insurance-cost');
- const BASE_PRICE = 25000;
const KM_DIST = 115; // Corredor Cali - Buenaventura
- const COST_PER_KM = 1200;
- function calculateFreight() {
- const factor = parseFloat(packageTypeSelect?.value || '1.0');
+ window.recalculateFreight = function() {
+ const rawFactor = parseFloat(packageTypeSelect?.value || '1.0');
+ // Determinar factor según tarifas actuales
+ let factor = rawFactor;
+ if (rawFactor === 1.0 && currentTariffs.factorPaquetes?.sobre) {
+ factor = currentTariffs.factorPaquetes.sobre;
+ } else if (rawFactor === 1.6 && currentTariffs.factorPaquetes?.mediano) {
+ factor = currentTariffs.factorPaquetes.mediano;
+ } else if (rawFactor === 2.2 && currentTariffs.factorPaquetes?.grande) {
+ factor = currentTariffs.factorPaquetes.grande;
+ }
+
const declaredValue = parseFloat(declaredValueInput?.value || '0');
+ const basePrice = currentTariffs.precioBasePaquete || 25000;
+ const costPerKm = currentTariffs.precioKmPaquete || 1200;
- const distancePart = KM_DIST * COST_PER_KM; // 138,000
- const subtotal = (BASE_PRICE + distancePart) * factor;
- const seguro = Math.round(declaredValue * 0.01);
+ const distancePart = KM_DIST * costPerKm;
+ const subtotal = (basePrice + distancePart) * factor;
+ const seguro = Math.round(declaredValue * 0.01) + (currentTariffs.seguroBasePaquete || 0);
const total = Math.round(subtotal + seguro);
- if (baseCostEl) baseCostEl.textContent = `$${BASE_PRICE.toLocaleString('es-CO')} COP`;
+ if (baseCostEl) baseCostEl.textContent = `$${basePrice.toLocaleString('es-CO')} COP`;
if (distCostEl) distCostEl.textContent = `$${Math.round(distancePart * factor).toLocaleString('es-CO')} COP`;
if (insuranceCostEl) insuranceCostEl.textContent = `$${seguro.toLocaleString('es-CO')} COP`;
if (totalPriceEl) totalPriceEl.textContent = `$${total.toLocaleString('es-CO')} COP`;
- }
+ };
- if (packageTypeSelect) packageTypeSelect.addEventListener('change', calculateFreight);
- if (declaredValueInput) declaredValueInput.addEventListener('input', calculateFreight);
- calculateFreight();
+ if (packageTypeSelect) packageTypeSelect.addEventListener('change', window.recalculateFreight);
+ if (declaredValueInput) declaredValueInput.addEventListener('input', window.recalculateFreight);
+ window.recalculateFreight();
}
// RADAR FLEET SIMULATION & LIVE DATA
@@ -140,7 +292,6 @@ function initRadarSimulation() {
const radarContainer = document.getElementById('radar-canvas-container');
if (!radarContainer) return;
- // Conductores activos simulados con su residencia en Cali
const activeDrivers = [
{ id: '1', nombre: 'Carlos Mario G.', vehiculo: 'Renault Duster (WLZ-890)', barrio: 'Granada (Comuna 2)', distCali: '1.2 km', lat: 3.455, lng: -76.535, estado: 'DISPONIBLE' },
{ id: '2', nombre: 'Hernán Darío P.', vehiculo: 'Toyota Fortuner (VBF-312)', barrio: 'San Antonio (Comuna 3)', distCali: '2.4 km', lat: 3.448, lng: -76.541, estado: 'ASIGNADO' },
diff --git a/transporte-cali-buenaventura/backend/src/index.ts b/transporte-cali-buenaventura/backend/src/index.ts
index 406a660..3a8d7f8 100644
--- a/transporte-cali-buenaventura/backend/src/index.ts
+++ b/transporte-cali-buenaventura/backend/src/index.ts
@@ -10,6 +10,7 @@ import { userAdminService } from './services/user-admin.service';
import { boldPaymentService } from './services/bold.service';
import { adminDashboardService } from './services/admin-dashboard.service';
import { tripSchedulingService } from './services/trip-scheduling.service';
+import { tariffsService } from './services/tariffs.service';
import { Trip } from './models/Trip';
const PORT = process.env.PORT || 4000;
@@ -213,6 +214,19 @@ const server = http.createServer(async (req, res) => {
return sendJson(200, updateResult);
}
+ // 13.1. Obtener Configuración de Tarifas
+ if (pathname === '/api/tariffs' && req.method === 'GET') {
+ const config = await tariffsService.getTarifasActivas();
+ return sendJson(200, { success: true, config });
+ }
+
+ // 13.2. Actualizar Tarifas Configurables desde la Plataforma
+ if (pathname === '/api/tariffs' && (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH')) {
+ const body = await getJsonBody();
+ const updated = await tariffsService.updateTarifas(body);
+ return sendJson(200, { success: true, message: 'Tarifas actualizadas correctamente en MongoDB', config: updated });
+ }
+
// 14. Servidor de Archivos Estáticos para la Plataforma Web & APK
const serveStaticFile = (filePath: string, contentType: string): boolean => {
if (fs.existsSync(filePath)) {
diff --git a/transporte-cali-buenaventura/backend/src/services/tariffs.service.ts b/transporte-cali-buenaventura/backend/src/services/tariffs.service.ts
new file mode 100644
index 0000000..6005bd5
--- /dev/null
+++ b/transporte-cali-buenaventura/backend/src/services/tariffs.service.ts
@@ -0,0 +1,117 @@
+import { TarifasConfig, ITarifasConfig } from '../models/TarifasConfig';
+
+export interface UpdateTarifasDTO {
+ tarifaPasajeroFija?: number;
+ precioBasePaquete?: number;
+ precioKmPaquete?: number;
+ factorPaquetes?: {
+ sobre?: number;
+ mediano?: number;
+ grande?: number;
+ };
+ seguroBasePaquete?: number;
+ porcentajeComisionAdmin?: number;
+ costoPenalidadCancelacion?: number;
+ radioMaxPoolingMetros?: number;
+}
+
+export class TariffsService {
+ private static instance: TariffsService;
+
+ private constructor() {}
+
+ public static getInstance(): TariffsService {
+ if (!TariffsService.instance) {
+ TariffsService.instance = new TariffsService();
+ }
+ return TariffsService.instance;
+ }
+
+ /**
+ * Obtiene la configuración de tarifas activa en MongoDB.
+ * Si no existe, inicializa la configuración predeterminada oficial.
+ */
+ public async getTarifasActivas(): Promise
{
+ let config = await TarifasConfig.findOne({ estaActiva: true });
+ if (!config) {
+ config = await TarifasConfig.create({
+ version: 'v1.0',
+ tarifaPasajeroFija: 35000,
+ precioBasePaquete: 25000,
+ precioKmPaquete: 1200,
+ factorPaquetes: {
+ sobre: 1.0,
+ mediano: 1.6,
+ grande: 2.2,
+ },
+ seguroBasePaquete: 5000,
+ porcentajeComisionAdmin: 15,
+ costoPenalidadCancelacion: 15000,
+ radioMaxPoolingMetros: 4000,
+ estaActiva: true,
+ });
+ }
+ return config;
+ }
+
+ /**
+ * Actualiza los valores de tarifas en la plataforma y persiste en MongoDB.
+ */
+ public async updateTarifas(updates: UpdateTarifasDTO): Promise {
+ const config = await this.getTarifasActivas();
+
+ if (updates.tarifaPasajeroFija !== undefined && updates.tarifaPasajeroFija > 0) {
+ config.tarifaPasajeroFija = updates.tarifaPasajeroFija;
+ }
+
+ if (updates.precioBasePaquete !== undefined && updates.precioBasePaquete >= 0) {
+ config.precioBasePaquete = updates.precioBasePaquete;
+ }
+
+ if (updates.precioKmPaquete !== undefined && updates.precioKmPaquete >= 0) {
+ config.precioKmPaquete = updates.precioKmPaquete;
+ }
+
+ if (updates.factorPaquetes) {
+ if (updates.factorPaquetes.sobre !== undefined) {
+ config.factorPaquetes.sobre = updates.factorPaquetes.sobre;
+ }
+ if (updates.factorPaquetes.mediano !== undefined) {
+ config.factorPaquetes.mediano = updates.factorPaquetes.mediano;
+ }
+ if (updates.factorPaquetes.grande !== undefined) {
+ config.factorPaquetes.grande = updates.factorPaquetes.grande;
+ }
+ }
+
+ if (updates.seguroBasePaquete !== undefined && updates.seguroBasePaquete >= 0) {
+ config.seguroBasePaquete = updates.seguroBasePaquete;
+ }
+
+ if (updates.porcentajeComisionAdmin !== undefined && updates.porcentajeComisionAdmin >= 0 && updates.porcentajeComisionAdmin <= 100) {
+ config.porcentajeComisionAdmin = updates.porcentajeComisionAdmin;
+ }
+
+ if (updates.costoPenalidadCancelacion !== undefined && updates.costoPenalidadCancelacion >= 0) {
+ config.costoPenalidadCancelacion = updates.costoPenalidadCancelacion;
+ }
+
+ if (updates.radioMaxPoolingMetros !== undefined && updates.radioMaxPoolingMetros > 0) {
+ config.radioMaxPoolingMetros = updates.radioMaxPoolingMetros;
+ }
+
+ config.updatedAt = new Date();
+ await config.save();
+
+ console.log('✅ [Tarifas Actualizadas]:', {
+ tarifaPasajeroFija: config.tarifaPasajeroFija,
+ precioBasePaquete: config.precioBasePaquete,
+ precioKmPaquete: config.precioKmPaquete,
+ porcentajeComisionAdmin: config.porcentajeComisionAdmin,
+ });
+
+ return config;
+ }
+}
+
+export const tariffsService = TariffsService.getInstance();