Disponibiliza uma API REST no Dashboard para visualizar um Card. Com o fonte abaixo (Exemplo) foi obtido o seguinte Card de exemplo:

Para a disponibilização de um card, é necessário que sua API possua 4 endpoints:
1. Um POST para carregar as informações que serão apresentadas nos detalhes deste card (Opção Detalhes): No exemplo abaixo, essas informações são retornadas pelo endpoint /CardsCustomer/cards/itemsDetails. 
O endpoint deve retornar a seguinte estrutura json: {
"header":[
{
"showFilter": boolean,
"property": string,
"label":string
}
],
"items":[
{
"property": <Valor retornado pela query>,
}
],
"hasNext": boolean
} |
A propriedade header refere-se ao cabeçalho da tabela. Como a tabela utilizada pelo dashboard é um componente da biblioteca PO-UI (po-table), deve-se informar as propriedades obrigatórias para este componente: property (nome identificador para a coluna) e label (título para a coluna). Para mais informações, clique aqui. A propriedade items refere-se aos valores que serão apresentados nas colunas da tabela, ou seja, para cada propriedade informada, a query irá retornar um valor. Exemplo: { "code": "000001" }. Este deve ter a propriedade "hasNext" para informar se há próxima página (true) ou não (false). |
{ "header": [ { "showFilter": true, "type": "link", "property": "code", "visible": true, "label": "Código", "action": "Link" }, { "showFilter": true, "property": "store", "visible": true, "label": "Loja" }, { "showFilter": true, "property": "name", "visible": true, "label": "Nome" }, { "showFilter": true, "property": "fantasyName", "visible": true, "label": "Nome Fantasia" }, { "showFilter": true, "property": "risk", "visible": true, "label": "Risco" } ], "items": [ { "code": "API001", "store": "01", "name": "CLIENTE API INCLUSAO", "fantasyName": "CLIENTE API INCLUSAO", "risk": "A" }, { "code": "API002", "store": "01", "name": "CLIENTE API ALTERACAO", "fantasyName": "API ALTERACAO", "risk": "A" }, { "code": "API003", "store": "01", "name": "CLIENTE API EXCLUSAO", "fantasyName": "API EXCLUSAO", "risk": "A" }, { "code": "API005", "store": "01", "name": "API 005 MANUT.COMISSAO ALTERA", "fantasyName": "API005", "risk": "A" }, { "code": "API006", "store": "01", "name": "API006 MANUT.COMISSAO DELETA", "fantasyName": "API006", "risk": "A" }, { "code": "API007", "store": "01", "name": "API007 - MATS030 - ALTERAR", "fantasyName": "API007 MATS030", "risk": "A" }, { "code": "API008", "store": "01", "name": "API008 - MATS030 - DELETAR", "fantasyName": "API008 MATS030", "risk": "A" }, { "code": "BETIM", "store": "01", "name": "LOJA BETIM", "fantasyName": "BETIM MG", "risk": "A" }, { "code": "C16190", "store": "01", "name": "DSERFINR-16190", "fantasyName": "16190", "risk": "A" }, { "code": "CLIBH", "store": "01", "name": "CLIENTE BELO HORIZONTE", "fantasyName": "BELO HORIZONTE", "risk": "A" } ], "hasNext": true } |
2. Um GET para retornar os campos que poderão ser utilizados no filtro do card (Opção 'Filtrar' na inclusão/alteração do card): No exemplo abaixo, essas informações são retornadas pelo endpoint /CardsCustomer/cards/cardFilter. 
O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"showFilter": boolean,
"property": string,
"label": string
}
]
} |
A propriedade items refere-se ao campo "Campo" da inclusão de filtro. O property é o nome identificador para a opção e o label é o título para a opção. |
{
"items": [
{
"showFilter": true,
"property": "code",
"label": "Código"
},
{
"showFilter": true,
"property": "store",
"label": "Loja"
},
{
"showFilter": true,
"property": "name",
"label": "Nome"
},
{
"showFilter": true,
"property": "fantasyName",
"label": "Nome Fantasia"
},
{
"showFilter": true,
"property": "risk",
"label": "Risco"
}
]
} |
|
3. Um GET para retornar o valor das informações que serão apresentadas no card (no máximo 4). Exemplo: A informação Total clientes risco A possui o valor 197. No exemplo abaixo, essas informações são retornadas pelo endpoint /CardsCustomer/cards/cardInfo. O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"propriedade": "valor"
}
],
"hasNext": "false"
} |
A propriedade items deve trazer todas as informações que poderão ser apresentadas no card e seu respectivo valor. No exemplo que temos acima com a informação de Total a Receber, ele se refere à propriedade "total" apresentada neste retorno. |
{
"items": [
{
"risco_a": 197,
"risco_b": 10,
"risco_c": 1
}
],
"hasNext": "false"
} |
|
4. Um GET para retornar os campos/propriedades que podem ser utilizados na apresentação do card: No exemplo abaixo, essas informações são retornadas pelo endpoint /CardsCustomer/cards/fieldsInfo. 
O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"value": string,
"label": string
}
]
} |
A propriedade items deve retornar a lista com todas as opções que podem aparecer nos campos Primeira Informação, Segunda Informação, Terceira Informação e Quarta Informação. Esse campo é um componente da biblioteca PO-ui (po-combo), portanto deve retornar as propriedades obrigatórias para sua implementação: label (título para a opção) e value (valor daquela opção). Para mais informações, clique aqui. |
{
"items": [
{
"value": "risco_a",
"label": "Total clientes risco A: "
},
{
"value": "risco_b",
"label": "Total clientes risco B: "
},
{
"value": "risco_c",
"label": "Total clientes risco C: "
}
]
} |
|
|
#INCLUDE "TOTVS.CH" #INCLUDE "RESTFUL.CH" //------------------------------------------------------------------- /*/{Protheus.doc} WSCardsCustomer Exemplo de API de integração de Cards @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- WSRESTFUL CardsCustomer DESCRIPTION "Cards de Clientes" WSDATA Fields AS STRING OPTIONAL WSDATA Order AS STRING OPTIONAL WSDATA Page AS INTEGER OPTIONAL WSDATA PageSize AS INTEGER OPTIONAL WSMETHOD POST itemsDetails ; DESCRIPTION "Carrega os Itens Utilizados para Montagem do Painel" ; WSSYNTAX "/cards/itemsDetails/{Order, Page, PageSize, Fields}" ; PATH "/cards/itemsDetails"; PRODUCES APPLICATION_JSON WSMETHOD GET cardFilter; DESCRIPTION "Disponibiliza os campos que poderão ser utilizados no filtro do Card" ; WSSYNTAX "/cards/cardFilter/" ; PATH "/cards/cardFilter"; PRODUCES APPLICATION_JSON WSMETHOD GET cardInfo; DESCRIPTION "Carrega as informações do Painel" ; WSSYNTAX "/cards/cardInfo/" ; PATH "/cards/cardInfo"; PRODUCES APPLICATION_JSON WSMETHOD GET fieldsInfo ; DESCRIPTION "Carrega os campos que podem que ser utilizados" ; WSSYNTAX "/cards/fieldsInfo/" ; PATH "/cards/fieldsInfo"; PRODUCES APPLICATION_JSON ENDWSRESTFUL //------------------------------------------------------------------- /*/{Protheus.doc} POST itemsDetails Carrega os Itens Utilizados para Montagem do Painel @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- WSMETHOD POST itemsDetails WSRECEIVE Order, Page, PageSize, Fields WSSERVICE CardsCustomer Local aHeader := {} Local aRet := {} Local cError := "Erro na Requisição" Local lRet := .T. Local oCoreDash := CoreDash():New() Local aFilter := {} Local nQtdFilter := 0 Local cFilter := "" Local nX := 0 Local cBody := DecodeUtf8(Self:GetContent()) Local oBody := JsonObject():New() Local oJDetails := JsonObject():New() Local cCampos := "" Self:SetContentType("application/json") If !Empty(cBody) oBody:FromJson(cBody) If ValType(oBody["detailFilter"]) == "A" oJDetails := oBody["detailFilter"] EndIf EndIf If Len(oJDetails) == 0 aHeader := RetHeader("SA1") oCoreDash:SetFields(DePara("SA1")) oCoreDash:SetApiQstring(Self:aQueryString) aFilter := oCoreDash:GetApiFilter() nQtdFilter := Len(aFilter) If nQtdFilter > 0 For nX := 1 to nQtdFilter cFilter += " AND " + aFilter[nX][1] Next EndIf //Chama a função responsavel por montar a Expressão SQL aRet := MntQuery(, cFilter + " AND (SA1.A1_RISCO = 'A' OR SA1.A1_RISCO = 'B' OR SA1.A1_RISCO = 'C')",,"SA1") ElseIf Len(oJDetails) == 1 aHeader := RetHeader("SC5") oCoreDash:SetFields(DePara("SC5")) oCoreDash:SetApiQstring(Self:aQueryString) aFilter := oCoreDash:GetApiFilter() nQtdFilter := Len(aFilter) If nQtdFilter > 0 For nX := 1 to nQtdFilter cFilter += " AND " + aFilter[nX][1] Next EndIf cFilter := " AND SC5.C5_CLIENTE = '" + oJDetails[1]['code'] + "'" cCampos := "SC5.C5_NUM, SC5.C5_TIPO, SC5.C5_CONDPAG, SC5.C5_EMISSAO" aRet := MntQuery(cCampos, cFilter,,"SC5") EndIf //Define a Query padrão utilizada no Serviço oCoreDash:SetQuery(aRet[1]) oCoreDash:SetWhere(aRet[2]) If Len(aRet) >= 3 oCoreDash:SetGroupBy(aRet[3]) EndIf oCoreDash:BuildJson() lRet := ValType(oCoreDash:GetJsonObject()['items']) == "A" If lRet oCoreDash:SetPOHeader(aHeader) Self:SetResponse( oCoreDash:ToObjectJson()) Else SetRestFault(500, EncodeUtf8(cError)) EndIf oCoreDash:Destroy() aSize(aRet, 0) aSize(aHeader, 0) Return lRet //------------------------------------------------------------------- /*/{Protheus.doc} MntQuery Monta a query responsável por trazer os itens utilizados no Painel @param cCampos, Caractere, Campos que serão retornados no SELECT @param cFiltro, Caractere, Filtro a ser adicionado na query @param cGroupBy, Caractere, Expressão group by a ser adicionada na query @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function MntQuery(cCampos, cFiltro, cGroupBy, cTable) Local cQuery Local cWhere Local cGroup Default cTable := "SA1" Default cCampos := "SA1.A1_COD, SA1.A1_LOJA, SA1.A1_NOME, SA1.A1_NREDUZ, SA1.A1_RISCO" If cTable == "SA1" cQuery := " SELECT " + cCampos + " FROM " + RetSqlName("SA1") + " SA1 " cWhere := " SA1.A1_FILIAL = '" + xFilial("SA1") + "'" + cFiltro cWhere += " AND SA1.D_E_L_E_T_ = ' ' " ElseIf cTable == "SC5" cQuery := " SELECT " + cCampos + " FROM " + RetSqlName("SC5") + " SC5 " cWhere := " SC5.C5_FILIAL = '" + xFilial("SC5") + "'" + cFiltro cWhere += " AND SC5.D_E_L_E_T_ = ' ' " EndIf If !Empty(cGroupBy) cGroup := cGroupBy EndIf Return {cQuery, cWhere, cGroup} //------------------------------------------------------------------- /*/{Protheus.doc} DePara Efetua o Conversão entre os atributos objetos do Json e os campos utilizados na Consulta @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function DePara(cTable) Local aCampos := {} Default cTable := "SA1" If cTable == "SA1" aCampos := {; {"code" , "SA1.A1_COD" },; {"store" , "SA1.A1_LOJA" },; {"name" , "SA1.A1_NOME" },; {"fantasyName", "SA1.A1_NREDUZ"},; {"risk" , "SA1.A1_RISCO" }; } ElseIf cTable == "SC5" aCampos := {; {"num" , "SC5.C5_NUM" },; {"type" , "SC5.C5_TIPO" },; {"payment" , "SC5.C5_CONDPAG" },; {"emission", "SC5.C5_EMISSAO"}; } EndIf Return aCampos //------------------------------------------------------------------- /*/{Protheus.doc} GET cardInfo Método para retornar os dados que podem ser apresentados pelo Card @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- WSMETHOD GET cardInfo WSRESTFUL CardsCustomer Local aFilter := {} Local cWhere := "" Local nFiltro := 0 Local oCoreDash := CoreDash():New() Local oResponse := JsonObject():New() //Converte os campos utilizados na consulta para os campos utilizados no card. oCoreDash:SetFields(DePara()) //Converte o Filtro informado no parametro Query String. oCoreDash:SetApiQstring(Self:aQueryString) aFilter := oCoreDash:GetApiFilter() For nFiltro := 1 to Len(aFilter) cWhere += " AND " + aFilter[nFiltro][1] Next RetCardInfo(@oResponse, cWhere) self:SetResponse( EncodeUtf8(FwJsonSerialize(oResponse,.T.,.T.)) ) oResponse := Nil FreeObj( oResponse ) oCoreDash:Destroy() FreeObj( oCoreDash ) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} RetCardInfo Retorna os dados que poderão ser apresentados no painel @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function RetCardInfo(oResponse, cApiFilter) Local oItem Local aItems := {} Default cApiFilter := "" oItem := JsonObject():New() oItem["risco_a"] := RetRisco(" AND SA1.A1_RISCO = 'A' " + cApiFilter) oItem["risco_b"] := RetRisco(" AND SA1.A1_RISCO = 'B' " + cApiFilter) oItem["risco_c"] := RetRisco(" AND SA1.A1_RISCO = 'C' " + cApiFilter) aAdd(aItems, oItem) oResponse['hasNext'] := 'false' oResponse["items"] := aItems Return Nil //------------------------------------------------------------------- /*/{Protheus.doc} RetRisco Retorna os dados de acordo com o filtro @param cFiltro, Caractere, Filtro a ser adicionado na query @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function RetRisco(cFiltro) Local aQuery := MntQuery("COUNT(SA1.A1_COD) TOTAL_REGISTROS", cFiltro) Local cQuery := "" Local cTemp := GetNextAlias() Local xRet Default cWhere := "" Default cInfo := "" cQuery := aQuery[1] + " WHERE " + aQuery[2] DBUseArea( .T., "TOPCONN", TCGenQry( ,, cQuery ), cTemp, .T., .T. ) xRet := (cTemp)->TOTAL_REGISTROS (cTemp)->( DBCloseArea() ) Return xRet //------------------------------------------------------------------- /*/{Protheus.doc} GET fieldsInfo Carrega os campos que podem que ser utilizados @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- WSMETHOD GET fieldsInfo WSSERVICE CardsCustomer Local aItems := {} Local oResponse := JsonObject():New() Local oCoreDash := CoreDash():New() aAdd(aItems, { "risco_a" , "Total clientes risco A: " }) aAdd(aItems, { "risco_b" , "Total clientes risco B: " }) aAdd(aItems, { "risco_c" , "Total clientes risco C: " }) /*Retorna um Objeto no formato de Value e Label*/ oResponse["items"] := oCoreDash:SetPOCombo(aItems) Self:SetResponse( EncodeUtf8(oResponse:ToJson())) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} GET cardFilter Retorna os campos que poderão ser filtrados @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD GET cardFilter WSSERVICE CardsCustomer Local aFields := {} Local oCoreDash := CoreDash():New() Local oResponse := JsonObject():New() aFields := {; {"code" , "Código" },; {"store" , "Loja" },; {"name" , "Nome" },; {"fantasyName", "Nome Fantasia"},; {"risk" , "Risco" }; } oResponse["items"] := oCoreDash:SetPOHeader(aFields) Self:SetResponse( EncodeUtf8(oResponse:ToJson())) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} RetHeader Retorna os campos do cabeçalho dos detalhes @author Squad CRM & Faturamento @since 18/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function RetHeader(cTable) Local aHeader Default cTable := "SA1" If cTable == "SA1" aHeader := {; {"code" , "Código" , 'link',,,.T.},; {"store" , "Loja" ,,,,.T.},; {"name" , "Nome" ,,,,.T.},; {"fantasyName", "Nome Fantasia",,,,.T.},; {"risk" , "Risco" ,,,,.T.}; } ElseIf cTable == "SC5" aHeader := {; {"num" , "Número Pedido" ,,,,.T.},; {"type" , "Tipo Pedido" ,,,,.T.},; {"payment" , "Condição de Pagamento",,,,.T.},; {"emission", "Emissão" ,,,,.T.}; } EndIf Return aHeader |
|
Disponibiliza uma API REST no Dashboard para visualizar um gráfico de Pizza ou Polar. Com o fonte abaixo (Exemplo) foi obtido o seguinte Gráfico de exemplo:

