feat(tariffs): configurador dinamico de precios y tarifas desde la plataforma con persistencia MongoDB
This commit is contained in:
parent
8d5ee782ae
commit
b38157b94a
@ -129,6 +129,9 @@
|
|||||||
<button class="tab-btn" data-tab="tab-conductores" id="tab-btn-conductores">
|
<button class="tab-btn" data-tab="tab-conductores" id="tab-btn-conductores">
|
||||||
🚗 4. Portal Conductor
|
🚗 4. Portal Conductor
|
||||||
</button>
|
</button>
|
||||||
|
<button class="tab-btn" data-tab="tab-tarifas" id="tab-btn-tarifas">
|
||||||
|
⚙️ 5. Tarifas & Precios
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TAB 1: RESERVA PASAJEROS -->
|
<!-- TAB 1: RESERVA PASAJEROS -->
|
||||||
@ -451,6 +454,98 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- TAB 5: CONFIGURACIÓN DE PRECIOS Y TARIFAS -->
|
||||||
|
<section class="tab-content" id="tab-tarifas">
|
||||||
|
<div class="glass-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h2>⚙️ Configuración de Tarifas y Precios</h2>
|
||||||
|
<p>Ajuste dinámico de precios para pasajeros, mensajería, fletes, comisiones y penalidades. Los cambios se guardan en tiempo real en la base de datos MongoDB y se reflejan inmediatamente en toda la plataforma.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="form-tariffs-config">
|
||||||
|
<div class="grid-2col">
|
||||||
|
<!-- Columna 1: Transporte de Pasajeros y Plataforma -->
|
||||||
|
<div>
|
||||||
|
<h3 style="color: var(--accent-cyan); font-size: 1.15rem; margin-bottom: 1.25rem;">👥 Transporte de Pasajeros</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-tarifa-pasajero">Tarifa Fija por Pasajero ($ COP)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-tarifa-pasajero" value="35000" min="5000" step="1000" required>
|
||||||
|
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">Cobro fijo por cupo en vehículo de 4 pasajeros Cali ↔ Buenaventura.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-radio-pooling">Radio Máximo de Agrupación en Cali (Metros)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-radio-pooling" value="4000" min="500" step="500" required>
|
||||||
|
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">Distancia máxima geoespacial ($near) para agrupar pasajeros en el mismo vehículo.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-penalidad">Penalidad por Cancelación Tardía ($ COP)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-penalidad" value="15000" min="0" step="1000" required>
|
||||||
|
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">Cobro automático en Bold si el usuario cancela con viaje ya asignado/activo.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-comision">Comisión de la Plataforma (%)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-comision" value="15" min="0" max="50" step="1" required>
|
||||||
|
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">Porcentaje retenido por Pacifictrans Go sobre cada servicio completado.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Columna 2: Mensajería y Fletes -->
|
||||||
|
<div>
|
||||||
|
<h3 style="color: var(--accent-amber); font-size: 1.15rem; margin-bottom: 1.25rem;">📦 Mensajería y Fletes Especiales</h3>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-base-paquete">Tarifa Base Logística ($ COP)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-base-paquete" value="25000" min="5000" step="1000" required>
|
||||||
|
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">Costo base fijo de recepción y despacho de paquetería.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-km-paquete">Costo por Kilómetro Recorrido ($ COP / Km)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-km-paquete" value="1200" min="100" step="50" required>
|
||||||
|
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">Multiplicador por distancia en el corredor (115 km Cali-Buenaventura).</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-2col" style="gap: 1rem; margin-bottom: 0;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-factor-sobre">Factor Sobre (Doc.)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-factor-sobre" value="1.0" min="0.5" max="3" step="0.1" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-factor-mediano">Factor Mediano (15kg)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-factor-mediano" value="1.6" min="0.5" max="5" step="0.1" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-2col" style="gap: 1rem; margin-bottom: 0;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-factor-grande">Factor Grande (30kg)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-factor-grande" value="2.2" min="1" max="10" step="0.1" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label" for="cfg-seguro-base">Seguro Base ($ COP)</label>
|
||||||
|
<input type="number" class="form-input" id="cfg-seguro-base" value="5000" min="0" step="500" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Botón de Guardado y Estado -->
|
||||||
|
<div style="margin-top: 2rem; border-top: 1px solid var(--border-glass); padding-top: 1.5rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem;">
|
||||||
|
<div id="tariffs-save-status" style="font-size: 0.92rem; color: #94a3b8;">
|
||||||
|
💾 Tarifas sincronizadas con MongoDB.
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn-primary" id="btn-save-tariffs" style="width: auto; padding: 0.9rem 2.5rem;">
|
||||||
|
💾 Guardar y Aplicar Nuevas Tarifas
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- SECCIÓN DESCARGA APK -->
|
<!-- SECCIÓN DESCARGA APK -->
|
||||||
<section class="download-card" id="descarga-apk">
|
<section class="download-card" id="descarga-apk">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -1,9 +1,22 @@
|
|||||||
// Pacifictrans Go - Interactive Web Platform Logic
|
// 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', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
initTabs();
|
initTabs();
|
||||||
initPassengerCalculator();
|
initPassengerCalculator();
|
||||||
initFreightCalculator();
|
initFreightCalculator();
|
||||||
initRadarSimulation();
|
initRadarSimulation();
|
||||||
|
initTariffsManager();
|
||||||
|
fetchTariffs();
|
||||||
});
|
});
|
||||||
|
|
||||||
// TABS NAVIGATION
|
// 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 = `
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
|
||||||
|
</svg>
|
||||||
|
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 = `✅ <strong>¡Tarifas guardadas y sincronizadas en MongoDB!</strong> 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 = `✅ <strong>Tarifas actualizadas en la plataforma:</strong> 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
|
// PASSENGER POOLING & BOOKING LOGIC
|
||||||
function initPassengerCalculator() {
|
function initPassengerCalculator() {
|
||||||
const originSelect = document.getElementById('pool-origin');
|
const originSelect = document.getElementById('pool-origin');
|
||||||
@ -81,7 +222,8 @@ function initPassengerCalculator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bookBtn.disabled = false;
|
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);
|
}, 1200);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -102,7 +244,7 @@ function initPassengerCalculator() {
|
|||||||
updateSeatVisuals(currentSeatsFilled);
|
updateSeatVisuals(currentSeatsFilled);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FREIGHT & PARCEL CALCULATOR
|
// FREIGHT & PARCEL CALCULATOR CON TARIFAS CONFIGURABLES
|
||||||
function initFreightCalculator() {
|
function initFreightCalculator() {
|
||||||
const packageTypeSelect = document.getElementById('package-type');
|
const packageTypeSelect = document.getElementById('package-type');
|
||||||
const declaredValueInput = document.getElementById('declared-value');
|
const declaredValueInput = document.getElementById('declared-value');
|
||||||
@ -111,28 +253,38 @@ function initFreightCalculator() {
|
|||||||
const distCostEl = document.getElementById('freight-dist-cost');
|
const distCostEl = document.getElementById('freight-dist-cost');
|
||||||
const insuranceCostEl = document.getElementById('freight-insurance-cost');
|
const insuranceCostEl = document.getElementById('freight-insurance-cost');
|
||||||
|
|
||||||
const BASE_PRICE = 25000;
|
|
||||||
const KM_DIST = 115; // Corredor Cali - Buenaventura
|
const KM_DIST = 115; // Corredor Cali - Buenaventura
|
||||||
const COST_PER_KM = 1200;
|
|
||||||
|
|
||||||
function calculateFreight() {
|
window.recalculateFreight = function() {
|
||||||
const factor = parseFloat(packageTypeSelect?.value || '1.0');
|
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 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 distancePart = KM_DIST * costPerKm;
|
||||||
const subtotal = (BASE_PRICE + distancePart) * factor;
|
const subtotal = (basePrice + distancePart) * factor;
|
||||||
const seguro = Math.round(declaredValue * 0.01);
|
const seguro = Math.round(declaredValue * 0.01) + (currentTariffs.seguroBasePaquete || 0);
|
||||||
const total = Math.round(subtotal + seguro);
|
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 (distCostEl) distCostEl.textContent = `$${Math.round(distancePart * factor).toLocaleString('es-CO')} COP`;
|
||||||
if (insuranceCostEl) insuranceCostEl.textContent = `$${seguro.toLocaleString('es-CO')} COP`;
|
if (insuranceCostEl) insuranceCostEl.textContent = `$${seguro.toLocaleString('es-CO')} COP`;
|
||||||
if (totalPriceEl) totalPriceEl.textContent = `$${total.toLocaleString('es-CO')} COP`;
|
if (totalPriceEl) totalPriceEl.textContent = `$${total.toLocaleString('es-CO')} COP`;
|
||||||
}
|
};
|
||||||
|
|
||||||
if (packageTypeSelect) packageTypeSelect.addEventListener('change', calculateFreight);
|
if (packageTypeSelect) packageTypeSelect.addEventListener('change', window.recalculateFreight);
|
||||||
if (declaredValueInput) declaredValueInput.addEventListener('input', calculateFreight);
|
if (declaredValueInput) declaredValueInput.addEventListener('input', window.recalculateFreight);
|
||||||
calculateFreight();
|
window.recalculateFreight();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RADAR FLEET SIMULATION & LIVE DATA
|
// RADAR FLEET SIMULATION & LIVE DATA
|
||||||
@ -140,7 +292,6 @@ function initRadarSimulation() {
|
|||||||
const radarContainer = document.getElementById('radar-canvas-container');
|
const radarContainer = document.getElementById('radar-canvas-container');
|
||||||
if (!radarContainer) return;
|
if (!radarContainer) return;
|
||||||
|
|
||||||
// Conductores activos simulados con su residencia en Cali
|
|
||||||
const activeDrivers = [
|
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: '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' },
|
{ 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' },
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { userAdminService } from './services/user-admin.service';
|
|||||||
import { boldPaymentService } from './services/bold.service';
|
import { boldPaymentService } from './services/bold.service';
|
||||||
import { adminDashboardService } from './services/admin-dashboard.service';
|
import { adminDashboardService } from './services/admin-dashboard.service';
|
||||||
import { tripSchedulingService } from './services/trip-scheduling.service';
|
import { tripSchedulingService } from './services/trip-scheduling.service';
|
||||||
|
import { tariffsService } from './services/tariffs.service';
|
||||||
import { Trip } from './models/Trip';
|
import { Trip } from './models/Trip';
|
||||||
|
|
||||||
const PORT = process.env.PORT || 4000;
|
const PORT = process.env.PORT || 4000;
|
||||||
@ -213,6 +214,19 @@ const server = http.createServer(async (req, res) => {
|
|||||||
return sendJson(200, updateResult);
|
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
|
// 14. Servidor de Archivos Estáticos para la Plataforma Web & APK
|
||||||
const serveStaticFile = (filePath: string, contentType: string): boolean => {
|
const serveStaticFile = (filePath: string, contentType: string): boolean => {
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
|
|||||||
@ -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<ITarifasConfig> {
|
||||||
|
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<ITarifasConfig> {
|
||||||
|
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();
|
||||||
Loading…
Reference in New Issue
Block a user