# Cliente Dinaup

Esta guía explica, paso a paso, cómo usar el cliente `Dinaup` en .NET para conectarte al servicio, consultar informes, gestionar archivos, añadir anotaciones, generar documentos dinámicos, leer/escribir datos de secciones y trabajar con paginación y filtros.

## Sorprendentemente sencillo

{% stepper %}
{% step %}

#### Instala

{% code title="Terminal" %}

```bash
dotnet add package Dinaup
dotnet add package Demoup.MyDinaup
```

{% endcode %}
{% endstep %}

{% step %}

#### Conecta

{% tabs %}
{% tab title="Básico" %}

```csharp
var client = await DinaupClientC.ConnectAsync(
    endPoint: "https://api.dinaup.com/v2/tu-codigo",
    publicKey: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    secretKey: "tu-secret-key-aqui"
);

if (client == null || client.IsConnected == false)
{
    throw new Exception("No se pudo conectar a Dinaup");
}

```

{% endtab %}

{% tab title="Vault" %}

```csharp
using Dinaup;

// Las unicas credenciales en variables de entorno
var vault = new VaultData(
    Environment.GetEnvironmentVariable("VAULT_URL"),
    Environment.GetEnvironmentVariable("VAULT_PASSWORD")
);
vault.Initialize();

// Todo lo demas viene del Vault
var client = await DinaupClientC.ConnectAsync(
    endPoint: vault.Read("dinaup.endpoint"),
    publicKey: vault.Read("dinaup.publickey"),
    secretKey: vault.Read("dinaup.secretkey")
);

if (client == null || client.IsConnected  == false)
{
    throw new Exception("No se pudo conectar a Dinaup");
}
```

{% endtab %}

{% tab title="DI" %}
{% code title="program.cs" %}

```csharp

using Dinaup;

var builder = WebApplication.CreateBuilder(args);

// Cargar Vault
var vault = new VaultData(
    Environment.GetEnvironmentVariable("VAULT_URL"),
    Environment.GetEnvironmentVariable("VAULT_PASSWORD")
);
vault.Initialize();

// Conectar a Dinaup
var client = await DinaupClientC.ConnectAsync(
    endPoint: vault.Read("dinaup.endpoint"),
    publicKey: vault.Read("dinaup.publickey"),
    secretKey: vault.Read("dinaup.secretkey")
);

if (client == null || client.IsConnected == false)
    throw new Exception("No se pudo conectar a Dinaup");


// Registrar como Singleton
builder.Services.AddSingleton(client);

var app = builder.Build();

```

{% endcode %}
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Opera

{% tabs %}
{% tab title="Leer" %}

```csharp
// Use pre-built reports from DemoUp.MyDinaup
using DemoUp.MyDinaup.Reports.FuncionalidadD;

// Create and execute the report
var ventasReport = new APIVentasC();
await ventasReport.ExecuteQueryAsync(
    dinaupClient: client,
    page: 1,
    resultsPerPage: 50
);

// Access results
Console.WriteLine($"Total: {ventasReport.TotalResults} records");
foreach (var row in ventasReport.Rows)
{
    Console.WriteLine($"- {row.NumerodefacturaCompleto}: {row.Total}");
}

```

{% endtab %}

{% tab title="Agregar" %}

```csharp
Guid sectionId = /* sección destino */;

var data = new Dictionary<string, string>
{
    {
        SeccionDePruebasAPIES.TextoPrincipal,
        $"Prueba {Guid.NewGuid()}"
    }
};

var wOp = new WriteOperation("", data); // "" => alta

var result= dinaupClient.RunWriteOperation(
    dinaupClient.DefaultSession,
    SeccionDePruebasAPIES.SectionId,
    wOp,
    false  
);

// Lanza excepción si hubo errores globales
result.EnsureSuccess();

Guid newId = wOp.WriteOperationResult.RowID;
```

{% endtab %}

{% tab title="Editar" %}

