Dashboard y Biblioteca SAP Business One

La Guía Definitiva: Estrategia, Desarrollo y Reportería

¡Ahora con detalle! Haz clic en cualquier título para expandir la metodología o las bibliotecas de código.

Fases de Implementación (ASAP)

La hoja de ruta del proyecto (El "Macro").

1. Preparación del Proyecto

Objetivo: Planificar el proyecto y preparar los recursos.

Es la fase de "Sizing". Se define la infraestructura (servidores, HANA vs. SQL, Cloud vs. On-premise) y el alcance inicial.

  • Actividades Clave:
    • Definir el Alcance (Scope) del proyecto.
    • Identificar al Equipo del Proyecto (Sponsor, Project Manager, Key Users).
    • Crear el Plan de Proyecto (cronograma, hitos).
    • Instalar los ambientes (Desarrollo, QAS/Pruebas, Producción).
  • Entregable Principal: `Project Charter` (Acta de Constitución) y `Kick-off Meeting` (Patada inicial).

La Trampa: Un "Scope Creep" (alcance descontrolado). Si el alcance no se define y se firma aquí, el proyecto fracasará. "Definir lo que NO se va a hacer es tan importante como definir lo que SÍ se va a hacer."

2. Business Blueprint (BBP)

Objetivo: Crear el "plano" de la solución. Esta es la fase MÁS CRÍTICA de toda la implementación.

  • Actividades Clave:
    • Talleres AS-IS: Entrevistas con los Key Users para entender CÓMO trabajan hoy (sus procesos actuales).
    • Talleres TO-BE: Sesiones de diseño donde se muestra el estándar de SAP B1 y se mapea CÓMO trabajarán mañana.
    • Análisis de Gaps (Brechas): La lista de ORO. Aquí se documenta cada requerimiento del cliente que el estándar de SAP B1 *no puede* cumplir.
  • Entregable Principal: El `Documento BBP` firmado. Este documento es un *contrato* que define qué se va a construir.

La Trampa: Que el cliente no se involucre ("no tengo tiempo") o que el consultor no entienda el negocio. Un BBP débil o no firmado garantiza un desastre en la Fase 3, lleno de "esto no es lo que pedí".

3. Realización

Objetivo: Configurar y construir la solución definida en el BBP.

Esta es la fase de "manos a la obra" (construcción) donde los consultores y desarrolladores trabajan.

  • Actividades Clave:
    • Configuración "Base" (Módulos principales, Finanzas, Plan de Cuentas).
    • Configuración "Avanzada" (Autorizaciones, Flujos de aprobación, Precios).
    • Creación de UDFs, UDTs, Queries, FMS y Alertas.
    • Desarrollo de Gaps: Aquí es donde se construyen los Add-ons (SDK), integraciones (Service Layer) o reportes (Crystal) definidos en el BBP.
    • Pruebas Unitarias (hechas por el equipo consultor).
  • Entregable Principal: Sistema configurado y listo para Pruebas de Aceptación.
4. Preparación Final

Objetivo: Validar el sistema, entrenar a los usuarios y prepararse para el "Go-Live".

  • Actividades Clave:
    • Capacitación: Entrenar a los Key Users (Train the Trainer) y luego a los Usuarios Finales.
    • UAT (User Acceptance Testing): Pruebas de Aceptación. Los Key Users ejecutan flujos de negocio *completos* (Ej: "Compra hasta Pago") en el ambiente QAS para validar que el sistema hace lo que dice el BBP.
    • Migración de Datos: La parte más dolorosa. Limpieza de datos maestros (clientes, items) y saldos iniciales (inventario, cuentas por cobrar/pagar) para cargarlos con el DTW.
  • Entregable Principal: `Acta de UAT` firmada y `Plan de Go-Live` (el cronograma del "día D").

La Trampa: Datos "basura". Si los datos migrados (Maestro de Items, Clientes) son de mala calidad, el sistema nuevo será inútil desde el primer día (GIGO: Garbage In, Garbage Out).

5. Go-Live y Soporte

