# Programação

Informações aprofundadas sobre o uso de JavaScript, C# e Query (SQL) no agilityflow.

# SQL Server (Query) - Dicas e Funções (Versões Mais Antigas do Agilityflow)

<p class="callout warning">**IMPORTANTE**: Essa documentação é referente as versões antes de 2024 que são mais antigas do agilityflow e que utilizam o Banco de Dados **SQL SERVER.** O agilityflow nas últimas versões está utilizando **POSTGRESQL** .</p>

No agilityflow você pode buscar os dados utilizando SQL, através de queries. As queries devem ser compatíveis com **SQL Server.** Além disso existem alguns padrões que você deve seguir para obter um melhor resultado no agilityflow. Abaixo estão alguns detalhes importantes.

### Tratamento de Registros Deletados

<p class="callout danger">**IMPORTANTE:**   
É de extrema importância o tratamento de registros deletados, usando as regras abaixo.   
  
Já está no nosso Roadmap o tratamento automático dos deletados, enquanto isso, o tratamento se torna obrigatório pelo desenvolvedor.</p>

Para manter a integridade dos dados, o agilityflow nunca apaga um registro de uma tabela. Apenas o marca como deletado. Portanto, para qualquer query, é preciso excluir os deletados da lista.

Essa marcação é feita com campo deletado. Se o valor for 0, significa que ele não foi deletado e está visível no sistema. Caso esteja 1, significa que o registro já foi excluído e não é possível mais vê-lo na listagem principal do sistema.

**Exemplo 1, sem o tratamento de deletados:**

```SQL
select usu_nome, usu_email from tbl_usuario
```

**Resultado**:

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552935887749.png)

**Exemplo 2, com o tratamento de deletados:**

```SQL
select usu_nome, usu_email from tbl_usuario
where deletado = 0
```

**Resultado**:

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552935923310.png)

### Campo do tipo Datetime (Data e Hora)

Sempre que um campo de texto com máscara do tipo data é criado, o sistema cria uma cópia com o mesmo nome seguido de "<span style="color: #ff6600;">**\_\_datetime\_\_**</span>" essa informação é gravada em uma coluna no banco de dados do tipo **<span style="text-decoration: underline;">datetime</span>**, ao invés de varchar. Esses campos são criados para facilitar o uso tipado do dado nas queries

#### **Exemplo de campo Datetime**

Por exemplo, se o campo se chama "data\_e\_hora", haverá um outro chamado "data\_e\_hora\_\_datetime\_\_" e o conteúdo estará no formato padrão do sql server, **YYYY-MM-DD hh:mm:ss**

O campo "data\_e\_hora" estará sempre como varchar e o "data\_e\_hora\_\_datetime\_\_" estará como datetime

