From 0cd6048bf28681bb67b864284b5c6e65c5476b28 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 12 Nov 2025 14:33:11 +0000
Subject: [PATCH] Add QR scanner for wallet selection and transaction
management features
Co-authored-by: naielv <109038805+naielv@users.noreply.github.com>
---
src/page/pagos.js | 456 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 453 insertions(+), 3 deletions(-)
diff --git a/src/page/pagos.js b/src/page/pagos.js
index 50fca98..4662502 100644
--- a/src/page/pagos.js
+++ b/src/page/pagos.js
@@ -25,6 +25,13 @@ PAGES.pagos = {
}
}
+ // Check for scanned persona from QR scanner
+ var scannedPersona = sessionStorage.getItem('pagos_scanned_persona');
+ if (scannedPersona) {
+ prefilledData.persona = scannedPersona;
+ sessionStorage.removeItem('pagos_scanned_persona');
+ }
+
var field_tipo = safeuuid();
var field_monto = safeuuid();
var field_persona = safeuuid();
@@ -392,6 +399,14 @@ PAGES.pagos = {
PAGES.pagos.datafono(JSON.parse(atob(tid2[2])))
return
}
+ if (tid == "scan_qr") {
+ PAGES.pagos.__scanQR()
+ return
+ }
+ if (tid == "edit_transaction") {
+ PAGES.pagos.__editTransaction(tid2[2])
+ return
+ }
var nameh1 = safeuuid();
var field_ticket = safeuuid();
@@ -408,6 +423,9 @@ PAGES.pagos = {
var div_origen = safeuuid();
var btn_volver = safeuuid();
var btn_volver2 = safeuuid();
+ var btn_edit = safeuuid();
+ var btn_delete = safeuuid();
+ var btn_revert = safeuuid();
container.innerHTML = `
Transacción
@@ -471,6 +489,19 @@ PAGES.pagos = {
+
+
`;
document.getElementById(btn_volver).onclick = () => {
@@ -511,6 +542,95 @@ PAGES.pagos = {
document.getElementById(field_origen).value = data.Origen + (data.OrigenID ? " (" + data.OrigenID + ")" : "");
document.getElementById(div_origen).style.display = 'block';
}
+
+ // Edit button - navigate to edit mode
+ document.getElementById(btn_edit).onclick = () => {
+ setUrlHash("pagos,edit_transaction," + key);
+ };
+
+ // Delete button
+ document.getElementById(btn_delete).onclick = () => {
+ if (!checkRole("pagos:edit")) {
+ alert("No tienes permisos para eliminar transacciones");
+ return;
+ }
+
+ if (confirm("¿Estás seguro de que quieres ELIMINAR esta transacción?\n\nEsta acción NO se puede deshacer y los cambios en los monederos NO se revertirán automáticamente.\n\nPara revertir los cambios en los monederos, usa el botón 'Revertir Transacción' en su lugar.")) {
+ betterGunPut(gun.get(TABLE).get("pagos").get(key), null);
+ toastr.success("Transacción eliminada");
+ setTimeout(() => {
+ setUrlHash("pagos");
+ }, 1000);
+ }
+ };
+
+ // Revert button - reverses wallet balance changes and deletes transaction
+ document.getElementById(btn_revert).onclick = () => {
+ if (!checkRole("pagos:edit")) {
+ alert("No tienes permisos para revertir transacciones");
+ return;
+ }
+
+ if (confirm("¿Estás seguro de que quieres REVERTIR esta transacción?\n\nEsto revertirá los cambios en los monederos y eliminará la transacción.")) {
+ // Reverse the wallet balance changes
+ var tipo = data.Tipo;
+ var monto = parseFloat(data.Monto || 0);
+ var personaId = data.Persona;
+
+ // For Ingreso, subtract from balance (reverse)
+ // For Gasto, add to balance (reverse)
+ // For Transferencia, reverse both sides
+
+ if (tipo === "Ingreso") {
+ revertWalletBalance(personaId, "Gasto", monto, () => {
+ deleteTransaction(key);
+ });
+ } else if (tipo === "Gasto") {
+ revertWalletBalance(personaId, "Ingreso", monto, () => {
+ deleteTransaction(key);
+ });
+ } else if (tipo === "Transferencia") {
+ var destinoId = data.PersonaDestino;
+ revertWalletBalance(personaId, "Ingreso", monto, () => {
+ revertWalletBalance(destinoId, "Gasto", monto, () => {
+ deleteTransaction(key);
+ });
+ });
+ }
+ }
+ };
+
+ function revertWalletBalance(personaId, tipo, monto, callback) {
+ var persona = SC_Personas[personaId];
+ if (!persona) {
+ toastr.error("Error: Persona no encontrada");
+ return;
+ }
+
+ var currentBalance = parseFloat(persona.Monedero_Balance || 0);
+ var newBalance = currentBalance;
+
+ if (tipo === "Ingreso") {
+ newBalance = currentBalance + monto;
+ } else if (tipo === "Gasto") {
+ newBalance = currentBalance - monto;
+ }
+
+ persona.Monedero_Balance = newBalance;
+
+ TS_encrypt(persona, SECRET, (encrypted) => {
+ betterGunPut(gun.get(TABLE).get("personas").get(personaId), encrypted);
+ if (callback) callback();
+ });
+ }
+
+ function deleteTransaction(transactionKey) {
+ betterGunPut(gun.get(TABLE).get("pagos").get(transactionKey), null);
+ toastr.success("Transacción revertida y eliminada");
+ setTimeout(() => {
+ setUrlHash("pagos");
+ }, 1000);
+ }
}
if (typeof data == "string") {
@@ -654,16 +774,20 @@ PAGES.pagos = {
const monto = parseFloat(data.Monto || 0) || 0;
const tipo = data.Tipo;
+ const metodo = data.Metodo || "";
+
+ // Only count Tarjeta Monedero transactions in balance totals
+ const isMonedero = metodo === "Tarjeta";
// Reset entries on every call for this ID
- if (tipo === "Ingreso") {
+ if (isMonedero && tipo === "Ingreso") {
totalData.gastos[id] = 0;
totalData.ingresos[id] = monto;
- } else if (tipo === "Gasto") {
+ } else if (isMonedero && tipo === "Gasto") {
totalData.ingresos[id] = 0;
totalData.gastos[id] = monto;
} else {
- // For Transferencias, count as gasto + ingreso (neutral)
+ // For non-Monedero transactions or Transferencias, don't count in totals
totalData.ingresos[id] = 0;
totalData.gastos[id] = 0;
}
@@ -694,5 +818,331 @@ PAGES.pagos = {
setUrlHash("pagos," + safeuuid(""));
};
}
+ },
+
+ // QR Scanner for selecting wallet/persona
+ __scanQR: function() {
+ if (!checkRole("pagos:edit")) {
+ setUrlHash("pagos");
+ return;
+ }
+
+ var qrscan = safeuuid();
+ var btn_cancel = safeuuid();
+
+ container.innerHTML = `
+
+
+ 📷 Escanear QR de Monedero
+
+
+
+
+ Escanea el código QR del monedero de la persona para seleccionarlo automáticamente
+
+
+
+
+
+
+ `;
+
+ // Initialize QR scanner
+ var html5QrcodeScanner = new Html5QrcodeScanner(
+ qrscan, { fps: 10, qrbox: 250 }
+ );
+
+ function onScanSuccess(decodedText, decodedResult) {
+ html5QrcodeScanner.clear();
+
+ // Parse the QR code result
+ // Expected format: "personas,{personaId}" or just "{personaId}"
+ var personaId = decodedText;
+
+ // If it's a full URL hash, extract the persona ID
+ if (decodedText.includes("personas,")) {
+ var parts = decodedText.split(",");
+ if (parts.length > 1) {
+ personaId = parts[1];
+ }
+ }
+
+ // Verify the persona exists
+ if (SC_Personas[personaId]) {
+ toastr.success("✅ Monedero escaneado: " + SC_Personas[personaId].Nombre);
+
+ // Store the selected persona in sessionStorage and return to datafono
+ sessionStorage.setItem('pagos_scanned_persona', personaId);
+
+ // Navigate back to datafono
+ setUrlHash("pagos,datafono");
+ } else {
+ toastr.error("❌ Código QR no reconocido como un monedero válido");
+ setTimeout(() => {
+ setUrlHash("pagos,datafono");
+ }, 2000);
+ }
+ }
+
+ html5QrcodeScanner.render(onScanSuccess);
+ EventListeners.QRScanner.push(html5QrcodeScanner);
+
+ // Cancel button
+ document.getElementById(btn_cancel).onclick = () => {
+ html5QrcodeScanner.clear();
+ setUrlHash("pagos,datafono");
+ };
+ },
+
+ // Edit existing transaction
+ __editTransaction: function(transactionId) {
+ if (!checkRole("pagos:edit")) {
+ setUrlHash("pagos");
+ return;
+ }
+
+ var field_tipo = safeuuid();
+ var field_monto = safeuuid();
+ var field_persona = safeuuid();
+ var field_persona_destino = safeuuid();
+ var field_metodo = safeuuid();
+ var field_notas = safeuuid();
+ var field_estado = safeuuid();
+ var div_persona_destino = safeuuid();
+ var btn_save = safeuuid();
+ var btn_cancel = safeuuid();
+
+ var selectedPersona = "";
+ var selectedPersonaDestino = "";
+ var originalData = null;
+
+ container.innerHTML = `
+
+
+ ✏️ Editar Transacción
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ // Load transaction data
+ gun.get(TABLE).get("pagos").get(transactionId).once((data, key) => {
+ function loadTransactionData(data) {
+ originalData = data;
+
+ document.getElementById(field_tipo).value = data.Tipo || "Ingreso";
+ document.getElementById(field_metodo).value = data.Metodo || "Efectivo";
+ document.getElementById(field_monto).value = data.Monto || 0;
+ document.getElementById(field_estado).value = data.Estado || "Completado";
+ document.getElementById(field_notas).value = data.Notas || "";
+
+ selectedPersona = data.Persona || "";
+ selectedPersonaDestino = data.PersonaDestino || "";
+
+ loadPersonaSelector();
+
+ if (data.Tipo === "Transferencia") {
+ document.getElementById(div_persona_destino).style.display = 'block';
+ loadPersonaDestinoSelector();
+ }
+ }
+
+ if (typeof data == "string") {
+ TS_decrypt(data, SECRET, loadTransactionData);
+ } else {
+ loadTransactionData(data || {});
+ }
+ });
+
+ // Tipo change handler
+ document.getElementById(field_tipo).addEventListener('change', function() {
+ var tipo = this.value;
+ var divDestino = document.getElementById(div_persona_destino);
+ if (tipo === 'Transferencia') {
+ divDestino.style.display = 'block';
+ loadPersonaDestinoSelector();
+ } else {
+ divDestino.style.display = 'none';
+ }
+ });
+
+ function loadPersonaSelector() {
+ var container = document.querySelector('#personaSelector');
+ container.innerHTML = '';
+ document.getElementById(field_persona).value = selectedPersona;
+ addCategory_Personas(
+ container,
+ SC_Personas,
+ selectedPersona,
+ (personaId) => {
+ document.getElementById(field_persona).value = personaId;
+ selectedPersona = personaId;
+ },
+ "Monedero",
+ false,
+ "- No hay personas registradas -"
+ );
+ }
+
+ function loadPersonaDestinoSelector() {
+ var container = document.querySelector('#personaDestinoSelector');
+ container.innerHTML = '';
+ document.getElementById(field_persona_destino).value = selectedPersonaDestino;
+ addCategory_Personas(
+ container,
+ SC_Personas,
+ selectedPersonaDestino,
+ (personaId) => {
+ document.getElementById(field_persona_destino).value = personaId;
+ selectedPersonaDestino = personaId;
+ },
+ "Monedero Destino",
+ false,
+ "- No hay personas registradas -"
+ );
+ }
+
+ // Save button
+ document.getElementById(btn_save).onclick = () => {
+ var tipo = document.getElementById(field_tipo).value;
+ var monto = parseFloat(document.getElementById(field_monto).value);
+ var personaId = document.getElementById(field_persona).value;
+ var metodo = document.getElementById(field_metodo).value;
+ var notas = document.getElementById(field_notas).value;
+ var estado = document.getElementById(field_estado).value;
+
+ if (!personaId) {
+ alert("Por favor selecciona un monedero");
+ return;
+ }
+
+ if (isNaN(monto) || monto < 0) {
+ alert("Por favor ingresa un monto válido");
+ return;
+ }
+
+ if (tipo === 'Transferencia') {
+ var personaDestinoId = document.getElementById(field_persona_destino).value;
+ if (!personaDestinoId) {
+ alert("Por favor selecciona el monedero destino");
+ return;
+ }
+ if (personaId === personaDestinoId) {
+ alert("No puedes transferir al mismo monedero");
+ return;
+ }
+ }
+
+ if (!confirm("¿Estás seguro de que quieres guardar los cambios?\n\nNOTA: Los cambios en los monederos NO se ajustarán automáticamente. Si cambiaste el monto, tipo o persona, deberías revertir la transacción original y crear una nueva.")) {
+ return;
+ }
+
+ // Update transaction data
+ var updatedData = {
+ ...originalData,
+ Tipo: tipo,
+ Monto: monto,
+ Persona: personaId,
+ Metodo: metodo,
+ Notas: notas,
+ Estado: estado
+ };
+
+ if (tipo === 'Transferencia') {
+ updatedData.PersonaDestino = document.getElementById(field_persona_destino).value;
+ } else {
+ delete updatedData.PersonaDestino;
+ }
+
+ TS_encrypt(updatedData, SECRET, (encrypted) => {
+ document.getElementById("actionStatus").style.display = "block";
+ betterGunPut(gun.get(TABLE).get("pagos").get(transactionId), encrypted);
+ toastr.success("¡Transacción actualizada!");
+ setTimeout(() => {
+ document.getElementById("actionStatus").style.display = "none";
+ setUrlHash("pagos," + transactionId);
+ }, 1500);
+ });
+ };
+
+ // Cancel button
+ document.getElementById(btn_cancel).onclick = () => {
+ if (confirm("¿Seguro que quieres cancelar? Los cambios no se guardarán.")) {
+ setUrlHash("pagos," + transactionId);
+ }
+ };
}
};