Eficiencia alemana

Con la actualización a SAP Business One 9.0 se decidió cambiar la nomenclatura de los almacenes. En la práctica, esto supuso crear almacenes nuevos y mover masimavente la mercancía en el sistema (necesario, de todas formas, para aprovechar el nuevo sistema de ubicaciones). Para evitar que los usuarios metieran accidentalmente mercancía en los almacenes viejos, se marcaron como Inactivos (un bonito check en el formulario de almacenes, que se corresponde con el campo Inactive de la tabla correspondiente). Hasta ahí, sin problemas.

Como baja colateral, el enlace entre el gestor de proyectos (desarrollo propio) y SAP dejó de funcionar. El enlace permite tanto crear artículos en SAP desde el gestor como actualizarlos después y supuso un gran ahorro de trabajo en el departamento, que hasta entonces debía copiar los datos logísticos de un nuevo producto a mano desde el gestor a SAP.

El error que da al actualizar el artículo es que hay almacenes inactivos. Guay. La cosa se pone interesante porque el código del error no aparece en la documentación (he descubierto estos meses que lo raro es que aparezca). Es más, buscando en la documentación de la DI Api (la interfaz de datos que se usa para comunicarse con SAP) el dichoso campo Inactive no aparece en el objeto Almacén.

Vale, puede ser que yo sea un cegato y venga con otro nombre (no es raro), así que me voy a la documentación de la base de datos para ver si la descripción del campo me da alguna pista sobre qué nombre buscar en la DI Api.

Y no lo encuentro. En la documentación de la base de datos de la versión 9.0 no viene, para la tabla maestra de almacenes, el campo Inactive. Por más que exista y por más que tenga un bonito check en el formulario de gestión de almacenes. Con un par.

A eso se le llama «eficiencia alemana».

Relación maestro-detalle entre dos ComboBox

Me encontré el otro día con la necesidad de establecer una relación maestro-detalle entre dos combobox. Básicamente, tenía que mostrar en un combobox una lista de procesos y, en el otro, las tipologías de cada proceso. Ahí donde lo ven ustedes, no es algo difícil de hacer, pero sí de encontrar cómo hacerlo. Quizás la solución más elegante sea usar un dataset con las dos tablas en cuestión (Procesos y Tipologías) y la relación entre ambas. De este modo, yo puedo asignar la tabla Procesos como DataSource del combobox de la forma habitual:

            cboProceso.DataSource = dsProcesos.Procesos
            cboProceso.DisplayMember = «Descripcion»
            cboProceso.ValueMember = «Id»

Donde cboProceso es el combobox en cuestión y dsProcesos, el dataset tipado. Para la parte detalle de nuestro sistema, esto es, el combo de Tipologías, usamos el mismo DataSource que para el maestro, es decir, la tabla Procesos. La magia la hacemos en el DisplayMember (en el ValueMember no funciona), que lo expresaremos de la formarelación_entre_tablas.campo_a_mostrar. Es decir, el nombre de la DataRelation que une las tablas de nuestro dataset y el nombre del campo de la tabla detalles (Tipologías en nuestro caso) que mostrará el combobox:

cboTipologia.DataSource = dsProcesos.Procesos
‘ Nótese la forma del displaymember para que muestre sólo las tipologías
‘ del proceso seleccionado en el combo de procesos:
cboTipologia.DisplayMember = «FK_Tipologias_Procesos.Descripcion»
cboTipologia.ValueMember = «IdTipologia»

Muy intuitivo, como pueden observar.

El sistema funciona incluso si queremos hacer un filtrado del maestro. Siguiendo con el ejemplo, puedo crear un dataview de Procesos, usar ese dataview como DataSource de ambos combobox y el invento funcionará sin más.

Donde ya no he conseguido que funcione es con un filtrado en la parte detalles. Digamos que necesito mostrar las tipologías que cumplen cierto requisito. Paso 1: filtrar los procesos, sólo necesito aquellos que tengan tipologías que cumplan el requisito pedido. No es una consulta simple que se pueda hacer en el Filter del DataView, pero puedo tirar de LINQ o de métodos de extensión y hacer algo como esto:

dvProcesos = dsProcesos.Procesos.Where( _
                 Function(Proceso) Proceso.GetTipologiasRows.Any( _
                 Function (Tipologia) (Tipologia.OpcionesTipologia _
                              And EOpcionesTipologia.TareaAutomática) _
                              = EOpcionesTipologia.TareaAutomática)).AsDataView
 

Ya tenemos un dataview (dvProcesos) con los procesos que tienen alguna (any) tipología que cumple los requisitos pedidos. Si yo uso este dataview como DataSource de los combobox, obtengo los procesos que busco, pero todas las tipologías de esos procesos. ¿Cómo filtro ahora las tipologías?

Pues ni idea. El objeto DataRelation no me permite «meter» nada en la relación que no sea equivalencia entre columnas y no he encontrado ninguna forma de filtrar el lado «detalle» de nuestra relación. Al final, he tenido que recurrir a dos bindingsources auxiliares, uno ligado al dataview de procesos y el segundo al bindingsource anterior, especificando en su DataMemeber el nombre de la DataRelation:

mbsProcesos.DataSource = dvProcesos
mbsTipologias.DataSource = mbsProcesos
mbsTipologias.DataMember = «FK_Tipologias_Procesos» 

Ahora uso estos bindingsources para «alimentar» los dos combobox y listo. Aunque me hubiera gustado una solución más elegante.

Problema con el correo

Semana plagada de problemas con nuestro proveedor de correo, más un ordenador infectado haciendo de bot y repartiendo spam a espuertas, más problemas con imágenes en las firmas y lusers que no ponen destinatario. Ganas de tirar el teléfono por la ventana o poner un contestador automático que filtre las llamadas por el problema del correo. En estas se acerca un luser con cara de pena.

—No puedo enviar un correo, me da error.

—¿Qué error te da?

—Error al enviar correo, no sé qué de un servidor.

—Necesito la información del error para buscarle solución al problema. Haz una captura de la ventana del error y me la mandas por correo.

Y el luser asiente y se va a su puesto.

Las risas aún resuenan por el departamento.

Y ahora, ¿qué?

Me encuentro dando los últimos retoques a la v3 del SOSNext, un programa de gestión de incidencias que ha crecido tanto que vamos a tener que cambiarle el nombre, porque el módulo de incidencias es ya sólo una pequeña parte del total.

En fin, que estos últimos tres meses le he hecho la cirugía mayor al programa, pasándolo de VB2010 Express a VS2012 (la versión Express ya no daba más), haciéndolo más modular (aumentando la complejidad un montón, de paso), adaptando algunos controles bajados de internet, cambiando algunas cosas de la BDD, metiéndolo todo en Team Foundation Server (Express) para coordinarme con un compañero… Un jaleo enorme.

Y ahora, cuando estoy dando los últimos retoques, un usercontrol que no ha sufrido modificaciones (ni una), que no lo he tocado ni abierto ni cambiado de proyecto o espacio de nombres, que ha pasado desapercibido y no ha recibido ni una mirada de atención, ese cabronazo, digo, ha decidido:

1) Cambiar el alto de las etiquetas y cajas de texto, cortando las letras tipo p, q…

2) Un ListBox con funciones de búsqueda de cadenas (que funciona bien en el resto de controles donde se usa) decide, de paso, funcionar como un ListBox normal (sólo primera letra).

Creo que le he contagiado la gripe, porque otra explicación no se me ocurre.

Validación de filas en DataGridView

Entrada muy rápida y sin código explicativo, pero que quizás sirva a alguien como me ha servido a mí.

El problema es la necesidad de validar los datos que el usuario introduce en un DataGridView. Si controlo el evento RowValidating sólo muestro aviso del error cuando el usuario ha rellenado toda la fila, con el consiguiente problema visual (la celda afectada puede estar fuera de la pantalla visible en ese momento). Si lo que hago es controlar el evento CellValidating, impido que el usuario pueda salir de la celda sin meter un valor válido, lo que puede no interesarme.

