Archivo del Autor: Cubano

El documento perdido

—Acabo de guardar un documento y no lo encuentro ni con el buscador de Windows.

—Espera… Mira, abres el LibreOffice, vas a Archivo, documentos recientes y ahí está. Pulsamos y…

Plunch, mensaje de error.

—Espera un momento que miro…

Conéctome al servidor, reviso la ruta. Revísola otra vez.

—La carpeta donde debería estar el archivo no existe. Alguien debe haberla borrado.

—He sido yo. Ya no hacía falta.


Empiezo a pensar que la inteligencia de los lusers de una empresa es constante e independiente de su número.

Windows Phone, un bonito sueño

Hace tres años ya me hice con un HTC HD7 con Windows Phone 7. Fue mi primer smartphone moderno y sus bondades me hicieron olvidar rápidamente mis preocupaciones sobre privacidad y esas cosas. Me duró menos de dos años: la placa empezó a fallar y el servicio técnico pasó de mí porque, al haberme tocado en un concurso, no tenía factura. Lo sustituí por un Nokia Lumia 620 con WP 8 y seguí más feliz que unas castañuelas. Mi señora también pasó a WP al jubilar su viejo Nokia con Symbian. Supongo que tanto tiempo pegándose con Android en el curro le hizo querer algo cómodo y que funcionase.

Y es que, frente al sistema orientado como plataforma de aplicaciones de Android, en la que ir en montado en un teléfono sólo era algo circunstancial, Windows presentaba un sistema pensado para ser una herramienta de comunicación: la aplicación de contactos permitía unificar contactos de múltiples fuentes (Hotmail/Outlook, la tarjeta SIM, Facebook, LinkedIn) y, desde ahí, seguir sus publicaciones (y dejarles comentarios), enviarles correos, llamarles (obvio) y mandarles mensajes. Para esto último, la aplicación de mensajería permitía usar SMS/MMS, Messenger y el chat de Facebook, teniendo una única conversación por contacto y pudiendo cambiar entre los distintos tipos de mensaje en cualquier momento (que no está conectado por Messenger, pues le mando un SMS, y así). Podía anclar contactos o grupos de contactos a la pantalla de inicio y ver si tenía correos, mensajes o llamadas pendientes y su última publicación en redes sociales. Podía comunicarme rápida y fácilmente sin preocuparme del cómo.

No era perfecto: no se podía seguir las publicaciones en grupos de Facebook ni chatear en grupo, aunque yo esperaba que eso se terminara añadiendo, así como soporte para más redes sociales. Luego este verano llegó la actualización 8.1 y desperté de mis sueños.

Ahora, Mensajes sólo sirve para mandar SMS/MMS. Si quiero mensajería de la mano de Microsoft, hay que usar Skype, que para eso se gastaron una pasta en comprarla. Pero en lugar de agregarla a la aplicación de mensajería, tengo que instalar su propia aplicación, que no sigue el diseño de Windows Phone, tarda un montón en abrir y no puedo hacerlo desde el contacto con el que quiero hablar. Con Facebook, igual: se acabó el comentar publicaciones desde la ficha del contacto, toca abrir la aplicación de Facebook. Y para mensajes, usar la propia aplicación de mensajería de Facebook, que se pasa el diseño de Windows Phone por el forro, tarda un montón en abrir y a la que no puedo acceder desde la ficha del contacto con el que quiero hablar. Al final, la única aplicación de mensajería que mantiene el aspecto minimalista de WP es la de WhatsApp, que en las últimas versiones abre bastante rápido, pero que tampoco se integra en la aplicación de contactos.

Es decir, ahora para mandar un mensaje a alguien tengo que pensar primero qué protocolo usaré (Skype, SMS, Facebook, WhatsApp…) para luego ir a la aplicación correspondiente y buscar en ella al contacto (que puede perfectamente tener un nombre distinto en cada una, sí así quiso). Si quiero seguir, digamos, por Skype una conversación que empecé por Facebook, más me vale tener buena memoria, porque no puedo simplemente remontarme en la conversación y verlo. Sin contar con que ahora necesito tener todas esas aplicaciones instaladas.

