Ejemplos de código Javascript

Para poder utilizar las funciones cstr, now, len, etc de VBScript, se ha implementado un fichero “vbscriptsupport.js” que tendremos que incluir en la colección empresas, que expone estas funciones para tenerlas disponibles cuando utilizamos scripts javascript usando la notación “vbSupport.left(XXXXX)”, “vbSupport.now()”, “vbSupport.cstr(XXXXX)”, etc.

Código para recorrer una colección registro a registro (Cuando son muchos datos para cargarlos todos en memoria).

function recorrerColeccion() {
	var sMensaje = "Estos son los usuarios:";
 
	// Variable con la coleccion que se va a utilizar
	var mColeccion = appData.getCollection("Usuarios");
 
	// Guardamos el filtro que tiene la colección actualmente
	var filtro=mColeccion.getFilter();
 
	// Le ponemos un filtro a la colección
	mColeccion.setFilter("t1.ACTIVO=1 AND t1.IDUSUARIO=" + user.ID + " AND date(t1.FECHA)=date('" + devolverFecha() + "')");
 
	// Variable con la coleccion que se va a utilizar
	mColeccion.startBrowse();
	try {
		var mObjeto;
		while((mObjeto = mColeccion.getCurrentItem()) != undefined) {
			sMensaje = sMensaje + "\n" + "Nombre: " + mObjeto.NOMBRE + " Login: " + mObjeto.LOGIN;
			mColeccion.moveNext();
		}
	} catch(ex) {
		ui.showToast("Hubo un error al hacer startBrowse() en: " + mColeccion.getName());
	} finally {
		// Cerramos el recordset
		mColeccion.endBrowse();
 
		// Dejamos la colección con el filtro que tenía al principio
		mColeccion.setFilter(filtro);
	}
	return sMensaje;
}

Una colección con pocos datos (Por ejemplo, las filas de detalles de un contents), pueden recorrerse cargando los registros en un array de memoria en lugar de ir recorriendo registro a registro.

function recorrerContents()
{
	// Todas las variables que definamos dentro de una funcion son locales a ésta.
	var coll;
	var collCount;
	var SUM=0;
	var i;
	var obj;
 
	// (Para hacer referencia al objeto actual se utiliza self, para no interferir con el this de javascript).
	self.MAP_TLCABECERAROJO="TOTAL ENTREGAS";
	self.MAP_TLCABECERAROJO1="SUMATORIO ENTREGADAS";
 
	collCount=0;
	SUM=0;
	// Cogemos la coll contents Detalles del objeto actual 
	coll=self.getContents("ContentSinCobrar");
 
	// Cargamos los registros en memoria y averiguamos el número de registros
	coll.loadAll();
	collCount=coll.count();
 
	// Utilizamos un For para recorrer los registros del contents.
	for (i=0;i<collCount;i++){
		// Tomamos un registro del content con el indice de dicho registro.
		obj=coll.get(i);
		SUM=parseFloat(SUM)+parseFloat(obj.IMPORTETOTAL);
	}
	self.MAP_TLVISITASSIN="Entregadas Sin Cobrar: ";
	self.MAP_VISITASSIN=SUM;
 
	// Liberamos la memoria que cargamos con el loadall.
	coll.clear();
}



function MarcarIncidencia(idIncid)
{
	// Buscamos una incidencia en concreto conociendo el valor de algún campo único (Siempre se busca como cadena).
	var collInc = appData.getCollection("IncidenciasAct");
	var objInc = collInc.getObject(vb.cstr(idIncid));
 
	objInc.CERRADA=1;
	objInc.FECHACIERREXONE=vb.now();
	objInc.save();
 
	objInc=null;
	collInc.clear();
}



AppData.PushValue objLoquesea lanza un objeto en edición.

function ejemploFindObject() {
	var nId = 2;
	var mObjeto = appData.getCollection("Usuarios").findObject("ID = " + nId);
	if(mObjeto == undefined) {
		ui.msgBox("No se encontró el usuario con ID " + nId, "Mensaje", 0);
	}
	else {
		ui.msgBox("El login del usuario con ID " + nId + " es: " + mObjeto.LOGIN, "Mensaje", 0);
 
		// Lo lanzamos en edición
		appData.pushValue(mObjeto);
	}
}