Para disponibilização de gráficos em pizza/polar, é necessário que a API possua 3 endpoints:
1. Um GET para retornar o formulário de cadastro do gráfico: No exemplo abaixo, essas informações são retornadas pelo endpoint /Exemplo2/charts/form. 
Diferente dos cards, o gráfico possui um formulário dinâmico e é o serviço acima mencionado que deve retornar os dados para a montagem do formulário de cadastro. Neste exemplo, ele nos retornou o formulário a partir da linha 'Tipo de Gráfico'. Tudo o que vem antes dessa linha, é padrão para todos os gráficos. O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"divider": string,
"gridSmColumns": number,
"type": string,
"property": string,
"gridColumns": number,
"required": boolean,
"label": string
}
]
} |
A propriedade items deve retornar a lista com as configurações de todos os campos que devem ser apresentados no formulário. Para a montagem deste formulário, é utilizado o componente Dynamic Form do PO-ui, portanto para entender cada uma das propriedades a serem retornadas neste serviço, clique aqui. |
{
"items": [
{
"divider": "Tipo de Gráfico",
"gridSmColumns": 12,
"options": [
{
"value": "pie",
"label": "Pizza"
},
{
"value": "polarArea",
"label": "Polar"
}
],
"type": "string",
"property": "charttype",
"gridColumns": 6,
"required": true,
"label": "Tipo de Gráfico"
},
{
"divider": "Datas",
"gridSmColumns": 12,
"type": "date",
"property": "datainicio",
"gridColumns": 6,
"required": true,
"label": "Dt. Emissão De"
},
{
"gridSmColumns": 12,
"type": "date",
"property": "datafim",
"gridColumns": 6,
"required": true,
"label": "Dt. Emissão Ate"
}
]
} |
|
2. Um POST para carregar as informações do gráfico: No exemplo abaixo, essas informações são retornadas pelo endpoint /Exemplo2/charts/retdados. 
O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"chartData": [ Lista das informações a serem apresentadas no gráfico ],
"chartLabels": [ Lista dos títulos das informações ],
"charttype": string - Tipo do gráfico,
"title": string - Título do gráfico,
"currencyMask": [ Máscaras a serem utilizadas ]
}
]
} |
A propriedade items deve retornar as informações do gráfico como valores a serem apresentados, títulos a representarem esses valores, título do gráfico e máscara de valores a ser utilizada. |
{
"items": [
{
"chartData": [
90,
470,
369,
150,
993,
488,
599,
539,
650,
5,
362,
346,
518,
983,
481,
34,
481,
377,
571,
127,
283,
898,
545,
279,
851,
844,
367,
971
],
"chartLabels": [
"AC",
"AL",
"AM",
"AP",
"BA",
"CE",
"DF",
"ES",
"EX",
"GO",
"MA",
"MG",
"MS",
"MT",
"PA",
"PB",
"PE",
"PI",
"PR",
"RJ",
"RN",
"RO",
"RR",
"RS",
"SC",
"SE",
"SP",
"TO"
],
"charttype": "",
"title": "Nota fiscal por Estado",
"currencyMask": [
{
"maskFrac": "R$",
"maxiFrac": 10,
"miniFrac": 2
}
]
}
]
} |
|
3. Um POST para carregar as informações que serão apresentadas nos detalhes do gráfico: No exemplo abaixo, essas informações são retornadas pelo endpoint /Exemplo2/charts/itemsDetails. 
O endpoint deve retornar a seguinte estrutura json: {
"header":[
{
"showFilter": boolean,
"property": string,
"label":string
}
],
"items":[
{
"property": <Valor retornado pela query>,
}
],
"hasNext": boolean
} |
A propriedade header refere-se ao cabeçalho da tabela. Como a tabela utilizada pelo dashboard é um componente da biblioteca PO-UI (po-table), deve-se informar as propriedades obrigatórias para este componente: property (nome identificador para a coluna) e label (título para a coluna). Para mais informações, clique aqui. A propriedade items refere-se aos valores que serão apresentados nas colunas da tabela, ou seja, para cada propriedade informada, a query irá retornar um valor. Exemplo: { "documentType": "N" }. Este deve ter a propriedade "hasNext" para informar se há próxima página (true) ou não (false). |
{
"header": [
{
"showFilter": true,
"type": "link",
"property": "document",
"label": "Documento",
"action": "Link"
},
{
"showFilter": true,
"property": "series",
"label": "Serie"
},
{
"showFilter": true,
"property": "state",
"label": "UF"
},
{
"showFilter": true,
"property": "documentType",
"label": "Tipo"
},
{
"showFilter": true,
"property": "dateOfIssue",
"label": "Emissão"
}
],
"items": [
{
"series": "004",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "14/07/20"
},
{
"series": "133",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "10/02/21"
},
{
"series": "3",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "30/08/17"
},
{
"series": "317",
"document": "000000001",
"state": "MG",
"documentType": "N",
"dateOfIssue": "10/06/20"
},
{
"series": "626",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "26/08/21"
},
{
"series": "FAT",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "14/10/16"
},
{
"series": "MN",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "02/09/21"
},
{
"series": "NF",
"document": "000000001",
"state": "SP",
"documentType": "N",
"dateOfIssue": "28/03/19"
},
{
"series": "3",
"document": "000000002",
"state": "SP",
"documentType": "N",
"dateOfIssue": "22/11/17"
},
{
"series": "317",
"document": "000000002",
"state": "MG",
"documentType": "N",
"dateOfIssue": "10/06/20"
}
],
"hasNext": true
}
|
|
|
#INCLUDE "TOTVS.CH" #INCLUDE "RESTFUL.CH" //------------------------------------------------------------------------ /*/{Protheus.doc} Exemplo2 Exemplo de API de integração de Graficos de Pizza e/ou Polar @author Squad CRM & Faturamento @since 26/03/2020 @version 12.1.27 /*/ //------------------------------------------------------------------------ WSRESTFUL Exemplo2 DESCRIPTION "Exemplo de API - Grafico Pizza e Polar" WSDATA JsonFilter AS STRING OPTIONAL WSDATA drillDownFilter AS STRING OPTIONAL WSDATA Page AS INTEGER OPTIONAL WSDATA PageSize AS INTEGER OPTIONAL WSMETHOD GET form ; DESCRIPTION "Form de Cadastro do Gráfico" ; WSSYNTAX "/charts/form/" ; PATH "/charts/form"; PRODUCES APPLICATION_JSON WSMETHOD POST retdados ; DESCRIPTION "Retorna as Informações do Gráfico" ; WSSYNTAX "/charts/retdados/{JsonFilter}" ; PATH "/charts/retdados"; PRODUCES APPLICATION_JSON WSMETHOD POST itemsDetails ; DESCRIPTION "Carrega o detalhamento do gráfico" ; WSSYNTAX "/charts/itemsDetails/{JsonFilter}" ; PATH "/charts/itemsDetails"; PRODUCES APPLICATION_JSON ENDWSRESTFUL //------------------------------------------------------------------- /*/{Protheus.doc} GET form Retorna os campos que apresentados no Cadastro do Grafico. Devera seguir o formato do Dynamic Form do PO-UI. https://po-ui.io/tools/dynamic-form @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD GET form WSSERVICE Exemplo2 Local oResponse := JsonObject():New() Local oCoreDash := CoreDash():New() oCoreDash:SetPOForm("Tipo de Gráfico" , "charttype" , 6 , "Tipo de Gráfico" , .T., "string",; oCoreDash:SetPOCombo({{"pie","Pizza"},{"polarArea","Polar"}})) oCoreDash:SetPOForm("Datas" , "datainicio" , 6 , "Dt. Emissão De" , .T., "date") oCoreDash:SetPOForm("" , "datafim" , 6 , "Dt. Emissão Ate" , .T., "date") oResponse := oCoreDash:GetPOForm() Self:SetResponse( EncodeUtf8(oResponse:ToJson())) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} POST retdados Retorna um objeto JSON com as informações e valores do Grafico @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD POST retdados WSRECEIVE JsonFilter WSSERVICE Exemplo2 Local oResponse := JsonObject():New() Local oCoreDash := CoreDash():New() Local oJson := JsonObject():New() oJson:FromJson(DecodeUtf8(Self:GetContent())) retDados(@oResponse, oCoreDash, oJson) Self:SetResponse( EncodeUtf8(oResponse:ToJson())) oResponse := Nil FreeObj( oResponse ) oCoreDash:Destroy() FreeObj( oCoreDash ) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} POST retdados Retorna os items utilizado no Drilldown @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD POST itemsDetails WSRECEIVE JsonFilter, drillDownFilter WSRESTFUL Exemplo2 Local aHeader := {} Local aItems := {} Local aRet := {} Local cBody := DecodeUtf8(Self:GetContent()) Local cError := "Erro na Requisição" Local cSelect := "" Local cWhere := "" Local lRet := .T. Local oCoreDash := CoreDash():New() Local oBody := JsonObject():New() Local oJsonFilter := JsonObject():New() Local oJsonDD := JsonObject():New() If !Empty(cBody) oBody:FromJson(cBody) If ValType(oBody["chartFilter"]) == "J" oJsonFilter := oBody["chartFilter"] EndIf If ValType(oBody["detailFilter"]) == "A" oJsonDD := oBody["detailFilter"] EndIf EndIf Self:SetContentType("application/json") If oJsonFilter:GetJsonText("level") == "null" .Or. Len(oJsonFilter["level"]) == 0 If Len(oJsonDD) == 0 //Nivel 1 do Drilldown aHeader := {; {"document" , "Documento" ,"link" },; {"series" , "Serie" },; {"state" , "UF" },; {"documentType" , "Tipo" },; {"dateOfIssue" , "Emissão" }; } aItems := {; {"document" , "F2_DOC" },; {"series" , "F2_SERIE" },; {"state" , "F2_EST" },; {"documentType" , "F2_TIPO" },; {"dateOfIssue" , "F2_EMISSAO" }; } cSelect := " F2_DOC, F2_SERIE, F2_EST, F2_TIPO, F2_EMISSAO, F2_TIPO " aRet := QuerySF2(cSelect) Elseif Len(oJsonDD) == 1 //Nivel 2 do Drilldown aHeader := {; {"D2_ITEM" , "Item" },; {"D2_COD" , "Cod. Produto", "link" },; {"B1_DESC" , "Desc. Produto " },; {"D2_PRCVEN", "Vlr.Unitario", "currency","BRL" },; {"D2_QUANT" , "Quantidade","number",'1.2-5' },; {"D2_TOTAL" , "Vlr. Total", "currency","BRL" },; {"D2_LOCAL" , "Armazem" }; } aItems := {; {"D2_ITEM" , "D2_ITEM"},; {"D2_COD" , "D2_COD"},; {"B1_DESC" , "B1_DESC"},; {"D2_PRCVEN" , "D2_PRCVEN"},; {"D2_QUANT" , "D2_QUANT"},; {"D2_TOTAL" , "D2_TOTAL"},; {"D2_LOCAL" , "D2_LOCAL"}; } cSelect := " D2_ITEM, D2_COD, B1_DESC, D2_PRCVEN, D2_QUANT, D2_TOTAL, D2_LOCAL " cWhere := " D2_DOC = '" + oJsonDD[1]['document'] + "' AND D2_SERIE = '" + oJsonDD[1]['series'] + "' " aRet := QuerySD2(cSelect, cWhere) Elseif Len(oJsonDD) == 2 //Nivel 3 do Drilldown aHeader := {; {"B1_COD" , "Cod. Produto" },; {"B1_DESC" , "Desc. Produto " },; {"B1_PRV1" , "Preço de Venda", "currency","BRL" },; {"B1_TE" , "TES Entrada Pad" },; {"B1_TS" , "TES Saída Pad" }; } aItems := {; {"B1_COD" , "B1_COD"},; {"B1_DESC" , "B1_DESC"},; {"B1_PRV1" , "B1_PRV1"},; {"B1_TE" , "B1_TE"},; {"B1_TS" , "B1_TS"}; } cSelect := " B1_COD, B1_DESC, B1_PRV1, B1_TE, B1_TS " cWhere := " B1_COD = '" + oJsonDD[2]['D2_COD'] + "' " aRet := QuerySB1(cSelect, cWhere) Endif Else //Monta o Drilldown do grafico nivel 2 aHeader := {; {"document" , "Documento" },; {"series" , "Serie" },; {"state" , "UF" },; {"documentType" , "Tipo" },; {"dateOfIssue" , "Emissão" }; } aItems := {; {"document" , "F2_DOC" },; {"series" , "F2_SERIE" },; {"state" , "F2_EST" },; {"documentType" , "F2_TIPO" },; {"dateOfIssue" , "F2_EMISSAO" }; } cSelect := " F2_DOC, F2_SERIE, F2_EST, F2_TIPO, F2_EMISSAO, F2_TIPO " aRet := QuerySF2(cSelect) EndIf oCoreDash:SetQuery(aRet[1]) oCoreDash:SetWhere(aRet[2]) oCoreDash:SetFields(aItems) oCoreDash:SetApiQstring(Self:aQueryString) oCoreDash:BuildJson() If lRet oCoreDash:SetPOHeader(aHeader) Self:SetResponse( oCoreDash:ToObjectJson() ) Else cError := oCoreDash:GetJsonError() SetRestFault( 500, EncodeUtf8(cError) ) EndIf oCoreDash:Destroy() FreeObj(oJsonDD) FreeObj(oJsonFilter) FreeObj(oBody) aSize(aRet, 0) aSize(aItems, 0) aSize(aHeader, 0) Return lRet //------------------------------------------------------------------- /*/{Protheus.doc} retDados Retorna os dados que poderão ser apresentados no Grafico @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- Static Function retDados(oResponse, oCoreDash, oJson) Local aData := {} Local aDataFim := {} Local aCab := {} Local aCores := oCoreDash:GetColorChart() Local nLoop := 0 If oJson:GetJsonText("level") == "null" .Or. Len(oJson["level"]) == 0 aCab := GetUF() For nLoop := 1 To Len(aCab) aAdd(aData, Randomize(1,999) ) Next nLoop aDataFim := {} aAdd(aDataFim, oCoreDash:SetChart(aCab,aData,/*lCurrency*/,,"Nota fiscal por Estado")) ElseIf Len(oJson["level"]) == 1 aCab := GetTipoNF() For nLoop := 1 To Len(aCab) aAdd(aData, Randomize(1,999) ) Next nLoop aAdd(aDataFim, oCoreDash:SetChart(aCab,aData,/*lCurrency*/,"pie","Quantidade de Notas por Tipos")) ElseIf Len(oJson["level"]) == 2 aCab := GetTopCli() For nLoop := 1 To Len(aCab) aAdd(aData, Randomize(1, 999) ) Next nLoop oCoreDash:SetChartInfo( aData, 'Notas', 'bar', aCores[8][3] ) //Cor utilizada: OrangeLht aAdd(aDataFim, oCoreDash:SetChart(aCab,,/*lCurrency*/,"bar","TOP 10 Clientes por Tipo de Nota")) Endif oResponse["items"] := aDataFim Return Nil //------------------------------------------------------------------- /*/{Protheus.doc} QuerySF2 Monta a query responsável por trazer os registro da Nota fiscal @param cSelect, Caractere, Campos que serão retornados no SELECT @param cFilter, Caractere, Filtro complementar utilizado na clausula WHERE @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- Static Function QuerySF2(cSelect, cFilter ) Local cQuery Local cWhere Default cSelect := " SF2.F2_DOC, SF2.F2_SERIE, SF2.F2_CLIENTE, SF2.F2_LOJA, SF2.F2_TIPO " Default cFilter := "" cQuery := " SELECT " + cSelect + " FROM " + RetSqlName("SF2") + " SF2 " cWhere := " SF2.F2_FILIAL = '" + xFilial("SF2") + "' " If !Empty(cFilter) cWhere += " AND " + cFilter Endif cWhere += " AND SF2.D_E_L_E_T_ = ' ' " Return { cQuery, cWhere } //------------------------------------------------------------------- /*/{Protheus.doc} QuerySD2 Monta a query responsável por trazer os registro da Nota fiscal @param cSelect, Caractere, Campos que serão retornados no SELECT @param cFilter, Caractere, Filtro complementar utilizado na clausula WHERE @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- Static Function QuerySD2(cSelect, cFilter) Local cQuery Local cWhere Default cSelect := " SD2.D2_DOC, SD2.D2_ITEM, SD2.D2_COD " Default cFilter := "" cQuery := " SELECT " + cSelect + " FROM " + RetSqlName("SD2") + " SD2 " cQuery += " INNER JOIN " + RetSqlName("SB1") + " SB1 " cQuery += " ON B1_COD = D2_COD AND SB1.D_E_L_E_T_ = ' ' AND B1_FILIAL = '" + xFilial("SB1") + "' " cWhere := " SD2.D2_FILIAL = '" + xFilial("SD2") + "' " If !Empty(cFilter) cWhere += " AND " + cFilter Endif cWhere += " AND SD2.D_E_L_E_T_ = ' ' " Return {cQuery, cWhere} //------------------------------------------------------------------- /*/{Protheus.doc} QuerySB1 Monta a query responsável por trazer o registro do Cad. Produto @param cSelect, Caractere, Campos que serão retornados no SELECT @param cFilter, Caractere, Filtro complementar utilizado na clausula WHERE @author Squad CRM & Faturamento @since 08/06/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function QuerySB1(cSelect, cFilter) Local cQuery Local cWhere Default cSelect := " SB1.B1_COD, SB1.B1_DESC " Default cFilter := "" cQuery := " SELECT " + cSelect + " FROM " + RetSqlName("SB1") + " SB1 " cWhere := " SB1.B1_FILIAL = '" + xFilial("SB1") + "' " If !Empty(cFilter) cWhere += " AND " + cFilter Endif cWhere += " AND SB1.D_E_L_E_T_ = ' ' " Return {cQuery, cWhere} //------------------------------------------------------------------- /*/{Protheus.doc} GetUF Retorna UF @author Aline Navarro @since 22/12/2020 @version 12.1.27 /*/ //------------------------------------------------------------------- Static Function GetUF() Local aUF := {} Local aUFX5 := {} Local nX := 0 Local nLenAUFX5 := 0 aUFX5 := FWGetSX5("12") nLenAUFX5 := Len(aUFX5) For nX := 1 To nLenAUFX5 aAdd(aUF,RTrim(aUFX5[nX,3])) //X5_CHAVE Next nX Return aUF
//------------------------------------------------------------------- /*/{Protheus.doc} GetTipoNF Retorna os tipos de NF @author Rafael Mota Previdi @since 08/06/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function GetTipoNF()
Local aTipoNF := {}
aAdd(aTipoNF, 'N=Normal') aAdd(aTipoNF, 'C=Compl.Preco/Quantidade') aAdd(aTipoNF, 'I=Compl.ICMS') aAdd(aTipoNF, 'P=Compl.IPI') aAdd(aTipoNF, 'D=Dev.Compras') aAdd(aTipoNF, 'B=Utiliza Fornecedor')
Return aTipoNF |
|
Disponibiliza uma API REST no Dashboard para visualizar um gráfico de Barra ou Linha. Com o fonte abaixo foi obtido o seguinte Gráfico de exemplo:

