aaaa En XOne hay tres tipos de relaciones entre colecciones:

TIPO DE RELACIÓN REPRESENTACIÓN VISUAL DESCRIPCIÓN
Relación de 1 a 1 (Lupa) Utilizado en relaciones con colecciones que tienen muchos datos.
Relación de 1 a 1 (Combo) Utilizado en relaciones con colecciones que tienen pocos datos.
Relación de 1 a Varios (Contents) Utilizado cuando no sabemos el número de registros que podemos asociar. Un ejemplo típico de contents son los Pedidos y sus detalles.


Este tipo de relación se utiliza para seleccionar un único valor de otra colección.

Únicamente se grabará en Base de Datos el ID de la fila seleccionada, pudiendo rescatar además, simplemente para mostrar en pantalla, otros campos de la fila seleccionada.


Vista campo de enlace de tipo Lupa en Pocket PC
Vista campo de enlace de tipo Lupa en BlackBerry
	<prop name="IDCLIENTE" group="1" type="N" visible="0" mapcol="Clientes" mapfld="ID"/>
	<!-- 
		Este campo es el que realmente SE GRABARÁ en la BD, normalmente no está visible.
		Enlaza con la colección "Clientes" y nos guardaremos el valor del campo "ID" seleccionado.
	-->
 
	<prop name="MAP_CLIENTE" ... linkedto="IDCLIENTE" linkedfield="NOMBRE" onchange="Refresh" value="##ID##">Cliente</prop>
	<prop name="MAP_CONTACTO" ... linkedto="IDCLIENTE" linkedfield="CONTACTO" locked="true" onchange="Refresh">Contacto</prop>
	<!-- 
		Estos campos están asociados con el campo "IDCLIENTE". Al llevar el prefijo "MAP_" NO se graban en BD.
		Para obtener sus valores utilizan el campo "IDCLIENTE" (linkedto) como enlace a la otra colección.
		El linkedfield es el campo que mostraremos por pantalla, el cual nos traemos de la otra colección.
		La lupa aparece en el campo que no está locked="true", para poder buscar directamente desde él.
		Los demás campos podemos ponerlos con el locked="true" para que NO se pueda editar su contenido NI TAMPOCO salga la lupa.
	-->


Similar al anterior, pero se utiliza únicamente cuando la colección llamada tiene pocos datos.

Esto es importante, porque cuando se va a pintar la ventana de edición, y uno de los campos tiene un combo, cargará en memoria todos los valores que puede contener dicho combo, con lo que si son muchos datos a cargar en el momento de abrir dicha ventana de edición, puede demorarse bastante.


Vista campo de enlace de tipo Lupa en Pocket PC
Vista campo de enlace de tipo Lupa en BlackBerry

Ejemplo de Código

	<prop name="IDTIPO" group="1" type="N" visible="0" mapcol="Tiposvisitas" mapfld="ID"></prop>
		<!-- 
			Este campo es el que realmente SE GRABARA BD, normalmente no está visible.
			Enlaza con la colección "TiposVisitas" y nos guardaremos el valor del campo "ID" seleccionado.
		-->
 
	<prop name="MAP_TIPO" ... type="T" linkedto="IDTIPO" linkedfield="DESCRIPCION" showinline="true">Tipo</prop>
		<!-- 
			Este campo está asociado con el campo "IDTIPO". Al llevar el prefijo "MAP_" NO se graba en BD.
			Para obtener sus valores utiliza el campo "IDTIPO" como enlace a la otra colección.
			La diferencia con el control tipo "lupa" es el atributo showinline="true", que es realmente
			el que le da la apariencia de combo. 
		-->

La diferencia con el tipo lupa es simplemente el atributo showinline=“true” y además el hecho de que el combo se carga de valores cuando se entra en la colección a diferencia de la lupa.


La estructura es similar a los anteriores, pero se utiliza exclusivamente cuando la colección llamada tiene pocos datos y claramente definidos.
NO se tira contra una Tabla en Base de Datos, los valores se definen en un primer PROP y se rescatan en otro PROP.

Ejemplo de Código

<prop name="MAP_IDTIPO" visible="0" group="1" type="N" mapcol-values="COMERCIAL,DPT INTRODUCCION,RESPONSABLE S EXT"/>
<!--
   Al ser un MAP_ no se graba en BD y el atributo mapcol-values define los valores posibles, separados por comas.