function getCurrentCollName() {
    let ventana = ui.getView();
    if (!ventana) {
        return "";
    }
    let dataObject = ventana.getDataObject();
    if (!dataObject) {
        return "";
    }
    let ownerCollection = dataObject.getOwnerCollection();
    return ownerCollection.getName();
}


Crear un nuevo registro con script. El script se podrá ejecutar en un create, insert, delete, botón

function creaRegistro() {
	var cCierre = appData.getCollection("Cierre");
	var objCierre = cCierre.createObject();
 
	cCierre.addItem(objCierre);
 
	objCierre.NUMINCIDENCIA=self.NUMINCIDENCIA;
	objCierre.NUMPROGRESO=self.NUMPROGRESO;
	objCierre.CODTECNICO=self.CODTECNICO;
 
	objCierre.save();
	// Si queremos lanzarlo en edición
	appData.pushValue(objCierre);
}


Primero tendríamos un botón para llamar a este nodo:

  	<prop name="BTOPCION1" type="B" width="260p" height="320p" img="option_gastos.png" method="ExecuteNode(abrirColl(Socios))" />

El código del nodo quedaría:

	'Hemos llamado al nodo abrirColl pero podríamos haberlo llamado de cualquier otra forma.
	<abrirColl show-wait-dialog="false">
		<action name="runscript">
			<param name="parametro" />
			<script language="javascript">
				var coll = appData.getCollection(parametro);
				var det = coll.createObject();
				// Lo lanzamos en edición
				appData.pushValue(det);
 
				//En lugar de poner aqui el código directamente, podemos llamar a una función
				//ircoll(parametro);
			</script>	
		</action>
	</abrirColl>
	function ircoll(collname){
		var coll = appData.getCollection(collname);
		var obj = coll.createObject();
		coll.addItem(obj);
		appData.pushValue(obj);
	}

Tenemos varias formas de mostrar un mensaje en pantalla.

Comando Descripción
ui.msgBox(“Ejemplo”, “Mensaje”, 0);
msg1=ui.msgBox(“Ejemplo”, “Mensaje”, 2);
msg2=ui.msgBox(“Ejemplo”, “Mensaje”, 4);

Mensaje del framework que necesita respuesta por parte del usuario. El último parámetro puede ser:

  • 0, que no devuelve ningún valor, únicamente aparece un botón OK.
  • 2, que nos muestra 2 botones: ACEPTAR y CANCELAR que devuelven un 1 ó un 2 respectivamente, según se pulse uno u otro.
  • 4, que nos muestra 2 botones: SI y NO que devuelven un 6 ó un 7 respectivamente, según se pulse uno u otro.
ui.showNotification(1, “Título”, “Mensaje”); Notificación en la barra superior de notificaciones, se le especifica un ID (1) para si queremos modificar la notificación más adelante, podemos utilizar el mismo comando con otro mensaje y se modificará el que ya existía.
ui.dismissNotification(1); Elimina la Notificación de la barra superior de notificaciones realizado con el “ShowNotification”, hemos de especificar el ID (1) de la notificación para especificar el mensaje que queremos quitar.
ui.showToast(“Se ha recibido un nuevo mensaje”); Mensaje TOAST del sistema operativo, ver la captura de pantalla más abajo. El mensaje desaparece automáticamente tras un par de segundos en pantalla.


Ejemplo de código:

<!-- En este caso vamos a hacer uso del nodo replica-ok, para capturar el evento de cuando recibimos operaciones en la tabla gen_mensajeria -->
<replica-ok-gen_mensajeria>
	<action name="runscript">
		<script language="javascript">
 
			// AppData.UserInterface.MsgBox hace exactamente lo mismo, ui es una abreviatura de AppData.UserInterface.
			ui.msgBox("Ejemplo", "Mensaje", 0);
 
			// El primer parámetro es el ID por si hay que modificarla luego
			ui.showNotification(1, "Título", "Mensaje");
 
			// Con la siguiente línea cambiaríamos el título y el mensaje de la notificación existente, pues tienen el mismo ID.
			// ui.showNotification(1, "Título2", "Mensaje2");
 
			// Si queremos quitar la notificación, hemos de pasarle el "ID" de la que queremos quitar.
			//ui.dismissNotification(1);
 
			// Para sacar un mensajito efímero que dura 3 segundos en pantalla.
			ui.showToast("Se ha recibido un nuevo mensaje");
		</script>
	</action>
