This commit is contained in:
Lizandro Guarnizo
2026-04-10 23:04:28 -05:00
parent 64495816a9
commit c8bef9d9c4
8 changed files with 273 additions and 77 deletions
+104 -45
View File
@@ -422,28 +422,25 @@ $hoy = date('Y-m-d');
</div>
</div>
</div>
<!-- Métodos de pago (múltiples) -->
<div id="pago-campos-pago">
<div class="mb-3">
<label class="form-label small fw-semibold">Modo de pago <span class="text-danger">*</span></label>
<select class="form-select form-select-sm" id="pago-modo">
<option value="efectivo">💵 Efectivo</option>
<option value="transferencia">📲 Transferencia</option>
<option value="otro">📋 Otro</option>
</select>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold">Monto cobrado <span class="text-danger">*</span></label>
<div class="input-group input-group-sm">
<span class="input-group-text">$</span>
<input type="number" class="form-control" id="pago-monto"
placeholder="0" min="0" step="1000">
</div>
<label class="form-label small fw-semibold">Métodos de pago <span class="text-danger">*</span></label>
<div id="pago-metodos-lista"></div>
<button type="button" class="btn btn-outline-success btn-sm mt-1 w-100"
onclick="pagoModal.agregarMetodo()">
<i class="fas fa-plus me-1"></i>Agregar método de pago
</button>
<div class="d-flex justify-content-between align-items-center mt-2 pt-2 border-top">
<span class="small fw-semibold text-muted">Total:</span>
<span class="fw-bold text-success" id="pago-total-label">$0</span>
</div>
</div>
<div class="mb-2">
<label class="form-label small">Notas / referencia</label>
<div class="mb-2 mt-2">
<label class="form-label small">Notas generales</label>
<input type="text" class="form-control form-control-sm" id="pago-notas"
placeholder="Ej: nequi ref #123456">
placeholder="Ej: incluye copago Nequi">
</div>
</div>
<div class="modal-footer py-2">
@@ -604,12 +601,18 @@ const portal = {
${item.valor_copago ? `<span>Copago: <strong>$${Number(item.valor_copago).toLocaleString('es-CO')}</strong></span>` : ''}
<span class="fw-bold text-success">Total: <strong>$${totalCobro.toLocaleString('es-CO')}</strong></span>
</div>
${item.pago_estado === 'pagado' && item.pago_modo ? `
<div class="small text-success">
${item.pago_estado === 'pagado' && (item.pagos_detalle || []).length ? `
<div class="small text-success mt-1">
${item.pagos_detalle.map(p =>
`<div>${MODO_LABEL[p.modo] || p.modo}: <strong>$${Number(p.monto).toLocaleString('es-CO')}</strong>${p.notas ? ` · <em>${esc(p.notas)}</em>` : ''}</div>`
).join('')}
</div>` :
(item.pago_estado === 'pagado' && item.pago_modo ? `
<div class="small text-success mt-1">
${MODO_LABEL[item.pago_modo] || item.pago_modo}
${item.pago_monto ? ` · $${Number(item.pago_monto).toLocaleString('es-CO')}` : ''}
${item.pago_notas ? ` · <em>${esc(item.pago_notas)}</em>` : ''}
</div>` : ''}
</div>` : '')}
${item.pago_estado !== 'pagado' && item.pago_estado !== 'exento' ? `
<button class="btn btn-sm mt-2 text-white w-100" style="background:#065f46;font-size:.78rem"
onclick="pagoModal.abrir(${item.domicilio_id}, ${totalCobro})">
@@ -1898,23 +1901,29 @@ const formEnvio = {
};
// ══════════════════════════════════════════════════════════
// OBJETO: pagoModal — Gestión del modal de registro de pago
// OBJETO: pagoModal — Gestión del modal de registro de pago (multi-método)
// ══════════════════════════════════════════════════════════
const pagoModal = {
_bsModal: null,
_filas: [], // [{ modo, monto, notas }]
// Abrir modal pre-cargado con domicilio y monto sugerido
abrir(domId, totalSugerido = 0) {
document.getElementById('pago-dom-id').value = domId;
document.getElementById('pago-monto').value = totalSugerido > 0 ? totalSugerido : '';
document.getElementById('pago-notas').value = '';
document.getElementById('pago-modo').value = 'efectivo';
// Recuperar estado actual desde la agenda cargada
// Recuperar estado y pagos actuales desde la agenda cargada
const item = (portal._agenda || []).find(d => d.domicilio_id == domId);
const est = item ? (item.pago_estado || 'pendiente') : 'pendiente';
document.querySelector(`input[name="pago-estado"][value="${est}"]`).checked = true;
// Cargar filas existentes o inicializar con una vacía
const existentes = (item && item.pagos_detalle && item.pagos_detalle.length)
? item.pagos_detalle
: [{ modo: 'efectivo', monto: totalSugerido > 0 ? totalSugerido : '', notas: '' }];
this._filas = existentes.map(p => ({ modo: p.modo || 'efectivo', monto: p.monto || '', notas: p.notas || '' }));
this._renderFilas();
this.toggleModo();
if (!this._bsModal) {
@@ -1923,7 +1932,54 @@ const pagoModal = {
this._bsModal.show();
},
// Mostrar/ocultar campos de modo y monto según estado
// Renderiza la lista de filas de métodos de pago
_renderFilas() {
const lista = document.getElementById('pago-metodos-lista');
if (!lista) return;
lista.innerHTML = this._filas.map((f, i) => `
<div class="d-flex gap-1 align-items-center mb-2" id="pago-fila-${i}">
<select class="form-select form-select-sm" style="max-width:130px"
onchange="pagoModal._filas[${i}].modo=this.value">
<option value="efectivo" ${f.modo==='efectivo' ? 'selected':''}>💵 Efectivo</option>
<option value="transferencia" ${f.modo==='transferencia' ? 'selected':''}>📲 Transferencia</option>
<option value="otro" ${f.modo==='otro' ? 'selected':''}>📋 Otro</option>
</select>
<div class="input-group input-group-sm" style="flex:1">
<span class="input-group-text">$</span>
<input type="number" class="form-control" placeholder="0" min="0" step="1000"
value="${f.monto !== '' ? f.monto : ''}"
oninput="pagoModal._filas[${i}].monto=parseFloat(this.value)||0;pagoModal._actualizarTotal()"
>
</div>
<input type="text" class="form-control form-control-sm" style="flex:1;min-width:60px"
placeholder="Ref." value="${esc(String(f.notas||''))}"
oninput="pagoModal._filas[${i}].notas=this.value">
${this._filas.length > 1
? `<button type="button" class="btn btn-sm btn-outline-danger px-2"
onclick="pagoModal.eliminarFila(${i})"><i class="fas fa-times"></i></button>`
: ''}
</div>`
).join('');
this._actualizarTotal();
},
_actualizarTotal() {
const sum = this._filas.reduce((s, f) => s + (parseFloat(f.monto) || 0), 0);
const lbl = document.getElementById('pago-total-label');
if (lbl) lbl.textContent = '$' + sum.toLocaleString('es-CO');
},
agregarMetodo() {
this._filas.push({ modo: 'efectivo', monto: '', notas: '' });
this._renderFilas();
},
eliminarFila(i) {
this._filas.splice(i, 1);
this._renderFilas();
},
// Mostrar/ocultar sección de métodos según estado
toggleModo() {
const est = document.querySelector('input[name="pago-estado"]:checked')?.value;
document.getElementById('pago-campos-pago').style.display =
@@ -1932,23 +1988,26 @@ const pagoModal = {
// Guardar pago vía API
async guardar() {
const domId = document.getElementById('pago-dom-id').value;
const estado = document.querySelector('input[name="pago-estado"]:checked')?.value;
const modo = document.getElementById('pago-modo').value;
const monto = parseFloat(document.getElementById('pago-monto').value) || 0;
const notas = document.getElementById('pago-notas').value.trim();
const domId = document.getElementById('pago-dom-id').value;
const estado = document.querySelector('input[name="pago-estado"]:checked')?.value;
const notas = document.getElementById('pago-notas').value.trim();
if (!estado) { alert('Selecciona el estado del pago.'); return; }
if (estado === 'pagado' && (!modo || monto <= 0)) {
alert('Para marcar como pagado debes ingresar el modo y el monto cobrado.');
return;
let pagos = [];
if (estado === 'pagado') {
pagos = this._filas.map(f => ({
modo: f.modo,
monto: parseFloat(f.monto) || 0,
notas: f.notas || null,
}));
if (!pagos.length || pagos.some(p => p.monto <= 0)) {
alert('Ingresa al menos un método de pago con monto mayor a 0.');
return;
}
}
const payload = { domicilio_id: domId, pago_estado: estado };
if (estado === 'pagado') {
payload.pago_modo = modo;
payload.pago_monto = monto;
}
const payload = { domicilio_id: domId, pago_estado: estado, pagos };
if (notas) payload.pago_notas = notas;
try {
@@ -1960,16 +2019,16 @@ const pagoModal = {
const res = await r.json();
if (!res.success) throw new Error(res.error || 'Error al guardar');
// Actualizar item en la agenda local para evitar recarga completa
// Actualizar item en la agenda local
const idx = (portal._agenda || []).findIndex(d => d.domicilio_id == domId);
if (idx >= 0) {
portal._agenda[idx].pago_estado = estado;
portal._agenda[idx].pago_modo = estado === 'pagado' ? modo : null;
portal._agenda[idx].pago_monto = estado === 'pagado' ? monto : null;
portal._agenda[idx].pago_notas = notas || null;
portal._agenda[idx].pago_estado = estado;
portal._agenda[idx].pago_monto = estado === 'pagado' ? res.pago_total : null;
portal._agenda[idx].pago_modo = estado === 'pagado' && pagos.length ? pagos[0].modo : null;
portal._agenda[idx].pago_notas = notas || null;
portal._agenda[idx].pagos_detalle = estado === 'pagado' ? pagos : [];
}
// Cerrar modal y re-renderizar
if (this._bsModal) this._bsModal.hide();
portal._renderLista();
} catch(e) {