-->
<prop name="DESTINATARIO" labelwidth="0" type="T" size="40" onchange="Refresh" fieldsize="23" visible="1" showinline="true" group="1" linkedto="MAP_IDTIPO" linkedfield="DATA"/>
<!--
   Este campo es el que se graba en BD, el atributo linkedfield siempre vale DATA.
-->


Una colección de contenido (contents) es un grupo de datos que dependen de una cabecera, es decir, una colección que depende de otra.

Se utiliza cuando no sabemos el número de registros que vamos a poder asociar en la colección cabecera.

Ejemplos típicos de contents son los pedidos y sus líneas de detalle o artículos y sus existencias en almacén.



Campo de enlace entre la cabecera
y la colección de detalles(contents)


Para definir colección con content es necesario seguir varios pasos:


Se debe definir la colección principal (p.e colección de documentos). Cada una de las propiedades que se definen en esta colección corresponde a la cabecera del objeto.

Se define la colección content.

En el ejemplo que estamos utilizando, se creará una colección de detalles de documento. Cada una de las propiedades que se definan en la colección, corresponderán a las líneas de detalle del documento.

Se incluye una definición de content en la colección principal. La definición de content tiene el siguiente formato:

    <prop name="@NombrePropiedad" group="NumeroGrupo" type="Z" contents="nombre del content"></prop>
    <contents name="nombre del content" src="coleccion" filter="sentencia WHERE"></contents>

prop

  • name: Le asigna un nombre a la propiedad. Se suele poner una “@” delante para indicar que es una propiedad que hace referencia a un content. Recomendamos su uso para distinguir y localizar con mayor facilidad dentro del mappings.
  • group: Grupo o pestaña donde aparecerá el content.
  • type: Tipo de propiedad. Para referirse a content ha de ser “Z”.
  • contents: Nombre del content. Este atributo hace una llamada al nodo contents que tenga su mismo nombre. Es decir, el atributo contents ha de ser igual que el name del nodo contents.
  • width: Define el ancho del content.
  • height: Define la altura del content. En Android, es posible modificar este atributo por script. Supongamos que por ejemplo necesitas un cuadro de búsqueda que está oculto y que lo activas con un botón, y quieres que todo ocupe siempre el 100% de la pantalla. Entonces, lo que harías sería lo siguiente:
    <prop name="MAP_TAMCONTENT" visible="0" frame="id1" group="1" type="T" size="30" title="Tamaño content" />
    <prop name="@MaquinasContent" frame="id4" group="1" type="Z" contents="MaquinasContent" width="100%" height="##FLD_MAP_TAMCONTENT##" onchange="Refresh" editmodal="true" forceonchange="true" mask="0" locked="true" />
    <contents name="MaquinasContent" src="MaquinasContent" filter="IDCLIENTE=##FLD_MAP_IDCLIENTE##" />
<before-edit>
    <action name="runscript">
        <script language="VBScript">
            this("MAP_TAMCONTENT")="75%"
        <script>
    </action>
<before-edit>

Ahora, mediante script modificarías el tamaño cuando quisieras del mismo modo.

contents

  • name: Le asigna un nombre al content. Todo content tiene que tener un nombre, que deberá coincidir con el atributo contents, del punto anterior.
  • src: Indica el nombre de la colección contents. Cada vez que se crea un objeto contenedor, la colección origen es copiada y se llena con aquellos objetos que le pertenezcan al contenedor únicamente. En el ejemplo que estamos poniendo (documento con líneas de detalle) el objeto contenedor es el documento (un objeto por cada registro de cabecera). Cada objeto contenedor tiene asignadas varias líneas de detalle (en una tabla enlazada) Los registros de la tabla enlazada se colocan en la colección de contenidos.
  • filter: Es el filtro que indica cuáles registros de la tabla enlazada le “pertenecen” a este objeto en particular. Si la tabla de detalles se enlaza con la tabla principal por el campo IDDOCUMENTO, el filtro sería algo como IDDOCUMENTO=##ID##. Los componentes de datos se encargarán de sustituir la macro ##ID## por el ID del objeto propietario para cada uno de los documentos.


Si lo que se quiere es enlazar el pedido con los detalles en el momento de grabar el pedido(padre), se definen las acciones necesarias para que durante la grabación del pedido se realicen los enlaces necesarios, para que en las tablas queden almacenados los valores correctos que determinen la relación entre los registros del detalle y el pedido en la Base de Datos.