Objetivo: Iniciar la operación en el sistema nuevo y estabilizarlo.

  • Actividades Clave:
    • Ejecución del Plan de Go-Live (usualmente un fin de semana).
    • Carga final de datos (saldos) en el ambiente de Producción.
    • Hypercare (Soporte Intensivo): El equipo de consultores da soporte "en el sitio" (o dedicado) por 1 a 4 semanas.
  • Entregable Principal: Sistema operando en productivo. El hito clave es el **primer cierre de mes exitoso**.

La Trampa: Desaparecer después del Go-Live. El soporte "Hypercare" es vital para la adopción del usuario. Los problemas *siempre* surgen la primera semana, y la frustración del usuario debe ser manejada inmediatamente.

La Jerarquía de Soluciones

El árbol de decisión. (Clic para ver el detalle).

Configuración Nativa

Qué es: Usar las herramientas estándar de SAP B1. Autorizaciones, numeración de documentos, propiedades, parametrizaciones de módulo, determinaciones de cuenta, etc.

Cuándo usarlo: SIEMPRE. Es el primer lugar donde debes buscar. Si el cliente dice "No quiero que el almacenero vea los costos", la solución es un permiso, no un Add-on.

Por qué: Es 100% estándar, 100% mantenible, 0% costo de desarrollo y está 100% garantizado que funcionará tras una actualización.

La Trampa: El consultor no conoce bien el sistema e informa que "no se puede" y lo pasa a desarrollo. El 90% de los requerimientos de un cliente *deberían* resolverse en este nivel.

Queries y FMS

Qué es: Consultas (Query Manager) y Búsquedas Formateadas (FMS). Lógica simple de SQL/HANA que se ejecuta en la interfaz de usuario.

Cuándo usarlo: Para alertas simples (Ej: "Alertar al crear un pedido si el cliente tiene saldo vencido") o para autocompletar campos (Ej: "Al seleccionar un item, traer el peso de OITM a un UDF en la línea de la factura").

Por qué: Es rápido de implementar (un query guardado) y no requiere desarrollo de software (.NET).

La Trampa: Un FMS *no es una validación de negocio robusta*. Se ejecuta en el cliente (UI) y puede ser lento si el query es pesado. Más importante: **es 100% omitido (bypassed) por una inserción vía DI-API o Service Layer**. Para validaciones de negocio *reales* (que no se pueden saltar), usa el Nivel 3.5: `TransactionNotification`.

UDFs y UDTs

Qué es: Campos Definidos por Usuario (UDF) y Tablas Definidas por Usuario (UDT). Es la forma estándar de extender el modelo de datos.

Cuándo usarlo: "Necesito guardar la 'Placa del Vehículo' en la Guía de Remisión" -> `UDF` en la tabla `ODLN`. "Necesito una tabla nueva para gestionar 'Tarifas por Distrito'" -> `UDT`.

Por qué: Es 100% estándar y mantenible. Las herramientas (DI-API, Service Layer, Crystal) las reconocen automáticamente. Es la base de casi cualquier personalización.

La Trampa: Crear 50 UDFs en la cabecera de la factura (OINV) cuando lo que realmente necesitabas era una UDT (tabla de cabecera) y un UDF (tabla de detalle) para guardar esa información. Abusar de los UDFs en tablas maestras las hace lentas.

Reportería (Crystal / Query)

Qué es: Usar el Query Manager o Crystal Reports para *presentar* información.

Cuándo usarlo: Cuando el usuario dice "Necesito un módulo para..." y, tras preguntar, te das cuenta que solo quiere *ver* datos. (Ej: "Necesito un módulo de Pedidos Pendientes").

Por qué: Un reporte es **10 veces más barato y rápido** de construir que un Add-on. Siempre pregunta: "¿Necesitas modificar esta información o solo verla?". Si es "solo verla", es un reporte.

La Trampa: Intentar construir reportes transaccionales complejos (como un P&L o un Balance) usando solo el Query Manager. Esos reportes requieren lógica de SPs y el poder de formato de Crystal Reports. (Ver Biblioteca de Reportería).

B1iF (Integration Framework)

Qué es: El middleware de integración oficial de SAP. Es una plataforma gráfica (basada en XML/XSLT) para construir flujos de integración.

