Scripts en SAP Business One Service Layer II — El script

Como dije en la anterior entrada, mi primer script iba a ser de una operación sencilla: crear dos documentos, un recibo de producción y su emisión de componentes correspondiente, en una transacción.

Como siempre, nuestro punto de partida es el manual de Service Layer que tenemos en nuestro servidor (https://<miservidor>:50000/Working_with_SAP_Business_One_Service_Layer.pdf) y que también podemos consultar aquí. Todo el apartado 3.20 está dedicado a esto. El código de ejemplo que trae lo tenemos también en la carpeta de instalación de Service Layer y es un buen punto de partida.

Sigue leyendo

Scripts en SAP Business One Service Layer I — Preparando el entorno

Una vez montado el cliente OData de Service Layer, realizar las operaciones individuales que hasta ahora hacía por B1WS, como, no sé, hacer traslados, crear y modificar ofertas y pedidos, crear y modificar UDOs varios, ha sido sencillo. Otra cosa muy distinta son las operaciones complejas que encapsulo en una transacción por Di Server, algo que no permite B1WS, ni tampoco el flamante Service Layer.

O, por lo menos, no tal cual. Pero tiene su propio SDK que nos permite hacer una suerte de addons en JavaScript que, sin ser tan potentes como lo que podemos hacer por Di Api, es, en algunos aspectos, más potente que lo que nos ofrece Di Server.

Me he lanzado a probar este extraño mundo con una operación sencilla en mente, pero que en la empresa se hace con mucha frecuencia: un recibo de producción con su emisión de componentes correspondiente.

Sigue leyendo

ASP Core — Autenticación por cookie y Active Directory

Estamos estos días planteando la migración de nuestra intranet a ASP Core MVC (quien dice «migración» dice «nueva intranet»). El primer reto a resolver es desentrañar los misterios de la autenticación. Es decir, cómo lo vamos a gestionar. Tenemos que validar a nuestros usuarios contra Active Directory, pero no nos vale la autenticación de Windows (o, por lo menos, no nos vale con la información que he encontrado; que luego igual resulta que sí nos servía, pero la documentación oficial es tremendamente parca): no sólo tenemos equipos con Windows en la red, también MacOS, y teléfonos, y tablets. Y equipos en fábrica con usuarios genéricos de Windows y luego la persona que lo usa en ese momento utiliza sus credenciales para entrar en las aplicaciones. Y equipos fuera de nuestro dominio.

Por todo esto, me centré en la autenticación general por cookies, por ser más flexible. La pregunta es: ¿cómo hacerlo?

Sigue leyendo

Eliminar líneas de un objeto con Sap Business One Service Layer

En las primeras pruebas con SAP Service Layer y su cliente OData para .Net me he encontrado con un problema que, por otra parte, me esperaba. Quiero decir que la actualización en OData se basa en enviar el conjunto de los cambios y no toda la entidad, para tener un flujo de datos lo más reducido posible. En Service Layer esto implica que si, por ejemplo, quiero actualizar un pedido de ventas, enviaré los campos de la cabecera que cambian, así como las líneas que se cambian o se añaden.

Pero, ¿y si lo que quiero es eliminar una línea? En Di Server/B1WS debíamos enviar todas las líneas, por lo que, si faltaba alguna, SAP entendía que la queríamos eliminar.

¿Es posible hacer eso en Service Layer?

Atendiendo al manual del usuario que tenemos en https://<miservidor>:50000/Working_with_SAP_Business_One_Service_Layer.pdf, no.

Sigue leyendo

Sap Business One Service Layer

Después de meses de retrasos y problemas (que si falla la controladora RAID del nuevo servidor, que si hay que esperar a un parche de SAP que se retrasa, que si pruebas por aquí y por allá), en noviembre por fin actualizamos a SAP 10. De lo más interesante de esta versión es que por fin tenemos Service Layer en la versión para SQL Server (hasta entonces, estaba sólo para Hana).

Service Layer es una nueva api de datos para que nuestras aplicaciones puedan trabajar con SAP Business One. Es una api web moderna, Restful, con protocolo OData. Por supuesto, con algunas cosillas particulares, que para algo hablamos de SAP.

Yo esperaba con muchas ganas el poder meterle mano a Service Layer. El venerable Di Server se nos ha quedado pequeño y nos da mil y un extraños problemas. No cuento con que Service Layer sea la panacea, pero se supone que debe funcionar mejor que algo marcado como obsoleto años antes de que yo empezara a usarlo. El problema, como siempre, es la formación y la documentación. O la falta de. Como es habitual, me toca buscarme las habichuelas.

Sigue leyendo

De EF clásico a EF Core II — Configurar las entidades

Decía el otro día que me había puesto a ampliar mi mapeo de SAP Business One en Entity Framework clásico para hacerlo también en Entity Framework Core 7 y .Net 6.0.

Tuve que empezar creando unos métodos de extensión para tener la misma funcionalidad que en EF clásico.

Luego, dividí la clase del DbContext en tres archivos:

  1. Uno para la declaración de los DbSet.
  2. Otro con constructores y configuración para EF clásico.
  3. Y el tercero, para los constructores y configuración para EF Core.

El paso que me quedaba era revisar las clases de configuración de las entidades.

Sigue leyendo

De EF clásico a EF Core I — El Context

Tengo un mapeo de parte de la base de datos de SAP Business One en Entity Framework desde hace unos años que ya pasa de las 150 entidades. Hace no mucho modifiqué el tipo de proyecto de Visual Studio, del clásico de .Net Framework al nuevo estilo usado en .Net Core. El motivo era que este nuevo tipo permitía compilar con facilidad para distintas versiones de .Net. Este otoño, aprovechando esto, me planteé añadir .Net 6.0 y el nuevo EF Core 7 como plataformas de destino.

Ya comenté ayer los problemas que me encontré en las primeras pruebas. Solventados (o trampeados) éstos, pasé al plato principal.

Sigue leyendo

Microsoft y el mundo

De un tiempo a esta parte, me da la impresión de que los desarrolladores de Visual Studio (y .Net en general) tienen una visión del mundo bastante peculiar que se deja fuera a muchos de sus clientes. Algo que no veía en los tiempos de .Net Framework y la década del 2000 y primera mitad del 2010. La última vez que he pensado esto ha sido al empezar a usar Visual Studio 2022. Lo primero que me saltó fue esta advertencia:

NU1803    You are running the ‘restore’ operation with an ‘HTTP’ source. Non-HTTPS access will be removed in a future version. Consider migrating to an ‘HTTPS’ source.

Siguiendo el enlace de la advertencia, me encuentro con lo siguiente:

«El acceso no HTTPS se quitará en una versión futura.»

Toma ya. ¿En qué mundo viven estos? Busqué en el GitHub de NuGet el tema y lo encontré rápido, junto con las mismas quejas que podía escribir yo. Que, básicamente es:

Pero, alma de cántaro, ¿no te has parado a pensar que los que usamos servidores NuGet con http lo hacemos porque son servidores locales, montados para el grupo de desarrollo de la empresa? Yo lo hago con NuGet Server y ahora lo tengo en un servidor virtualizado que se utiliza para otras cosas, con los archivos alojados en un servidor de archivos aparte. Es muy cómodo para compartir nuestras bibliotecas comunes entre los distintos proyectos. Y esta decisión nos va a complicar la vida una barbaridad. ¿Qué hacemos? ¿Https con certificado autofirmado? Esto dará problemas por otro lado, seguro. ¿Pagar un certificado para un ordenador que ahora está (física o virtualmente) en un rincón, sin acceso a internet? ¿Y el tiempo de investigación y configuración de todo el tinglado?

Me queda bastante claro que a los que se les ha ocurrido la maravillosa idea no han trabajado mucho en pequeñas y medianas empresas.

Raw SQL Query en EF Core

Cuando, en 2016, empecé a trastear con Entity Framework, tuve que elegir entre la versión clásica o la nueva versión «Core». En la documentación te indicaba que, para proyectos nuevos, lo mejor era elegir EF Core, que era lo que se iba a desarrollar a futuro. Pero, ¡ay!, la versión entonces disponible no soportaba vistas y yo las necesitaba como el comer. No me quedó otra que empezar con EF clásico y dejar EF Core para más adelante.

El año pasado, aprovechando que ya teníamos una versión para .Net Standard de EF clásico, modifiqué mis proyectos con EF para pasarlos del tipo .Net Framework al tipo actual .Net Core, en donde se puede tener varios frameworks de destino. Así, los compilé para .Net Framework y .Net Core.

Este otoño, preparando mi salto a .Net Core del año que viene (posiblemente con Blazor), se me ha ocurrido también compilar mis proyectos de EF tanto para la versión clásica como para la nueva EF 7.0, recién salida del horno. En parte también porque en la documentación decía más o menos que los desarrolladores de EF clásico ya no teníamos excusa. Bien, me dije, veamos si es así. La madre del cordero es el mapeo parcial de la base de datos de SAP Business One, que es sólo relacional en parte. Más de 150 entidades (hasta la fecha), entre tablas y vistas.

Lo primero fue revisar qué usaba en EF clásico y cómo se hacía en EF Core, así que monté un prototipo y mapeé un par de tablas y vistas, con sus relaciones. Sin problemas.

Luego llegó el turno de las consultas SQL a pelo y ahí, ¡ah, amigo!, ahí se complicó todo.

En EF clásico teníamos una opción maravillosa que era:

public DbRawSqlQuery<TElement> SqlQuery<TElement>(string sql, params object[] parameters)

Sigue leyendo

Cliente WCF asíncrono

Volviendo con lo del otro día, se me ocurrió ampliar los contratos de servicio para definir métodos asíncronos, de cara a trabajar con .Net Core este invierno. Quería hacerlo sólo en los clientes, para no tocar toda la parte de los servicios, que ya está creada, así que jugué con las compilaciones condicionales:

<ServiceContract([Namespace]:="http://CdA.Cromo.Services", SessionMode:=SessionMode.NotAllowed)>
Public Interface ILotesService

    <OperationContract()>
    <FaultContract(GetType(ServiceFault))>
    Function Modificar(Lote As LoteSAP, InfoConfig As InfoConfiguracion) As Mensaje

#If NET5_0_OR_GREATER Then
    <OperationContract()>
    Function ModificarAsync(Lote As LoteSAP, InfoConfig As InfoConfiguracion) As Task(Of Mensaje)
#End If

End Interface

Sigue leyendo