Los códigos siguientes tienen el sentido de asignar al campo IDPEDIDO de los detalles el valor del ID del pedido de la colección de cabecera, de forma que queden enlazados los registros. (relación cabecera-detalles)


En la colección Pedidos (padre) habría que añadir el siguiente código:

<insert>
  <!-- Al grabar un nuevo pedido en base de datos, a cada detalle se le pone el ID 
  del pedido creado -->
  <action name="link" coll="Detalles" field="IDPEDIDO" value="##ID##"></action>
</insert>


En la colección Detalles (colección contenida ó hija) habría que añadir el siguiente código:

<create>
    <!-- Al crear una nueva línea de Detalle, al IDPEDIDO de ese detalle se le pone 
      el ID del PEDIDO que estamos realizando -->
    <action name="setfldval" targetfld="IDPEDIDO" sourcefld="ID"></action>
</create>



Finalmente se tiene la estructura de padre-hijo como se muestra a continuación:

Supongamos que tenemos dos tablas con las siguientes definiciones:

A) Pedidos

CAMPO DESCRIPCIÓN
ID Identificador autonumérico del pedido
NUMERO Número del pedido
IDCLIENTE Identificador del cliente
FECHA Fecha del Pedido


B) Detalles del Pedido. Por cada pedido habrá uno o varios registros en esta tabla.

CAMPO DESCRIPCIÓN
ID Identificador autonumérico del detalle.
IDPEDIDO Identificador del pedido padre de este detalle.
IDARTICULO Identificador del artículo seleccionado en este registro.
CANTIDAD Cantidad pedida del artículo en cuestión.


Ahora vamos a crear una estructura de colecciones para manejar estos datos en una aplicación utilizando las definiciones que describimos en esta sección y la anterior.

Primeramente, definimos la colección contenida (aunque no tiene por qué ser así, ya que las colecciones pueden definirse en cualquier orden). La colección se define como si fuera a usarse de forma independiente.

En este caso tendríamos la siguiente definición:

<coll name="Detalles" title="Detalle pedido" sql="SELECT d.* FROM ##PREF##Detalles d" objname="Detalles" updateobj="Detalles" progid="ASData.CASBasicDataObj">
     <prop name="IDPEDIDO" group="1" visible="0" type="N" mapcol="Pedidos" mapfld="ID"></prop>
     <prop name="IDARTICULO" group="1" visible="0" type="N" mapcol="Articulos" mapfld="ID"></prop>
     <prop name="CANTIDAD" locked="true" visible="7" group="1" type="N2" fieldsize="12" size="12">Cantidad</prop>
     <create>
        <action name="setfldval" targetfld="IDPEDIDO" sourcefld="ID"></action>
     </create>
</coll>


Ahora podemos definir la colección principal, y dentro de ella incluimos la propiedad de mapeo de contenidos.

La definición de la colección quedaría de esta manera:

  <coll name="Pedidos" title="el Pedido" sql="SELECT p.* FROM ##PREF##Pedidos p" objname="Pedidos" updateobj="Pedidos" progid="ASData.CASBasicDataObj" loadall="false" withopen="false">
    <group name="General" id="1"></group>
    <group name="Detalles" id="2"></group>
       <prop name="IDCLIENTE" group="1" visible="0" type="N" mapcol="Clientes" mapfld="ID"></prop>
       <prop name="NUMERO" visible="3" group="1" type="N" fieldsize="12" size="12"></prop>
       <prop name="FECHA" visible="3" group="1" type="D" fieldsize="12" size="12"></prop>
       <prop name="MAP_CLIENTE" visible="3" group="1" type="T" fieldsize="12" size="12" linkedto="IDCLIENTE" linkedfield="NOMBRE"></prop>
       <prop name="@DETALLES" group="2" type="Z" contents="Detalles">Detalles</prop>
       <contents name="Detalles" src="Detalles" filter="IDPEDIDO=##ID##"></contents>
       <insert>
           <action name="link" coll="Detalles" field="IDPEDIDO" value="##ID##"></action>
       </insert>
  </coll>


Puede darse el caso de que una misma colección pueda usarse como content para más de una colección principal, por ejemplo, una colección de detalles de documentos puede emplearse tanto para las facturas como para los albaranes.

En este caso, aquellas propiedades de la colección que se refirieran a la propietaria no deben referenciar directamente un nombre, ya que no hay manera de saber en cuál de los posibles objetos contenedores se está evaluando el atributo.