Cuándo usarlo: Para escenarios de integración complejos y robustos. Su uso estrella es **Intercompany** (sincronizar datos entre dos bases de datos SAP B1). También es la vía preferida para conectar B1 con R/3 o S/4HANA.

Por qué: Es muy potente, soporta colas de mensajes (asincronía) y tiene monitoreo de transacciones.

La Trampa: Es complicado de aprender y mantener. Es *overkill* (excesivo) para una integración simple como "conectar mi página web". Para eso, usa Service Layer.

Service Layer (API)

Qué es: La API web moderna de SAP B1. Es RESTful, habla JSON y usa OData. Es el reemplazo de la funcionalidad *backend* de la DI-API.

Cuándo usarlo: ¡Para casi cualquier integración moderna! Conectar un E-commerce (Shopify, Magento, VTEX), un portal de clientes en PHP, una App móvil en React Native, un script de Python que corre en un servidor. Es independiente de la plataforma.

Por qué: Es rápido, stateless (no consume licencias por conexión persistente) y usa estándares web (HTTP, JSON).

La Trampa: Pensar que sirve para modificar la interfaz de SAP B1. No lo hace. El Service Layer es 100% *backend*. No puede usarse para añadir un botón a una ventana de SAP.

SDK (Add-on)

Qué es: El kit de desarrollo clásico (.NET C#/VB) que usa la **UI-API** (para modificar la interfaz) y la **DI-API** (para transacciones de backend).

Cuándo usarlo: ¡ÚLTIMO RECURSO! Cuando el requerimiento es SÍ O SÍ "Necesito un botón nuevo en la pantalla de Facturas", "Necesito una pantalla completamente nueva *dentro* de SAP B1" o "Necesito capturar un clic y bloquearlo con una lógica muy compleja".

Por qué: Es la *única* forma de alterar el comportamiento del cliente pesado (el SAP Business One.exe).

La Trampa: Usarlo para todo. Un Add-on es la solución más **cara**, más **lenta de desarrollar** y más **frágil** (requiere reinstalación y recompilación en actualizaciones mayores). Cada requerimiento que se resuelve con un Add-on en lugar de un nivel inferior es una deuda técnica que la empresa pagará por años.

Caja de Herramientas

Los "fierros" para ejecutar la estrategia.

DTW (Data Transfer Workbench)

Qué es: Una aplicación de escritorio (cliente) que permite la carga masiva de datos en SAP B1 usando plantillas de Excel (guardadas como .csv o .txt).

Cuándo se usa: Principalmente en la **Fase 4 (Preparación Final)** para la migración de datos.

Para qué se usa:

  • Datos Maestros (Clientes, Proveedores, Artículos, Cuentas Contables).
  • Datos Transaccionales (Saldos Iniciales de Inventario, Saldos de Apertura de Cuentas por Cobrar/Pagar, Asientos de Apertura).

Mejor Práctica: Siempre usa el modo "Simulación" (`Run Simulation`) primero. El DTW usa la DI-API por debajo, por lo que simular la carga te dirá *todos* los errores de lógica de negocio (ej: "El artículo X no existe") sin insertar un solo registro.

Transaction Notification (TN)

Qué es: Un *único* Procedimiento Almacenado (Stored Procedure) en la base de datos (SBO_SP_TransactionNotification) que SAP B1 ejecuta *automáticamente* con cada transacción (Crear/Actualizar) de un objeto de negocio.

Cuándo se usa: En la **Fase 3 (Realización)** para implementar validaciones de negocio "inquebrantables".

Para qué se usa: Es la *única* forma 100% segura de bloquear una acción. A diferencia de un FMS (que se salta por la DI-API), el TN se ejecuta siempre.

  • Ejemplo: IF @object_type = '13' AND @transaction_type IN ('A', 'U') ... (Bloquear una Factura - 13 - al Agregar 'A' o Actualizar 'U').
  • "No permitir crear una factura si el UDF 'Placa' está vacío".
  • "No permitir crear un pedido si el cliente tiene más de 3 facturas vencidas".

La Trampa: Un TN mal escrito (un query lento o un bucle) puede **destruir el rendimiento** de *toda* la compañía, ya que se ejecuta en cada transacción. Debe ser híper-optimizado.

Extension Manager

Qué es: El panel dentro de SAP B1 (Gestión -> Add-ons -> Gestor de Add-ons) que se usa para instalar y administrar los Add-ons del SDK.

Cuándo se usa: En la **Fase 3 y 4** para desplegar los desarrollos (Gaps) en los ambientes de QAS (para UAT) y Producción (para Go-Live).

Cómo funciona:

  1. El desarrollador compila su Add-on (.exe) y lo empaqueta en un archivo .ard (un manifest) y un .zip.
  2. El administrador usa el Extension Manager para "Registrar" el Add-on (subir el .ard).
  3. El administrador asigna el Add-on a la compañía y a los usuarios.
  4. La próxima vez que el usuario inicia sesión, B1 descarga e instala el .zip automáticamente.

La Trampa: Problemas de permisos de Windows (UAC) o de arquitectura (Add-on de 32-bits en cliente de 64-bits). Siempre probar la instalación en una máquina de usuario "limpia", no solo en la del desarrollador.

Biblioteca de Código: Desarrollo (SDK y Service Layer)

El "Cómo": DI-API (C#), Service Layer (API) y UI-API (Add-on)

💻 DI-API (SDK Clásico - C#)
Ej 1: Conexión a la Compañía
SAPbobsCOM.Company oCompany = new SAPbobsCOM.Company();
oCompany.Server = "SAP-SERVER";
oCompany.CompanyDB = "SBODEMO_PERU";
oCompany.UserName = "manager";
oCompany.Password = "1234";
oCompany.DbServerType = SAPbobsCOM.BoDbServerTypes.dst_MSSQL2019;

int lRetCode = oCompany.Connect();
if (lRetCode != 0) {
    Console.WriteLine("Error: " + oCompany.GetLastErrorDescription());
}
Ej 2: Mejor Práctica - Liberar Memoria COM
// Después de usar un objeto COM (ej. oInvoice)
if (oInvoice != null) {
    System.Runtime.InteropServices.Marshal.ReleaseComObject(oInvoice);
    oInvoice = null;
}
GC.Collect(); // Forzar recolección de basura
  • ¡CRÍTICO! Si no liberas los objetos COM, el proceso (o el cliente SAP) tendrá fugas de memoria y se colgará.
Ej 3: Mejor Práctica - Manejo de Errores
int lRetCode = oDocument.Add();
if (lRetCode != 0) {
    string errorMsg;
    oCompany.GetLastError(out lRetCode, out errorMsg);
    throw new Exception($"Error SAP: {lRetCode} - {errorMsg}");
}
Ej 4: Mejor Práctica - Transacciones
try {
    oCompany.StartTransaction();
    
    // ... (código que crea factura, ej. oInvoice.Add()) ...

    if (oCompany.InTransaction) {
        oCompany.EndTransaction(SAPbobsCOM.BoWfTransOpt.wf_Commit);
    }
} catch (Exception) {
    if (oCompany.InTransaction) {
        oCompany.EndTransaction(SAPbobsCOM.BoWfTransOpt.wf_RollBack);
    }
}
  • Usa esto para documentos que crean asientos contables (Facturas, Pagos) para asegurar la integridad (Todo o Nada).
Ej 5: Crear Socio de Negocio (Cliente)
SAPbobsCOM.BusinessPartners oBP;
oBP = (SAPbobsCOM.BusinessPartners)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oBusinessPartners);
oBP.CardCode = "C-NUEVO-01";
oBP.CardName = "Cliente de Prueba";
oBP.CardType = SAPbobsCOM.BoCardTypes.cCustomer;
oBP.GroupCode = 100; // Asumiendo 100 es el grupo "Clientes"
oBP.Add();
// (No olvidar manejo de errores y liberar COM)
Ej 6: Actualizar Socio de Negocio
SAPbobsCOM.BusinessPartners oBP;
oBP = (SAPbobsCOM.BusinessPartners)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oBusinessPartners);
if (oBP.GetByKey("C-NUEVO-01")) { // ¡Importante buscarlo primero!
    oBP.Phone1 = "999-888-777";
    oBP.EmailAddress = "test@cliente.com";
    oBP.Update();
}
Ej 7: Crear Factura de Venta (con Líneas)
SAPbobsCOM.Documents oInvoice;
oInvoice = (SAPbobsCOM.Documents)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oInvoices);
oInvoice.CardCode = "C-NUEVO-01";
oInvoice.DocDate = DateTime.Now;