![](https://wiki.agilityflow.io/uploads/images/gallery/2021-04-Apr/scaled-840-0/image-1617632924330.png)

### Campo do tipo Numérico (decimal, float, double, int, number)

Sempre que um campo de texto com máscara do tipo número é criado, o sistema cria uma cópia com o mesmo nome seguido de "<span style="color: #ff6600;">**\_\_number\_\_**</span>" essa informação é gravada em uma coluna no banco de dados do tipo **<span style="text-decoration: underline;">numeric(18,6)</span>,** ao invés de varchar. Esses campos são criados para facilitar o uso tipado do dado nas queries

#### **Exemplo de campo numérico**

Por exemplo, se o campo se chama "numero\_exemplo", haverá um outro chamado "numero\_exemplo\_\_number\_\_" e o conteúdo estará no formato padrão do sql server.

O campo "numero\_exemplo" estará sempre como varchar e o "numero\_exemplo\_\_number\_\_" estará como numeric(18,6)

![](https://wiki.agilityflow.io/uploads/images/gallery/2021-04-Apr/scaled-840-0/image-1617633138473.png)

### Formatação de números 

#### Nome da Função

`<span style="color: #ff00ff;"><strong>format</strong></span>(number,format,idioma)`

##### Parâmetro: **number**

Pode ser um número inteiro ou um decimal

##### Parâmetro: **format**

Aqui você passa a formatação que deve ser retornada:

'n0' = retorna o número sem nenhuma casa decimal  
'n1' = retorna o número com 1 casa decimal  
'n2' = retorna o número com 2 casas decimais  
'n3' = retorna o número com 3 casas decimais  
'n4' = retorna o número com 4 casas decimais  
'n5' = retorna o número com 5 casas decimais  
'n6' = retorna o número com 6 casas decimais

##### Parâmetro: **idioma** - **@sysCurrentLanguage**

Aqui você precisa passar o idioma do usuário logado, pois alguns idiomas invertem o . (ponto) e a , (virgula) do número.  
O agilityflow guarda o idioma do usuário dentro da variável <span style="text-decoration: underline;">@sysCurrentLanguage</span> então apenas passe no parâmetro esse variável.

#### Exemplo de utilização

Abaixo o número será formato para 5 casas decimais, no padrão do idioma do usuário logado.

`select FORMAT(5800000.888, 'n5', @sysCurrentLanguage)`

#### Retorno para o usuário que está logado no agilityflow em português ou espanhol

<table aria-describedby="dt_info" cellspacing="0" class="dataTable display no-footer" id="bkmrk-5.800.000%2C88800" role="grid"><tbody><tr class="row-query-result" role="row"><td><span title="5.800.000,88800">5.800.000,88800</span></td></tr></tbody></table>

#### Retorno para o usuário que está logado no agilityflow usando em inglês

<table aria-describedby="dt_info" cellspacing="0" class="dataTable display no-footer" id="bkmrk-5%2C800%2C000.88800" role="grid"><tbody><tr class="row-query-result" role="row"><td><span title="5.800.000,88800">5,800,000.88800</span></td></tr></tbody></table>

### Funções de data no SQL Server

As principais funções para manipular datas são: **GETDATE**, **DATEPART**, **DATEADD** e **DATEDIFF**.

Um detalhe importante é que as funções de data trabalham referenciando unidades de data. As mais comuns são:

- year(ano);
- month(mês);
- day(dia);

### GETDATE ( )

A função **GETDATE** retorna a data e a hora atuais do sistema.

```
SELECT GETDATE()
```

### DATEPART (unidade, data)

A função **DATEPART** retorna a parte especificada de uma data como um inteiro. Observe os exemplos:

```
SELECT DATEPART ( YEAR , '2024-02-01' )
```

Reposta: 2024

```
SELECT DATEPART ( MONTH, '2024-02-01' )
```

Reposta: 2

```
SELECT DATEPART ( DAY, '2024-02-01' )
```

Reposta: 1

### DATEADD (unidade, numero\_unid, data)

A função **DATEADD** retorna uma nova data através da soma do número de unidades especificadas pelo valor *unidade* a uma data. Observe os exemplos:

```
SELECT DATEADD ( DAY ,6, '2024-02-01' )
```

Reposta: 2024-02-07

```
SELECT DATEADD ( MONTH ,6, '2024-02-01' )
```

Reposta: 2024-08-01

```
SELECT DATEADD ( YEAR ,6, '2024-02-01' )
```

Reposta: 2030-02-01

### DATEDIFF (unidade, data1, data2)

A função **DATEDIFF** calcula a diferença entre as datas *data2 e data1*, retornando o resultado como um inteiro, cuja unidade é definida pelo valor *unidade*. Observe os exemplos:

```
SELECT DATEDIFF ( DAY , '2024-02-01' , '2024-05-25' )
```

Reposta: 114 (dias)

```
SELECT DATEDIFF ( MONTH , '2024-02-01' , '2024-05-25' )
```

Reposta: 3 (meses)

```
SELECT DATEDIFF ( YEAR , '2024-02-01' , '2026-05-25' ) )
```

Reposta: 2 (anos)

Sabendo disso, para por exemplo saber o número de dias vivido por você até hoje é:

```SQL
SELECT DATEDIFF(DAY, suadata, GETDATE())
```

**suadata** deve ser substituída pela sua data de nascimento.

Outro exemplo interessante é mostrado através do código SQL abaixo. Usando funções de data, exibimos, para cada cliente, a idade em dias, meses e em anos (idade do cliente na data atual e em 31 de dezembro).

Observe a lógica utilizada no comando **CASE**. Neste caso, é testado se o cliente já fez aniversário, comparando o mês em que ele nasceu com o mês corrente e comparando o dia em que ele nasceu com o dia corrente. Se essa comparação for verdadeira, basta diminuir o ano atual do ano de nascimento do cliente. Caso contrário (o cliente ainda não fez aniversário), temos que diminuir 1 do valor anterior.

```
SELECT NOME, NASCIMENTO,
  DATEDIFF(DAY,NASCIMENTO,GETDATE())AS DIASVIVIDOS,
  DATEDIFF(MONTH,NASCIMENTO,GETDATE()) AS MESESVIVIDOS,
  CASE WHEN
     DATEPART(MONTH,NASCIMENTO)<= DATEPART(MONTH,GETDATE()) AND 
     DATEPART(DAY,NASCIMENTO)<= DATEPART(DAY,GETDATE()) 
  THEN
        (DATEDIFF(YEAR,NASCIMENTO,GETDATE()))
  ELSE
       (DATEDIFF(YEAR,NASCIMENTO,GETDATE()))- 1
       END AS IDADEATUAL,
  DATEDIFF(YEAR,NASCIMENTO,GETDATE())AS IDADE3112
FROM CLIENTE
```

![26-05pic.JPG](https://www.devmedia.com.br/imagens/sqlmagazine/abr2006/26-05pic.JPG)

## Relatório

### Relatório - Filtro

Sempre que um relatório possuir um filtro, é necessário incluir esse filtro na query que gera os dados do relatório.

Não importa como é a query, ela deve incluir a cláusula where, como a chamada da função Filter. Essa função, possui 3 parâmetros:

`<span style="color: #ff00ff;"><strong>Filter</strong> </span>(variável_filtro, tabela, campo)`

- variável\_filtro: é a variável que o agilityflow criou, após a configuração do [filtro](http://wiki.agilityflow.io/link/71#bkmrk-filtro).
- tabela: nome da tabela (definido nos [Dados Técnicos](http://wiki.agilityflow.io/link/21#bkmrk-%E2%A0-5 "Dados Técnicos")) onde se encontra o dado a ser filtrado.
- campo: é o nome do campo (conforme informado no campo Coluna Banco de Dados SQL) onde a informação que o filtro utiliza se encontra.

No exemplo abaixo, a query lista os campos *usu\_nome*, *usu\_email e* *usu\_sexo* da tabela do sistema de usuário (tbl\_usuario) e se inclui a função Filter, usando a variável @sexo aplicada no campo *usu\_sexo*.

```SQL
select usu_nome, usu_email, usu_sexo from tbl_usuario 
where Filter(@sexo, tbl_usuario, usu_sexo) 
```

#### Exemplo de relatório sem filtro

Baseado na query acima, mas sem filtro configurado no relatório.

```SQL
select usu_nome, usu_email, usu_sexo from tbl_usuario 
```

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552917985929.png)

#### Exemplo de relatório com filtro

O mesmo exemplo acima, com o filtro no campo sexo.

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552918030578.png)

```
select usu_nome, usu_email, usu_sexo from tbl_usuario 
where Filter(@sexo, tbl_usuario, usu_sexo) 
```

[![relatorio_filtro.gif](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/relatorio_filtro.gif)](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/relatorio_filtro.gif)

Caso seja criado um novo filtro, basta colocar "*and*" depois do primeiro Filter, e colocar o segundo Filter. Não importa a quantidade de filtros, desde que sejam adicionados todos os Filter e os *and*.

```
select usu_nome, usu_email, usu_sexo from tbl_usuario 
where Filter(@sexo, tbl_usuario, usu_sexo) and
      Filter(@nome, tbl_usuario, usu_nome) and
      Filter(@email, tbl_usuario, usu_email)
```

### Relatório - Tabela de Dados

Para as tabelas de dados, existem algumas regrinhas para a customização via Query Sql

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = 0.."

2 - **Não** pode conter 'Order by' no select. Essa informação será controlada automaticamente pelo agilityflow e você poderá manipular essa informação na tela de configuração, como na imagem abaixo.

3 - **Não** pode conter o controle de 'Top' no select. Essa informação será controlada automaticamente pelo agilityflow e você poderá manipular essa informação na tela de configuração, como na imagem abaixo.

4 - **Não** pode conter regras de paginação. Essa informação será controlada automaticamente pelo agilityflow e você poderá manipular essa informação na tela de configuração, como na imagem abaixo.

![](https://wiki.agilityflow.io/uploads/images/gallery/2021-04-Apr/scaled-840-0/image-1617637188465.png)

### Relatório - Gráfico

Para gráficos, existem algumas regrinhas para a customização via Query Sql

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = 0.."

2 - A query precisa iniciar com 'select **top** ' e o máximo do Top para essa query é <span style="text-decoration: underline;">**100**</span>

3 - Diferentemente da query da tabela de dados, nessa query pode sim conter regras de 'Order by'

### Relatório - Label

Para as labels do relatório, existem algumas regrinhas para a customização via Query Sql

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = 0.."

2 - A query precisa iniciar com 'select **top** ' e o máximo do Top para essa query é <span style="text-decoration: underline;">**1**</span>

3 - Diferentemente da query da tabela de dados, nessa query pode sim conter regras de 'Order by'

## Formulário

### Lista Dinâmica - Regras

Os campos que são carregados com Lista dinâmica, podem ser customizados utilizando Query:

**Algumas regras importantes:**  
  
1 - O resultado final da query precisa sempre ser os dados do formulário que você usou como base de dados  
  
2 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = 0.."  
  
3 - A query deve retornar duas colunas com os seguintes *alias*: "Name" e "Value". O "Name" deve ser a mesma coluna definida no campo de apresentação (Nome) e o "Value", deve ser a coluna "id" do formulário definido como base de dados (Cliente). Exemplo:

4 - Na query, o campo de apresentação "Name" pode ser um campo concatenado entre outras colunas.

Exemplo simples:

  
`SELECT Id as Value, [CAMPO_DE_APRESENTACAO] as Name FROM [TABELA] where deletado = 0`

### Report Print - Relatório de Impressão

Para as labels do relatório, existem algumas regrinhas para a customização via Query Sql

Para acessar a customização, clique no botão "Adicionar Outras Fontes de Dados" e selecione o tipo:

1. **Variáveis:** para retornar uma query com apenas 1 linha e várias colunas, cada coluna se torna um campo separado para ser utilizado no relatório
2. **Tabela de Dados:** para retornar uma tabela com diversas colunas e diversas linhas e usa-las em conjunto em uma tabela

![](https://wiki.agilityflow.io/uploads/images/gallery/2021-04-Apr/scaled-840-0/image-1617638740767.png)

**Algumas regras importantes:**

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = 0.."

2 - Utilizar no "where" da query, o parâmetro **@formularioid** para filtrar pelo formulário que está sendo solicitada a impressão, caso contrário trará informações de formulários aleatórios.

3 - A query precisa iniciar com 'select **top** ' e o máximo do Top para essa query quando for uma query do tipo:  
\- **Tabela de Dados** é <span style="text-decoration: underline;">**500**</span>,   
\- **Variáveis** é <span style="text-decoration: underline;">**1**</span>

4 - Essa query pode sim conter regras de 'Order by'

# Postgresql (Query) - Dicas e Funções (Versões Mais Recentes do Agilityflow)

<p class="callout warning">**IMPORTANTE**: Essa documentação é referente as versões pós 2023 do agilityflow que utilizam o Banco de Dados **Postgresql.** O agilityflow nas últimas versões está utilizando **POSTGRESQL** .</p>

No agilityflow você pode buscar os dados utilizando SQL, através de queries. As queries devem ser compatíveis com **SQL Server.** Além disso existem alguns padrões que você deve seguir para obter um melhor resultado no agilityflow. Abaixo estão alguns detalhes importantes.

### Tratamento de Registros Deletados

<p class="callout danger">**IMPORTANTE:**   
É de extrema importância o tratamento de registros deletados, usando as regras abaixo.   
  
Já está no nosso Roadmap o tratamento automático dos deletados, enquanto isso, o tratamento se torna obrigatório pelo desenvolvedor.</p>

Para manter a integridade dos dados, o agilityflow nunca apaga um registro de uma tabela. Apenas o marca como deletado. Portanto, para qualquer query, é preciso excluir os deletados da lista.

Essa marcação é feita com campo deletado. Se o valor for **FALSE**, significa que ele não foi deletado e está visível no sistema. Caso esteja **TRUE**, significa que o registro já foi excluído e não é possível mais vê-lo na listagem principal do sistema.

**Exemplo 1, sem o tratamento de deletados:**

```SQL
select usu_nome, usu_email from tbl_usuario
```

**Resultado**:

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552935887749.png)

**Exemplo 2, com o tratamento de deletados:**

```SQL
select usu_nome, usu_email from tbl_usuario
where deletado = false
```

**Resultado**:

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552935923310.png)

### Campo do tipo Date, Timestamp e Datetime (Data e Hora)

Sempre que um campo de texto com máscara do tipo data é criado, o sistema cria uma cópia com o mesmo nome seguido de "<span style="color: #ff6600;">**\_\_datetime\_\_**</span>" essa informação é gravada em uma coluna no banco de dados do tipo **"<span style="text-decoration: underline;">date"</span>** quando for uma coluna sem hora ou "<span style="text-decoration: underline;">timestamp without time zone"</span>** quando for uma coluna com hora<span style="text-decoration: underline;"> </span>, ao invés de varchar. Esses campos são criados para facilitar o uso tipado do dado nas queries

#### **Exemplo de campo Datetime**

Por exemplo, se o campo se chama "data\_e\_hora", haverá um outro chamado "data\_e\_hora\_\_datetime\_\_" e o conteúdo estará no formato padrão do sql server, **YYYY-MM-DD hh:mm:ss**

O campo "data\_e\_hora" estará sempre como varchar e o "data\_e\_hora\_\_datetime\_\_" estará como timestamp

**data\_e\_hora | data\_e\_hora\_\_datetime\_\_**

2027-01-31 23:5959 2027-01-31 23:5959   
2027-01-31 23:5959 2027-01-31 23:5959   
2027-01-31 23:5959 2027-01-31 23:5959

### Campo do tipo Numérico (decimal, float, double, int, number)

Sempre que um campo de texto com máscara do tipo número é criado, o sistema cria uma cópia com o mesmo nome seguido de "<span style="color: #ff6600;">**\_\_number\_\_**</span>" essa informação é gravada em uma coluna no banco de dados do tipo **<span style="text-decoration: underline;">numeric(18,6)</span>,** ao invés de varchar. Esses campos são criados para facilitar o uso tipado do dado nas queries

#### **Exemplo de campo numérico**

Por exemplo, se o campo se chama "numero\_exemplo", haverá um outro chamado "numero\_exemplo\_\_number\_\_" e o conteúdo estará no formato padrão do sql server.

O campo "numero\_exemplo" estará sempre como varchar e o "numero\_exemplo\_\_number\_\_" estará como numeric(18,6)

**numero\_exemplo | numero\_exemplo\_\_number\_\_**

999.99 999.99   
123.89 123.89

### Formatação de números 

#### Nome da Função

`<span style="color: #ff00ff;"><strong>format_number</strong></span>(number,format,idioma)`

##### Parâmetro: **number**

Pode ser um número inteiro ou um decimal

##### Parâmetro: **format**

Aqui você passa a formatação que deve ser retornada:

'0' = retorna o número sem nenhuma casa decimal  
'1' = retorna o número com 1 casa decimal  
'2' = retorna o número com 2 casas decimais  
'3' = retorna o número com 3 casas decimais  
'4' = retorna o número com 4 casas decimais  
'5' = retorna o número com 5 casas decimais  
'6' = retorna o número com 6 casas decimais

##### Parâmetro: **idioma** - **@sysCurrentLanguage**

Aqui você precisa passar o idioma do usuário logado, pois alguns idiomas invertem o . (ponto) e a , (virgula) do número.  
O agilityflow guarda o idioma do usuário dentro da variável <span style="text-decoration: underline;">@sysCurrentLanguage</span> então apenas passe no parâmetro esse variável.

#### Exemplo de utilização

Abaixo o número será formato para 5 casas decimais, no padrão do idioma do usuário logado.

`select format_number(5800000.888, '5', @sysCurrentLanguage)`

#### Retorno para o usuário que está logado no agilityflow em português ou espanhol

<table aria-describedby="dt_info" cellspacing="0" class="dataTable display no-footer" id="bkmrk-5.800.000%2C88800" role="grid"><tbody><tr class="row-query-result" role="row"><td><span title="5.800.000,88800">5.800.000,88800</span></td></tr></tbody></table>

#### Retorno para o usuário que está logado no agilityflow usando em inglês

<table aria-describedby="dt_info" cellspacing="0" class="dataTable display no-footer" id="bkmrk-5%2C800%2C000.88800" role="grid"><tbody><tr class="row-query-result" role="row"><td><span title="5.800.000,88800">5,800,000.88800</span></td></tr></tbody></table>

### Funções de data no Postgresql

As principais funções para manipular datas são: **GETDATE**, **DATEPART**, **DATEADD** e **DATEDIFF**.

Um detalhe importante é que as funções de data trabalham referenciando unidades de data. As mais comuns são:

- year(ano);
- month(mês);
- day(dia);

### CURRENT\_TIMESTAMP)

A função CURRENT\_TIMESTAMP retorna a data e a hora atuais do sistema.

```
SELECT CURRENT_TIMESTAMP
```

### Extrair parte de uma data

A função EXTRACT retorna a parte especificada de uma data como um inteiro. Observe os exemplos:

```
SELECT EXTRACT(YEAR FROM '2024-02-01'::DATE)
```

Reposta: 2024

```
SELECT EXTRACT(MONTH FROM '2024-02-01'::DATE)
```

Reposta: 2

```
SELECT EXTRACT(DAY FROM '2024-02-01'::DATE)
```

Reposta: 1

### Adicionar dias em uma data

A função **abaixo** retorna uma nova data através da soma do número de unidades especificadas pelo valor *unidade* a uma data. Observe os exemplos:

```
SELECT '2024-02-01'::DATE + INTERVAL '7 days';
```

Reposta: 2024-02-07

### Calcular a diferença entre datas (date diff)

A função **abaixo** calcula a diferença entre as datas *data2 e data1*, retornando o resultado como um inteiro, cuja unidade é definida pelo valor *unidade*. Observe os exemplos:

```
SELECT EXTRACT(DAY FROM '2024-02-01'::DATE - '2023-10-10'::DATE);
```

Reposta: 114 (dias)

### Cálculo de idade em anos, meses e dias

```SQL
SELECT DATE_PART('year', AGE(NOW(), '1990-05-15')) AS idade_anos;
```


## Relatório

### Como fazer Filtro nos Relatórios? 

Sempre que um relatório possuir um filtro, é necessário incluir esse filtro na query que gera os dados do relatório.

Não importa como é a query, ela deve incluir a cláusula where, como a chamada da função Filter. Essa função, possui 3 parâmetros:

`<span style="color: #ff00ff;"><strong>Filter</strong> </span>(variável_filtro, tabela, campo)`

- variável\_filtro: é a variável que o agilityflow criou, após a configuração do [filtro](http://wiki.agilityflow.io/link/71#bkmrk-filtro).
- tabela: nome da tabela (definido nos [Dados Técnicos](http://wiki.agilityflow.io/link/21#bkmrk-%E2%A0-5 "Dados Técnicos")) onde se encontra o dado a ser filtrado.
- campo: é o nome do campo (conforme informado no campo Coluna Banco de Dados SQL) onde a informação que o filtro utiliza se encontra.

No exemplo abaixo, a query lista os campos *usu\_nome*, *usu\_email e* *usu\_sexo* da tabela do sistema de usuário (tbl\_usuario) e se inclui a função Filter, usando a variável @sexo aplicada no campo *usu\_sexo*.

```SQL
select usu_nome, usu_email, usu_sexo from tbl_usuario 
where Filter(@sexo, tbl_usuario, usu_sexo) 
```

#### Exemplo de relatório sem filtro

Baseado na query acima, mas sem filtro configurado no relatório.

```SQL
select usu_nome, usu_email, usu_sexo from tbl_usuario 
```

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552917985929.png)

#### Exemplo de relatório com filtro

O mesmo exemplo acima, com o filtro no campo sexo.

![](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/scaled-840-0/image-1552918030578.png)

```
select usu_nome, usu_email, usu_sexo from tbl_usuario 
where Filter(@sexo, tbl_usuario, usu_sexo) 
```

[![relatorio_filtro.gif](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/relatorio_filtro.gif)](http://wiki.agilityflow.io/uploads/images/gallery/2019-03-Mar/relatorio_filtro.gif)

Caso seja criado um novo filtro, basta colocar "*and*" depois do primeiro Filter, e colocar o segundo Filter. Não importa a quantidade de filtros, desde que sejam adicionados todos os Filter e os *and*.

```
select usu_nome, usu_email, usu_sexo from tbl_usuario 
where Filter(@sexo, tbl_usuario, usu_sexo) and
      Filter(@nome, tbl_usuario, usu_nome) and
      Filter(@email, tbl_usuario, usu_email)
```

### Relatório - Tabela de Dados

Para as tabelas de dados, existem algumas regrinhas para a customização via Query Sql

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = false.."

2 - **Não** pode conter 'Order by' no select. Essa informação será controlada automaticamente pelo agilityflow e você poderá manipular essa informação na tela de configuração, como na imagem abaixo.

3 - **Não** pode conter o controle de 'Limit' no select. Essa informação será controlada automaticamente pelo agilityflow e você poderá manipular essa informação na tela de configuração, como na imagem abaixo.

4 - **Não** pode conter regras de paginação. Essa informação será controlada automaticamente pelo agilityflow e você poderá manipular essa informação na tela de configuração, como na imagem abaixo.

![](https://wiki.agilityflow.io/uploads/images/gallery/2021-04-Apr/scaled-840-0/image-1617637188465.png)

### Relatório - Gráfico

Para gráficos, existem algumas regrinhas para a customização via Query Sql

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = false.."

2 - A query precisa conter '**limit** ' e o máximo do limit para essa query é <span style="text-decoration: underline;">**100**</span>

3 - Diferentemente da query da tabela de dados, nessa query pode sim conter regras de 'Order by'

### Relatório - Label

Para as labels do relatório, existem algumas regrinhas para a customização via Query Sql

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = false.."

2 - A query precisa conter '**limit** ' e o máximo do limit para essa query é <span style="text-decoration: underline;">**1**</span>

3 - Diferentemente da query da tabela de dados, nessa query pode sim conter regras de 'Order by'

## Formulário

### Lista Dinâmica - Regras

Os campos que são carregados com Lista dinâmica, podem ser customizados utilizando Query:

**Algumas regras importantes:**  
  
1 - O resultado final da query precisa sempre ser os dados do formulário que você usou como base de dados  
  
2 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = false.."  
  
3 - A query deve retornar duas colunas com os seguintes *alias*: "Name" e "Value". O "Name" deve ser a mesma coluna definida no campo de apresentação (Nome) e o "Value", deve ser a coluna "id" do formulário definido como base de dados (Cliente). Exemplo:

4 - Na query, o campo de apresentação "Name" pode ser um campo concatenado entre outras colunas.

Exemplo simples:

  
`SELECT Id as Value, CAMPO_DE_APRESENTACAO as Name FROM [TABELA] where deletado = false`

### Report Print - Relatório de Impressão

Para as labels do relatório, existem algumas regrinhas para a customização via Query Sql

Para acessar a customização, clique no botão "Adicionar Outras Fontes de Dados" e selecione o tipo:

1. **Variáveis:** para retornar uma query com apenas 1 linha e várias colunas, cada coluna se torna um campo separado para ser utilizado no relatório
2. **Tabela de Dados:** para retornar uma tabela com diversas colunas e diversas linhas e usa-las em conjunto em uma tabela

![](https://wiki.agilityflow.io/uploads/images/gallery/2021-04-Apr/scaled-840-0/image-1617638740767.png)

**Algumas regras importantes:**

1 - Lembre-se de tratar os dados que já foram deletados, usando no where "...deletado = false.."

2 - Utilizar no "where" da query, o parâmetro **@formularioid** para filtrar pelo formulário que está sendo solicitada a impressão, caso contrário trará informações de formulários aleatórios.

3 - A query precisa conter '**limit** ' e o máximo do limit para essa query quando for uma query do tipo:  
\- **Tabela de Dados** é <span style="text-decoration: underline;">**500**</span>,   
\- **Variáveis** é <span style="text-decoration: underline;">**1**</span>

4 - Essa query pode sim conter regras de 'Order by'

# Programação em Html

Para acessar os detalhes desse conteúdo, [clique aqui](http://wiki.agilityflow.io/link/78#bkmrk-page-title).

# Programação em CSS

Para acessar os detalhes desse conteúdo, [clique aqui](http://wiki.agilityflow.io/link/78#bkmrk-page-title).

# Programação em Javascript

Para acessar os detalhes desse conteúdo, [clique aqui](http://wiki.agilityflow.io/link/78#bkmrk-page-title).

# Programação em C# - Na Regra de Negócio

<p class="callout danger">IMPORTANTE: as variáveis e métodos descritos aqui só funcionarão na programação C# na **Regra de Negócio** do formulário. Para a programação C# nas **APIs** [clique aqui.](https://wiki.agilityflow.io/books/manual-de-customiza%C3%A7%C3%A3o/page/programa%C3%A7%C3%A3o-em-c---na-api-de-integra%C3%A7%C3%A3o "Programação em C# - Na API de integração")</p>

O agilityflow permite a customização em C#, além de já disponibilizar diversas bibliotecas e funções para facilitar sua programação, incluindo acesso a dados, validações, envio de e-mail, notificação, entre outras.

Abaixo mostramos alguns exemplos, se o material abaixo não for suficiente , entre em contato com nossa equipe.

##### C# - Propriedades

<table border="1" id="bkmrk-vari%E3%A1vel-tipo-%E2%A0-fo" style="border-collapse: collapse; width: 150.864%; height: 540px;"><tbody><tr style="height: 31px;"><td style="width: 35.1824%; height: 31px;">**Variável**</td><td style="width: 34.3841%; height: 31px;">**Tipo**</td><td style="width: 81.2977%; height: 31px;"> </td></tr><tr style="height: 94px;"><td style="width: 35.1824%; height: 41px;">IsNovoFormulario</td><td style="width: 34.3841%; height: 41px;">bool</td><td style="width: 81.2977%; height: 41px;">retorna true, caso seja um novo formulário, e false caso contrário.</td></tr><tr style="height: 147px;"><td style="width: 35.1824%; height: 28px;">IdFormulario</td><td style="width: 34.3841%; height: 28px;"><span class="csharp_custom_class_var">Guid</span> </td><td style="width: 81.2977%; height: 28px;">retorna o ID do formulário

</td></tr><tr style="height: 400px;"><td style="width: 35.1824%; height: 400px;">CurrentStatusFluxo</td><td style="width: 34.3841%; height: 400px;"><span class="csharp_custom_class_var">StatusFluxo? (<span style="color: #999999;">Enum</span>)</span></td><td style="width: 81.2977%; height: 400px;">Variável disponível apenas para <span style="text-decoration: underline;">Workflow</span>

Nessa ENUM será retornado o Status do fluxo.

As opções podem ser:

- **StatusFluxo.Pendente -** Enquanto o fluxo ainda estiver pendente em alguma das etapas.
- **StatusFluxo.Aprovado -** Depois do fluxo encerrado e completamente aprovado
- **StatusFluxo.Reprovado -** Depois do fluxo encerrado e completamente reprovado

Exemplo de utilização:

`<span style="color: #0000ff;">if</span>(CurrentStatusFluxo == <span style="color: #00ccff;">StatusFluxo</span>.Aprovado){    <span style="color: #339966;">//restante do código</span>}`

</td></tr><tr style="height: 31px;"><td style="width: 35.1824%; height: 40px;"><span style="color: #ff00ff;">(**deprecated)**</span>

<span style="text-decoration: line-through;"><span style="color: #999999; text-decoration: line-through;">FormularioCamposPreenchidos</span></span>

</td><td style="width: 34.3841%; height: 40px;"><span style="color: #ff00ff;">(**deprecated)**</span>

<span style="text-decoration: line-through;"><span style="color: #999999; text-decoration: line-through;">dynamic (Json)</span></span>

</td><td style="width: 81.2977%; height: 40px;"><span style="color: #ff00ff;">(**deprecated**: utilizar o método FormContext.GetValue descrito mais abaixo)</span>

retorna um json com os campos do preenchidos no formulário, exemplo:

{

 "campo1": "xxxxx",

 "campo2": "yyyyy",

 "campo3": "zzzzz",

}

Para resgatar a informação do campo1, utilize:

`<span style="color: #3366ff;">var</span> campo1 = FormularioCamposPreenchidos[<span style="color: #993300;">"campo1"</span>];`

</td></tr></tbody></table>

##### C# - Buscar e preencher valor de um campo

<table border="1" id="bkmrk-op%E3%A7%E3%A3o-retorno-%E2%A0-g" style="border-collapse: collapse; width: 119.753%; height: 942px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 12.0385%; height: 31px;">**Retorno**</td><td style="width: 51.2976%; height: 31px;"> </td></tr><tr><td style="width: 61.7256%;">**FormContext.SetValue(**string idCampo, string value)

</td><td style="width: 12.0385%;">void</td><td style="width: 51.2976%;">Esse método preencherá um campo com um novo **Valor** (passado por paramêtro)

Para valores que são **Datas**, utilizar o padrão dd/MM/aaaa HH:mm:ss:

Exemplo:<span style="text-decoration: underline;"> ***31/12/2010 22:55***</span>

```C#
var data_e_hora = "31/01/2023 22:55";
FormContext.SetValue("data_e_hora",data_e_hora);
```

Para valores **Numéricos**, o número deve estar no formato com ponto no separador decimal e sem utilização de virgulas.

Exemplo para 2 milhões e 50 centavos**:**

<span style="text-decoration: underline;">***2000000.50***</span>

```
<strong><code class="language-C#">var moeda = "2000000.50";
FormContext.SetValue("moeda",moeda);</code></strong>
```

[Veja exemplo](https://wiki.agilityflow.io/link/82#bkmrk-atualizar-um-outro-f)

</td></tr><tr style="height: 94px;"><td style="width: 61.7256%; height: 94px;">**FormContext**.**GetValue**(string idCampo)</td><td style="width: 12.0385%; height: 94px;">string</td><td style="width: 51.2976%; height: 94px;">Esse método retornará o **Valor** de um determinado campo

</td></tr><tr style="height: 147px;"><td style="width: 61.7256%; height: 147px;">**FormContext.GetText(**string idCampo**)**</td><td style="width: 12.0385%; height: 147px;">string</td><td style="width: 51.2976%; height: 147px;">Esse método retornará o **Texto** de um determinado campo, utilizado apenas se o campo for um campo de **lista dinâmica**, exemplo: Combo, Auto complete, Campo de Múltipla escolha, radio etc.

[Veja exemplo](#bkmrk-para-recuperar-o-tex)

</td></tr><tr><td style="width: 61.7256%;">**FormContext.GetInt**(string idColuna)  
</td><td style="width: 12.0385%;">int?</td><td style="width: 51.2976%;">Retorna o valor da coluna no tipo INT (inteiro), caso esse campo no formulário seja um número, se o valor for 10.000,99

Será retornado: 10000

[Veja exemplo](https://wiki.agilityflow.io/link/82#bkmrk-atualizar-um-outro-f)

</td></tr><tr><td style="width: 61.7256%;">**FormContext.GetDecimal**(string idColuna)</td><td style="width: 12.0385%;">decimal?</td><td style="width: 51.2976%;">Retorna o valor da coluna no tipo Decimal, caso esse campo no formulário seja um número, se o valor for 10.000,99

Será retornado: 10000.99

[Veja exemplo](https://wiki.agilityflow.io/link/82#bkmrk-atualizar-um-outro-f)

</td></tr><tr><td style="width: 61.7256%;">**FormContext.GetDateTime**(string idColuna)</td><td style="width: 12.0385%;">DateTime?</td><td style="width: 51.2976%;">Retorna o valor da coluna no tipo DateTime para valores que estão no formato de data dd/MM/yyyy ou dd/MM/yyyy hh:mm

[Veja exemplo](https://wiki.agilityflow.io/link/82#bkmrk-atualizar-um-outro-f)

</td></tr></tbody></table>

##### C# - Buscar Rascunho por Id

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f" style="border-collapse: collapse;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**FormContext**.**GetDrafJsontById**(string draftid)  
ou

**FormContext**.**GetDrafJsontById**(Guid?draftid)

</td><td style="width: 14.0138%; height: 31px;">dynamic</td><td style="width: 44.0137%; height: 31px;">```
`var str_preco_de_venda = "";var str_quantidade = "";var temRascunho = false;var rascunho_json = PageContext.GetDraftJsontById(str_draftid);if(rascunho_json != null){	temRascunho = true;	str_preco_de_venda = rascunho_json.preco_de_venda;	str_quantidade = rascunho_json.quantidade;}`
```

</td></tr></tbody></table>

##### C# - Executar uma consulta SQL (Query SQL)

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-0" style="border-collapse: collapse;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**await FormContext**.**GetDataTableAsync**(string sql, params DbParameter\[\] parameters)</td><td style="width: 14.0138%; height: 31px;">DataTable</td><td style="width: 44.0137%; height: 31px;">Permite retornar informações dos bancos de dados através de uma query.

[Veja exemplo](#bkmrk-recuperando-dados-do "Veja exemplo")

</td></tr></tbody></table>


##### C# - Inserir (salvar/criar), alterar, deletar e aprovar outros formulários

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-1" style="border-collapse: collapse; width: 119.753%; height: 942px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr><td style="width: 61.7256%;">**await FormContext.SaveEntityAsync**(Guid estruturaformularioid\_ASerCriadoOuAtualizado, DataDictionary campos\_e\_valores)</td><td style="width: 14.0138%;">Guid</td><td style="width: 44.0137%;">Salva (Insere e Atualiza) as informações de um formulário no bancos de dados.

Para atualizar um registro, no parâmetro **campos\_e\_valores**, apenas adicione um parâmetro chamado "**id**" com o id do elemento que você deseja atualizar.

Obs: Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

[Veja exemplo](https://wiki.agilityflow.io/link/82#bkmrk-salvando-os-dados-re "Veja exemplo")

</td></tr><tr><td style="width: 61.7256%;">**await FormContext.ApproveEntityAsync(**Guid estruturaformularioid\_ASerAprovado, DataDictionary campos\_e\_valores)</td><td style="width: 14.0138%;">Guid</td><td style="width: 44.0137%;">Avança uma etapa em um formulário com Workflow.

As regras de usuário aprovador, permissão de aprovação e responsabilidade por uma etapa devem ser validadas antes desse método ser executado.

Obs: Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

[Veja exemplo](https://wiki.agilityflow.io/link/82#bkmrk-aprovando-uma-etapa-)

</td></tr><tr><td style="width: 61.7256%;">**await FormContext.DeleteEntityAsync(Guid formularioid)**</td><td style="width: 14.0138%;">void</td><td style="width: 44.0137%;">Deletar um um registro

</td></tr></tbody></table>

##### C# - Apresentar mensagem para o usuário

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-2" style="border-collapse: collapse; width: 119.753%; height: 942px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 59px;"><td style="width: 61.7256%; height: 59px;">**FormContext.WarningMessage**(string message)

</td><td style="width: 14.0138%; height: 59px;">void</td><td style="width: 44.0137%; height: 59px;">Apresenta um aviso para o usuário

[veja detalhes aqui](https://wiki.agilityflow.io/link/82#bkmrk-a-execu%E3%A7%E3%A3o-do-c%E3%B3d)

</td></tr></tbody></table>

##### C# - Buscar informações do usuário logado

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-3" style="border-collapse: collapse; width: 119.753%; height: 193px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 29px;"><td style="width: 61.7256%; height: 29px;">**FormContext.**GetUsuarioLogadoNome();  
</td><td style="width: 14.0138%; height: 29px;">string</td><td style="width: 44.0137%; height: 29px;">Nome Completo do usuário logado</td></tr><tr style="height: 29px;"><td style="width: 61.7256%; height: 29px;">**FormContext.**GetUsuarioLogadoPrimeiroNome();</td><td style="width: 14.0138%; height: 29px;">string</td><td style="width: 44.0137%; height: 29px;">Primeiro nome do usuário logado</td></tr><tr style="height: 46px;"><td style="width: 61.7256%; height: 46px;">**FormContext.**GetUsuarioLogadoPrimeiroESegundoNome();</td><td style="width: 14.0138%; height: 46px;">string</td><td style="width: 44.0137%; height: 46px;">Primeiro e Segundo nome do usuário logado</td></tr><tr style="height: 29px;"><td style="width: 61.7256%; height: 29px;">**FormContext.**GetUsuarioLogadoEmail();</td><td style="width: 14.0138%; height: 29px;">string</td><td style="width: 44.0137%; height: 29px;">E-mail do usuário logado</td></tr><tr style="height: 29px;"><td style="width: 61.7256%; height: 29px;">**FormContext.**GetUsuarioLogadoId();</td><td style="width: 14.0138%; height: 29px;">Guid</td><td style="width: 44.0137%; height: 29px;">Id do usuário logado</td></tr></tbody></table>

##### C# - Buscar variáveis de ambiente

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-4" style="border-collapse: collapse; width: 119.753%; height: 942px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 153px;"><td style="width: 61.7256%; height: 153px;">**FormContext.GetEnvironmentVariable**(string nomeVariavel)

ou

**FormContext.GetEnvironmentVariable\_Text**(string nomeVariavel) \*\*

</td><td style="width: 14.0138%; height: 153px;">string</td><td style="width: 44.0137%; height: 153px;">Recupera o Valor de uma variável de ambiente.

\*\* Recupera a Descrição (texto), no caso de variáveis do Tipo "Query com Id + Descrição":

</td></tr></tbody></table>

##### C# - Formatação Numérica

<p class="callout info">***Se você está utilizando o método GetValue(..) para buscar o valor de um campo numérico, esse método já retornará o número formatado e não será necessário nenhuma formatação adicional.  
  
Se você está utilizando o método GetDecimal(..) ele não virá formatado pois ele retorna o tipo DECIMAL do C#, sendo assim, se você precisa do dado formatado você pode utilizar o GetText(..)***</p>

<table border="1" id="bkmrk-string-formatdecimal"><tbody><tr><td>string **FormContext.FormatDecimalToString**(object numDecimal, string mascaraTipo = "dec2")</td><td>formata object 999999999.55 para o formato da mascara de acordo com o idioma.

\- se for ingles, será 999,999,999.55  
\- se for portugues, será 999.999.999,55

</td></tr><tr><td>string **FormContext.FormatNumberToString\_EnglishFormat**(object num, \[optional\]int? qtdCasasDecimais)</td><td>converte decimal para string, porém sempre a string vem no formato Inglês.

coloca a a mesma qtd de casas decimais, caso o parâmetro qtdCasasDecimais estivar em branco

</td></tr></tbody></table>

##### C# - Gerar log

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-5" style="border-collapse: collapse; width: 119.753%; height: 942px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 130px;"><td style="width: 61.7256%; height: 130px;">**FormContext.Log**(string log, string logTipo)

</td><td style="width: 14.0138%; height: 130px;">void</td><td style="width: 44.0137%; height: 130px;">Cria um log na tabela de log geral, o log pode ser consultado entrando na Área de Customização e clicando na opção "Log Geral &gt;&gt; consultar Log"

</td></tr></tbody></table>

##### C# - Métodos Úteis

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-f-6" style="border-collapse: collapse; width: 119.753%; height: 942px;"><tbody><tr style="height: 31px;"><td style="width: 61.7256%; height: 31px;">**Método**</td><td style="width: 14.0138%; height: 31px;">**Retorno**</td><td style="width: 44.0137%; height: 31px;"> </td></tr><tr style="height: 177px;"><td style="width: 61.7256%; height: 177px;">**FormContext.GetUrlBase()**  
</td><td style="width: 14.0138%; height: 177px;"> </td><td style="width: 44.0137%; height: 177px;">Retorna a URL base da sua aplicação

exemplo:

[https://suaempresa.agilityflow.io/](https://suaempresa.agilityflow.io/)

\*A url sempre virá com uma barra no final **/**

</td></tr></tbody></table>

#### Método principal para utilização da Programação em C# na opção "Regra de Negócio"

A única regra é que o método se chame Execute, não tenha parâmetros de entrada e o retorno seja void.

Exemplo:

```C#
public void Execute(){            



  //aqui você pode programar   



}

  //aqui você pode programar outros métodos
```

#### Exemplos

##### Recuperando dados do banco de dados através de uma query SQL

```C#
//parametros da query
var paramsQuery = new List<NpgsqlParameter>();
paramsQuery.Add(new NpgsqlParameter("@param1", "xxx"));
paramsQuery.Add(new NpgsqlParameter("@param2", "yyy"));
paramsQuery.Add(new NpgsqlParameter("@param3", "zzz"));

//query usando (nolock) nas tabelas para evitar problema com a transação que está ativa
var sql = "select column1,column2,column3 from table (nolock) where column1 = @param1 or  column2 = @param2 or  column3 = @param3 ";

//executar no banco de dados
var dt = await FormContext.GetDataTableAsync(sql, paramsQuery.ToArray());

//percorrendo as linhas de retorno da tabela
foreach (DataRow dr in dt.Rows)
{
  if (dr["column1"] != DBNull.Value)
  	FormContext.WarningMessage(dr["column1"].ToString());
}
```

##### Modificando os campos do próprio formulário que está sendo salvo

```C#
//concatenando o texto "abcdef" ao campo de texto chamado "texto_simples" 
var texto_simples = FormContext.GetValueField("texto_simples");
texto_simples += " abcdef ";
FormContext.SetValue("texto_simples",texto_simples);

//colocando uma data fixa em um campo de data chamado "data_e_hora" 
var data_e_hora = "31/01/2023 22:55";
FormContext.SetValue("data_e_hora",data_e_hora);


//colocando um valor decimal fixo em um campo de data chamado "moeda" 
var moeda = "2000000.50";
FormContext.SetValue("moeda",moeda);



//somando 200 dias a um campo de data chamado "data_e_hora_2" 
var dt_data_e_hora = FormContext.GetDateTime("data_e_hora_2");
if(dt_data_e_hora != null){
  var nova_data = dt_data_e_hora.Value.AddDays(200);
  FormContext.SetValue("data_e_hora_2", nova_data);
}

//somando 900.12 a um campo de moeda chamado "moeda2" 
var num_moeda = FormContext.GetDecimal("moeda2");
if(num_moeda != null){
  var novo_valor = num_moeda.Value + 9000000.12m;

  FormContext.SetValue("moeda2",novo_valor);
}
```

##### Salvando/Criar um outro formulário através do método SaveEntityAsync

\* Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

```C#
//guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");

var values = new DataDictionary();
values.Add("nome", FormContext.GetValue("nome"));
values.Add("email", FormContext.GetValue("email"));


//salva a informação no banco de dados
await FormContext.SaveEntityAsync(idEstruturaFormulario_PESSOA, values);

```

##### Atualizar um outro formulário através do método SaveEntityAsync

\* Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

```C#
//guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");

var values = new DataDictionary();
values.Add("id", "33f6bdf8-26d9-42b9-94ae-ad44c62725b5"); //insere aqui o ID do registro que você deseja atualizar
values.Add("nome", FormContext.GetValue("nome"));
values.Add("email", FormContext.GetValue("email"));


//salva a informação no banco de dados
await FormContext.SaveEntityAsync(idEstruturaFormulario_PESSOA, values);

```

<p class="callout warning">Caso você queira atualizar informações do próprio formulário que está sendo salvo, utilize a função SetValue, descrita mais acima nos métodos da Classe FormContext</p>

##### Aprovar um outro formulário através do método ApproveEntityAsync

\* Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

```C#
//guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");

var values = new DataDictionary();
values.Add("nome", FormContext.GetValue("nome"));
values.Add("email", FormContext.GetValue("email"));



//salva a informação no banco de dados
await FormContext.ApproveEntityAsync(idEstruturaFormulario_PESSOA, values);

```

##### Salvando um outro formulário com Tabela Associativa através do método SaveEntityAsync

\* Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

```C#
//guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");

var values = new DataDictionary();
values.Add("nome", json["nome"].ToString());
values.Add("email", json["email"].ToString());


        //no caso do campo do formulário ser uma tabela associativa (tabela relacional N:N)
        var idCampoTabelaAssociativa = "70b6f362-2587-4fd2-a2fb-148bd0caf437";

        //itens da tabela associativa que serão adicionados
        var idItem1 = "51cf02f1-3787-4dca-8a2c-e219a5ce1298";
        var idItem2 = "f999f103-c775-4245-92d3-034cb3ded5e4";                          
        var idItem3 = "XXXXf103-c775-4245-92d3-034cb3ded5e4";                          
        var idItem4 = "YYYYf103-c775-4245-92d3-034cb3ded5e4";                          
        var idsJuntos_ConcatenadosComVirgula = idItem1 + "," + idItem2 + "," + idItem3 + "," + idItem4 + ",";

        //adiciona
        values.Add(idCampoTabelaAssociativa, idItem1); //adiciona idItem1
        values.Add(idCampoTabelaAssociativa, idItem2); //adiciona idItem2
        values.Add(idCampoTabelaAssociativa, idItem3); //adiciona idItem3
        values.Add(idCampoTabelaAssociativa, idItem4); //adiciona idItem4
        values.Add("tabela_associativa_added-"+idCampoTabelaAssociativa,idsJuntos_ConcatenadosComVirgula);// itens concatenados com virgula
        values.Add("tabela_associativa_removed-"+idCampoTabelaAssociativa,"");//em branco
        values.Add("tabela_associativa_colunas_adicionais-"+idCampoTabelaAssociativa,"");//em branco
        //---------------------------------------



//salva a informação no banco de dados
await FormContext.SaveEntityAsync(idEstruturaFormulario_PESSOA, values);

```

##### Para recuperar o Texto de um campo que seja uma Lista Dinâmica (Combo, Auto completar, Múltipla seleção, Radio):

Suponhamos que você tenha um formulário de Pedido e nesse formulário você tenha um campo chamado 'Produto'.  
Esse campo é do tipo 'Pesquisa com Auto Completar' e listará os 'Produtos' por 'Nome'.  
Quando o usuário salvar o formulário, no Json de envio não retornará o Nome do Produto, apenas o Id do produto selecionado (Esse id estará no formato de um GUID).  
Para recuperar o Nome do produto, você precisará executar o método **GetText** da seguinte forma:

```C#
var nomeProduto = FormContext.GetText("produto");
```

##### Recuperar os valores das "Variáveis de Ambiente"

Para saber mais sobre variáveis de ambiente entre em [Variáveis de ambiente](https://wiki.agilityflow.io/link/88#bkmrk-page-title)

```JavaScript
var valorDaVariavel = FormContext.GetEnvironmentVariable("var_nomeDaVariavel");
```

Recuperar a Descrição (texto), no caso de variáveis do Tipo "Query com Id + Descrição":

```JavaScript
var textDaVariavel = FormContext.GetEnvironmentVariable_Text("var_nomeDaVariavel");
```

##### Enviar Mensagem para o usuário: A execução do código abaixo apresentará uma mensagem para o usuário

```C#
FormContext.WarningMessage("Exemplo de mensagem para o usuário");
```

<p class="callout danger">O código C# se encerrará no exato momento em que for chamado o método para apresentação de mensagem.</p>

Retorno da mensagem

![](https://wiki.agilityflow.io/uploads/images/gallery/2019-09-Sep/scaled-840-0/image-1569710675959.png)

#### Envio de e-mail

Responsável pelo envio de e-mail

<table border="1" id="bkmrk-op%E3%A7%E3%A3o-retorno-%E2%A0-s" style="border-collapse: collapse; width: 121.652%; height: 137px;"><tbody><tr style="height: 31px;"><td style="width: 70.1206%; height: 31px;">**Método**</td><td style="width: 14.0139%; height: 31px;">**Retorno**</td><td style="width: 35.6186%; height: 31px;"> </td></tr><tr style="height: 31px;"><td style="width: 70.1206%; height: 31px;">**EmailSender.SendToGrupoUsuario**(Guid grupoUsuarioId, string subject, string htmlContent)</td><td style="width: 14.0139%; height: 31px;">void</td><td style="width: 35.6186%; height: 31px;">Enviar um e-mail para um determinado Grupo de Usuario</td></tr><tr style="height: 46px;"><td style="width: 70.1206%; height: 46px;">**EmailSender.SendEmail**(string toEmail, string subject, string htmlContent)</td><td style="width: 14.0139%; height: 46px;">void</td><td style="width: 35.6186%; height: 46px;">Enviar um e-mail </td></tr><tr style="height: 29px;"><td style="width: 70.1206%; height: 29px;">**EmailSender.SendEmail**(string toEmail, string subject, string htmlContent, string ccEmail**)**</td><td style="width: 14.0139%; height: 29px;">void</td><td style="width: 35.6186%; height: 29px;">Enviar um e-mail, com copia (CC)</td></tr></tbody></table>

##### Enviar e-mail para um Grupo de Usuário

Antes de iniciar, crie um "Grupo de Usuário" no menu "Segurança e Acesso". Depois de criado, associe os usuários que receberão e-mail ao novo grupo de usuário que você acabou de criar.

Pegue o ID desse novo Grupo de usuário. Você vai precisar dele para enviar o e-mail.

```C#
var grupoUsuarioId = Guid.Parse("af7c9275-0e23-4b64-a433-f238bb457005"); //substitua o ID "af7c9275-0e23-4b64-a433-f238bb457005" pelo Id do grupo de usuario que você deseja enviar o e-mail
var assunto = "Novo E-mail para um Grupo de usuario ";
var html = "Olá Grupo, <BR><BR> Teste para envio de e-mail para um Grupo de Usuário no AgilityFlow!";

EmailSender.SendToGrupoUsuario(grupoUsuarioId, assunto, html);
```

##### Enviar e-mail 

```C#
var htmlContent = "<strong>Olá,</strong> teste envio de e-mail.";
var emailTo = "john@email.com";
var subject = "Subject do E-mail";

EmailSender.SendEmail(emailTo, subject, htmlContent);
```

# Programação em C# - Na API de integração (POST)

As informações descritas abaixo referem-se as APIs de <span style="text-decoration: underline;">**POST**</span>

<p class="callout danger">IMPORTANTE: as variáveis e métodos descritos aqui só funcionarão na programação C# na **API de integração**. Para a programação C# nas Regras de Negócio de um formulário [clique aqui](https://wiki.agilityflow.io/books/manual-de-customiza%C3%A7%C3%A3o/page/programa%C3%A7%C3%A3o-em-c---na-regra-de-neg%C3%B3cio "Programação em C# - Na Regra de Negócio")</p>

O agilityflow permite a customização da API em C#, além de já disponibilizar diversas bibliotecas e funções para facilitar sua programação, incluindo acesso a dados, validações, envio de e-mail, notificação, entre outras.

<p class="callout info">Para cada requisição na API é criada uma transação de banco de dados, essa transação é única em toda execução da API e é gerenciada automaticamente pelo Agilityflow, caso você prefira, você pode fazer esse gerenciamento seguindo esses passos, [veja mais detalhes aqui](https://wiki.agilityflow.io/link/90#bkmrk-%E2%A0-15)</p>

Abaixo mostramos alguns exemplos, se o material abaixo não for suficiente , entre em contato com nossa equipe.

#### Variáveis disponíveis

<table border="1" id="bkmrk-vari%E3%A1vel-tipo-%E2%A0-fo" style="border-collapse: collapse; width: 147.516%; height: 41px;"><tbody><tr style="height: 31px;"><td style="width: 22.4663%; height: 31px;">**Variável**</td><td style="width: 26.6063%; height: 31px;">**Tipo**</td><td style="width: 127.347%; height: 31px;"> </td></tr><tr style="height: 400px;"><td style="width: 22.4663%; height: 10px;">content</td><td style="width: 26.6063%; height: 10px;">string</td><td style="width: 127.347%; height: 10px;">Conteúdo enviado no Body da API

No caso se ser enviado uma string com o JSON, por exemplo no formato:

 <span style="background-color: #f8f8f8; font-family: 'Lucida Console', 'DejaVu Sans Mono', 'Ubunto Mono', Monaco, monospace; font-size: 0.8em; white-space: pre;">{ nome: 'José', email: 'jose@xxxx.com'}</span>

<div></div>Você pode converter essa string para JSON e utilizá-la como object

`var json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);`

</td></tr></tbody></table>

#### Classe JsonHelper e outras para auxiliar o tratamento de JSON

Para utilizar execute `JsonHelper.NomeDoMetodo(....)`

<table border="1" id="bkmrk-op%E3%A7%E3%A3o-retorno-%E2%A0-g" style="border-collapse: collapse; width: 121.652%; height: 303px;"><tbody><tr style="height: 31px;"><td style="width: 48.7627%; height: 31px;">**Método**</td><td style="width: 16.6063%; height: 31px;">**Retorno**</td><td style="width: 54.3841%; height: 31px;"> </td></tr><tr style="height: 31px;"><td style="width: 48.7627%; height: 31px;">**HasProperty**(dynamic json, string nomePropriedade)</td><td style="width: 16.6063%; height: 31px;">bool

(true or false)

</td><td style="width: 54.3841%; height: 31px;">Testa se o JSON tem uma determinada propriedade.

Por exemplo, a sua API espera receber o JSON

no formato:

 <span style="background-color: #f8f8f8; font-family: 'Lucida Console', 'DejaVu Sans Mono', 'Ubunto Mono', Monaco, monospace; font-size: 0.8em; white-space: pre;">{ <span style="color: #0000ff;">nome</span>: 'José', <span style="color: #0000ff;">email</span>: 'jose@xxxx.com'}</span>

Porém recebe o JSON formato:

 <span style="background-color: #f8f8f8; font-family: 'Lucida Console', 'DejaVu Sans Mono', 'Ubunto Mono', Monaco, monospace; font-size: 0.8em; white-space: pre;">{ <span style="color: #ff0000;">nomecompleto</span>: 'José da Silva', <span style="color: #ff0000;">idade</span>: '21'}</span>

Como a propriedade NOME e EMAIL não existem no JSON recebido, o seu programa pode dar erro. Para evitar o erro, utilize o método <span style="color: #0000ff;">HasProperty</span>(...)

Como no exemplo abaixo:

`if (json != null)`  
`{`  
  
`if (JsonHelper.HasProperty(json, "nome") && JsonHelper.HasProperty(json, "email") ){`  
  
`var nome =  json["nome"].ToString();`

`var email=  json["email"].ToString();`  
  
`}`  
  
`}`

</td></tr><tr><td style="width: 48.7627%;">`Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);`  
</td><td style="width: 16.6063%;">dynamic

</td><td style="width: 54.3841%;">converte o conteúdo string para json

`var content = "{ nome: 'José', email: 'jose@xxxx.com'}";`

`var json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);`

`var nome =  json["nome"].ToString();`

</td></tr></tbody></table>

#### Métodos Disponíveis no contexto da API

<table border="1" id="bkmrk-m%E3%A9todo-retorno-%E2%A0-s" style="border-collapse: collapse; width: 119.753%; height: 599px;"><tbody><tr style="height: 31px;"><td style="width: 56.0466%; height: 31px;">**Método**</td><td style="width: 17.1002%; height: 31px;">**Retorno**</td><td style="width: 49.6927%; height: 31px;"> </td></tr><tr style="height: 82px;"><td style="width: 56.0466%; height: 82px;">**await ApiContext.GetDataTableAsync**(string sql, params DbParameter\[\] parameters)</td><td style="width: 17.1002%; height: 82px;">DataTable</td><td style="width: 49.6927%; height: 82px;">Recuperar informações dos bancos de dados através de uma query sql.

[Veja exemplo](https://wiki.agilityflow.io/link/90#bkmrk-recuperando-dados-do "Veja exemplo")

</td></tr><tr style="height: 31px;"><td style="width: 56.0466%; height: 31px;">**await ApiContext.SaveEntityAsync**(Guid estruturaformularioid\_ASerCriadoOuAtualizado, DataDictionary campos\_e\_valores)</td><td style="width: 17.1002%; height: 31px;">Guid</td><td style="width: 49.6927%; height: 31px;">Salva as informações de um formulário no bancos de dados.

Obs: Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas. Caso seja necessário, utilizar o .ToString() para formatar um campo decimal, não use. Utilize a conversão usando o string.Format, como no exemplo abaixo:

`string.Format(new System.Globalization.CultureInfo("en-gb"), "{0:F2}", json["valor"]);`

[Veja exemplo](https://wiki.agilityflow.io/link/90#bkmrk-salvando-os-dados-re "Veja exemplo")

</td></tr><tr style="height: 31px;"><td style="width: 56.0466%; height: 31px;">**await ApiContext.ApproveEntityAsync**(Guid estruturaformularioid\_ASerAprovado, DataDictionary campos\_e\_valores)</td><td style="width: 17.1002%; height: 31px;">Guid</td><td style="width: 49.6927%; height: 31px;">Avança uma etapa em um formulário com Workflow.

As regras de usuário aprovador, permissão de aprovação e responsabilidade por uma etapa devem ser validadas antes desse método ser executado.

Obs: Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas. Caso seja necessário, utilizar o .ToString() para formatar um campo decimal, não use. Utilize a conversão usando o string.Format, como no exemplo abaixo:

`string.Format(new System.Globalization.CultureInfo("en-gb"), "{0:F2}", json["valor"]);`

[Veja exemplo](https://wiki.agilityflow.io/link/90#bkmrk-aprovando-uma-etapa-)

</td></tr><tr><td style="width: 56.0466%;">**await ApiContext.DeleteEntityAsync(Guid formularioId)**</td><td style="width: 17.1002%;">void</td><td style="width: 49.6927%;">deletar um formulario

</td></tr><tr style="height: 130px;"><td style="width: 56.0466%; height: 130px;">**ApiContext.GetEnvironmentVariable**(string nomeVariavel)

ou

**ApiContext.GetEnvironmentVariable\_Text**(string nomeVariavel) \*\*

</td><td style="width: 17.1002%; height: 130px;">string</td><td style="width: 49.6927%; height: 130px;">Recupera o Valor de uma variável de ambiente.

\*\* Recupera a Descrição (texto), no caso de variáveis do Tipo "Que

</td></tr><tr style="height: 224px;"><td style="width: 56.0466%; height: 224px;">**await ApiContext.LogAsync(string log, string logTipo)**

</td><td style="width: 17.1002%; height: 224px;">void</td><td style="width: 49.6927%; height: 224px;">Cria um log na tabela de log geral, o log pode ser consultado entrando na Área de Customização e clicando na opção "Log Geral &gt;&gt; consultar Log"

\*\* Além do Log Padrão da API que o sistema gera, você pode gerar esses Logs customizados para o seu próprio controle.

</td></tr><tr style="height: 35px;"><td style="width: 56.0466%; height: 35px;">**await ApiContext.DbTransaction\_BeginTransactionAsync()**

**await ApiContext.DbTransaction\_CommitAsync()**

**await ApiContext.DbTransaction\_RollbackAsync()**

</td><td style="width: 17.1002%; height: 35px;">void</td><td style="width: 49.6927%; height: 35px;">Para gerenciar a transação de banco de dados manualmente. Leia os detalhes [aqui mais abaixo ](https://wiki.agilityflow.io/link/90#bkmrk-%E2%A0-15)

</td></tr><tr style="height: 35px;"><td style="width: 56.0466%; height: 35px;">**ApiContext.GetUrlBase()**

</td><td style="width: 17.1002%; height: 35px;">void</td><td style="width: 49.6927%; height: 35px;">Retorna a URL base da sua aplicação

exemplo:

[https://suaempresa.agilityflow.io/](https://suaempresa.agilityflow.io/)

\*A url sempre virá com uma barra no final **/**

</td></tr></tbody></table>

##### Recuperando dados do banco de dados através de uma query SQL

```C#
//parametros da query
var paramsQuery = new List<SqlParameter>();
paramsQuery.Add(new SqlParameter("@param1", "xxx"));
paramsQuery.Add(new SqlParameter("@param2", "yyy"));
paramsQuery.Add(new SqlParameter("@param3", "zzz"));

//query usando (nolock) nas tabelas para evitar problema com a transação que está ativa
var sql = "select column1,column2,column3 from table (nolock) where column1 = @param1 or  column2 = @param2 or  column3 = @param3 ";

//executar no banco de dados
var dt = await ApiContext.GetDataTableAsync(sql, paramsQuery.ToArray());

//percorrendo as linhas de retorno da tabela
foreach (DataRow dr in dt.Rows)
{
  if (dr["column1"] != DBNull.Value){
  	var xx = dr["column1"].ToString();
  }
}
```

##### Salvando os dados recebidos através do método SaveEntityAsync

\* Para cadastrar valores numéricos, o números devem estar no formato com ponto no separador decimal: 999999.99 sem utilização de virgulas.

```C#
public void Execute(){
        
  //opcional para registrar no log de controle
  await ApiContext.LogAsync("Entrou da API", "log_api_xpto");
  
            //converte o conteúdo enviado no body da API para JSON
            var json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
      
            //guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
            var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");
    
    
            if (json != null)
            {
    
                    //verifica se o JSON recebido contém a propriedade "nome", "email" e "cel" que são necessários p cadastro
                    if (JsonHelper.HasProperty(json, "nome") && JsonHelper.HasProperty(json, "email") && JsonHelper.HasProperty(json, "perfil")){
           
                        var values = new DataDictionary();
                        values.Add("nome", json["nome"].ToString());
                        values.Add("email", json["email"].ToString());
                      
                        //no caso do campo do formulário ser uma tabela associativa (tabela relacional N:N)
                        var idCampoTabelaAssociativa = "70b6f362-2587-4fd2-a2fb-148bd0caf437";
                        if (json["perfil"].ToString() == "todos_os_perfis" ) {
                          
                          var idPerfilAdmin = "51cf02f1-3787-4dca-8a2c-e219a5ce1298";
                          var idPerfilColaborador = "f999f103-c775-4245-92d3-034cb3ded5e4";                          
                          var idPerfisConcatenadosComVirgula = idPerfilAdmin + "," + idPerfilColaborador + ",";
                          
                          //adiciona os perfis
                          values.Add(idCampoTabelaAssociativa, idPerfilAdmin); //adiciona perfil de admin
                          values.Add(idCampoTabelaAssociativa, idPerfilColaborador); //adiciona perfil de colaborador
                          values.Add("tabela_associativa_added-"+idCampoTabelaAssociativa,idPerfisConcatenadosComVirgula);// perfis concatenados com virgula
                          values.Add("tabela_associativa_removed-"+idCampoTabelaAssociativa,"");//em branco, se fosse pra remover, era só colocar os perfis concatenados com virgula
                          //---------------------------------------

                        }
                        else if (json["perfil"].ToString() == "apenas_colaborador" ) {
                          
                          
                          var idPerfilColaborador = "f999f103-c775-4245-92d3-034cb3ded5e4";                        
                          var idPerfilConcatenadoComVirgula = idPerfilColaborador + ",";
                          
                          //adiciona o perfil
                          values.Add(idCampoTabelaAssociativa, idPerfilColaborador); //adiciona perfil de colaborador
                          values.Add("tabela_associativa_added-"+idCampoTabelaAssociativa,idPerfilConcatenadoComVirgula);// perfis concatenados com virgula
                           values.Add("tabela_associativa_removed-"+idCampoTabelaAssociativa,"");//em branco, se fosse pra remover, era só colocar os perfis concatenados com virgula
                          //---------------------------------------
                          
                          
                        }
                      
                        //salva a informação no banco de dados
                        await ApiContext.SaveEntityAsync(idEstruturaFormulario_PESSOA, values);
                        
                    }
                
            }
//opcional para registrar no log de controle
  await ApiContext.LogAsync("Saiu da API", "log_api_xpto");
    
    }
```

##### Aprovando uma etapa através do método ApproveEntityAsync

```C#
public void Execute(){

  //opcional para registrar no log de controle
  await ApiContext.LogAsync("Entrou da API", "log_api_xpto");
  
            //converte o conteúdo enviado no body da API para JSON
            var json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
      
            //guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
            var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");
    
    
            if (json != null)
            {
    
                    //verifica se o JSON recebido contém a propriedade "nome", "email" e "cel" que são necessários p cadastro
                    if (JsonHelper.HasProperty(json, "nome") && JsonHelper.HasProperty(json, "email") && JsonHelper.HasProperty(json, "perfil")){
           
                        var values = new DataDictionary();
                        values.Add("nome", json["nome"].ToString());
                        values.Add("email", json["email"].ToString());
                      	values.Add("id", json["id"].ToString());
                      
                        //no caso do campo do formulário ser uma tabela associativa (tabela relacional N:N)
                        var idCampoTabelaAssociativa = "70b6f362-2587-4fd2-a2fb-148bd0caf437";
                        if (json["perfil"].ToString() == "todos_os_perfis" ) {
                          
                          var idPerfilAdmin = "51cf02f1-3787-4dca-8a2c-e219a5ce1298";
                          var idPerfilColaborador = "f999f103-c775-4245-92d3-034cb3ded5e4";                          
                          var idPerfisConcatenadosComVirgula = idPerfilAdmin + "," + idPerfilColaborador + ",";
                          
                          //adiciona os perfis
                          values.Add(idCampoTabelaAssociativa, idPerfilAdmin); //adiciona perfil de admin
                          values.Add(idCampoTabelaAssociativa, idPerfilColaborador); //adiciona perfil de colaborador
                          values.Add("tabela_associativa_added-"+idCampoTabelaAssociativa,idPerfisConcatenadosComVirgula);// perfis concatenados com virgula
                          values.Add("tabela_associativa_removed-"+idCampoTabelaAssociativa,"");//em branco, se fosse pra remover, era só colocar os perfis concatenados com virgula
                          //---------------------------------------

                        }
                        else if (json["perfil"].ToString() == "apenas_colaborador" ) {
                          
                          
                          var idPerfilColaborador = "f999f103-c775-4245-92d3-034cb3ded5e4";                        
                          var idPerfilConcatenadoComVirgula = idPerfilColaborador + ",";
                          
                          //adiciona o perfil
                          values.Add(idCampoTabelaAssociativa, idPerfilColaborador); //adiciona perfil de colaborador
                          values.Add("tabela_associativa_added-"+idCampoTabelaAssociativa,idPerfilConcatenadoComVirgula);// perfis concatenados com virgula
                          values.Add("tabela_associativa_removed-"+idCampoTabelaAssociativa,"");//em branco, se fosse pra remover, era só colocar os perfis concatenados com virgula
                          //---------------------------------------
                          
                          
                        }
                      
                        //salva a informação no banco de dados
                        await ApiContext.ApproveEntityAsync(idEstruturaFormulario_PESSOA, values);
                        
                    }
                
            }
//opcional para registrar no log de controle
    await ApiContext.LogAsync("saiu da API", "log_api_xpto");
    
    }
```

##### Recuperar os valores das "Variáveis de Ambiente"

Para saber mais sobre variáveis de ambiente entre em [Variáveis de ambiente](https://wiki.agilityflow.io/link/88#bkmrk-page-title)

```JavaScript
var valorDaVariavel = ApiContext.GetEnvironmentVariable("var_nomeDaVariavel");
```

Recuperar a Descrição (texto), no caso de variáveis do Tipo "Query com Id + Descrição":

```JavaScript
var textDaVariavel = ApiContext.GetEnvironmentVariable_Text("var_nomeDaVariavel");
```

#### Envio de e-mail

Responsável pelo envio de e-mail

<table border="1" id="bkmrk-op%E3%A7%E3%A3o-retorno-%E2%A0-s" style="border-collapse: collapse; width: 121.652%; height: 303px;"><tbody><tr style="height: 31px;"><td style="width: 70.1206%; height: 31px;">**Método**</td><td style="width: 14.0139%; height: 31px;">**Retorno**</td><td style="width: 35.6186%; height: 31px;"> </td></tr><tr style="height: 31px;"><td style="width: 70.1206%; height: 31px;">**EmailSender.SendToGrupoUsuario**(Guid grupoUsuarioId, string subject, string htmlContent)</td><td style="width: 14.0139%; height: 31px;">void</td><td style="width: 35.6186%; height: 31px;">Enviar um e-mail para um determinado Grupo de Usuario</td></tr><tr><td style="width: 70.1206%;">**EmailSender.SendEmail**(string toEmail, string subject, string htmlContent)</td><td style="width: 14.0139%;">void</td><td style="width: 35.6186%;">Enviar um e-mail </td></tr><tr><td style="width: 70.1206%;">**EmailSender.SendEmail(**string toEmail, string subject, string htmlContent, string ccEmail)</td><td style="width: 14.0139%;">void</td><td style="width: 35.6186%;">Enviar um e-mail, com copia (CC)</td></tr></tbody></table>

##### Enviar e-mail para um Grupo de Usuário

Antes de iniciar, crie um "Grupo de Usuário" no menu "Segurança e Acesso". Depois de criado, associe os usuários que receberão e-mail ao novo grupo de usuário que você acabou de criar.

Pegue o ID desse novo Grupo de usuário. Você vai precisar dele para enviar o e-mail.

```C#
var grupoUsuarioId = Guid.Parse("af7c9275-0e23-4b64-a433-f238bb457005"); //substitua o ID "af7c9275-0e23-4b64-a433-f238bb457005" pelo Id do grupo de usuario que você deseja enviar o e-mail
var assunto = "Novo E-mail para um Grupo de usuario ";

var html = "Olá Grupo, <BR><BR> Teste para envio de e-mail para um Grupo de Usuário no AgilityFlow!";

EmailSender.SendToGrupoUsuario(grupoUsuarioId, assunto, html);
```

##### Enviar e-mail 

```C#
var htmlContent = "<strong>Olá,</strong> teste envio de e-mail.";
var emailTo = "john@email.com";
var subject = "Subject do E-mail";

EmailSender.SendEmail(emailTo, subject, htmlContent);
```

#### Método principal para utilização da Programação em C# na api de post

A única regra é que o método se chame Execute, não tenha parâmetros de entrada e o retorno seja void.

Exemplo:

```C#
public void Execute(){            



  //aqui você pode programar   



}
```

#### Gerenciar as transações de Banco de dados manualmente

Caso você escolha por gerenciar as transactions de banco de dados manualmente, você precisará obrigatoriamente utilizar as 3 funções abaixo:

<p class="callout warning">Atenção: Configuração recomendada apenas para especialista.</p>

1. **ApiContext.DbTransaction\_BeginTransaction()** Para abrir uma transação, antes de iniciar as alterações no banco de dados
2. **ApiContext.DbTransaction\_Commit()** Para finalizar e confirmar com sucesso as alterações no banco de dados
3. **ApiContext.DbTransaction\_Rollback()** Para finalizar e desfazer as alterações no banco de dados

Exemplo de uso:

<p class="callout danger">Atenção: Utilize as transactions sempre dentro de TRY e CATCH</p>

<p class="callout info">Podem ser abertas uma ou mais transações na mesma API. Você ficará responsável por todas as aberturas e encerramentos das transações criadas.</p>

```C#
public async Task RunAsync(){
        
            //converte o conteúdo enviado no body da API para JSON
            var json = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
      
            //guarda em uma variável o ID da  E S T R U T U R A  do formulário de pessoa
            var idEstruturaFormulario_PESSOA = Guid.Parse("0b56b66a-4f6f-4ded-ad04-016d7c0724e1");
    
            try
            {
              
              await ApiContext.DbTransaction_BeginTransactionAsync(); //ABRE A TRANSAÇÃO, DENTRO DO TRY CATCH

              var values = new DataDictionary();
              values.Add("nome", "exemplo1_PRIMEIRA_transacao");
              
              await ApiContext.SaveEntityAsync(idEstruturaFormulario_PESSOA, values); //salva a informação no banco de dados
              
              await ApiContext.DbTransaction_CommitAsync(); //CONFIRMA A TRANSAÇÃO, DENTRO DO TRY CATCH

            }
            catch
            {              
              await ApiContext.DbTransaction_RollbackAsync(); //DESFAZ A TRANSAÇÃO, DENTRO DO CATCH em caso de erro
              throw;
            }
    
  
  			//mais código
  			//...
  			//mais código
  			//...
    		//mais código
  			//...
  			//mais código
  			//...
  
  
  
  
  			try
            {
              
              await ApiContext.DbTransaction_BeginTransactionAsync(); //ABRE A TRANSAÇÃO, DENTRO DO TRY CATCH

              var values2 = new DataDictionary();
              values2.Add("nome", "exemplo2_SEGUNDA_transacao");
              
              await ApiContext.SaveEntityAsync(idEstruturaFormulario_PESSOA, values2); //salva a informação no banco de dados
              
              await ApiContext.DbTransaction_CommitAsync(); //CONFIRMA A TRANSAÇÃO, DENTRO DO TRY CATCH

            }
            catch
            {              
              await ApiContext.DbTransaction_RollbackAsync(); //DESFAZ A TRANSAÇÃO, DENTRO DO CATCH em caso de erro
              throw;
            }
  	
  
    
    }
```

# Tabela de dados customizada através de programação: Query SQL, C#, HTML e CSS

Para acessar os detalhes desse conteúdo, [clique aqui](https://wiki.agilityflow.io/link/78#bkmrk-criar-html-com-tabel).