</replica-ok-gen_mensajeria>


<!-- En éste caso al nodo que ejecutamos lo hemos llamado "onback", de forma que de paso, nos sirve en android, para
     que el botón de volver atrás físico de android nos ejecute ésta misma acción -->
 
<onback refresh="false" show-wait-dialog="false">
	<action name="runscript">
		<script language="javascript">
			//st_msg="Usuario o contraseña incorrectos";
			//if (st_msg!="")
			//	appData.failWithMessage(-8100,st_msg);
 
			appData.failWithMessage(-11888,"##EXIT##");
		</script>
	</action>
</onback>



Si estamos trabajando offline, por defecto, todo lo que se hace en el framework se va almacenando en la tabla master_replica_queue del dispositivo para posteriormente replicarla al servidor.
Si en algún momento no deseamos que ésto suceda, podemos realizar alguna operación directamente sobre la base de datos del dispositivo sin que se llegue a replicar al servidor.
Este comportamiento es muy utilizado en los mantenimientos sobre la base de datos local del dispositivo para eliminar información que ya no necesitamos (Partes o Pedidos antiguos, etc…).
La metodología es muy simple, se trata de crear una colección que “apunte” directamente a la base de datos del dispositivo. Por regla general la solemos llamar siempre igual en todos los proyectos: “ForMaintenance”. Lo realmente especial es el atributo connection=“mtto” que posee esta colección, que apunta al nodo <connection> que tenga dicho name.

No puede haber 2 nodos <connection> que tengan el mismo name en el mappings. Se recomienda que todos los nodos <connection> se definan en el nodo APP y así tenerlos todos “centralizados” en un único lugar y estar disponibles para cualquier colección a la cual le definamos el atributo connection=“mtto” (o cualquier otro nombre que le hayamos puesto al nodo connection).

<!-- Primero hemos de definir una colección que tenga un atributo connection que apunte directamente a la base de datos local del dispositivo -->
<coll name="ForMaintenance" title="ForMaintenance" objname="ForMaintenance" updateobj="ForMaintenance" progid="ASData.CASBasicDataObj" 
	sql="SELECT t1.*
	FROM ##PREF##ForMaintenance t1" connection="mtto">
 
	<!-- Esta definición de connection es preferible hacerla en el nodo APP y así otras colecciones puedan hacer uso de ella. -->
	<connection name="mtto" connstring="bd/gestion.db" />
 
	<group name="General" id="1" />
</coll>
 
<!-- En cualquier momento podemos ejecutar un script que nos ejecute las operaciones "al margen" de la réplica -->
<sin_replica>
	<action name="runscript">
		<script language="Javascript">
 			//Borramos las lineas de Documentos con ID Null
			appData.getCollection("ForMaintenance").executeSqlString("DELETE FROM Gen_detdocumento WHERE IDDOCUMENTO IS NULL");																						</script>
	</action>
</sin_replica>

Podemos crear una MACRO de usuario en una colección para sustituir en tiempo real la SQL de una colección, pudiendo llegar a sustituir incluso la SQL de la colección completa.

<coll name="Gastos" 
sql="SELECT g.*, tg.NOMBRE as MAP_GASTO, g.FOTO1 as MAP_FOTO1 ... FROM ##PREF##GASTOS g 
LEFT JOIN ##PREF##TiposGasto tg ON g.IDTIPOGASTO=tg.ID WHERE ##TIPO## " 
filter="g.BAJA=0" objname="Gastos" updateobj="Gastos" ... />
 
<macro name="##TIPO##" value="1=1" default="true" />

Una vez definida la macro en la colección, posteriormente podemos utilizarla para cambiar el valor de dicha macro y por lo tanto cambiar el SQL.
En éste caso concreto, cambiamos el filtro de una colección cuando cambie el valor de un campo “MAP_FTTIPOGASTO”.

<onchange>
	<field name="MAP_FTTIPOGASTO">
		<action name="runscript">
			<script language="javascript">
				collGastos = self.getContents("Gastos");
 
				//Aquí cambiamos el valor de la macro que tenemos definida en la colección Gastos.
				collGastos.setMacro("##TIPO##","tg.NOMBRE LIKE '%"+self.MAP_FTTIPOGASTO+"%'");
 
				//Así sería el comando para obtener el valor de una macro.
				var Valor=collGastos.getMacro("##TIPO##");
 
				self.executeNode("refrescaTotal");
			</script>
		</action>
	</field>