// Línea 1
oInvoice.Lines.ItemCode = "ITEM001";
oInvoice.Lines.Quantity = 10;
oInvoice.Lines.Price = 150.0;
oInvoice.Lines.Add();

// Línea 2
oInvoice.Lines.ItemCode = "ITEM002";
oInvoice.Lines.Quantity = 5;
oInvoice.Lines.Price = 300.0;
oInvoice.Lines.Add();

oInvoice.Add();
// (Usar Transacciones, Manejo de Errores y Liberar COM)
Ej 8: Cerrar Pedido de Venta
SAPbobsCOM.Documents oOrder;
oOrder = (SAPbobsCOM.Documents)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oOrders);
if (oOrder.GetByKey(1234)) { // 1234 = DocEntry del Pedido
    oOrder.Close();
}
  • Usa `.Close()` para cerrar documentos. Usa `.Cancel()` para anularlos (si se permite).
Ej 9: Ejecutar Consulta (Recordset)
SAPbobsCOM.Recordset oRecordset;
oRecordset = (SAPbobsCOM.Recordset)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.BoRecordset);
string sql = "SELECT CardName, Balance FROM OCRD WHERE CardCode = 'C-NUEVO-01'";
oRecordset.DoQuery(sql);

if (!oRecordset.EoF) { // EoF = End of File
    string name = oRecordset.Fields.Item("CardName").Value.ToString();
    double balance = (double)oRecordset.Fields.Item("Balance").Value;
}
// (Liberar COM para oRecordset)
  • Seguridad: ¡Nunca uses SQL concatenado con input del usuario! (`sql = "..." + txtBox.Text`). Es un riesgo de Inyección SQL. Usa parámetros si es posible o limpia la entrada.