Por eso existe la macro ##OWNERCOLL## que se emplea para evaluar los atributos mapcol de aquellas propiedades que se refieren a la colección propietaria.

El ejemplo de colecciones propietaria y contenido que mostramos a continuación ilustra el uso de ##OWNERCOLL## en un caso como el mencionado anteriormente.

Primeramente tomemos la siguiente definición de colección de detalles:

<coll name="Detalles" title="el detalle " .... otros atributos omitidos ... >
  <prop name="IDDOCUMENTO" group="1" type="N" visible="0" mapcol="##OWNERCOLL##" mapfld="ID"></prop>
  <otras propiedades omitidas>
</coll>


Ahora supongamos que tenemos dos colecciones de documentos que utilizan la colección anteriormente definida:

<coll name="FacturasVentas" title="la factura de venta" ... otros atributos omitidos ...>
  <propiedades omitidas>
  <prop name="@DETALLES" type="Z" contents="Detalles" lines="8"></prop>
  <contents name="Detalles" src="Detalles" filter="IDDOCUMENTO=##ID##"></contents>
</coll>
 
<coll name="AlbaranesVentas" title="el albaran de venta" ... otros atributos omitidos ...>
  <propiedades omitidas>
  <prop name="@DETALLES" type="Z" contents="Detalles" lines="8"></prop>
<contents name="Detalles" src="Detalles" filter="IDDOCUMENTO=##ID##"></contents>
</coll>


En el fragmento de código mostrado anteriormente se observa que la colección “Detalles” ha sido empleada como colección de contenido tanto por la colección “FacturasVentas” como por “AlbaranesVentas”.

Esto hace que en la definición de “Detalles” el campo IDDOCUMENTO se refiera indistintamente a una u otra colección en dependencia de cuál de ellas sea la que haga mención a dicho campo.

Por tanto es necesario indicar la macro ##OWNERCOLL## que será evaluada con el nombre correcto para cada uno de los detalles.

Así, cuando se le pida la colección a que mapea el campo IDDOCUMENTO en el contents “Detalles” de “FacturasVentas” el atributo tomará valor “FacturasVentas”, y cuando se le pida en la de “AlbaranesVentas” tomará el nombre de dicha colección.

Esto permite que con una sola definición se puedan definir colecciones contenidas para otras muchas, sin necesidad de crear copias de la misma definición solamente porque mapcol debe tener valor único.

Hay que tener en cuenta, que en la colección principal, siempre que haya un contents debe llevar un nodo DELETE con un action executesql, para cuando se borre el registro padre, también borre los registros hijos.


disablevisible="condición"


Si se cumple la condición, el content quedará deshabilitado.

Ejemplo:

  ...
  <contents name="Detalles" src="Detalles" filter="IDDOCUMENTO=##ID##" disablevisible="cliente=1"></contents>
  ...

disableedit="condición"


Se le pone una condición. Si esta condición se cumple no deja editar, si por lo contrario es falsa se deja editar.

Ejemplo:

  ...
  <contents name="Detalles" src="Detalles" filter="IDDOCUMENTO=##ID##" disableedit="editar=1"></group>
  ...

mask="valor"


Este atributo funciona como una máscara. Su función viene dada según su valor:

- 1 → nuevo.

- 2 → editar.

- 4 → borrar.

- 8 → filtro.

- 16 → salir.

La suma de algunos o todos los valores daría las diferentes opciones.

 Ej: para el valor 27 podríamos realizar las operaciones de nuevo, editar, filtrar y salir (1+2+8+16=27).

edit_inline / forceonchange / editmodal =" true | false "

- edit_inline → Edita en línea.

- forceonchange → Fuerza el refresco al volver de la ventana anterior.

- editmodal → Cuando se hace doble click edita en otra ventana.

Ejemplo:

Este ejemplo, hacemos que el content sea editado en otra pestaña en vez de ser en la misma.

   ...
   <prop name="@DETALLESULTIMOS" visible="1" group="3" type="Z" contents="Detalles" 
     lines="9" edit_inline="false" editmodal="true" forceonchange="true" onchange="refresh"></prop>
   <contents name="Detalles" src="Detalles" filter="IDDOCUMENTO=##ID##"></contents>
   ...

Para ello, se ponen los atributos forceonchange y editmodal a true, mientras edit_inline a false.