</onchange>


Podemos crear variables que tengan ámbito global y así puedan estar disponibles desde cualquier lugar de la aplicación.

Una opción sería incluir un fichero .js en la colección de empresas, donde se definirían con “var” las diferentes variables javascript que vayamos a necesitar que sean globales.

El otro método sería el que se ha utilizado hasta ahora en la plataforma:

// Para establecer el valor de una variable global:
 
	appData.getCurrentEnterprise().setVariables("GPSTime", objTransac.FECHA);
 
// Para obtener el valor de una variable global:
 
	vLastPieza=appData.getCurrentEnterprise().getVariables("LastPieza");
 
// Para establecer el valor de una variable de la colección "Usuarios":
 
	var mColeccion = appData.getCollection("Usuarios");
	mColeccion.setVariables("mensaje", "hola que tal");
 
// Para obtener el valor de una variable de la colección "Usuarios":
 
	var mColeccion = appData.getCollection("Usuarios");
	var sMensaje = mColeccion.getVariables("mensaje");


	ui.showGroup(self.MAP_PAGINA,"##LEFT_IN##",500,"##LEFT_OUT##",500);
//Para poder dar de __ALTA__ un registro en un contents tendríamos que pasarle el ID de la cabecera...
function addContents(coll,campoenlace) {
	var CollCV = appData.getCollection(coll);
	var ObjCV = CollCV.createObject();
	CollCV.addItem(ObjCV);
	ObjCV(campoenlace) = self.ID;
 
	appData.pushValue(ObjCV);
}
 
//A ésta función tenemos que pasarle la referencia al contents y el ID que queremos EDITAR. 
function editContents(coll,idselected) {
	var CollCV = appData.getCollection(coll);
	var ObjCV = CollCV.findObject("ID="+idselected);
	if (ObjCV) {
		appData.pushValue(ObjCV); 
	}	
}
 
//Realmente no borramos el registro físicamente, lo que hacemos es cambiar el campo de enlace con la cabecera
//ó podemos ponerle un BAJA=1, para darlo de baja lógica. Posteriormente tendríamos un mantenimiento para
//eliminar éstos registros del dispositivo (Para que no se replique el borrado físico del registro).
function deleteContents(coll,idselected,campoenlace)
	var result = ui.msgBox("¿Esta seguro de que desea eliminar el registro seleccionado?","¡ALERTA!",4);
	if (result == 6) {
		var CollCV = appData.getCollection(coll);
		var ObjCV = CollCV.findObject("ID="+idselected);
		if (ObjCV) {
			ObjCV.campoenlace = -9999;
			ObjCV.save();
		}	
	}
}


<script language="VBScript">
    'ERROR DISPONIBLE EN TODAS LAS PLATAFORMAS
        'Muestra un mensaje de error con el texto que le pongamos
        appdata.failwithmessage -8100,"Mensaje de error"
 
    'ERRORES DISPONIBLES EN PLATAFORMA POCKET PC
        'Muestra un mensaje de error con el texto que le pongamos
        appdata.failwithmessage -7500,"Mensaje de warning"
 
        'Refresca la cabecera cuando se ejecuta con el Selecteditem. YA NO ES NECESARIO.
        appdata.failwithmessage -666,"Mensaje de información"
 
        'Mensaje de informacion con icono de Información
        appdata.failwithmessage -11888,"Mensaje que queremos mostrar"
 
        'Mensaje personalizado con posiblidad de bloqueo con botón.
        appdata.failwithmessage -11822,"Mensaje de información"
 
        'Mensaje propio de CGS
        appdata.failwithmessage -11811,"Mensaje de información"
 
        'Inicia la replica desde un script
        Appdata.FailWithMessage -8100,"##STARTREPLICA##"
 
        'Esta macro funciona conjuntamente con CGStart (kiosko de PDA). 
        'Cierra el frame y le dice al kiosko q reinicie la PDA. 
        'Si el kiosko no está instalado, simplemente se cierra la aplicación.
        Appdata.FailWithMessage -8100,"##RESETAPP##"
 
        'Salir de la aplicacion.
        Appdata.FailWithMessage -8100,"##EXITAPP##"
 
        'Usado para salir de las ventanas de Nomen y Edit.
        Appdata.FailWithMessage -8100,"##EXIT##"