Para disponibilização de gráficos em barra/linha, é necessário que a API possua 3 endpoints:
1. Um GET para retornar o formulário de cadastro do gráfico: No exemplo abaixo, essas informações são retornadas pelo endpoint /Exemplo/charts/form. 
Diferente dos cards, o gráfico possui um formulário dinâmico e é o serviço que deve retornar o formulário de cadastro dele. Neste exemplo, ele nos retornou o formulário a partir da linha 'Tipo de Gráfico'. Tudo o que vem antes dessa linha, é padrão para todos os gráficos. O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"divider": string,
"gridSmColumns": number,
"type": string,
"property": string,
"gridColumns": number,
"required": boolean,
"label": string
}
]
} |
A propriedade items deve retornar a lista com as configurações de todos os campos que devem ser apresentados no formulário. Para a montagem deste formulário, é utilizado o componente Dynamic Form do PO-ui, portanto para entender cada uma das propriedades a serem retornadas neste serviço, clique aqui. |
{
"items": [
{
"divider": "Tipo de Gráfico",
"gridSmColumns": 12,
"options": [
{
"value": "line",
"label": "Linha"
},
{
"value": "bar",
"label": "Barra"
}
],
"type": "string",
"property": "charttype",
"gridColumns": 6,
"required": true,
"label": "Tipo de Gráfico"
},
{
"divider": "Filtros",
"gridSmColumns": 12,
"type": "date",
"property": "dateIni",
"gridColumns": 6,
"required": true,
"label": "Data Inicial"
},
{
"gridSmColumns": 12,
"type": "date",
"property": "dateFim",
"gridColumns": 6,
"required": true,
"label": "Data Final"
}
]
} |
|
2. Um POST para carregar as informações do gráfico: No exemplo abaixo, essas informações são retornadas pelo endpoint /Exemplo3/charts/retdados. 
O endpoint deve retornar a seguinte estrutura json: {
"items": [
{
"chartData": [
{
"data": [ Valores a serem apresentados ],
"fill": false,
"drillDown": true,
"backgroundColor": "rgba(255,162, 54, 1)" - Cor da linha,
"hoverBackgroundColor": "rgba(255,162, 54, 1)" - Cor da linha ao passar o mouse,
"label": string - Título que representa o conjunto de informação e que irá aparecer na legenda,
"lineTension": number - Tenão da curva da linha. Caso for 0, a linha será reta
}
],
"chartLabels": [ Títulos correspondentes aos valores que serão apresentados ],
"charttype": string - Tipo de gráfico,
"title": string - Título do gráfico,
"chartMask": string,
"currencyMask": [ Máscaras utilizadas para apresentar valores ]
}
]
} |
A propriedade items deve retornar as informações do gráfico como valores a serem apresentados, títulos a representarem esses valores, título do gráfico e máscara de valores a ser utilizada. O gráfico utiliza a biblioteca chart.js. Para mais informações sobre suas propriedades, clique aqui. |
{
"items": [
{
"chartData": [
{
"data": [
100006444,
100026546,
100015701
],
"fill": false,
"drillDown": true,
"backgroundColor": "rgba(255,162, 54, 1)",
"hoverBackgroundColor": "rgba(255,162, 54, 1)",
"label": "Vendedor 01",
"lineTension": 0
},
{
"data": [
100009979,
100024524,
100029629
],
"fill": false,
"drillDown": true,
"backgroundColor": "rgba( 0,178,142, 1)",
"hoverBackgroundColor": "rgba( 0,178,142, 1)",
"label": "Vendedor 02",
"lineTension": 0
},
{
"data": [
100023984,
100019381,
100026231
],
"fill": false,
"drillDown": true,
"backgroundColor": "rgba(255,212,100, 1)",
"hoverBackgroundColor": "rgba(255,212,100, 1)",
"label": "Vendedor 03",
"lineTension": 0
},
{
"data": [
100028268,
100031184,
100010768
],
"fill": false,
"drillDown": true,
"backgroundColor": "rgba(128, 0, 0, 1)",
"hoverBackgroundColor": "rgba(128, 0, 0, 1)",
"label": "Vendedor 04",
"lineTension": 0
},
{
"data": [
100017168.75,
100025408.75,
100020582.25
],
"type": "line",
"fill": false,
"drillDown": false,
"backgroundColor": "rgba( 0,120,255, 1)",
"hoverBackgroundColor": "rgba( 0,120,255, 1)",
"label": "Média",
"lineTension": 0
}
],
"chartLabels": [
"Janeiro",
"Fevereiro",
"Março"
],
"charttype": "",
"title": null,
"chartMask": "currency",
"currencyMask": [
{
"maskFrac": "R$",
"maxiFrac": 10,
"miniFrac": 2
}
]
}
]
} |
|
3. Um POST para carregar as informações que serão apresentadas nos detalhes do gráfico: No exemplo abaixo, essas informações são retornadas pelo endpoint /Exemplo3/charts/itemsDetails. 
O endpoint deve retornar a seguinte estrutura json: {
"header":[
{
"showFilter": boolean,
"property": string,
"label":string
}
],
"items":[
{
"property": <Valor retornado pela query>,
}
],
"hasNext": boolean
} |
A propriedade header refere-se ao cabeçalho da tabela. Como a tabela utilizada pelo dashboard é um componente da biblioteca PO-UI (po-table), deve-se informar as propriedades obrigatórias para este componente: property (nome identificador para a coluna) e label (título para a coluna). Para mais informações, clique aqui. A propriedade items refere-se aos valores que serão apresentados nas colunas da tabela, ou seja, para cada propriedade informada, a query irá retornar um valor. Exemplo: { "totalValor": }. Este deve ter a propriedade "hasNext" para informar se há próxima página (true) ou não (false). |
{
"header": [
{
"showFilter": true,
"type": "link",
"property": "codigo",
"label": "Código",
"action": "Link"
},
{
"showFilter": true,
"property": "nome",
"label": "Nome Vendedor"
},
{
"showFilter": false,
"format": "1.2-5",
"type": "number",
"property": "totalItens",
"label": "Total de Itens"
},
{
"showFilter": false,
"format": "BRL",
"type": "currency",
"property": "totalValor",
"label": "Valor Total"
}
],
"items": [
{
"totalValor": 2000,
"totalItens": 1,
"nome": "10% COMIS 100%EMISSAO",
"codigo": "FIN058"
},
{
"totalValor": 2000,
"totalItens": 1,
"nome": "10% COMISSAO 100% BAIXA ABATE IMPOSTOS",
"codigo": "FIN056"
},
{
"totalValor": 2000,
"totalItens": 1,
"nome": "10% COMISSAO 100% BAIXA ABATE IMPOSTOS",
"codigo": "FIN060"
},
{
"totalValor": 2000,
"totalItens": 1,
"nome": "10% COMISSAO 100% EMISSAO ABATE IMPOSTOS",
"codigo": "FIN055"
},
{
"totalValor": 2000,
"totalItens": 1,
"nome": "10% COMISSAO 100% EMISSAO ABATE IMPOSTOS",
"codigo": "FIN059"
},
{
"totalValor": 5000,
"totalItens": 4,
"nome": "100% BAIXA 10%COMISSAO",
"codigo": "FVEN10"
},
{
"totalValor": 1000,
"totalItens": 1,
"nome": "100%baixa 0%comissao",
"codigo": "FIN048"
},
{
"totalValor": 2000,
"totalItens": 2,
"nome": "100%emissao 0%comissao",
"codigo": "FIN047"
},
{
"totalValor": 200000,
"totalItens": 2,
"nome": "5% COMISSAO 100% BAIXA",
"codigo": "FIN600"
},
{
"totalValor": 1000,
"totalItens": 1,
"nome": "50 50 - 10%",
"codigo": "FIN073"
}
],
"hasNext": true
}
|
|
|
#INCLUDE "TOTVS.CH" #INCLUDE "RESTFUL.CH" //------------------------------------------------------------------------ /*/{Protheus.doc} Exemplo3 Exemplo de API de integração de Graficos de Barra e Linha @author Squad CRM & Faturamento @since 28/07/2020 @version 12.1.27 /*/ //------------------------------------------------------------------------ WSRESTFUL Exemplo3 DESCRIPTION "Exemplo de API - Grafico Barra e Linha" WSDATA JsonFilter AS STRING OPTIONAL WSDATA drillDownFilter AS STRING OPTIONAL WSDATA Fields AS STRING OPTIONAL WSDATA Order AS STRING OPTIONAL WSDATA Page AS INTEGER OPTIONAL WSDATA PageSize AS INTEGER OPTIONAL WSMETHOD GET form ; DESCRIPTION "Formulario de Cadastro do Gráfico" ; WSSYNTAX "/charts/form/" ; PATH "/charts/form"; PRODUCES APPLICATION_JSON WSMETHOD POST retDados ; DESCRIPTION "Deverá retornar as informações apresentadas no gráfico." ; WSSYNTAX "/charts/retDados/{JsonFilter}" ; PATH "/charts/retDados"; PRODUCES APPLICATION_JSON WSMETHOD POST itemsDetails ; DESCRIPTION "Carrega o detalhamento do gráfico" ; WSSYNTAX "/charts/itemsDetails/{JsonFilter}" ; PATH "/charts/itemsDetails"; PRODUCES APPLICATION_JSON ENDWSRESTFUL //------------------------------------------------------------------- /*/{Protheus.doc} GET form Retorna os campos que serão apresentados no formulário. O padrão do campo deve seguir o Dynamic Form do Portinari. @author Squad CRM & Faturamento @since 27/03/2020 @version Protheus 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD GET form WSSERVICE Exemplo3 Local oResponse := JsonObject():New() Local oCoreDash := CoreDash():New() oCoreDash:SetPOForm("Tipo de Gráfico", "charttype" , 6, "Tipo de Gráfico" , .T., "string" , oCoreDash:SetPOCombo({{"line","Linha"}, {"bar","Barra"}})) oCoreDash:SetPOForm("Filtros" , "dateIni" , 6, "Data Inicial" , .T., 'date' , , .T.) oCoreDash:SetPOForm("" , "dateFim" , 6, "Data Final" , .T., 'date' , , .T.) oCoreDash:SetPOForm("" , "person" , 6, "Pessoa" , .F., 'string' , oCoreDash:SetPOCombo({{"F","Física"} , {"J","Jurídica"}}), .T.) oCoreDash:SetPOForm("" , "blocked" , 6, "Bloqueado?" , .F., 'string' , oCoreDash:SetPOCombo({{"1","Sim"} , {"2","Não"}}) , .F.) oResponse := oCoreDash:GetPOForm() Self:SetResponse( EncodeUtf8(oResponse:ToJson())) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} POST retDados Retorna os dados do Gráfico @author Squad CRM & Faturamento @since 27/03/2020 @version Protheus 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD POST retDados WSRECEIVE JsonFilter WSSERVICE Exemplo3 Local oResponse := JsonObject():New() Local oCoreDash := CoreDash():New() Local oJson := JsonObject():New() oJson:FromJson(DecodeUtf8(Self:GetContent())) retDados(@oResponse, oCoreDash, oJson) Self:SetResponse( EncodeUtf8(oResponse:ToJson())) oResponse := Nil FreeObj( oResponse ) oCoreDash:Destroy() FreeObj( oCoreDash ) Return .T. //------------------------------------------------------------------- /*/{Protheus.doc} Function retDados Retorna o valor das Meta e o Valor Vendido de acordo com parâmetros informados @author Squad CRM & Faturamento @since 16/05/2022 @version Protheus 12.1.33 /*/ //------------------------------------------------------------------- Static Function retDados(oResponse, oCoreDash, oJson) Local aData := {} Local aDataFim := {} Local aData1 := {} Local aData2 := {} Local aData3 := {} Local aData4 := {} Local aData5 := {} Local aCab := {} Local aCores := oCoreDash:GetColorChart() Local aSaldo := {} Local nSaldo := 0 Local cPessoa := "" Local cFiltros := "" Local nFilter := 0 Local nCntFilt := 0 If oJson:GetJsonText("level") == "null" .Or. Len(oJson["level"]) == 0 aCab := {'SP', 'RJ', 'MG' }
//######### Obter os filtros do cadastro ou de usuário e aplicar na query ######### // Filtro por tipo da pessoa - Com Multi-seleção If oJson:HasProperty("person") .And. ValType(oJson["person"]) == "A" cPessoa := oJson["person"] nCntFilt := Len(oJson["person"]) For nFilter := 1 To nCntFilt If nFilter == 1 cFiltros += " AND (" Else cFiltros += " OR " EndIf cFiltros += "SA1.A1_PESSOA = '" + oJson["person"][nFilter] + "' "
If nFilter == nCntFilt cFiltros += ") " EndIf Next EndIf
// Filtro por tipo da pessoa - com uma única seleção If oJson:HasProperty("blocked") .And. ValType(oJson["blocked"]) == "C" cFiltros += "AND SA1.A1_MSBLQL = '" + oJson["blocked"] + "'" EndIf
//######### Fim da obtenção os filtros do cadastro ou de usuário e aplicar na query ######### aData1 := { RetRisco(cFiltros + " AND SA1.A1_RISCO = 'A' AND SA1.A1_EST = 'SP'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'A' AND SA1.A1_EST = 'RJ'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'A' AND SA1.A1_EST = 'MG'") } aData2 := { RetRisco(cFiltros + " AND SA1.A1_RISCO = 'B' AND SA1.A1_EST = 'SP'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'B' AND SA1.A1_EST = 'RJ'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'B' AND SA1.A1_EST = 'MG'") } aData3 := { RetRisco(cFiltros + " AND SA1.A1_RISCO = 'C' AND SA1.A1_EST = 'SP'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'C' AND SA1.A1_EST = 'RJ'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'C' AND SA1.A1_EST = 'MG'") } aData4 := { RetRisco(cFiltros + " AND SA1.A1_RISCO = 'D' AND SA1.A1_EST = 'SP'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'D' AND SA1.A1_EST = 'RJ'"), RetRisco(cFiltros + " AND SA1.A1_RISCO = 'D' AND SA1.A1_EST = 'MG'") } oCoreDash:SetChartInfo( aData1, 'Risco A', , aCores[8][3] ) //Cor utilizada: OrangeLht oCoreDash:SetChartInfo( aData2, 'Risco B', , aCores[1][3] ) //Cor utilizada: GreenDk oCoreDash:SetChartInfo( aData3, 'Risco C', , aCores[6][3] ) //Cor utilizada: YellowDk oCoreDash:SetChartInfo( aData4, 'Risco D', , aCores[15][3] ) //Cor utilizada: BrowDk nSaldo := (aData1[1] + aData2[1] + aData3[1] + aData4[1] ) / 4 aAdd(aSaldo, nSaldo) nSaldo := (aData1[2] + aData2[2] + aData3[2] + aData4[2] ) / 4 aAdd(aSaldo, nSaldo) nSaldo := (aData1[3] + aData2[3] + aData3[3] + aData4[3] ) / 4 aAdd(aSaldo, nSaldo) oCoreDash:SetChartInfo( aSaldo, 'Média', "line", aCores[9][3] ,,.F.) //Cor utilizada: BlueDk aDataFim := {} aAdd(aDataFim, oCoreDash:SetChart(aCab,,.F., ,"Quantidade de Clientes por Risco em cada Estado")) ElseIf Len(oJson["level"]) == 1 aCab := {'Semana 1', 'Semana 2', 'Semana 3', 'Semana 4' } aData1 := { Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 )} aData2 := { Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 )} aData3 := { Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 )} aData4 := { Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 )} aData5 := { Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 ), Randomize( 100000000.00, 999000000.00 )} oCoreDash:SetChartInfo( aData1, '00000101 - Cliente 000001',,aCores[8][3] ) //Cor utilizada: OrangeLht oCoreDash:SetChartInfo( aData2, '00000102 - Cliente 000002',,aCores[1][3] ) //Cor utilizada: GreenDk oCoreDash:SetChartInfo( aData3, '00543501 - Cliente 000003',,aCores[6][3] ) //Cor utilizada: YellowDk oCoreDash:SetChartInfo( aData4, '00543502 - Cliente 000004',,aCores[15][3] ) //Cor utilizada: BrowDk oCoreDash:SetChartInfo( aData5, '00543503 - Cliente 000005',,aCores[10][3] ) //Cor utilizada: BlueLht aDataFim := {} aAdd(aDataFim, oCoreDash:SetChart(aCab,,.T., ,"Maiores Vendas - " + oJson["level"][1]["labelDataSet"] + " - " + oJson["level"][1]["label"])) ElseIf Len(oJson["level"]) == 2 aCab := {"Pedido - 234323", "Pedido - 234322", "Pedido - 234456", "Pedido - 234533", "Pedido - 234222" }
oData := JsonObject():New() aData := { 26589, 25000,23560,10000,35000} aDataFim := {} aAdd(aDataFim, oCoreDash:SetChart(aCab, aData, .T.,"pie", "Maiores Clientes - " + oJson["level"][2]["labelDataSet"] + " - " + oJson["level"][1]["label"])) EndIf oResponse["items"] := aDataFim Return //------------------------------------------------------------------- /*/{Protheus.doc} POST itemsDetails Método para retornar os dados do Painel @author Squad CRM & Faturamento @since 27/03/2020 @version Protheus 12.1.27 /*/ //------------------------------------------------------------------- WSMETHOD POST itemsDetails WSRECEIVE JsonFilter, drillDownFilter WSRESTFUL Exemplo3 Local aHeader := {} Local aItems := {} Local aRet := {} Local cBody := DecodeUtf8(Self:GetContent()) Local cError := "Erro na Requisição" Local lRet := .T. Local oCoreDash := CoreDash():New() Local oBody := JsonObject():New() Local oJsonFilter := JsonObject():New() Local oJsonDD := JsonObject():New() If !Empty(cBody) oBody:FromJson(cBody) If ValType(oBody["chartFilter"]) == "J" oJsonFilter := oBody["chartFilter"] EndIf If ValType(oBody["detailFilter"]) == "A" oJsonDD := oBody["detailFilter"] EndIf EndIf Self:SetContentType("application/json") //Verifico o Nivel do grafico If oJsonFilter:GetJsonText("level") == "null" .Or. Len(oJsonFilter["level"]) == 0 //Verifico o nivel do Drilldpwn If Len(oJsonDD) == 0 aHeader := {; {"codigo" , "Código" ,"link" },; {"nome" , "Nome Vendedor" },; {"totalItens" , "Total de Itens","number",'1.2-5',.F. },; {"totalValor" , "Valor Total" , "currency","BRL",.F.}; } aItems := {; {"codigo" , "SA3.A3_COD" },; {"nome" , "SA3.A3_NOME" },; {"totalItens" , "QTDITEM" },; {"totalValor" , "TOTAL","N" }; } aRet := MntQuery1() ElseIf Len(oJsonDD) == 1 //Caso eu queira pegar o nome do nível selecionado : oJsonFilter["level"][1]["labelDataSet"] // Se fosse gráfico do tipo pizza: oJsonFilter["level"][1]["label"] aHeader := {; {"codigoPed" , "Código do Pedido" },; {"codigoCli" , "Código do Cliente" },; {"nome" , "Nome" },; {"totalValor" , "TOTAL","currency","BRL",.F.}; } aItems := {; {"codigoPed" , "SC5.C5_NUM" },; {"codigoCli" , "SC5.C5_CLIENTE" },; {"nome" , "SA1.A1_NOME" },; {"totalValor" , "TOTAL","N" }; } aRet := MntQuery2("SC5.C5_VEND1 = '" + oJsonDD[1]["codigo"] + "'") EndIf ElseIf Len(oJsonFilter["level"]) == 1 aHeader := {; {"codigoPed" , "Código do Pedido" },; {"codigoCli" , "Código do Cliente" },; {"nome" , "Nome" },; {"totalValor" , "TOTAL","currency","BRL",.F.}; } aItems := {; {"codigoPed" , "SC5.C5_NUM" },; {"codigoCli" , "SC5.C5_CLIENTE" },; {"nome" , "SA1.A1_NOME" },; {"totalValor" , "TOTAL","N" }; } aRet := MntQuery2("SA1.A1_RISCO = '" + Right(oJsonFilter["level"][1]["labelDataSet"],1) + "' AND SA1.A1_EST = '" + oJsonFilter["level"][1]["label"] + "' ") ElseIf Len(oJsonFilter["level"]) == 2 aHeader := {; {"codigo" , "Código" },; {"nome" , "Nome Vendedor" },; {"totalItens" , "Total de Itens","number",'1.2-5',.F.},; {"totalValor" , "Valor Total", "currency","BRL",.F.}; } aItems := {; {"codigo" , "SA3.A3_COD" },; {"nome" , "SA3.A3_NOME" },; {"totalItens" , "QTDITEM" },; {"totalValor" , "TOTAL","N" }; } aRet := MntQuery1() EndIf oCoreDash:SetQuery(aRet[1]) oCoreDash:SetWhere(aRet[2]) oCoreDash:SetGroupBy(aRet[3]) oCoreDash:SetFields(aItems) oCoreDash:SetApiQstring(Self:aQueryString) oCoreDash:BuildJson() If lRet oCoreDash:SetPOHeader(aHeader) Self:SetResponse( oCoreDash:ToObjectJson() ) Else cError := oCoreDash:GetJsonError() SetRestFault( 500, EncodeUtf8(cError) ) EndIf oCoreDash:Destroy() FreeObj(oBody) FreeObj(oJsonFilter) FreeObj(oJsonDD) FreeObj(oCoreDash) aSize(aRet, 0) aSize(aItems, 0) aSize(aHeader, 0) Return( lRet ) //------------------------------------------------------------------- /*/{Protheus.doc} MntQuery1 Monta a Query o Total dos Pedidos de Venda @author Squad CRM & Faturamento @since 27/03/2020 @version Protheus 12.1.27 /*/ //------------------------------------------------------------------- Static Function MntQuery1() Local cQuery := "" Local cWhere := "" Local cGroup := "" cQuery := " SELECT SA3.A3_COD, SA3.A3_NOME, COUNT(C6_ITEM) QTDITEM, SUM(C6_VALOR) TOTAL " cQuery += " FROM " + RetSqlName("SC5") + " SC5 " cQuery += " INNER JOIN " + RetSqlName("SC6") + " SC6 ON SC6.C6_NUM = SC5.C5_NUM " cQuery += " INNER JOIN " + RetSqlName("SA3") + " SA3 ON SA3.A3_COD = SC5.C5_VEND1 " cWhere := " SC5.D_E_L_E_T_ = ' ' " cWhere += " AND SC6.D_E_L_E_T_ = ' ' " cWhere += " AND SA3.D_E_L_E_T_ = ' ' " cWhere += " AND SC5.C5_FILIAL = '" + xFilial("SC5") + "' " cWhere += " AND SC6.C6_FILIAL = '" + xFilial("SC6") + "' " cWhere += " AND SA3.A3_FILIAL = '" + xFilial("SA3") + "' " cWhere += " AND SC5.C5_EMISSAO >= '20220401' " cGroup := " SA3.A3_COD, SA3.A3_NOME " Return {cQuery, cWhere, cGroup} //------------------------------------------------------------------- /*/{Protheus.doc} MntQuery2 Monta Query do Pedido de vendas realizando um filtro específo. @author Squad CRM & Faturamento @since 27/03/2020 @version Protheus 12.1.27 /*/ //------------------------------------------------------------------- Static Function MntQuery2(cFilter) Local cQuery := "" Local cGroup := "" Local cWhere := "" Default cFilter := "" cQuery := " SELECT SC5.C5_NUM, SC5.C5_CLIENTE, SA1.A1_NOME, SUM(C6_VALOR) TOTAL " cQuery += " FROM " + RetSqlName("SC5") + " SC5 " cQuery += " INNER JOIN " + RetSqlName("SC6") + " SC6 ON SC6.C6_NUM = SC5.C5_NUM " cQuery += " INNER JOIN " + RetSqlName("SA1") + " SA1 ON SA1.A1_COD = SC5.C5_CLIENTE if !Empty(cFilter) cWhere += cFilter + " AND " EndIf cWhere += " SC5.D_E_L_E_T_ = ' ' " cWhere += " AND SC6.D_E_L_E_T_ = ' ' " cWhere += " AND SA1.D_E_L_E_T_ = ' ' " cWhere += " AND SC5.C5_FILIAL = '" + xFilial("SC5") + "' " cWhere += " AND SC6.C6_FILIAL = '" + xFilial("SC6") + "' " cWhere += " AND SA1.A1_FILIAL = '" + xFilial("SA1") + "' " cWhere += " AND SC5.C5_EMISSAO >= '20220401' " // Limite adicionado para evitar lentidao no retorno da query em bases com uma quantidade muito grande de pedidos de vendas cGroup := " C5_NUM, C5_CLIENTE, SA1.A1_NOME " Return {cQuery, cWhere, cGroup} //------------------------------------------------------------------- /*/{Protheus.doc} RetRisco Retorna o total de clientes de acordo com o risco informado no filtro @param cFiltro, Caractere, Filtro a ser adicionado na query @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function RetRisco(cFiltro) Local aQuery := MntQuery3("COUNT(SA1.A1_COD) TOTAL_REGISTROS", cFiltro) Local cQuery := "" Local cTemp := GetNextAlias() Local xRet Default cWhere := "" Default cInfo := "" cQuery := aQuery[1] + " WHERE " + aQuery[2] DBUseArea( .T., "TOPCONN", TCGenQry( ,, cQuery ), cTemp, .T., .T. ) xRet := (cTemp)->TOTAL_REGISTROS (cTemp)->( DBCloseArea() ) Return xRet //------------------------------------------------------------------- /*/{Protheus.doc} MntQuery3 Monta a query responsável por trazer os clientes de acordo com o risco informado no filtro @param cCampos, Caractere, Campos que serão retornados no SELECT @param cFiltro, Caractere, Filtro a ser adicionado na query @param cGroupBy, Caractere, Expressão group by a ser adicionada na query @author Squad CRM & Faturamento @since 13/04/2022 @version 12.1.33 /*/ //------------------------------------------------------------------- Static Function MntQuery3(cCampos, cFiltro, cGroupBy) Local cQuery Local cWhere Local cGroup Default cTable := "SA1" Default cCampos := "SA1.A1_COD, SA1.A1_LOJA, SA1.A1_NOME, SA1.A1_NREDUZ, SA1.A1_RISCO" cQuery := " SELECT " + cCampos + " FROM " + RetSqlName("SA1") + " SA1 " cWhere := " SA1.A1_FILIAL = '" + xFilial("SA1") + "'" + cFiltro cWhere += " AND SA1.D_E_L_E_T_ = ' ' " If !Empty(cGroupBy) cGroup := cGroupBy EndIf Return {cQuery, cWhere, cGroup} |
|
Para os serviços cujo retorno solicita a propriedade "hasNext", quer dizer que eles possuem paginação. A paginação com a utilização da classe CoreDash é obrigatória e funciona através dos parâmetros page (indica a página ser retornada) e pageSize (indica quantos registros da página devem ser retornados). Seus valores default são page = 1 e pageSize = 10. Exemplo de utilização: /Exemplo1/cards/itemsDetails?page=2&pageSize=10 |
|