<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Un principiante en las Artes Gráficas</title>
	<atom:link href="http://www.pinelo.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.pinelo.com/blog</link>
	<description>Una experiencia en un sector que nos envuelve... El blog personal de David Pinelo.</description>
	<lastBuildDate>Wed, 03 Feb 2010 11:35:45 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Lo prometido es deuda: Versión compilada para Windows con mis cambios</title>
		<link>http://www.pinelo.com/blog/2010/02/03/lo-prometido-es-deuda-version-compilada-para-windows-con-mis-cambios/</link>
		<comments>http://www.pinelo.com/blog/2010/02/03/lo-prometido-es-deuda-version-compilada-para-windows-con-mis-cambios/#comments</comments>
		<pubDate>Wed, 03 Feb 2010 11:34:24 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[ERP Open Source]]></category>
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=334</guid>
		<description><![CDATA[Me lo estaba pidiendo Chencho. He sacado un día, y aquí lo tenéis. La versión compilada, Quick, con mis cambios incluídos para Windows. Sólo hay que descomprimirla en c:\Facturalux.
En los próximos días pondré la versión debug para Linux.
]]></description>
			<content:encoded><![CDATA[<p>Me lo estaba pidiendo Chencho. He sacado un día, y <a href="http://pinelo.com/blog/postimgs/2010/02/03/facturalux.zip">aquí</a> lo tenéis. La versión compilada, Quick, con mis cambios incluídos para Windows. Sólo hay que descomprimirla en c:\Facturalux.</p>
<p>En los próximos días pondré la versión debug para Linux.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2010/02/03/lo-prometido-es-deuda-version-compilada-para-windows-con-mis-cambios/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AbanQ: Integrando con JasperServer / JasperReports (II)</title>
		<link>http://www.pinelo.com/blog/2009/12/10/abanq-integrando-con-jasperserver-jasperreports-ii/</link>
		<comments>http://www.pinelo.com/blog/2009/12/10/abanq-integrando-con-jasperserver-jasperreports-ii/#comments</comments>
		<pubDate>Thu, 10 Dec 2009 10:27:55 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=311</guid>
		<description><![CDATA[Bien, la idea es usar una librería C++ para interaccionar con webservices. Para ello, escogí el toolkit gSOAP. Librería en software libre que permite crear aplicaciones tanto clientes (consumidores) como servidores de webservices.
Nosotros vamos a hacer un uso muy parcial de la misma, ya que no vamos a instalar ningún servidor ni nada por el [...]]]></description>
			<content:encoded><![CDATA[<p>Bien, la idea es usar una librería C++ para interaccionar con webservices. Para ello, escogí el toolkit <a href="http://www.cs.fsu.edu/~engelen/soap.html">gSOAP</a>. Librería en software libre que permite crear aplicaciones tanto clientes (consumidores) como servidores de webservices.</p>
<p>Nosotros vamos a hacer un uso muy parcial de la misma, ya que no vamos a instalar ningún servidor ni nada por el estilo, y sólo vamos a crear un consumidor. No me voy a meter en detalles con gSOAP (ya que se me escapa de los artículos) y sólo comentaré aquellos aspectos que nos interesan para integrar Webservices.</p>
<p>Lo primero, es obtener el <a href="http://es.wikipedia.org/wiki/WSDL">WSDL</a> de JasperServer. Para ello, y desde una instalación del mismo, se puede obtener sin más que abrir un navegador y solicitar http://servidor:puerto_tomcat/jasperserver/services/repository?wsdl</p>
<p>Bien. gSOAP proporciona dos ejecutables wsdl2h y soapcpp2 para generar los &#8220;proxys&#8221;, esto es, código C++ que encapsula el acceso por webservices. Los ejecutamos tal que así: (INCLUDEGSOAP es una variable que apunta al directorio de includes de gsoap</p>
<p><span id="more-311"></span></p>
<pre>echo "" &gt; env.h
soapcpp2 -L -C -d../proxy -penv env.h
# Las dos lineas anteriores son para generar un esqueleto de código de acceso a los webservices, que permitan
# una compilación sin problemas.
wsdl2h -nJasperServer -NJasperServer -y -g -I${INCLUDEGSOAP}/ -o JasperServer.h JasperServer.wsdl
soapcpp2 -C -d../proxy -I${INCLUDEGSOAP}/ -n -pjasperserver -qJasperServer JasperServer.h</pre>
<p>Con esto se generan una serie de ficheros, .c, .cpp y .h que incluiremos en AbanQ. Concretamente, a flbase.pro, le añadiremos los siguientes ficheros:</p>
<pre>-De la distribución de gsoap directamente
stdsoap2.cpp
-Archivos generados con el código anterior
JasperServerClientLib.cpp
envC.cpp
envClient.cpp
stdsoap2.h
JasperServerrepositorySoapBindingProxy.h
JasperServer.nsmap</pre>
<p>Ya podemos meter código propio en AbanQ. En el zip que puse días anteriores, viene todo esto integrado. Destaco aquí el código de acceso a los webservices.</p>
<p>Voy a crear un nuevo método en FLUtil que permita, desde QSA, acceder a los webservices. Ese método será tal que así</p>
<pre>// dpinelo
bool FLUtil::downloadJasperServerFile(const QString &amp; xmlRequest, const QString &amp; rutaDestino, QString &amp; mensajeSalida)
{
 bool value = false;
 int r;
 std::string result;
 std::string xml = xmlRequest;

 QSettings settings;
 settings.setPath( "InfoSiAL", "FacturaLUX", QSettings::User );
 QString keybase( "/facturalux/lite/jasperserver/" );
 // Lo hago así para garantizar que los objetos existen hasta el final
 QString *userJasperServer = new QString;
 QString *passwordJasperServer = new QString;
 QString *endPointJasperServer = new QString;
 *userJasperServer = settings.readEntry( keybase + "User", "" );
 *passwordJasperServer = settings.readEntry( keybase + "Password", "" );
 *endPointJasperServer = settings.readEntry( keybase + "EndPoint", "" );

 JasperServer::repositorySoapBinding rep;
 // si lo hago sin punteros, esto es peligroso
 rep.soap-&gt;userid = (*userJasperServer).ascii();
 rep.soap-&gt;passwd = (*passwordJasperServer).ascii();
 rep.endpoint = (*endPointJasperServer).ascii();

 qDebug("Antes de llamar a jasperserver");
 r = rep.JasperServer__runReport(xml, result);
 if ( r == SOAP_OK ) {
 qDebug("Lo ha llamado y es correcto");
 soap_multipart::iterator attachment = rep.soap-&gt;mime.begin();
 ++attachment;
 if ( attachment != rep.soap-&gt;mime.end() ) {
 saveDocument (*attachment, rutaDestino);
 value = true;
 } else {
 value = false;
 mensajeSalida = result;
 }
 } else {
 qDebug("Hubo un fallo");
 soap_print_fault(rep.soap, stderr);
 value = false;
 }
 delete userJasperServer;
 delete passwordJasperServer;
 return value;    

}

// dpinelo
void FLUtil::saveDocument(const struct soap_multipart &amp; attachment, const QString &amp; path)
{
 QFile file (path);
 file.open ( IO_WriteOnly | IO_Truncate );
 file.writeBlock(attachment.ptr, attachment.size);
 file.close();
}</pre>
<p>Listo. ¿Cómo invocar a un informe en JasperServer? Desde QSA es tan sencillo como: Por ejemplo, para imprimir un recibo de proveedor</p>
<pre>function oficial_imprimir(codRecibo:String)
{
 var util:FLUtil = new FLUtil;
 var xml:String = "&lt;request operationName=\"runReport\" locale=\"es\"&gt;";
 xml = xml + "&lt;argument name=\"RUN_OUTPUT_FORMAT\"&gt;PDF&lt;/argument&gt;";
 xml = xml + "&lt;resourceDescriptor name=\"\" wsType=\"\" ";
 xml = xml + "uriString=\"/Documentos/Pagare_a_Proveedores/PagareProveedores\" ";
 xml = xml + "isNew=\"false\"&gt;";
 xml = xml + "&lt;label&gt;null&lt;/label&gt;";
 xml = xml + "&lt;parameter name=\"P_ID_PAGARE\"&gt;&lt;![CDATA[100]]&gt;&lt;/parameter&gt;";
 xml = xml + "&lt;parameter name=\"ANIO\"&gt;&lt;![CDATA[09]]&gt;&lt;/parameter&gt;";
 xml = xml + "&lt;/resourceDescriptor&gt;";
 xml = xml + "&lt;/request&gt;";
 var rutaDestino:String = "/home/david/prueba.pdf";
 var mensajeSalida:String;
 util.downloadJasperServerFile ( xml, rutaDestino, mensajeSalida);

}</pre>
<p>Como veis, construyo un XML con el formato adecuado, según describe JasperServer, indicando los valores de los parámetros del propio report. El archivo se obtiene directamente en PDF (aunque se puede obtener en otros muchos formatos cambiando el RUN_OUTPUT_FORMAT), y por supuesto, podéis ponerle el nombre que querais. (construid adecuadamente rutaDestino).</p>
<p>La integración seía aún mejor en la medida en la que se oculte ese código XML que se construye en QSA, asociando parámetros de JasperReports/JasperServer con columnas de FLSqlCursor. Pero, a mí, este nivel me sirve.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/12/10/abanq-integrando-con-jasperserver-jasperreports-ii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AbanQ: Antes de seguir&#8230; cómo se compila</title>
		<link>http://www.pinelo.com/blog/2009/12/05/abanq-antes-de-seguir-como-se-compila/</link>
		<comments>http://www.pinelo.com/blog/2009/12/05/abanq-antes-de-seguir-como-se-compila/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 08:48:42 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[ERP Open Source]]></category>
		<category><![CDATA[Librerías Qt]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=317</guid>
		<description><![CDATA[Bueno, antes de seguir con los artículos de cómo integrar JasperServer, u OpenKM, o introducir alguno de los cambios que propongo&#8230; creo que es mejor proporcionaros las herramientas para compilar estas versiones de AbanQ tanto en windows como en linux, para crear así los clientes con las opciones de debug, o quick.
He creado un .zip [...]]]></description>
			<content:encoded><![CDATA[<p>Bueno, antes de seguir con los artículos de cómo integrar JasperServer, u <a href="http://www.openkm.com/">OpenKM</a>, o introducir alguno de los cambios que propongo&#8230; creo que es mejor proporcionaros las herramientas para compilar estas versiones de AbanQ tanto en windows como en linux, para crear así los clientes con las opciones de debug, o quick.</p>
<p>He creado un <a href="http://pinelo.com/blog/postimgs/2009/12/abanq-dpinelo-20091204.zip">.zip</a> con <em>mi</em> versión actual de AbanQ, y digo <em>&#8220;mi&#8221;</em> versión, porque incluye los múltiples cambios que he ido publicando en este blog, y que enumero</p>
<ul>
<li>Formateado automático de los FLFieldDB que contienen números, de tal forma, que las cantidades tales como 100000 se representan como 100.000,00 Esto hace mucho más legible las cantidades. <a href="http://www.pinelo.com/blog/2009/03/20/abanq-dando-formato-a-los-flfielddb-numericos/">Aquí</a></li>
<li>Desde QSA (el lenguaje de scripting) pueden accederse al manejo de los QComboBox (así, por ejemplo, he podido meter en algunos formularios objetos QComboBox y utilizarlos directamente sin necesidad de tener que estar asociados a columnas de bases de datos. Esto es especialmente útil para realizar filtrados de campos estáticos o no guardados en bases de datos).</li>
<li>Los FLTableDB pueden presentar el resultado de Querys complejas, al agregarse una propiedad: qryVisualizacion. De esta forma, la riqueza visual de lo que pueden representar es mayor. La diferencia con la versión oficial de AbanQ, es que ese FLTableDB no es readonly, sino que se utiliza en la edición, creación y eliminación de registros. <a href="http://www.pinelo.com/blog/2009/03/05/mostrando-el-resultado-de-consultas-en-los-flformdb-de-abanq/">Aquí</a></li>
<li>Cambios estéticos que afectan principalmente a la versión para Windows, ya que la original, no me gustaba (dejo el tema de windows por defecto, sin modificarlo). <a href="http://www.pinelo.com/blog/2009/02/26/modificaciones-en-abanq">Aquí</a></li>
<li>Los tamaños y posiciones de los formularios utilizados se almacenan, permitiendo una mejor experiencia del usuario. <a href="http://www.pinelo.com/blog/2009/03/02/guardando-automaticamente-los-tamanos-de-los-formularios-en-abanq/">Aquí</a>.</li>
<li>Posibilidad de utilizar vistas cuando se utilice un motor PostgreSQL. Esto, entre otras cosas, permite una mejor integración de los datos de otros programas con AbanQ. <a href="http://www.pinelo.com/blog/2009/05/27/buenas-practicas-en-abanq-usando-vistas-con-postgresql/">Aquí</a></li>
<li>Corrección de un bug por el que no se filtraban adecuadamente campos con valores int, uint o double. <a href="http://www.pinelo.com/blog/2009/06/08/bug-en-abanq-filtrado-de-campos-double-int-o-uint-desde-fltabledb">Aquí</a>.</li>
<li>Incorporación del estilo Windows XP (esto no lo incluye la versión original de AbanQ).</li>
<li>El código de la integración con JasperServer que se verá en siguientes artículos.</li>
</ul>
<p>Sólo tenéis que descomprimir el archivo .zip que os adjunto. Una vez hecho esto, si utilizáis Windows, debéis ajustar las variables señaladas en los archivos setenv.bat y build-config.bat. Si utilizáis Linux, debéis hacer lo mismo en el archivo setenv.h</p>
<p>Para compilar en Linux, necesitáis gcc, g++ y demás herramientas que seguro tendréis instaladas. Para compilar en Windows, necesitáis descargaros, <a href="http://www.mingw.org/">MinGW</a>. Además, es conveniente instalar el MinGW API for MS-Windows  de <a href="http://sourceforge.net/projects/mingw/files">aquí</a>.</p>
<p>Compilación bajo Windows. Ubicados en el directorio donde habéis descomprimido las fuentes, ejecutar en este orden (por si no me explico bien haced un Inicio-&gt;Ejecutar-&gt; cmd y cd al directorio en el que tengáis las fuentes ;-):</p>
<pre>setenv.bat
build-config.bat
build-pthreads.bat
build-qt.bat
build-qsa.bat
build-abanq.bat</pre>
<p>Para los que estéis en Linux, es tan sencillo, como desde una consola, y en el directorio de las fuentes, ejecutar</p>
<pre>setenv.sh
compilar-qt.sh
compilar-qsa.sh
compilar-abanq.sh</pre>
<p>Por defecto, la versión Windows viene preparada para compilar una versión final con un cliente quick, y la versión de Linux, para compilar una versión debug y con cliente &#8220;pesado&#8221; o completo.</p>
<p>Tened en cuenta que hay dependencias cruzadas, que no están muy bien resueltas: Por ejemplo, flbase depende de kugar, y kugar depende de flbase&#8230; así que al construir por primera vez, puede que os de error (la librería de flbase quiere enlazar a la de kugar, pero la de kugar no está construida porque faltan archivos objetos de flbase que no pueden construirse sin hacer previamente un moc de flbase&#8230;). Solución si os perdéis un poco en estos temas: entrad directamente en los directorios (src/flbase o src/kugar) y haced ahí el make.</p>
<p>Espero que con estas herramientas muchos os animéis a compilar y a introducir modificaciones, y corregir los muchos fallos que seguro he cometido. Cuanto más crezca la comunidad, mejor para todos.</p>
<p><a href="http://pinelo.com/blog/postimgs/2009/12/abanq-dpinelo-20091204.zip">Aquí teneís el archivo</a></p>
<p>Nota: Muchas de las modificaciones necesarias para compilar bajo Windows, las proporcionó juancrobles, a través del foro de AbanQ en Google, a  través de diversos hilos y del fichero Modificaciones_abanq2.3-v1.1.zip disponible <a href="http://groups.google.com/group/abanq/files">aqui</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/12/05/abanq-antes-de-seguir-como-se-compila/feed/</wfw:commentRss>
		<slash:comments>27</slash:comments>
		</item>
		<item>
		<title>Manifiesto en defensa de los derechos fundamentales en Internet</title>
		<link>http://www.pinelo.com/blog/2009/12/03/manifiesto-en-defensa-de-los-derechos-fundamentales-en-internet/</link>
		<comments>http://www.pinelo.com/blog/2009/12/03/manifiesto-en-defensa-de-los-derechos-fundamentales-en-internet/#comments</comments>
		<pubDate>Thu, 03 Dec 2009 09:14:57 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=314</guid>
		<description><![CDATA[Claro ejemplo de un legislador que quiere legislar sin saber qué legisla&#8230; Especialmente, cuando parte del legislador es parte interesada en la misma. Me sumo al clamor.
Ante la inclusión en el Anteproyecto de Ley de Economía sostenible de modificaciones legislativas que afectan al libre ejercicio de las libertades de expresión, información y el derecho de [...]]]></description>
			<content:encoded><![CDATA[<p>Claro ejemplo de un legislador que quiere legislar sin saber qué legisla&#8230; Especialmente, cuando parte del legislador es parte interesada en la misma. Me sumo al clamor.</p>
<p>Ante la inclusión en el Anteproyecto de Ley de Economía sostenible de modificaciones legislativas que afectan al libre ejercicio de las libertades de expresión, información y el derecho de acceso a la cultura a través de Internet, muchos periodistas, bloggers, usuarios, profesionales y creadores de Internet manifestamos nuestra firme oposición al proyecto, y declaramos que:</p>
<ol>
<li>Los derechos de autor no pueden situarse por encima de los derechos fundamentales de los ciudadanos, como el derecho a la privacidad, a la seguridad, a la presunción de inocencia, a la tutela judicial efectiva y a la libertad de expresión.</li>
<li>La suspensión de derechos fundamentales es y debe seguir siendo competencia exclusiva del poder judicial. Ni un cierre sin sentencia. Este anteproyecto, en contra de lo establecido en el artículo 20.5 de la Constitución, pone en manos de un órgano no judicial -un organismo dependiente del ministerio de Cultura-, la potestad de impedir a los ciudadanos españoles el acceso a cualquier página web.</li>
<li>La nueva legislación creará inseguridad jurídica en todo el sector tecnológico español, perjudicando uno de los pocos campos de desarrollo y futuro de nuestra economía, entorpeciendo la creación de empresas, introduciendo trabas a la libre competencia y ralentizando su proyección internacional.</li>
<li>La nueva legislación propuesta amenaza a los nuevos creadores y entorpece la creación cultural. Con Internet y los sucesivos avances tecnológicos se ha democratizado extraordinariamente la creación y emisión de contenidos de todo tipo, que ya no provienen prevalentemente de las industrias culturales tradicionales, sino de multitud de fuentes diferentes.</li>
<li>Los autores, como todos los trabajadores, tienen derecho a vivir de su trabajo con nuevas ideas creativas, modelos de negocio y actividades asociadas a sus creaciones. Intentar sostener con cambios legislativos a una industria obsoleta que no sabe adaptarse a este nuevo entorno no es ni justo ni realista. Si su modelo de negocio se basaba en el control de las copias de las obras y en Internet no es posible sin vulnerar derechos fundamentales, deberían buscar otro modelo.</li>
<li>Consideramos que las industrias culturales necesitan para sobrevivir alternativas modernas, eficaces, creíbles y asequibles y que se adecuen a los nuevos usos sociales, en lugar de limitaciones tan desproporcionadas como ineficaces para el fin que dicen perseguir.</li>
<li>Internet debe funcionar de forma libre y sin interferencias políticas auspiciadas por sectores que pretenden perpetuar obsoletos modelos de negocio e imposibilitar que el saber humano siga siendo libre.</li>
<li>Exigimos que el Gobierno garantice por ley la neutralidad de la Red en España, ante cualquier presión que pueda producirse, como marco para el desarrollo de una economía sostenible y realista de cara al futuro.</li>
<li>Proponemos una verdadera reforma del derecho de propiedad intelectual orientada a su fin: devolver a la sociedad el conocimiento, promover el dominio público y limitar los abusos de las entidades gestoras.</li>
<li>En democracia las leyes y sus modificaciones deben aprobarse tras el oportuno debate público y habiendo consultado previamente a todas las partes implicadas. No es de recibo que se realicen cambios legislativos que afectan a derechos fundamentales en una ley no orgánica y que versa sobre otra materia.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/12/03/manifiesto-en-defensa-de-los-derechos-fundamentales-en-internet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AbanQ: Integrando con JasperServer / JasperReports (I)</title>
		<link>http://www.pinelo.com/blog/2009/12/02/abanq-integrando-con-jasperserverjasperreports/</link>
		<comments>http://www.pinelo.com/blog/2009/12/02/abanq-integrando-con-jasperserverjasperreports/#comments</comments>
		<pubDate>Wed, 02 Dec 2009 09:35:20 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[ERP Open Source]]></category>
		<category><![CDATA[Librerías Qt]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=297</guid>
		<description><![CDATA[No me gusta el editor de informes de AbanQ&#8230; ni me gusta Kugar, ni me gusta que Qt Designer como gestor de informes. El primero, porque creo que está muy por detrás de otros motores de generación de informes que existen en el mundo del software libre (aparte de que está abandonado como proyecto, para [...]]]></description>
			<content:encoded><![CDATA[<p>No me gusta el editor de informes de AbanQ&#8230; ni me gusta <a href="http://www.thekompany.com/projects/kugar/">Kugar</a>, ni me gusta que Qt Designer como gestor de informes. El primero, porque creo que está muy por detrás de otros motores de generación de informes que existen en el mundo del software libre (aparte de que está abandonado como proyecto, para integrarlo en <a href="http://www.koffice.org">KOffice</a> dentro de <a href="http://www.koffice.org/kexi">Kexi</a>, y del segundo, porque no es un generador de informes.</p>
<p>Me gusta <a href="http://www.jasperforge.org/jasperreports">JasperReports</a>: Es una herramienta muy completa, en software libre y que rivaliza con cualquier herramienta de generación de informes (he sido usuario/programador durante mucho tiempo de Crystal Reports y Oracle Reports y realmente, para mis necesidades y según mi experiencia, JasperReports está por encima).</p>
<p>Además, JasperReports, tiene una interfaz de usuario para la edición de informes, <a href="http://jasperforge.org/projects/ireport">iReports</a>. Si la probáis, veréis que es muy muy superior a Kugar o QtDesigner (utilizando esta como generación de informes) y por encima de Crystal Repors, por ejemplo.</p>
<p>Por si esto no fuera poco, dispone de <a href="http://jasperforge.org/plugins/project/project_home.php?projectname=jasperserver">JasperServer</a> que es una aplicación, que actúa como servidor y/o repositorio de los diferentes documentos que generéis con las anteriores aplicaciones. En realidad, es mucho más, tratando de ser una herramienta de Bussiness Inteligence&#8230; permitiendo, junto con JasperAnalysis, ser una herramienta de análisis&#8230;.</p>
<p>La idea de funcionamiento con estas herramientas es la siguiente:</p>
<p>Se instala JasperServer en vuestro servidor. (Yo lo tengo instalado en un Jboss con PostgreSQL). Os proporciona una aplicación web en la que podéis gestionar, administrar y utilizar los informes que vayáis definiendo. No sólo eso, permite programar informes, enviando los resultados a correo electrónico, generación automática de los informes de formato HTML, PDF, Excel, OOffice&#8230;</p>
<p>Utilizáis iReport en vuestro ordenador para crear los informes (bien sea con los asistentes o desde cero)&#8230; los informes permiten agregarles código Java, con lo cual podéis hacer cualquier cosa, repito <em>cualquier cosa</em> con los informes. Genero documentos, que nunca diríais que están generados por una herramienta de gestión de informes&#8230; pensaríais que son cartas escritas por una persona. Desde iReport, y mediante webservices podéis interactuar y manipular el repositorio de JasperServer de una manera muy cómoda, con lo que la  creación, modificación o actualización de informes es una tarea sencilla y fácilmente asumible.</p>
<p>¿Cómo integrar esto con AbanQ? Queremos unir mundo Java y C++&#8230; además, queremos integrar un servidor de informes en nuestro AbanQ. Solución: WebServices. JasperServer permite acceder a todo su repositorio desde WebServices&#8230; Y eso es lo que haremos en AbanQ. Para ello, integraremos dentro de AbanQ, la librería de C++ gsoap.</p>
<p>Vamos a hacer una integración sencilla, pero funcional (cuando termine los artículos indicaré por dónde creo que deberían ir las líneas de mejora de esto).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/12/02/abanq-integrando-con-jasperserverjasperreports/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Un amigo</title>
		<link>http://www.pinelo.com/blog/2009/12/01/un-amigo/</link>
		<comments>http://www.pinelo.com/blog/2009/12/01/un-amigo/#comments</comments>
		<pubDate>Tue, 01 Dec 2009 18:59:00 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=304</guid>
		<description><![CDATA[Mi amigo, Emilio Márquez, acaba de dar esta noticia luchando con el linfoma. Emilio es amigo mío desde hace ya más de 20 años&#8230; en su época nos conectábamos a las BBS, o bien, nos pasábamos juegos (increíble The Day of the Tentacle), todo ello mientras estábamos en el instituto, entre rezo y &#8220;Salve Don [...]]]></description>
			<content:encoded><![CDATA[<p>Mi amigo, <a href="http://emiliomarquez.com">Emilio Márquez</a>, acaba de dar esta noticia <a href="http://emiliomarquez.com/2009/12/01/luchando-con-el-linfoma/">luchando con el linfoma</a>. Emilio es amigo mío desde hace ya más de 20 años&#8230; en su época nos conectábamos a las BBS, o bien, nos pasábamos juegos (increíble The Day of the Tentacle), todo ello mientras estábamos en el instituto, entre rezo y &#8220;Salve Don Bosco Santo&#8221; al&#8230; Ha habido época en las que hemos mantenido el contacto, y otras en las que menos. Pero eso no deja de ser una anécdota, porque lo considero un amigo.</p>
<p>Emilio, para cualquier cosa que necesites, ya sabes dónde estoy.</p>
<p>Un abrazo.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/12/01/un-amigo/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>AbanQ: Añadiendo una nueva columna de ordenación en el FLDataTable</title>
		<link>http://www.pinelo.com/blog/2009/11/30/abanq-anadiendo-una-nueva-columna-de-ordenacion-en-el-fldatatable/</link>
		<comments>http://www.pinelo.com/blog/2009/11/30/abanq-anadiendo-una-nueva-columna-de-ordenacion-en-el-fldatatable/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 07:00:43 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=285</guid>
		<description><![CDATA[El trabajo con sistemas ERP, y especialmente con AbanQ siempre se basan en la navegación por listados de datos (o grids de datos). No estoy muy seguro de que realmente ese sea el sistema más usable o adecuado, pero al menos es muy popular.
En mi afán de tratar de hacer la herramienta aún más usable, [...]]]></description>
			<content:encoded><![CDATA[<p>El trabajo con sistemas ERP, y especialmente con AbanQ siempre se basan en la navegación por listados de datos (o grids de datos). No estoy muy seguro de que realmente ese sea el sistema más usable o adecuado, pero al menos es muy popular.</p>
<p>En mi afán de tratar de hacer la herramienta aún más usable, echaba en falta una segunda columna de ordenación. Si mi empresa factura todo a través de AbanQ, el número de datos en esa rejilla es grande. Puede filtrarse utilizando el campo de filtro directo que AbanQ provee, pero ese campo de filtro también limita la ordenación de los datos filtrados que se presentan. Tener una segunda columna, como aparece en la imágen, puede ayudar aún más a localizar datos rápidamente.<br />
<img src="http://www.pinelo.com/blog/postimgs/2009/11/ordenaciondoble.jpg" alt="Doble columna de ordenacion" width="480" /></p>
<p>Bien, veamos cómo hacerlo.</p>
<p>Como siempre, tocaremos un poquito de código de AbanQ&#8230; y de Qt 3.3.</p>
<p><span id="more-285"></span>Al lío:</p>
<p>Primero añadir, en el FLWidgetTableDB.ui mediante el Qt Designer 3.3 el segundo combobox de ordenación. Lo vamos a llamar comboBoxFieldToSearch2 (muy original&#8230; :P). También debemos añadir en el Designer un Slot,</p>
<p>void putSecondCol (const QString &amp;)</p>
<p>y añadimos una conexión:</p>
<pre>Sender: comboBoxFieldToSearch2
Signal: activated (const QString &amp;)
Receiver: FLWidgetTableDB
Slot: putSecondCol(const QString &amp;)</pre>
<p>Vale. Hemos terminado en Designer. Toca código en C++ de AbanQ.</p>
<p>Definimos ese slot que acabamos de crear en Designer en el código de AbanQ (en esto sí que han ganado las Qt 4 eliminando la definición de slots en el designer&#8230;)</p>
<pre>Archivo: FLObjectFactory.h
....
class FL_EXPORT FLTableDBInterface : public QObject {
  ...</pre>
<pre>public:
...
/**
dpinelo: Establece el segundo campo de búsqueda
*/
void putSecondCol( const QString &amp; c ) {
obj_-&gt;putSecondCol( c );
}</pre>
<p>De esta forma, cuando el usuario cliquee en el combo, se producirá la ordenación por el segundo campo.</p>
<pre>Archivo: FLTableDB.h
...
class FL_EXPORT FLTableDB: public FLWidgetTableDB {
  ...
  private:
  /**
  dpinelo Indica el número de columna por la que ordenar los registros
   */
  int sortColumn2_;
  /**
  dpinelo Indica el sentido ascendente o descendente del la ordenacion actual de los registros
   */
  bool orderAsc2_;

  protected slots:
  ...
  // dpinelo
  void putSecondCol( int c );
  void putSecondCol( const QString &amp; c );</pre>
<p>Y ahora vamos al código de verdad. Sólo he querido añadir la funcionalidad de ordenación secundaria&#8230; de modo que sigo dejando la mayor parte de la responsabilidad de la ordenación al código de AbanQ. Busco que el usuario pueda visualizar por una columna diferente, y cambiar el orden de visualización en esa columna.</p>
<p>Para este fichero, mejor pongo un diff &#8230;. Ya digo, tengo que organizar todas mis diferencias de código y publicarlas aquí adecuadamente. Espero poder hacerlo la semana que viene, antes de publicar las modificaciones de integración con OpenKM y JasperServer</p>
<pre>Index: abanq/src/flbase/FLTableDB.cpp
===================================================================
--- abanq/src/flbase/FLTableDB.cpp	(revisión: 117)
+++ abanq/src/flbase/FLTableDB.cpp	(copia de trabajo)
@@ -89,7 +89,9 @@
     tabDataLayout-&gt;addWidget( tableRecords_ );
     setTabOrder( tableRecords_, lineEditSearch );
     setTabOrder( lineEditSearch, comboBoxFieldToSearch );
-    lineEditSearch-&gt;installEventFilter( this );
+	// dpinelo
+	setTabOrder( comboBoxFieldToSearch, comboBoxFieldToSearch2 );
+	lineEditSearch-&gt;installEventFilter( this );
     tableRecords_-&gt;installEventFilter( this );
     connect( tableRecords_-&gt;horizontalHeader(), SIGNAL( clicked( int ) ), this, SLOT( switchSortOrder( int ) ) );
   }
@@ -109,7 +111,9 @@
     tabDataLayout-&gt;addWidget( tableRecords_ );
     setTabOrder( tableRecords_, lineEditSearch );
     setTabOrder( lineEditSearch, comboBoxFieldToSearch );
-    lineEditSearch-&gt;installEventFilter( this );
+	// dpinelo
+	setTabOrder( comboBoxFieldToSearch, comboBoxFieldToSearch2 );
+	lineEditSearch-&gt;installEventFilter( this );
     tableRecords_-&gt;installEventFilter( this );
   }
   FLSqlCursor * tCursor = tableRecords_-&gt;cursor();
@@ -127,7 +131,8 @@
 }
 bool FLTableDB::eventFilter( QObject * obj, QEvent * ev ) {
-  if ( !tableRecords_ || !lineEditSearch || !comboBoxFieldToSearch || !cursorVisualizacion_ )
+	// dpinelo
+  if ( !tableRecords_ || !lineEditSearch || !comboBoxFieldToSearch || !cursorVisualizacion_ || !comboBoxFieldToSearch2 )
     return FLWidgetTableDB::eventFilter( obj, ev );
   if ( ev-&gt;type() == QEvent::KeyPress &amp;&amp; obj == tableRecords_ ) {
     QKeyEvent * k = static_cast&lt;QKeyEvent *&gt;( ev );
@@ -165,21 +170,55 @@
 }
 void FLTableDB::putFirstCol( const QString &amp; c ) {
-        // dpinelo: No entiendo cómo el código funciona sin esto...
-        FLTableMetaData *tMD = cursorVisualizacion_-&gt;metadata();
-        cursorVisualizacion_-&gt;setSort( cursorVisualizacion_-&gt;index( tMD-&gt;fieldAliasToName( c ) ) );
-  moveCol( c, QString::null );
+	// dpinelo: Introducimos la ordenación por los dos campos
+	QSqlIndex sort;
+	FLTableMetaData *tMD = cursorVisualizacion_-&gt;metadata();
+	sort.append ( tMD-&gt;fieldAliasToName( c ) );
+	sort.append ( tMD-&gt;fieldAliasToName( comboBoxFieldToSearch2-&gt;text( comboBoxFieldToSearch2-&gt;currentItem() ) ) );
+	cursorVisualizacion_-&gt;setSort( sort );
+	moveCol( c, QString::null );
 }
 void FLTableDB::putFirstCol( int c ) {
-        // dpinelo: No entiendo cómo el código funciona sin esto...
-        QHeader *horizHeader = tableRecords()-&gt;horizontalHeader();
-        FLTableMetaData *tMD = cursorVisualizacion_-&gt;metadata();
-        cursorVisualizacion_-&gt;setSort( cursorVisualizacion_-&gt;index( tMD-&gt;fieldAliasToName( horizHeader-&gt;label(c) ) ) );
-  moveCol( c, 0 );
+	// dpinelo: Introducimos la ordenación por los dos campos
+	QSqlIndex sort;
+	QHeader *horizHeader = tableRecords()-&gt;horizontalHeader();
+	FLTableMetaData *tMD = cursorVisualizacion_-&gt;metadata();
+	sort.append ( tMD-&gt;fieldAliasToName( horizHeader-&gt;label(c) ) );
+	sort.append ( tMD-&gt;fieldAliasToName( comboBoxFieldToSearch2-&gt;text( comboBoxFieldToSearch2-&gt;currentItem() ) ) );
+	cursorVisualizacion_-&gt;setSort( sort );
+	moveCol( c, 0 );
 }
-void FLTableDB::moveCol( const QString &amp; from, const QString &amp; to ) {
+// dpinelo
+void FLTableDB::putSecondCol( const QString &amp; c ) {
+    // dpinelo: Introducimos la ordenación por los dos campos
+	QSqlIndex sort;
+	FLTableMetaData *tMD = cursorVisualizacion_-&gt;metadata();
+	sort.append ( tMD-&gt;fieldAliasToName( comboBoxFieldToSearch-&gt;text( comboBoxFieldToSearch-&gt;currentItem() ) ) );
+	sort.append ( tMD-&gt;fieldAliasToName( c ) );
+	cursorVisualizacion_-&gt;setSort( sort );
+	// Aprovechamos que siempre, el combo box de filtro presenta sus items ordenados igual que el grid
+	if ( c != comboBoxFieldToSearch-&gt;text( comboBoxFieldToSearch-&gt;currentItem() + 1 ) ) {
+		moveCol( c, comboBoxFieldToSearch-&gt;text( comboBoxFieldToSearch-&gt;currentItem() + 1 ), false );
+	}
+}
+
+void FLTableDB::putSecondCol( int c ) {
+    // dpinelo: No entiendo cómo el código funciona sin esto...
+	QSqlIndex sort;
+	QHeader *horizHeader = tableRecords()-&gt;horizontalHeader();
+	FLTableMetaData *tMD = cursorVisualizacion_-&gt;metadata();
+	sort.append ( tMD-&gt;fieldAliasToName( comboBoxFieldToSearch-&gt;text( comboBoxFieldToSearch-&gt;currentItem() ) ) );
+	sort.append (  tMD-&gt;fieldAliasToName( horizHeader-&gt;label(c) ) );
+	cursorVisualizacion_-&gt;setSort( sort );
+	// Aprovechamos que siempre, el combo box de filtro presenta sus items ordenados igual que el grid
+	if ( c != ( comboBoxFieldToSearch-&gt;currentItem() + 1 ) ) {
+		moveCol( c, ( comboBoxFieldToSearch-&gt;currentItem() + 1), false );
+	}
+}
+
+void FLTableDB::moveCol( const QString &amp; from, const QString &amp; to, bool firstSearch ) {
   if ( !topWidget || !cursorVisualizacion_ || !showed )
     return ;
@@ -210,11 +249,12 @@
 #endif
     return;
   }
-  moveCol( iFrom - sortColumn_, iTo - sortColumn_ );
+  moveCol( iFrom - sortColumn_, iTo - sortColumn_, firstSearch );
 }
-void FLTableDB::moveCol( int from, int to ) {
-  if ( from == to || !lineEditSearch || !comboBoxFieldToSearch || !cursorVisualizacion_ )
+void FLTableDB::moveCol( int from, int to, bool firstSearch ) {
+	// dpinelo
+  if ( from == to || !lineEditSearch || !comboBoxFieldToSearch || !cursorVisualizacion_ || !comboBoxFieldToSearch2 )
     return ;
   if ( comboBoxFieldToSearch-&gt;text( from ) == "*" || comboBoxFieldToSearch-&gt;text( to ) == "*" )
@@ -252,10 +292,13 @@
   refresh( true );
   if ( !textSearch.isEmpty() ) {
     refresh( false, true );
-    disconnect( lineEditSearch, SIGNAL( textChanged( const QString&amp; ) ), this, SLOT( filterRecords( const QString&amp; ) ) );
-    lineEditSearch-&gt;setText( textSearch );
-    connect( lineEditSearch, SIGNAL( textChanged( const QString&amp; ) ), this, SLOT( filterRecords( const QString&amp; ) ) );
-    lineEditSearch-&gt;selectAll();
+	if ( firstSearch ) {
+		disconnect( lineEditSearch, SIGNAL( textChanged( const QString&amp; ) ), this, SLOT( filterRecords( const QString&amp; ) ) );
+		// dpinelo
+    	lineEditSearch-&gt;setText( textSearch );
+    	connect( lineEditSearch, SIGNAL( textChanged( const QString&amp; ) ), this, SLOT( filterRecords( const QString&amp; ) ) );
+	    lineEditSearch-&gt;selectAll();
+	}
     seekCursor();
     QTimer::singleShot( 0, tableRecords_, SLOT( ensureRowSelectedVisible() ) );
   } else
@@ -329,7 +372,9 @@
 }
 void FLTableDB::refresh( const bool refreshHead, const bool refreshData ) {
-  if ( !lineEditSearch || !comboBoxFieldToSearch || !cursorVisualizacion_ || ( topWidget &amp;&amp; !topWidget-&gt;isShown() ) )
+	// dpinelo
+  if ( !lineEditSearch || !comboBoxFieldToSearch || !cursorVisualizacion_ || ( topWidget &amp;&amp; !topWidget-&gt;isShown() ) ||
+	 !comboBoxFieldToSearch2 )
     return ;
   FLTableMetaData * tMD = cursorVisualizacion_-&gt;metadata();
@@ -349,7 +394,7 @@
                                           true, 0, 0, false, false, false, QVariant(), false, QString::null, true, false, false );
         tMD-&gt;addFieldMD( fieldCheck );
       }
-          if ( !cursorVisualizacion_-&gt;contains( fieldNameCheckColumn_ ) ) {
+      if ( !cursorVisualizacion_-&gt;contains( fieldNameCheckColumn_ ) ) {
                   QSqlRecord recordCursor( *( cursorVisualizacion_-&gt;editBuffer() ) );
         QSqlFieldInfo fakeFieldInfo( fieldNameCheckColumn_, QVariant::Bool, -1, -1, -1, QVariant(), 0, false, false, true );
                 cursorVisualizacion_-&gt;clear();
@@ -432,9 +477,11 @@
 	comboBoxFieldToSearch-&gt;insertItem( "*" );
     horizHeader-&gt;setClickEnabled( false );
     horizHeader-&gt;setClickEnabled( true, sortColumn_ );
-    horizHeader-&gt;setSortIndicator( -1, Qt::Ascending );
+	// dpinelo
+	horizHeader-&gt;setClickEnabled( true, sortColumn2_ );
+	horizHeader-&gt;setSortIndicator( -1, Qt::Ascending );
     horizHeader-&gt;setSortIndicator( sortColumn_, ( orderAsc_ ? Qt::Ascending : Qt::Descending ) );
-    horizHeader-&gt;show();
+	horizHeader-&gt;show();
   }
   if ( refreshData || sender() ) {
@@ -1013,10 +1060,15 @@
   aliasCheckColumn_ = t;
 }
-void FLTableDB::switchSortOrder( int ) {
-  orderAsc_ = !orderAsc_;
-  tableRecords()-&gt;hide();
-  refresh( true, true );
+void FLTableDB::switchSortOrder( int col ) {
+  // dpinelo
+	if ( col == 0 ) {
+		orderAsc_ = !orderAsc_;
+	} else if ( col == 1 ) {
+		orderAsc2_ = !orderAsc2_;
+	}
+	tableRecords()-&gt;hide();
+	refresh( true, true );
 }
 void FLTableDB::activeTabData( bool on ) {</pre>
<p>Eah&#8230; por ahí va eso.</p>
<pre><!--EndFragment--></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/11/30/abanq-anadiendo-una-nueva-columna-de-ordenacion-en-el-fldatatable/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AbanQ: Ampliando la funcionalidad de los optionslist</title>
		<link>http://www.pinelo.com/blog/2009/11/27/abanq-ampliando-la-funcionalidad-de-los-optionslist/</link>
		<comments>http://www.pinelo.com/blog/2009/11/27/abanq-ampliando-la-funcionalidad-de-los-optionslist/#comments</comments>
		<pubDate>Fri, 27 Nov 2009 10:55:00 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=280</guid>
		<description><![CDATA[Una de las ayudas que tiene AbanQ a la hora de construir formularios que presentan registros asociados, es sin duda la posibilidad de definir OptionsList. Para una columna de base de datos, podemos definir un conjunto de posibles valores que se almacenarán en la misma, utilizando el tag optionslist dentro de la definición del campo, [...]]]></description>
			<content:encoded><![CDATA[<p>Una de las ayudas que tiene AbanQ a la hora de construir formularios que presentan registros asociados, es sin duda la posibilidad de definir OptionsList. Para una columna de base de datos, podemos definir un conjunto de posibles valores que se almacenarán en la misma, utilizando el tag optionslist dentro de la definición del campo, en el .mtd de la tabla correspondiente. AbanQ, automáticamente, creará un combobox en la vista (el formulario) para que el usuario sólo pueda escoger uno de esos valores.</p>
<p>Pero esta solución, aunque muy buena, no es óptima. Si la columna es un campo de tipo varchar (o string, como se le llama en AbanQ), el optionslist, guarda siempre en esa columna la cadena entera. Supongamos, por ejemplo, una columna &#8220;tipo_articulo&#8221;  y un options list</p>
<pre>&lt;optionslist&gt;Articulo prefabricado, Materia prima, Producto intermedio, Producto terminado&lt;/optionslist&gt;</pre>
<p>El detalle, es que esas cadenas se almacenan constantemente en la columna de la base de datos. Eso no es mayor problema si queremos indexar sobre esa columna, por ejemplo&#8230; Quizás es más interesante, el poder guardar en lugar de esos valores, otros, según la siguiente relación</p>
<pre>&lt;optionslist&gt;Articulo prefabricado, Materia prima, Producto intermedio, Producto terminado&lt;/optionslist&gt;
&lt;optionsvalues&gt;1,2,3,4&lt;/optionsvalues&gt;</pre>
<p>De esta forma, cuando el usuario escoga en el combo box &#8220;Artículo prefabricado&#8221; en la columna de la base de datos, se guardará un 1.</p>
<p><span id="more-280"></span>¿Cómo se puede implementar esto en AbanQ? Es relativamente sencillo. Os pongo a continuación las diferencias entre archivos.</p>
<pre>Index: abanq/src/flbase/FLDataTable.cpp
===================================================================
--- abanq/src/flbase/FLDataTable.cpp	(revisión: 114)
+++ abanq/src/flbase/FLDataTable.cpp	(copia de trabajo)
@@ -233,18 +233,37 @@
       break;
       case QVariant::String: {
-        text = field-&gt;value().toString();
+        QString textView;
+		text = field-&gt;value().toString();
         if ( fieldTMD-&gt;hasOptionsList() ) {
-          QStringList ol( fieldTMD-&gt;optionsList() );
-          if ( !ol.contains( text ) ) {
-            QVariant defVal( fieldTMD-&gt;defaultValue() );
-            if ( defVal.isValid() )
-              text = defVal.toString();
-            else
-              text = ol.first();
-          }
-          text = FLUtil::translate( "MetaData", text );
-        }
+			// dpinelo: El valor del campo lo buscamos en la lista de valores si está definida
+			if ( fieldTMD-&gt;optionsValues().isEmpty() ) {
+				QStringList ol( fieldTMD-&gt;optionsList() );
+				if ( !ol.contains( text ) ) {
+					QVariant defVal( fieldTMD-&gt;defaultValue() );
+					if ( defVal.isValid() )
+						text = defVal.toString();
+					else
+						text = ol.first();
+				}
+				text = FLUtil::translate( "MetaData", text );
+			} else {
+				QStringList ol( fieldTMD-&gt;optionsValues() );
+				QStringList olList ( fieldTMD-&gt;optionsList() );
+			// ¿Contiene el valor?
+				if ( !ol.contains( text ) ) {
+					QVariant defVal( fieldTMD-&gt;defaultValue() );
+					if ( defVal.isValid() )
+						textView = defVal.toString();
+					else {
+						textView = olList.first();
+					}
+				} else {
+					textView = olList[ol.findIndex(text)];
+				}
+			}
+			text = FLUtil::translate( "MetaData", textView );
+		}
         p-&gt;drawText( 2, 2, cr.width() - 4, cr.height() - 4, fieldAlignment( field ), text );
       }
       break;

Index: abanq/src/flbase/FLFieldDB.cpp
===================================================================
--- abanq/src/flbase/FLFieldDB.cpp	(revisión: 114)
+++ abanq/src/flbase/FLFieldDB.cpp	(copia de trabajo)
@@ -294,7 +294,12 @@
   QString tAux( t );

   if ( ol &amp;&amp; editor_ )
-    tAux = field-&gt;optionsList()[ ::qt_cast( editor_ )-&gt;currentItem()];
+	  // dpinelo
+	  if ( field-&gt;optionsValues().isEmpty() ) {
+	  	tAux = field-&gt;optionsList()[ ::qt_cast( editor_ )-&gt;currentItem()];
+	  } else {
+		tAux = field-&gt;optionsValues()[ ::qt_cast( editor_ )-&gt;currentItem()];
+	  }

   if ( !cursor_-&gt;bufferIsNull( fieldName_ ) ) {
     if ( tAux == cursor_-&gt;valueBuffer( fieldName_ ).toString() ) {
@@ -403,12 +408,25 @@

   if ( field-&gt;hasOptionsList() ) {
     int idxItem = -1;
-    if ( v.type() == QVariant::String )
-      idxItem = field-&gt;optionsList().findIndex( v.toString() );
-    if ( idxItem == -1 )
+    if ( v.type() == QVariant::String ) {
+		// dpinelo
+		if ( field-&gt;optionsValues().isEmpty() ) {
+			idxItem = field-&gt;optionsList().findIndex( v.toString() );
+		} else {
+			idxItem = field-&gt;optionsValues().findIndex( v.toString() );
+		}
+	}
+	if ( idxItem == -1 )
       idxItem = v.toInt();
     ::qt_cast( editor_ )-&gt;setCurrentItem( idxItem );
-    updateValue( ::qt_cast( editor_ )-&gt;currentText() );
+	if ( field-&gt;optionsValues().isEmpty() ) {
+		updateValue( ::qt_cast( editor_ )-&gt;currentText() );
+	} else {
+		int idxValue = field-&gt;optionsList().findIndex( ::qt_cast( editor_ )-&gt;currentText() );
+		if ( idxValue != -1 ) {
+			updateValue( field-&gt;optionsValues()[idxValue] );
+		}
+	}
 #ifdef FL_TEST
     ::qt_cast( qApp )-&gt;continueTesting( FLTester::FIELD_READY, this-&gt;name() );
 #endif
@@ -1317,19 +1335,39 @@
       break;

     case QVariant::String: {
-      bool doHome = false;
-      if ( ol ) {
-        if ( v.toString() == ::qt_cast( editor_ )-&gt;currentText() )
-          return ;
-      } else {
-        if ( v.toString() == ::qt_cast( editor_ )-&gt;text() )
-          return ;
-        doHome = ( ::qt_cast( editor_ )-&gt;text().isEmpty() );
-      }
+		bool doHome = false;
+		if ( ol ) {
+			// dpinelo
+			if ( field-&gt;optionsValues().isEmpty() ) {
+				if ( v.toString() == ::qt_cast( editor_ )-&gt;currentText() ) {
+					return ;
+				} else {
+					if ( v.toString() == ::qt_cast( editor_ )-&gt;text() )
+						return ;
+					doHome = ( ::qt_cast( editor_ )-&gt;text().isEmpty() );
+				}
+			} else {
+				int index = field-&gt;optionsValues().findIndex( v.toString() );
+				if ( index != -1 ) {
+					QString comp = field-&gt;optionsList()[index];
+					if ( comp == ::qt_cast( editor_ )-&gt;currentText() ) {
+						return ;
+					} else {
+						if ( comp == ::qt_cast( editor_ )-&gt;text() )
+							return ;
+						doHome = ( ::qt_cast( editor_ )-&gt;text().isEmpty() );
+					}
+				}
+			}
+		}
 	  disconnect( editor_, SIGNAL( textChanged( const QString &amp; ) ), this, SLOT( updateValue( const QString &amp; ) ) );
       if ( v.isValid() &amp;&amp; !v.isNull() ) {
-        if ( ol )
-          ::qt_cast( editor_ )-&gt;setCurrentItem( field-&gt;optionsList().findIndex( v.toString() ) );
+        if ( ol ) {
+			// dpinelo
+			int index = field-&gt;optionsValues().findIndex( v.toString() );
+			::qt_cast( editor_ )-&gt;setCurrentItem( index );
+		//		::qt_cast( editor_ )-&gt;setCurrentItem( field-&gt;optionsList().findIndex( v.toString() ) );
+		}
         else
           ::qt_cast( editor_ )-&gt;setText( v.toString() );
       } else {
@@ -1534,8 +1572,11 @@
         doHome = ( ::qt_cast( editor_ )-&gt;text().isEmpty() );
 	  disconnect( editor_, SIGNAL( textChanged( const QString &amp; ) ), this, SLOT( updateValue( const QString &amp; ) ) );
       if ( !null ) {
-        if ( ol )
-          ::qt_cast( editor_ )-&gt;setCurrentItem( field-&gt;optionsList().findIndex( v.toString() ) );
+        if ( ol ) {
+			// dpinelo
+			int index = field-&gt;optionsValues().findIndex( v.toString() );
+			::qt_cast( editor_ )-&gt;setCurrentItem( index );
+		}
         else
           ::qt_cast( editor_ )-&gt;setText( v.toString() );
       } else {

Index: abanq/src/flbase/FLFieldMetaData.cpp
===================================================================
--- abanq/src/flbase/FLFieldMetaData.cpp	(revisión: 114)
+++ abanq/src/flbase/FLFieldMetaData.cpp	(copia de trabajo)
@@ -59,6 +59,8 @@
     delete relationM1_;

   optionsList_.clear();
+  // dpinelo
+  optionsValues_.clear();
 }

 FLFieldMetaData::FLFieldMetaData( const QString &amp; n, const QString &amp; a, bool aN, bool iPK,
@@ -196,3 +198,24 @@
   }
   return type;
 }
+
+// dpinelo
+void FLFieldMetaData::setOptionsValues(const QString &amp; ol)
+{
+	d-&gt;optionsValues_.clear();
+	QString olTranslated = ol;
+	if ( ol.contains( "QT_TRANSLATE_NOOP" ) ) {
+		QStringList components = QStringList::split( ';', olTranslated );
+		QString component;
+
+		olTranslated = "";
+		for ( int i = 0; i &lt; components.count(); i++ ) {
+			component = components[ i ];
+			component = component.mid( 30, component.length() - 32 );
+			if ( i &gt; 0 )
+				olTranslated += ",";
+			olTranslated += component;
+		}
+	}
+	d-&gt;optionsValues_ = QStringList::split( ',', olTranslated );
+}

Index: abanq/src/flbase/FLFieldMetaData.h
===================================================================
--- abanq/src/flbase/FLFieldMetaData.h	(revisión: 114)
+++ abanq/src/flbase/FLFieldMetaData.h	(copia de trabajo)
@@ -384,6 +384,14 @@
   QStringList optionsList();

   /**
+  dpinelo: La lista de opciones para el campo se compone del valor que se guarda
+  y del valor que se muestra
+
+  @return Lista de valores del campo
+  */
+  QStringList optionsValues();
+
+  /**
   Establece la lista de opciones para el campo

   @param ol Cadena de texto con la opciones para el campo
@@ -392,6 +400,14 @@
   void setOptionsList( const QString &amp; ol );

   /**
+  dpinelo: Establece la lista de valores para el campo
+
+  @param ol Cadena de texto con la opciones para el campo
+  separada por comas, p.e. "opcion1,opcion2,opcion3"
+   */
+  void setOptionsValues( const QString &amp; ol );
+
+  /**
   Obtiene si el campo es de tipo Check
   */
   bool isCheck() const;
@@ -572,6 +588,11 @@
   Lista de opciones para el campo
   */
   QStringList optionsList_;
+
+  /**
+  dpinelo: Lista de valores para el campo
+  */
+  QStringList optionsValues_;

   /**
   Indica si las modificaciones del campo se hacen fuera de cualquier transaccion.
@@ -746,6 +767,11 @@
   return d-&gt;optionsList_;
 }

+// dpinelo
+inline QStringList FLFieldMetaData::optionsValues() {
+	return d-&gt;optionsValues_;
+}
+
 inline bool FLFieldMetaData::isCompoundKey() const {
   return d-&gt;isCompoundKey_;
 }

Index: abanq/src/flbase/FLManager.cpp
===================================================================
--- abanq/src/flbase/FLManager.cpp	(revisión: 114)
+++ abanq/src/flbase/FLManager.cpp	(copia de trabajo)
@@ -302,6 +302,8 @@
   bool aN = true, iPK = true, c = false, iNX = false, uNI = false, coun = false, oT = false, vG = true;
   int t = QVariant::Int, l = 0, pI = 4, pD = 0;
   QVariant dV = QVariant();
+  // dpinelo
+  QString olValues;

   QDomNode no = field-&gt;firstChild();

@@ -446,7 +448,13 @@
         no = no.nextSibling();
         continue;
       }
-    }
+	  // dpinelo
+	  if ( e.tagName() == "optionsvalues" ) {
+		  olValues = e.text();
+		  no = no.nextSibling();
+		  continue;
+	  }
+	}
     no = no.nextSibling();
   }

@@ -455,6 +463,10 @@

   if ( !ol.isEmpty() )
     f-&gt;setOptionsList( ol );
+
+  // dpinelo
+  if ( !olValues.isEmpty() )
+	  f-&gt;setOptionsValues ( olValues );

   no = field-&gt;firstChild();</pre>
<p>Detalles: Los números de líneas quizás no sean los correctos. Mi código está algo modificado, pero al menos sí aparecen el código que he modificado. En cualquier caso, voy a extraer todos las diferencias entre mi versión de AbanQ y la publicada por InfoSial indicando qué he añadido, para que así podáis sincronizar vuestro código con el mío sin problemas&#8230; pero necesito algo de tiempo para hacerlo.</p>
<pre><!--EndFragment--></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/11/27/abanq-ampliando-la-funcionalidad-de-los-optionslist/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perdón a todos&#8230;</title>
		<link>http://www.pinelo.com/blog/2009/11/27/perdon-a-todos/</link>
		<comments>http://www.pinelo.com/blog/2009/11/27/perdon-a-todos/#comments</comments>
		<pubDate>Fri, 27 Nov 2009 09:42:48 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=278</guid>
		<description><![CDATA[Perdonadme&#8230; Muchos meses de abandono del blog. Los motivos han sido personales y de toda índole. Algunos para bien, otros para mal. En cualquier caso, el blog ha sido el perjudicado.
Propósito de enmienda: Al menos una entrada semanal&#8230;
Saludos
]]></description>
			<content:encoded><![CDATA[<p>Perdonadme&#8230; Muchos meses de abandono del blog. Los motivos han sido personales y de toda índole. Algunos para bien, otros para mal. En cualquier caso, el blog ha sido el perjudicado.</p>
<p>Propósito de enmienda: Al menos una entrada semanal&#8230;</p>
<p>Saludos</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/11/27/perdon-a-todos/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ahorrando prácticamente: Implantemos una centralita Asterisk (II)</title>
		<link>http://www.pinelo.com/blog/2009/07/08/ahorrando-practicamente-implantemos-una-centralita-asterisk-ii/</link>
		<comments>http://www.pinelo.com/blog/2009/07/08/ahorrando-practicamente-implantemos-una-centralita-asterisk-ii/#comments</comments>
		<pubDate>Wed, 08 Jul 2009 15:27:02 +0000</pubDate>
		<dc:creator>David Pinelo</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.pinelo.com/blog/?p=272</guid>
		<description><![CDATA[Llegados a este punto ya tenemos configurado el entorno alrededor de Asterisk: Tenemos un ordenador, con las tarjetas digitalizadoras de voz instaladas, conectado a nuestra red (si Trixbox ha cogido sólo IP por existir en la red un servidor DHCP, será muy interesante que la IP de la centralita sea fija&#8230; salvo que queráis configurar [...]]]></description>
			<content:encoded><![CDATA[<p>Llegados a este punto ya tenemos configurado el entorno alrededor de Asterisk: Tenemos un ordenador, con las tarjetas digitalizadoras de voz instaladas, conectado a nuestra red (si Trixbox ha cogido sólo IP por existir en la red un servidor DHCP, será muy interesante que la IP de la centralita sea fija&#8230; salvo que queráis configurar adecuadamente los DNS de vuestro servidor de red, pero eso es otra historia), y tenemos configurado el driver Zaptel para que Asterisk pueda utilizar las tarjetas OpenVox.</p>
<p>A partir de este momento, podemos distinguir a grandes rasgos 3 grupos principales de elementos a configurar:</p>
<ul>
<li>La configuración del módulo encargado de utilizar las líneas analógicas (las de teléfono de toda la vida): Zapata</li>
<li>Configuración general de Asterisk</li>
<li>Dialplan (el conjunto de extensiones y reglas internas a nuestra centralita: nuestra instalación local)</li>
</ul>
<p><span id="more-272"></span>Veamos la configuración de Zapata. Ello se hace editando directamente el fichero /etc/asterisk/zapata.conf . Hay detalles muy importantes en él, que pueden dar más de un quebradero de cabeza. Os adjunto el zapata.conf que me función a mí, profusamente comentado. Digo el que me funcionó a mí, porque dependiendo de vuestra línea habréis de configurar una u otras cosas (en mi caso, por ejemplo, casi no tuve que configurar nada de cancelación de eco, por ejemplo).</p>
<p>La idea en este fichero es la de definir &#8220;canales&#8221;  por los que circulará la voz para Asterisk. En este caso, configuraremos un único canal (g0) que podrá ubicar 4 comunicaciones de voz con el exterior (tantas como puertos tiene la tarjeta digitalizadora, y como líneas de teléfono tengo contratadas).</p>
<pre>;
; Zapata telephony interface
;
; Configuration file

[trunkgroups]

[channels]
; Habilita la recepcion del Caller ID (es decir, del número
; del llamante). Sin esta opción, Asterisk no será capaz de
; obtener el número de teléfono de quién nos llama
usercallerid=yes

; Usando el valor asreceived en callerid, te pasa directamente
; la identificación que viene en la linea por el provedor de
; telefonía
callerid=asreceived

; Permitimos que en las llamadas salientes se indique nuestro
; número
hidecallerid=no

relaxdtmf=yes

; Esta opción es válida cuando tenemos una línea con ADSI
; (Analog Display Services Interface)
adsi=yes

; En esta opción se especifica en qué momento la operadora
; nos envía el identificador de llamada.
; El número que aparece es el número de tonos (de rings)
sendcalleridafter=2

; Esta opción es la que nos proporcionará identificación de
; llamada y es también vital. Sin ella, se produce algo raro
; en las llamadas: Al llamar se produce un evento
; "Starting switch on Zap" y un hangup justo antes de contestar;
; la llamada. Al contestar Asterisk la llamada se produce
; otro "Starting switch on Zap" (Es como si hubiera dos llamadas
; dentro de una misma, una justo antes de contestar y otra a
; posteriori). Sin este parámetro, el CALLERID(num)
; sólo es detectado en el primer evento, y cuando se
; descuelga, se ha perdido el CALLERID.
; Esta opción, hace que el callerid se mantenga en el segundo
; evento. Os dejo parte de la documentación original en inglés:
;
; distinctiveringaftercid = yes | no
;
; If this is enabled a short initial ring is generated
; followed by caller;  ID and then the normal or distinctive
; ring cadence is used. This type of ringing is used in
; Australia and on some UK cable company exchanges. If this
; is turned off then sendcalleridafter MUST be correct and all
; user defined cadences MUST have a suitable silence marked for
; caller ID.
distinctiveringaftercid=yes

; Si el caller ID se envía antes del primer ring, la operadora
; señalizará el inicio de ese envío con una inversión de la
; polaridad en la línea: con esta opción, nuestro hardware
; local, lo detectará.
polarityevents=yes

; Esta opcion da graves problemas en la deteccion del
; "descolgado", ya que es una opción que sólo funciona
; adecuadamente en EEUU. Sirve para poder identificar el
; fin de una llamada. (Sí, hay ciertas ocasiones en las que
; Asterisk puede no detectar que en el extremo remoto
; se ha colgado la llamada)
;callprogress=yes

; Esta opcion hará que suenen unos pips en las llamadas,
; cuando se este haciendo una llamada por un canal, y se
; produzca otra llamada por el mismo canal. (La opción de llamada en espera)
callwaiting=yes

; Puesta a yes establece que Asterisk enviara el Caller
; ID al telefono durante la indicación de una llamada en
; espera. Requiere que la anterior opción este a yes.
callwaitingcallerid=yes

; Esta opción permite poner una llamada en espera.
; Estamos hablando del canal analógico zaptel, nada
; tiene que ver con las extensiones que se crearán después.
threewaycalling=no

; This option has effect only when threewaycalling=yes.
; If threewaycalling=yes and transfer=yes, then once you've
; placed a call on hold with a hook flash, you can transfer
; that call to another extension by dialling the extension
; and hanging up
transfer=yes

; Cuando esta opción esté a yes, al transferir una llamada
; a otro teléfono, el CallerID original se transferirá también.
useincommingcalleridonzaptransfer=yes

; Opciones que indica el tipo de señalización que utiliza
; tu operadora para transmitir el callerId.
; En España (Telefónica) son estas:
cidsignalling=bell
cidstart=ring
;
; Type of caller ID signalling in use
;     bell     = bell202 as used in US
;     v23      = v23 as used in the UK
;     v23_jp   = v23 as used in Japan
;     dtmf     = DTMF as used in Denmark, Sweden and Netherlands
;     smdi     = Use SMDI for callerid.  Requires SMDI to be
;                enabled (usesmdi).
;
;cidsignalling=bell
;     ring     = a ring signals the start
;     polarity = polarity reversal signals the start
; 

; Opciones que sirven para que Asterisk detecte si en
; el otro extremo (a quien realizamos la llamada)
; está ocupado
busydetect=yes
busycount=6

; Para tener todo en español
language=es

; Definirá el contexto de este canal: Será utilizado
; dentro del Dialplan para indicarle a Asterisk, por qué
; canales, o contexto, pueden entrar y salir llamadas.
context=from-zaptel

; Señalización. Tenemos canales FXO con senializacion FXS
signalling=fxs_ks

; Estas tres opciones son necesarias para saber
; cuando se cuelga y descuelga. Sin ellas es probable que
; vuestra tarjeta digitalizadora no sepa cuándo está
; recibiendo una llamada
; o cuando en el otro extremo han descolgado.
answeronpolarityswitch=yes
hanguponpolarityswitch=yes
polarityonanswerdelay=1
faxdetect=both
group=0
channel =&gt; 1-4
; Cuando creemos la de salida y se quiera hacer una
; llamada, especificaremos que se haga a través del
; canal g0 . Esa primera letra g, puede cambiarse según
;   g: Utiliza el canal de menor número disponible del
;      grupo especificado
;   G: Utiliza el canal de mayor número disponible del
;      grupo especificado
;   r: Sigue una estrategia Round-Robin de menor a mayor
;   R: Round-Robin de mayor a menor
; En nuestro caso, g
; La configuración anterior es válida hasta aquí:
; Si queremos configurar otro canal,
; reescribiremos a partir de aquí su nueva configuración
; hasta acabar en una línea similar</pre>
<p>En el siguiente post estableceremos la configuración básica de Asterisk.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pinelo.com/blog/2009/07/08/ahorrando-practicamente-implantemos-una-centralita-asterisk-ii/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