```csharp
Guid rowId = /* Id del registro a editar */;

var data = new Dictionary<string, string>
{
    {
        SeccionDePruebasAPIES.TextoPrincipal,
        $"Prueba {Guid.NewGuid()}"
    }
};

var wOp= new WriteOperation(rowId, data);

var result= dinaupClient.RunWriteOperation(
    dinaupClient.DefaultSession,
    sectionId,
    wOp,
    false
);

// Lanza excepción si hubo errores globales
result.EnsureSuccess();

Guid updatedId = wOp.WriteOperationResult.RowID;



```

{% endtab %}
{% endtabs %}
{% endstep %}
{% endstepper %}

## Referencias

### Dinaup .NET

Antes de empezar necesitarás instalar el paquete Dinaup, que incluye el Cliente base: conexión, autenticación, sesiones, archivos, anotaciones, WriteOperations, etc.

{% embed url="<https://www.nuget.org/packages/Dinaup>" %}

### MyDinaup

Dinaup es una plataforma flexible: cada empresa cuenta con su propio modelo de datos (secciones, informes y documentos dinámicos). Para trabajar de forma tipada con ese modelo, necesitas la biblioteca MyDinaup específica de tu organización. MyDinaup incluye modelos y utilidades tipadas (Reports, SectionsD y DynamicDocuments) con los nombres reales de tus secciones y depende del paquete base Dinaup.

{% tabs %}
{% tab title="Genéricos" %}
**Modelo Ready to Go**

Este Nuget únicamente incluye la modelización de la estructura **Ready to Go**. Ideal para desarrollos compatibles con varias empresas.

{% embed url="<https://www.nuget.org/packages/ReadyToGo.MyDinaup>" %}

**Modelo DemoUp**

Este es un modelo de Pre-Producción que utiliza internamente el equipo de Dinaup. Su uso está desaconsejado ya que puede contener errores y estructuras incompatibles.

{% embed url="<https://www.nuget.org/packages/DemoUp.MyDinaup>" %}
{% endtab %}

{% tab title="Personalizado" %}