Sin embargo, puedo controlar el evento CellEndEdit para ir evaluando los datos introducidos por el usuario y, para aquellos no válidos, usar el ErrorText de la celda en cuestión para avisar del valor no válido. Esto es meramente informativo y usaríamos el evento RowValidating para hacer la validación real de los datos (evitando evaluar nada si IsNewRow es True).

VS2012, la dictadura de los diseñadores y la resistencia de los usuarios

Me he instalado la versión de prueba de Visual Studio 2012 para cacharrear con ella y ver qué tiene de interesante. Con un poco de suerte, pasaré de VB Express a VS Pro en el curro y estoy evaluando tanto VS2010 como VS2012. La verdad es que, si por mi fuera, me quedaba con VS2010. La interfaz de la versión 2012 es un horror de proporciones bíblicas, un canto al ego exacerbado de los diseñadores de interfaces, con un diseño esclavo del minimalismo total de Windows 8 (si Windows Vista y 7 compartían aire y filosofía con KDE, el nuevo Windows es la rendición al estilo Gnome) y sin la más mínima concesión a los pobres currantes que debemos pasarnos horas delante suya.

Resumiendo mucho, la interfaz de VS2012 es como retroceder 20 años al pasado: un entorno monocromo. Iconos en blanco y negro, fondos en grises claros, ninguna concesión al color. Bueno, miento: la Beta era total y absolutamente en blanco y negro, pero ante las quejas los diseñadores han dado unas pequeñas concesiones al color. Por ejemplo, resaltar en un azul plano la pestaña seleccionada o indicar en azul o verde las clases de VB o de C#. Pero todo lo demás (los iconos de controles para el diseñador de formularios, los iconos de todos los demás objetos del explorador de soluciones, como formularios, controles de usuario, carpetas, datasets…), todo lo demás, digo, en blanco y negro.

Menos mal que la comunidad resiste por el bien de nuestra salud visual y mental y nos ofrece herramientas para convertir VS2012 en algo aceptable (aunque no para las versiones Express, me temo).

Para empezar, Visual Studio 2012 Color Theme Editor es una extensión que nos permite crear y editar temas de escritorio: colores de menús, barras de herramientas y tal. Trae varios creados y uno de ellos, Blue, es muy similar al cómodo tema azul de Visual Studio 2010.

Para seguir, la herramienta Visual Studio Icon Patcher nos permite extraer los iconos de VS2010 y metérselo a VS2012. No reemplaza a todos los iconos, pero volveremos a tener los coloristas y fácilmente identificables iconos del Explorador de soluciones y del Cuadro de herramientas. Con eso, me vale. También nos permite dejar la barra de menús en formato normal (primera letra en mayúsculas, resto en minúsculas), no con todo en MAYÚSCULAS, QUE QUEDA HORROROSO DE VERDAD.

Con estas dos herramientas la cosa cambia y podemos centrarnos en descubrir qué trae VS2012.

Oh, cielos

Acabo de instalar en el curro la versión de prueba de VS2012. He conseguido convencerles de que VB Express no da más de sí y necesito algo más «tocho» (es totalmente cierto) y la duda está entre VS2010 y la nueva versión que sale estos días. La primera intención sería tirar por VS2012 si no da problemas con la conversión y compilación de las aplicaciones que estoy desarrollando. Desde VB2005 he ido cambiando de versión tan pronto me era posible. Pero esta vez…

En Visual Studio 2010 teníamos un esquema de colores que delimitaba muy bien las áreas de trabajo y la ventana o pestaña activa. Los iconos eran fácilmente reconocibles por su color antes incluso que por su forma (por ejemplo, amarillo para datos o carpetas… cosas así).

En VS2012, no. Toda el área de trabajo es gris sucio o gris oscuro. Todos los iconos son en blanco y negro, con pequeñas (pequeñísimas) notas de color plano. Si no fuera por esas notas de color disperso y la nitidez del texto y grises, me parecería estar frente al viejo Mac de mi tío, allá cuando mi padre y yo tirábamos de MS-DOS 4.01 y lo de la Multimedia sonaba a cosa futurista.