En fin, durante tres años tuvimos un sistema operativo para móviles pensado para el usuario y sus necesidades de comunicación y no como mera plataforma donde instalar aplicaciones. Fue bonito mientras duró.

He leído por ahí que con la actualización 8.1 Windows Phone se ha puesto a la altura de Android y de iOS. Me pregunto si de verdad hacía falta dar este salto atrás para rapiñar cuota de mercado.

Miedo, pánico, terror

Al otro lado del teléfono una voz muy nerviosa. Pareciera que le ha salido ardiendo el ordenador o ha explotado el servidor de archivos. Supongo que es el habitual problema con LibreOffice (a veces se queda pillado al intentar abrir un documento y hay que matar el proceso; como los usuarios vuelven y vuelven a darle al documento de marras, el número de procesos se dispara) y me levanto a corregirlo. Explicárselo no es factible (a quien tiene capacidad ya se lo he explicado). Llego y me encuentro al usuario con rostro desencajado frente al ordenador, señalando un correo electrónico. Con su nombre de usuario. Con otro dominio. Con el texto en inglés. Pánico ante tan extraño problema.

¡Venga ya! ¿En serio hay gente en España de veintitantos años, con carrera, que no ha visto un mensaje de spam en su vida? Tiemblo el día que le llegue un adjunto.

¿Jubilar XP? Ojalá

La primavera la sangre altera y los comerciales salen a cortejar empresas. Esta semana me han tocado un par de ellos intentando venderme planes para jubilar XP. Hasta nuestra empresa de soporte y apoyo me quiere quitar XP. Y, ojo, no es que no tenga ganas de perderlo de vista (ganas que no han hecho sino aumentar desde que lo mandé a paseo de mi viejo equipo personal en 2004). Si por mi fuera, salvo dos equipos virtualizados para administración, lo mandaba bien lejos de la empresa. Pero me encuentro con la dura realidad, que se traduce en dos situaciones bien distintas:

1) Venerables equipos (PIV, 1GB de RAM DDR 400 y cosas así) dando los últimos coletazos de su vida útil. No es que quiera jubilar XP, es que quiero jubilar los ordenadores. Ese hardware ya no me mueve la ofimática elemental de la empresa (Thunderbird, un navegador, LibreOffice y un visor de pdf) con razonable soltura. Si no tirarlos a la basura, pues mandarlos a las profundidades del almacén como terminales tontos, aunque me temo que su futuro es de equipos de becario.

2) Equipos que venían con Windows 7 y sufrieron un downgrade a XP. Los hay que intentaré cazarlos cuando el dueño esté de vacaciones y meterles su 7. Otros son críticos y me temo que tendrán que esperar: son los últimos XP de sus respectivos departamentos y son ab-so-lu-ta-men-te necesarios para el funcionamiento de la empresa. Porque al final las páginas de los bancos y de Hacienda con lo que mejor funcionan es con XP y versiones antiguas de Internet Explorer.

Bueno, y el miniportátil asignado a la impresora 3D, pues no hemos conseguido que su programa renderice bien en un equipo moderno.

En fin, que aún nos queda una temporada con XP en la empresa.

El Lenovo de alta gama que hundió la empresa

Problemón hoy en el curro con caídas de red, incluida telefonía (por ip) y que ha dejado una dolorosa baja, uno de los Lynksys que atesoramos. Desconozco aún si el causante del jamacuco del router fue el mismo del colapso de la red por la tarde, pero descubrir al causante de este último estropicio, que se ha llevado por delante toda nuestra red, ha sido un auténtico quebradero de cabeza. Primero dimos con el dispositivo causante, un Lenovo de gama alta. Luego, buscar el software responsable. Descartados antivirus, firewall e infecciones varias, todo apuntaba al software del fabricante o a un problema de drivers. Me llamó la atención el nombre de un proceso, Discovery.exe y, tirando de Google, llegué a esto. No era el mismo problema, pero merecía la pena probar y, ¡bingo! Desinstalado el Lenovo EMC Storage Connector (sirva para lo que sirva) y se acabó el problema.

Nunca me han gustado los equipos de marca, pero hoy Lenovo se ha ganado un huequecito especial en el cajón de «odiados profundamente».

Eficiencia alemana (II): actualizar listas de precios en SAP B1