1. Para actualizar MyDinaup debes Acceder con el usuario Sistema a [Dinaup Desktop](broken://pages/jvXuHgnyndKeTJaxBwYQ)
2. Navega hacia `Configuración` > `Generar MyDinaup` y pulsa sobre `Commit`
   {% endtab %}
   {% endtabs %}

### Informes

Los informes son consultas predefinidas y tipadas para acceder a datos de manera eficiente. Están en el espacio de nombres:

* `{Empresa}.MyDinaup.Reports.{CategoriaD}`
* Convención de nombres de clases: API + NombreDeSeccion + C
* Los nombres de secciones/clases están en español porque se generan a partir de tu licencia (por ejemplo, “Ventas”, “Impuestos”, etc.).

Ejemplos de clases:

* DemoUp.MyDinaup.Reports.FuncionalidadD.APISeccionDePruebasAPIC
* DemoUp.MyDinaup.Reports.FuncionalidadD.APIEntidadesC
* DemoUp.MyDinaup.Reports.VentasD.APIVentasC
* DemoUp.MyDinaup.Reports.ImpuestosD.APIImpuestosC

{% tabs %}
{% tab title="Consultar un informe" %}
Usa ExecuteQueryAsync para obtener resultados paginados. Accede a filas fuertemente tipadas desde report.Rows.

```csharp
using DemoUp.MyDinaup.Reports.FuncionalidadD;

var report = new APISeccionDePruebasAPIC(); // 

await report.ExecuteQueryAsync(dinaupClient, page: 1, pageSize: 10);

Console.WriteLine($"Total resultados: {report.TotalResults}");

if (report.Rows.IsNotEmpty())
{
    foreach (var fila in report.Rows)
    {
        // Accede a propiedades tipadas de cada fila
        // Ej.: fila.TextoPrincipal, fila.ValorEntero, etc.
    }
}
```

{% hint style="info" %}
Ajusta pageSize a tus necesidades. Si esperas muchos resultados, usa paginación.
{% endhint %}
{% endtab %}

{% tab title="Filtrar y Ordenar" %}

* **Filtrar resultados**\
  Filtro simple por campo de la misma sección

  ```csharp
  var report = new APISeccionDePruebasAPIC();

  // Igualdad estricta
  report.AddFilter(
      SeccionDePruebasAPIES.ValorEntero,
      "=", 
      3);

  await report.ExecuteQueryAsync(dinaupClient, page: 1, pageSize: 10);

  if (report.Rows.IsNotEmpty())
  {
      foreach (var fila in report.Rows)
      {
          // Usar fila...
      }
  }
  ```
* **Filtrar utilizando datos relacionados** (composición de rutas):

  Construye una clave de relación combinando el ID de sección y el campo de interés en la entidad relacionada.

  ```csharp
  // Clave de relación (ruta compuesta)
  var relationKey =
       SeccionDePruebasAPIES._SectionID.STR() + "." +
       SeccionDePruebasAPIES.ReferenciaAutorDelAlta + "." +
       EntidadesBaseES._SectionID.STR() + "." +
       EntidadesBaseES.VIP;
   
   
  var report = new APISeccionDePruebasAPIC();
  report.AddFilter(relationKey, "=", 3);

  await report.ExecuteQueryAsync(dinaupClient, page: 1, pageSize: 10);

  if (report.Rows.IsNotEmpty())
  {
      foreach (var fila in report.Rows)
      {
          // Usar fila...
      }
  }
  ```
* **Filtrado Between**

  ```csharp
  // Between (ej.: 1 a 2999)
  var reportBetween = new APISeccionDePruebasAPIC();
  reportBetween.AddFilterBetween(
     SeccionDePruebasAPIES.ValorEntero,
      1,
      2999
  );

  await reportBetween.ExecuteQueryAsync(dinaupClient, page: 1, pageSize: 10);
  ```
* **Solapamiento de rangos de fechas (overlap)**

  ```csharp
  var eventosReport = new APIEventosDeCRMC();

  var campoInicio = EventosDeCRMES.InicioEvento_UTC;
  var campoFin    = EventosDeCRMES.FinEvento_UTC;

  var rangoInicio = DateOnly.FromDateTime(new DateTime(2000, 1, 1));
  var rangoFin    = DateOnly.FromDateTime(DateTime.Today.AddYears(50));

  eventosReport.AddFilterDateRangeOverlapFilter(campoInicio, campoFin, rangoInicio, rangoFin);

  await eventosReport.ExecuteQueryAsync(dinaupClient, page: 1, pageSize: 25);
  ```

{% endtab %}

{% tab title="Paginación" %}
Cuando el informe tiene muchos resultados, recorre página a página con ExecuteQuery\_NextPage\_Async.

```csharp
 var report = new APISeccionDePruebasAPIC();
 await report.ExecuteQueryAsync(_dinaupClient, 1, 300);

 if (report.Rows.IsNotEmpty())
 {
     do
     {


         foreach (var row in report.Rows)
         {
              
         }   


     } while (await report.ExecuteQuery_NextPageAsync());
 }

```

{% endtab %}
{% endtabs %}

### Archivos

Dinaup permite subir archivos desde memoria o URL y obtener URLs firmadas para su consumo.

{% tabs %}
{% tab title="Básico" %}

* **Subir desde un array de bytes**

  ```csharp
  var bytes = System.Text.Encoding.UTF8.GetBytes("hola " + Guid.NewGuid());
  var upload = await dinaupClient.File_UploadBytesAsync(
      dinaupClient.DefaultSession,
      bytes,
      "prueba.txt"
  );

  Guid fileId = upload.FileId;
  ```
* **Subir desde una URL**

  ```csharp
  var upload = await dinaupClient.File_UploadURLAsync(
      dinaupClient.DefaultSession,
      "https://cdn.dinaup.com/dinaup/web/portal/img/dinabot_marca.png",
      "dinabot.png"
  );

  Guid fileId = upload.FileId;
  ```
* **Obtener una URL firmada (lectura)**

  ```csharp
  var signed = await dinaupClient.File_SignURLGetAsync(fileId);
  var url = signed?.url_original; // URL lista para usar
  ```

{% endtab %}

{% tab title="Buscar duplicados" %}
{% hint style="info" %}
Calcula el SHA1 en cliente y consulta antes de subir. Ahorra almacenamiento y ancho de banda.
{% endhint %}

```csharp

private async Task<Dinaup.DinaupFileDTO> GetExistingFileAsync(string sha1)
{
    var report = new MyDinaup.Reports.FuncionalidadD.APIIndiceDeArchivosEnSistemaC();
    report.AddFilter("crcbase", "=", sha1);
    await report.ExecuteQueryAsync(_dinaupClient, 1, 1, "", true, true); 
    return report.Files.Values.FirstOrDefault();
}

private async Task<Dinaup.DinaupFileDTO> GetExistingFileAsync(byte[] data)
{
    string sha1 = Dinaup.extensions.ToSHA1(data);
    return await GetExistingFileAsync(sha1);
}

```

{% endtab %}
{% endtabs %}

### Anotaciones

Todas las filas (registros) de Dinaup soportan 3 tipos de anotaciones:

* `AnnotationTypeE.`**`Comments`**: Mensajería tipo chat para el equipo, dentro del contexto del registro.
* `AnnotationTypeE.`**`PublicGallery`**: Archivos públicos en el CDN (p. ej., fotos de productos).
* `AnnotationTypeE.`**`Files`**: Documentos privados asociados (contratos, presupuestos, soportes, etc.).

{% hint style="warning" %}
Los archivos en **PublicGallery** son públicos: cualquiera con el enlace puede verlos.
{% endhint %}

A continuación, ejemplos usando AnnotationTypeE.Comments.

{% tabs %}
{% tab title="Ejemplos" %}

* **Agregar un comentario (solo texto)**

  ```csharp
  Guid sectionId = /* tu sección */;
  Guid rowId     = /* registro al que agregas el comentario */;

  await dinaupClient.Annotation_PutAsync(
      dinaupClient.DefaultSession,
      sectionId,
      rowId.STR(), 
      Guid.Empty,       // sin archivo adjunto
      "¡Hola mundo! Este es mi primer comentario.",
      AnnotationTypeE.Comments
  );
  ```
* **Agregar un comentario con archivo adjunto**

  ```csharp
  var bytes = System.Text.Encoding.UTF8.GetBytes("contenido adjunto");
  var upload = await dinaupClient.File_UploadBytesAsync(
      dinaupClient.DefaultSession,
      bytes,
      "nota.txt"
  );

  await dinaupClient.Annotation_PutAsync(
      dinaupClient.DefaultSession,
      sectionId,
      rowId.ToString(),
      upload.FileId, // adjuntamos el archivo
      "Comentario con adjunto",
      AnnotationTypeE.Comments
  );
  ```
* **Leer anotaciones**

  ```csharp
  var resultado = await dinaupClient.Annotations_GetAsync(
      dinaupClient.DefaultSession,
      sectionId,
      rowId,
      AnnotationTypeE.Comments
  );

  foreach (var anotacion in resultado.Annotations)
  {
      var texto    = anotacion.Text;
      var adjuntos = anotacion.AttachedFiles; // si hay archivos adjuntos
  }
  ```

{% endtab %}
{% endtabs %}

### Documentos dinámicos

Los Documentos Dinámicos permiten:

* Agrupar varias consultas en una sola operación para reducir latencia.
* Generar documentos de impresión (facturas, albaranes) delegando la composición en Dinaup.

Recomendación:

* Para consultar datos en general, usa Reports siempre que sea posible. Documentos Dinámicos son útiles para orquestar múltiples consultas y componer resultados.

{% tabs %}
{% tab title="Ejemplo" %}

* **Ejecutar un documento dinámico**

  ```csharp
  var documentoSesionExecutor = new DynamicDocuments.APID.SesionC();

  // Pasa la sesión actual.
  // Si dispones de una sesión de usuario distinta, úsala; en caso contrario, usa la sesión por defecto.
  await documentoSesionExecutor.ExecuteAsync(dinaupClient, dinaupClient.DefaultSession);

  ```

{% endtab %}
{% endtabs %}

### Secciones

Las secciones son el equivalente a tablas/entidades por empresa (Clientes, Facturas, Empleados, etc.). En MyDinaup existen utilidades tipadas para trabajar con ellas:

* Espacio de nombres: MyDinaup.SectionsD
* Incluye métodos para leer registros por Id o por criterios.

Advertencia de rendimiento:

* Las operaciones de lectura en SectionsD consultan todos los datos de la sección y los textos principales de los datos relacionados (p. ej., en una venta también recupera empleados, clientes, productos, impuestos, etc.). Esto tiene mayor coste computacional.
* Para rendimiento óptimo, usa Reports. Para escenarios “super optimizados”, usa PGSync.
* Usa SectionsD cuando el código se ejecute con poca frecuencia o para prototipos rápidos.

{% tabs %}
{% tab title="Leer datos" %}

* **Recibir un dato por Id**

  ```csharp
  Guid paisId = /* Id del país */;

  var pais = await MyDinaup.SectionsD.PaisesD.GetRowByIdAsync(dinaupClient, paisId);

  // Usa pais.Propiedad...
  ```
* **Recibir una lista**

  ```csharp
  var filtros = new List<FilterCondition>
  {
      new FilterCondition(
          MyDinaup.SectionsD.PaisesD.PaisesES.CodigoDePaisAlfabeticoDe2Caracteres,
          "=",
          "ES"
      )
  };

  var parametros = new RowsRequestParameters(filtros.ToArray());

  var paises = await MyDinaup.SectionsD.PaisesD.GetRowsAsync(
      dinaupClient,
      parametros,
      dinaupClient.DefaultSession
  );

  // Iterar resultados
  foreach (var p in paises.Rows)
  {
      // p.Propiedad...
  }
  ```

{% endtab %}
{% endtabs %}

### Escritura

Las operaciones de escritura usan WriteOperation para altas y ediciones.

* El ID vacío ("") indica alta
* Un ID válido indica edición.
* Un ID que no existe provocará una excepción.

{% tabs %}
{% tab title="Básico" %}

* **Alta simple (crear un registro)**

  ```csharp
  Guid sectionId = /* sección destino */;

  var data = new Dictionary<string, string>
  {
      {
          SeccionDePruebasAPIES.TextoPrincipal,
          $"Prueba {Guid.NewGuid()}"
      }
  };

  var wOp = new WriteOperation("", data); // "" => alta

  var result= dinaupClient.RunWriteOperation(
      dinaupClient.DefaultSession,
      SeccionDePruebasAPIES.SectionId,
      wOp,
      false  
  );

  // Lanza excepción si hubo errores globales
  result.EnsureSuccess();

  Guid newId = wOp.WriteOperationResult.RowID;
  ```
* Edición (actualizar un registro existente)

  ```csharp
  Guid rowId = /* Id del registro a editar */;

  var data = new Dictionary<string, string>
  {
      {
          SeccionDePruebasAPIES.TextoPrincipal,
          $"Prueba {Guid.NewGuid()}"
      }
  };

  var wOp= new WriteOperation(rowId, data);

  var result= dinaupClient.RunWriteOperation(
      dinaupClient.DefaultSession,
      sectionId,
      wOp,
      false
  );

  // Lanza excepción si hubo errores globales
  result.EnsureSuccess();

  Guid updatedId = wOp.WriteOperationResult.RowID;
  ```

{% endtab %}

{% tab title="Acciones por lote" %}

* **Alta 10 registros de golpe**

  ```csharp
  var operaciones = new List<WriteOperation>();

  for (int i = 0; i < 10; i++)
  {
      var fila = new Dictionary<string, string>
      {
          {
              SeccionDePruebasAPID.SeccionDePruebasAPIES.TextoPrincipal,
              $"Lote {i}: {Guid.NewGuid()}"
          }
      };

      operaciones.Add(new WriteOperation("", fila)); // alta
  }

  var batchAdd = dinaupClient.RunWriteOperation(
      dinaupClient.DefaultSession,
      sectionId,
      operaciones,
      false
  );

  var ids = operaciones.Select(o => o.WriteOperationResult.RowID).ToList();
  ```
* **Editar 10 registros**

  ```csharp
  // Partiendo de la lista 'ids' creada en el ejemplo anterior
  var operacionesEdicion = new List<WriteOperation>();

  foreach (var id in ids)
  {
      var actualizado = new Dictionary<string, string>
      {
          {
              SeccionDePruebasAPID.SeccionDePruebasAPIES.TextoPrincipal,
              $"Editado {DateTime.UtcNow.Ticks}"
          }
      };

      operacionesEdicion.Add(new WriteOperation(id.ToString(), actualizado)); // edición
  }

  var batchEdit = dinaupClient.RunWriteOperation(
      dinaupClient.DefaultSession,
      sectionId,
      operacionesEdicion,
      false
  );
  ```

{% hint style="info" %}
Si se vana procesar más de 20 registros, usa .Chunk(20) para dividir en bloques.
{% endhint %}
{% endtab %}

{% tab title="Escritura Virtualizada" %}
La escritura virtualizada permite que Dinaup ejecute automáticamente la lógica de negocio definida en una sección. Al activarla, cualquier operación de escritura se comporta como si se hubiera realizado desde la interfaz web: con scripts, validaciones, eventos, y cálculos incluidos.

#### ¿Qué hace la virtualización?

Cuando se activa la virtualización en una operación de escritura (`RunWriteOperationAsync` con `virtualized: true`), Dinaup:

* Ejecuta scripts personalizados de la sección (`onBeforeSave`, `onAfterSave`, etc.)
* Recalcula campos automáticos (ej. totales, impuestos)
* Aplica reglas de validación y lógica de negocio
* Dispara eventos definidos en la sección

Cuando se desactiva (`virtualized: false`), simplemente se guardan los valores tal como se reciben (Creando histórico). **No se ejecuta ningún comportamiento adicional.**

{% hint style="warning" %}
La escritura virtualizada es más costosa en términos de rendimiento.\
Cada operación implica ejecutar scripts del backend y lógica personalizada, lo que puede afectar la velocidad si estás procesando muchos registros o usas esta opción innecesariamente.
{% endhint %}

#### ¿Cuándo usar virtualización?

:white\_check\_mark: Usa escritura virtualizada cuando la sección contiene lógica de negocio activa que debe ejecutarse. Esto incluye cálculos automáticos de totales en ventas, generación de fechas de vencimiento, o scripts que actualizan otras entidades relacionadas. Por ejemplo, al registrar una nueva venta, al cerrar un pedido o al generar una factura, la virtualización asegura que todo se procese correctamente.

#### ¿Cuándo evitar virtualización?

:no\_entry: Evita la virtualización cuando realizas modificaciones simples que no requieren ejecutar lógica adicional. Esto incluye acciones como cambiar el estado de una venta, asignar un técnico a un ticket de soporte, registrar una nota interna o marcar una tarea como completada. En estos casos, desactivarla mejora el rendimiento sin afectar el comportamiento esperado.
{% endtab %}

{% tab title="Ejemplos" %}
{% content-ref url="/pages/qpW1O1dLegRYovJ70VmD" %}
[Ejemplo: Agregar un Recambio](/desarrollo/sdk/cliente/ejemplo-agregar-recambio.md)
{% endcontent-ref %}

{% content-ref url="/pages/pgOE0xo3Ktq806OOXjbq" %}
[Ejemplo: WriteOperations por Lotes](/desarrollo/sdk/cliente/ejemplo-writeoperations-por-lotes.md)
{% endcontent-ref %}

{% content-ref url="/pages/KZN6mzB9Gz88CqgpNlVu" %}
[Ejemplo: Agregar un Cliente](/desarrollo/sdk/cliente/ejemplo-agregar-cliente.md)
{% endcontent-ref %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.dinaup.com/desarrollo/sdk/cliente.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