Menos mal que el código mantiene sus colores. Imagino que eso ha sido una exigencia de los desarrolladores frente al bello monocromo minimalista que proponían los diseñadores. Un poco en plan «si trabajamos con VB4 y 5, a esto sobrevivimos».

No entiendo ni entenderé ese minimalismo conceptual que nos ha caído encima con Windows 8 (y que sufrí en tiempos con Gnome, antes de huir corriendo a KDE). El sufrir con un entorno espartano y retro, como si fuera pecado que nuestras máquinas puedan mostrar millones de colores, texturas, bordes redondeados, sombras…

Visualmente, VS2012 es una rendición a los diseñadores. Sin la ayuda de contrastes, de iconos identificables y agrupables de un vistazo es cansado para la vista y para el cerebro trabajar con él.

Compañías telefónicas

—Oiga, me mudo y quiero cambiar la línea a mi nuevo domicilio.

—Sin problema. Son tropecientos leuros.

—Disculpe, pero eso es lo que pagué por el alta. No quiero un alta nueva, sólo un cambio de domicilio.

—Da igual, son tropecientos leuros. Lo pone en el contrato: si no sabe bailar la jota aragonesa a la pata coja y haciendo el pino, son tropecientos leuros.

—Perdone, pero en mi contrato no pone nada de eso.

—Es que se aplica a los contratos yuxtapuestos.

—Pero es que el mío es pluscuamperfecto.

—Da igual, son tropocientos leuros.

—Quiero poner una reclamación.

—Da igual, son tropecientos leuros.

—Váyase a paseo y dé de baja la línea, que me voy a la competencia.

—Pero, hombre. No sea así, con lo bien que lo tratamos. Mire, le diré lo que vamos a hacer: le haremos el cambio de domicilio gratis, sin cobrarle nada. Para que vea que le apreciamos.

Alucinado estoy

Me encuentro con que hay un error en el encabezado de ciertos mensajes de correo enviados automáticamente por el gestor de incidencias (SOS). Debía poner «SOSNext: aviso de cierre de la acción de mejora nº tal», pero llega cortado en la «ó» de acción, que es sustituida por un carácter raro. Lo curioso es que el resto de correos con la palabra «acción» en el título llegan sin problemas.

Lo más curioso es que la contrapartida al cierre llega sin problemas: «SOSNext: aviso de reapertura de la acción de mejora nº tal». Curioso, ¿por qué?, diréis. Bueno, pues por el código que genera el título del mensaje es similar en ambos casos:

Titulo = «SOSNext: Aviso de cierre de la « & Singular & » nº « & Id

Y

Titulo = «SOSNext: Aviso de reapertura de la « & Singular & » nº « & Id

¿Cuál es la diferencia?

La longitud de la cadena. No sé el porqué, pero el problema está en los seis caracteres que ocupa «cierre». Cambiando la longitud de la palabra o, como he hecho, añadiendo un espacio más antes de «cierre», problema solucionado.

Aunque la cara de pasmo tardará más en quitárseme.

Si no es por las buenas…

El otro día, intentando filtrar un DataView, me encontré con un problema pintoresco: quería filtrar los usuarios con determinados permisos en la aplicación, para lo cual necesitaba hacer una comparación bit a bit. Hacer esa comparación en SQL no supone problema (Permisos & PermisoPedido = PermisoPedido). En VB, tampoco (UsuarioActivo.Permisos AND PermisoPedido = PermisoPedido), pero la propiedad RowFilter del DataView es mucho más restrictiva. Usa sintaxis SQL, pero con muchas limitaciones.

En la Geekpedia encontré una solución al problema: pasar de las operaciones bit a bit y resolver el problema con operaciones decimales normales y corrientes:

(Convert((Permisos – Permisos % PermisoPedido) / PermisoPedido,’System.Int32′) % PermisoPedido = 1)

Donde PermisoPedido es el valor numérico buscado de nuestra enumeración (1, 2, 4, 8, 16…). El operador % es, en Visual Basic, el módulo (resto de la división entera).