Ej 10: Crear un UDF (Campo de Usuario)
SAPbobsCOM.UserFieldsMD oUserField;
oUserField = (SAPbobsCOM.UserFieldsMD)oCompany.GetBusinessObject(SAPbobsCOM.BoObjectTypes.oUserFields);
oUserField.TableName = "OITM"; // Tabla de Artículos
oUserField.Name = "MiCampoExtra";
oUserField.Description = "Mi Campo Extra";
oUserField.Type = SAPbobsCOM.BoFieldTypes.db_Alpha;
oUserField.EditSize = 50;
oUserField.Add();
🔗 Service Layer (API Moderna - JSON)
Ej 11: Login (Conexión)
// POST https://sap-server:50000/b1s/v1/Login
{
    "CompanyDB": "SBODEMO_PERU",
    "UserName": "manager",
    "Password": "1234"
}
// Respuesta: Devuelve una Cookie (B1SESSION)
  • Guarda la `B1SESSION` y el `ROUTEID` devueltos en la Cookie. Debes enviarlos de vuelta en el Header de *todas* las siguientes peticiones.
Ej 12: Crear Pedido (con Líneas)
// POST .../b1s/v1/Orders
{
    "CardCode": "C-NUEVO-01",
    "DocDueDate": "2025-11-10",
    "Comments": "Pedido desde E-commerce",
    "DocumentLines": [
        { "ItemCode": "ITEM001", "Quantity": 10 },
        { "ItemCode": "ITEM002", "Quantity": 5 }
    ]
}
  • Service Layer es transaccional por defecto. Si una línea falla, todo el documento falla (Rollback automático).