</script>



CÓDIGO PARA REALIZAR UN BOTÓN.

Está formado por 2 nodos:

Nodo 1 Prop de tipo “B” (botón) Es el nodo que define donde sale el mismo, título y a que nodo llama para ejecutar su código. Tiene el atributo method, que es el que realiza la llamada al nodo que tiene su código, el nombre “ExecuteNode”, es el nombre del nodo “method”, y tiene que llamarse de la misma forma (mayúsculas-minúsculas).
Nodo 2 Nodo en el que se pone el código que se va a ejecutar El nombre es definido por el usuario, y como único requisito, es el nombre del nodo, tiene que llamarse igual (mayúsculas-minúsculas) que el nodo al que llama el nodo button en su atributo method.


<prop name="Iniciar" type="B" method="ExecuteNode (inicializar)" title="Iniciar Ruta" visible="1" labelwidth="24" onchange="refresh255"/>
 
<inicializar>
	<action name="runscript">
		<script language="javascript">
			var vResult;
			vResult =ui.msgBox("Esta acción reiniciará toda la ruta. ¿Está seguro que desea ejecutarla?","Advertencia",1);
			if (vResult==1) {
				//La siguiente sentencia METE las cosas en réplica, como siempre en operaciones atómicas,
				//es decir, operaciones que únicamente afectan a un registro cada vez. Si no se desea meter
				//éstas operaciones en réplica, utilizar el método del "ForMaintenance".
				appData.executeSql("UPDATE Gen_Rutas SET VISITADO=0 WHERE IDUSUARIO=" + self.IDUSUARIO);
				ui.msgBox("Ruta inicializada","Advertencia",0);
			}						
		</script>
	</action>
</inicializar>



Generar un UUID con Javascript.

function generateUUID() {
    var d = new Date().getTime();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (d + Math.random()*16)%16 | 0;
        d = Math.floor(d/16);
        return (c=='x' ? r : (r&0x3|0x8)).toString(16);
    });
    return uuid;
}


// Ejemplo de uso: var f=getFecha("dmy","/");
function getFecha(formato,separador)
{ 
	var sToday = "";
	var today = new Date();
	var dd = today.getDate();
	var mm = today.getMonth()+1; // De 0-11 así que sumamos 1.
	var yyyy = today.getFullYear();
 
	if(dd<10) {
	    dd='0'+dd;
	} 
 
	if(mm<10) {
	    mm='0'+mm;
	} 
 
	if (!separador) {
		separador="/";
	}
	if (formato) {
		switch (formato){
		case "dmy":
			sToday = dd+separador+mm+separador+yyyy;
			break;
		case "mdy":
			sToday = mm+separador+dd+separador+yyyy;
			break;
		case "ymd":
			sToday = yyyy+separador+mm+separador+dd;
			break;
		default: 
			sToday = dd+"/"+mm+"/"+yyyy;
			break;
		}
	}
	else {
		sToday = dd+"/"+mm+"/"+yyyy;
	}
	return sToday;
} 


COLECCION QUE MUESTRA LA CANTIDAD DE DATOS QUE QUEDAN POR ENVIAR.

Esta colección se puede complicar todo lo que se quiera, incluso poniendo Join con otras tablas, para decir de que tabla faltan los datos por enviar desde el dispositivo móvil a la central.

La tabla MASTER_REPLICA_QUEUE, solo se puede utilizar para ver datos, nunca para escribir directamente en la misma, ya que es una tabla de sistema, con la cual se enviarán los datos desde el dispositivo móvil al servidor central.

<coll name="InformacionQueue" title="la informacion queue" 
	sql="SELECT COUNT(ID) AS MAP_CUENTA
	FROM master_replica_queue"
	objname="master_replica_queue" updateobj="master_replica_queue" progid="ASData.CASBasicDataObj" forprint="false" 
	editwidth="75" editheight="76" autorefresh="true">
	<group name="General" id="1"/>
	<prop name="MAP_CUENTA" visible="7" group="1" type="N" fieldsize="20" labelwidth="8" size="30">DATOS PARA ENVIAR</prop>
</coll>