Problema pintoresco este que menciono. Las veces anteriores, mi compañero había actualizado los precios importando a partir de una hoja Excel desde dentro de SAP, pero, ya sea por mi poca pericia, ya por la actualización a 9.0, he sido incapaz de preparar el maldito Excel. La otra opción, que en su día se dejó por imposible, es importar a través de esa bestia del averno llamada Data Transfer. Así, preparo mi plantilla para los precios, tabla ITM1, con las columnas ItemCode, LineNum, PriceList, Price y Currency. De ellas, ItemCode es el código del artículo, PriceList es el id de la lista de precios. Añadimos el importe y la moneda (EUR) y listo… Uhm, salvo LineNum, que ponemos a 0, que es lo habitual (LineNum, como regla general, es el número de línea por código de la tabla maestra, es decir, que será 0 si sólo vamos a actualizar un precio por artículo, como era el caso).

Bien, pues eso no funciona.

Después de buscar por los foros oficiales, encuentro una respuesta ante este problema que es absurda. O, por lo menos, yo no le encuentro la lógica. Decía así: quite la columna PriceList y en LineNum ponga el id de la lista de precios menos 1.

Y funciona.

Si no quieres vender…

En el trabajo andamos buscando tablets. Descartado Android tras una serie de pruebas, el siguiente paso es Windows. La duda está en si 8, RT o CE. Para no entramparnos en comprar una tablet profesional (cuestan una pasta) decidimos buscar una baratita para pruebas en el departamento y luego decidir. Encontramos la Acer Iconia W3 como candidata ideal. Barata y con Windows 8 (8,1» de pantalla y una memoria interna ridícula, pues sólo vemos la versión de 32GB; vamos, que no me la compraba para mí ni loco).

Mi jefe de departamento se acerca a El Corte Inglés, donde la tienen clasificada como con Windows RT y el vendedor dice que tiene RT (y echa pestes del sistema; entiendo que se pueda considerar «capado» con respecto a Windows 8, pero Android e iOS entran en el mismo saco), así que ante la duda vuelve con las manos vacías.

Consultamos la página del fabricante. Queda bien claro. Procesador Atom, Windows 8… Encontramos de paso otro sitio donde está más barato, así que vamos allí, la pescamos y vuelta.

El tiempo que pierdes porque no te quieren vender.

Operador Update

El otro día necesitaba actualizar valores en una colección de DataRows tipadas y echaba de menos no poder hacerlo con LINQ, así que me puse a buscar. Y buscando, buscando, encontré el código para un método de extensión Update que soluciona la papeleta. El código está aquí en C# y en Visual Basic quedaría así:

Public Delegate Sub Func(Of TArg0)(element As TArg0)

''' <summary>
''' Executes an Update statement block on all elements in an IEnumerable(T) sequence.
''' </summary>
''' <typeparam name=TSource">The source element type.</typeparam>"
''' <param name=source">The source sequence.</param>"
''' <param name=Updater">The update statement to execute for each element.</param>"
''' <returns>The numer of records affected.</returns>
<System.Runtime.CompilerServices.Extension> _
Public Function Update(Of TSource)(source As IEnumerable(Of TSource), Updater As Func(Of TSource)) As Integer
    If source Is Nothing Then
        Throw New ArgumentNullException("source")
    End If
    If Updater Is Nothing Then
        Throw New ArgumentNullException("update")
    End If
    If GetType(TSource).IsValueType Then
        Throw New NotSupportedException("value type elements are not supported by update.")
    End If

    Dim count As Integer = 0
    For Each element As TSource In source
        Updater(element)
        count += 1
    Next
    Return count
End Function

Su uso sería algo así:

TareasParaArticulo.Where(Function(x) x.IdUsuario = 0).Update(Function(x) x.IdUsuario = JefeProyecto)

Donde TareasParaArticulo es un conjunto de datarows tipadas (un list, un datatable…, tanto da). Lo que hace esta parte de la aplicación, para ponernos en situación, es crear una serie de tareas (tomadas de una plantilla de tareas) cuando un artículo es añadido a un proyecto. El uso del Update permite asignar aquellas tareas que en la plantilla tienen como IdUsuario el 0 al jefe del proyecto de una forma sencilla.

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.