Ej 13: Crear Factura basada en Pedido
// POST .../b1s/v1/Invoices
{
    "CardCode": "C001", "DocDueDate": "2025-11-10",
    "DocumentLines": [
        {
            "BaseType": 17, // 17 = Pedido (ORDR)
            "BaseEntry": 1234, // DocEntry del Pedido
            "BaseLine": 0 // Número de línea del Pedido
        },
        {
            "BaseType": 17,
            "BaseEntry": 1234,
            "BaseLine": 1
        }
    ]
}
  • Usar `BaseType`, `BaseEntry` y `BaseLine` es la forma correcta de "copiar de" un documento a otro y mantener la referencia.
Ej 14: Actualizar (PATCH) Comentario de Pedido
// PATCH .../b1s/v1/Orders(1234)
{ "Comments": "Nuevo comentario del cliente." }
  • Usa `PATCH` (no `PUT`) para actualizaciones parciales. `PATCH` solo actualiza los campos que envías. `PUT` reemplaza el objeto entero.
Ej 15: Cerrar Línea de Pedido
// PATCH .../b1s/v1/Orders(1234)
{
    "DocumentLines": [
        {
            "LineNum": 1, // Línea que queremos cerrar
            "LineStatus": "bost_Close"
        }
    ]
}
Ej 16: Mejor Práctica - Consulta OData (Select)
// GET .../b1s/v1/BusinessPartners('C-NUEVO-01')?$select=CardName,Balance,EmailAddress
  • ¡La práctica #1! Nunca pidas el objeto completo (`*`). Pide solo los campos que necesitas con `$select`.
Ej 17: Mejor Práctica - Consulta OData (Filter)
// GET .../b1s/v1/Items?$filter=ItemsGroupCode eq 102 and Price gt 100
  • Usa `$filter` (el `WHERE`) para filtrar en el servidor, no en tu app.
Ej 18: Mejor Práctica - Consulta OData (Paginación)
// GET .../b1s/v1/Invoices?$top=20&$skip=40
// Trae 20 facturas, saltándose las primeras 40 (Página 3)
Ej 19: Consulta OData (Expandir Líneas)
// GET .../b1s/v1/Orders(1234)?$expand=DocumentLines
  • Por defecto, SL no trae las líneas. Usa `$expand` para forzar que las incluya.
Ej 20: Llamar a un Query (SQL View)
// GET .../b1s/v1/sml.svc/MY_QUERY_NAME
// (Asumiendo que creaste un "Query" o "Vista" en B1)
🧩 UI-API (SDK Add-on - C#)
Ej 21: Conexión (Inicio del Add-on)
// En el Main() de tu Add-on .exe
SAPbouiCOM.SboGuiApi oGuiApi = new SAPbouiCOM.SboGuiApi();
string sConnectionString = Environment.GetCommandLineArgs().GetValue(1).ToString();
oGuiApi.Connect(sConnectionString);
SAPbouiCOM.Application SBO_Application = oGuiApi.GetApplication();

// Conectarse a la DI-API (para transacciones)
oCompany = (SAPbobsCOM.Company)SBO_Application.Company.GetDICompany();
  • Un Add-on siempre recibe la "connection string" como argumento de línea de comandos.
  • Usa `GetDICompany()` para obtener la conexión DI-API sin pedirle la contraseña al usuario de nuevo.
Ej 22: Capturar Evento (Clic en Botón)
// Suscribirse al evento
SBO_Application.ItemEvent += SBO_Application_ItemEvent;

//...
private void SBO_Application_ItemEvent(string FormUID, ref SAPbouiCOM.ItemEvent pVal, out bool BubbleEvent)
{
    BubbleEvent = true;
    
    // Si es un clic, en el form de Factura (133), en el botón "Crear" (1)
    if (pVal.EventType == SAPbouiCOM.BoEventTypes.et_ITEM_PRESSED &&
        pVal.FormType == 133 && pVal.ItemUID == "1" && pVal.BeforeAction)
    {
        // ... poner aquí la lógica de validación ...
        
        // SBO_Application.MessageBox("¡Validación falló!");
        // BubbleEvent = false; // Esto cancela el clic
    }
}
  • ¡Usa `pVal.BeforeAction == true`! Esto te permite capturar el evento *antes* de que SAP B1 lo procese.
  • Poner `BubbleEvent = false` es la forma de "cancelar" la acción nativa de SAP (ej. impedir que se cree la factura).
Ej 23: Leer y Escribir Valor en Form
// Obtener el formulario activo
SAPbouiCOM.Form oForm = SBO_Application.Forms.ActiveForm;

// Obtener el campo (EditText) por su UID
SAPbouiCOM.EditText oCardCode = (SAPbouiCOM.EditText)oForm.Items.Item("4").Specific;
SAPbouiCOM.EditText oComments = (SAPbouiCOM.EditText)oForm.Items.Item("16").Specific;

// Leer valor
string cliente = oCardCode.Value;

// Escribir valor
oComments.Value = "Valor puesto por mi Add-on";
Ej 24: Añadir Botón a Form Estándar
// (Capturar el evento de Form_Load para el form de BP: 134)
SAPbouiCOM.Form oForm = SBO_Application.Forms.GetForm("134", pVal.FormUID);

SAPbouiCOM.Item oNewItem;
oNewItem = oForm.Items.Add("MiBoton", SAPbouiCOM.BoFormItemTypes.it_BUTTON);
oNewItem.Left = oForm.Items.Item("2").Left + oForm.Items.Item("2").Width + 10;
oNewItem.Top = oForm.Items.Item("2").Top;
oNewItem.Width = 100;

SAPbouiCOM.Button oButton = (SAPbouiCOM.Button)oNewItem.Specific;
oButton.Caption = "Acción Custom";
  • Añade tus items en el evento `et_FORM_LOAD` cuando `pVal.BeforeAction == false` (después de que el form se haya cargado).

Biblioteca de Reportería (Query Manager y Crystal Reports)

El "Cómo": Consultas SQL vs. HANA y el Proceso de Crystal Reports

📈 Query Manager (SQL vs. HANA)
  • Parámetros: La sintaxis `[%0]`, `[%1]` funciona igual en ambos.
  • Tablas: En HANA, los nombres de tablas y campos suelen ser sensibles a mayúsculas y requieren comillas dobles (Ej: `T0."CardName"` en lugar de `T0.CardName`).
  • Rendimiento (SQL): Usa `WITH (NOLOCK)` en tus `JOINs` para no bloquear usuarios.
  • Rendimiento (HANA): HANA no usa `NOLOCK`. Está diseñado para lecturas sin bloqueo.
🔵 Ejemplos SQL Server (T-SQL)

Top 5 Clientes:

SELECT TOP 5 T0.CardName, SUM(T0.DocTotal)
FROM OINV T0 WITH (NOLOCK)
GROUP BY T0.CardName ORDER BY SUM(T0.DocTotal) DESC

Manejo de Nulos:

SELECT T0.ItemCode, ISNULL(T0.U_MiCampo, 'Valor Vacio')
FROM OITM T0 WITH (NOLOCK)

Fecha Actual:

SELECT T0.DocNum FROM ORDR T0
WHERE T0.DocDueDate < GETDATE()

Subqueries:

SELECT T0.ItemCode,
(SELECT SUM(T1.OnHand) FROM OITW T1 WHERE T1.ItemCode = T0.ItemCode) AS 'TotalStock'
FROM OITM T0

Pedidos Abiertos:

SELECT T0.DocNum, T1.ItemCode, T1.OpenQty
FROM ORDR T0 JOIN RDR1 T1 ON T0.DocEntry = T1.DocEntry
WHERE T1.LineStatus = 'O'
⚫ Ejemplos HANA (SQLScript)

Top 5 Clientes:

SELECT T0."CardName", SUM(T0."DocTotal")
FROM "OINV" T0
GROUP BY T0."CardName" ORDER BY SUM(T0."DocTotal") DESC
LIMIT 5

Manejo de Nulos:

SELECT T0."ItemCode", IFNULL(T0."U_MiCampo", 'Valor Vacio')
FROM "OITM" T0

Fecha Actual:

SELECT T0."DocNum" FROM "ORDR" T0
WHERE T0."DocDueDate" < NOW()

Subqueries (HANA prefiere JOINs):

SELECT T0."ItemCode", SUM(T1."OnHand") AS "TotalStock"
FROM "OITM" T0
LEFT JOIN "OITW" T1 ON T0."ItemCode" = T1."ItemCode"
GROUP BY T0."ItemCode"

Pedidos Abiertos:

SELECT T0."DocNum", T1."ItemCode", T1."OpenQty"
FROM "ORDR" T0 JOIN "RDR1" T1 ON T0."DocEntry" = T1."DocEntry"
WHERE T1."LineStatus" = 'O'
📄 Cómo Hacer Reportes en Crystal Reports (El Proceso)

Crystal se usa cuando un Query no es suficiente (formatos, logos, gráficos, códigos de barra, lógica compleja).

Paso 1: Conexión (La Forma Correcta)
  • ¡No uses el Add-in de SAP! Es lento y obsoleto.
  • Abre Crystal Reports (standalone).
  • Ve a `Database Expert` -> `Create New Connection`.
  • Para SQL Server: Usa `OLE DB (ADO)`. Selecciona `Microsoft OLE DB Provider for SQL Server`. Ingresa IP, BBDD (`SBODEMO_PERU`), usuario (`sa` o un usuario de lectura) y contraseña.
  • Para HANA: Instala el cliente de 32-bits de HANA (Crystal es de 32-bits). Luego usa `ODBC (RDO)`. Busca el DSN que creaste (ej. `HDB_32`) e ingresa usuario (`SYSTEM` o de lectura) y contraseña.
Paso 2: Origen de Datos (Tablas vs. SP)

Tienes dos opciones en el `Database Expert`:

Opción A (Simple): Añade tablas (Ej: `OINV`, `INV1`, `OCRD`) y únelas visualmente en la pestaña `Links`. Bueno para reportes simples.

Opción B (Mejor Práctica):

  • Crea un **Procedimiento Almacenado (Stored Procedure)** en la BBDD (SQL o HANA) que contenga todo el SQL complejo (ej. un P&L).
  • En Crystal, solo añade ese SP. Es *infinitamente* más rápido y mantenible.
Paso 3: Parámetros (La Magia de B1)

Para que SAP B1 "hable" con Crystal, tus parámetros en Crystal (`Parameter Fields`) deben tener nombres especiales:

  • Para Layouts (Facturas, Pedidos): El parámetro debe llamarse `DocKey@`. SAP B1 le pasará automáticamente el `DocEntry` del documento que estás viendo.
  • Para Reportes de SN: Usa `CardCode@`. SAP B1 le pasará el código del cliente.
  • Para Parámetros de Query Manager: Nombra tus parámetros igual que en el query: `[%0]` o `FechaDesde@`. SAP B1 mostrará la misma ventana de selección.
Paso 4: Diseño, Fórmulas y Secciones

Aquí está el poder de Crystal:

  • Agrupación: Usa el `Group Expert` para agrupar (Ej: por `CardName`).
  • Secciones: Usa las secciones (Ej: `Group Header`, `Details`, `Group Footer`) para poner totales. (Ej: `SUM({INV1.LineTotal}, {OCRD.CardName})` en el `Group Footer`).
  • Fórmulas: Usa el `Formula Workshop` para lógica de *presentación*. (Ej: `IF {OINV.DocTotal} > 1000 THEN crRed ELSE crBlack`).
  • Códigos de Barra: Arrastra un campo (ej. `OITM.ItemCode`), dale clic derecho -> `Change to Barcode`. ¡Listo!
  • Sub-reportes: Evítalos si puedes. Son lentos. Prefiere `JOINS` en el SP.
Paso 5: Importación y Asignación en SAP B1
  1. Guarda tu reporte (`.rpt`).
  2. En SAP B1: `Gestión -> Definiciones -> General -> Gestor de informes y layouts`.
  3. Clic en `Importar`.
  4. Busca tu archivo `.rpt`.
  5. Define el "Tipo de Contenido" (Ej: "Factura de Clientes").
  6. Para un Layout: Ve a `Gestión -> Impresión de documentos`, busca el documento (Ej: Factura), y asígnalo como el "Layout Estándar".
  7. Para un Reporte: Simplemente busca dónde lo importaste en el menú principal (Ej: `Ventas -> Informes de Ventas -> Mi Reporte`) y ejecútalo.