Olá senhores, dando continuidade a serie sobre Rest(Post Anterior) no Protheus, hoje vou falar sobre como desenvolver o verbo GET de um API com dados do pedido de venda SC5 E SC6 trazendo paginação e utilização de parâmetros.
Pré-requisito
JsonObject disponível apenas para P12 build 131227
Código
O que me interessou a trabalhar com JsonObject ao invés de ficar montando string como alguns exemplos no TDN foram as questão de paginação e parâmetros que são imprescindíveis para esse tipo de trabalho, os parâmetros disponíveis são Page e cSearchKey.
Qualquer divida, vai de comentário!
#INCLUDE "PROTHEUS.CH"
#INCLUDE "RESTFUL.CH"
/*/{Protheus.doc} User Function SalesOrder
(Api REST para consulta de pedidos de venda)
@type Function
@author Leandro Lemos
@since 08/05/2020
@version P12
@param param_name, param_type, param_descr
@return return_var, return_type, return_description
@example
(examples)
@see (links_or_references)
/*/
WSRESTFUL SalesOrder DESCRIPTION "Api REST para consulta de pedidos de venda"
WSDATA page AS INTEGER OPTIONAL
WSDATA pageSize AS INTEGER OPTIONAL
WSDATA cSearchKey AS STRING OPTIONAL
WSMETHOD GET salesorder DESCRIPTION 'Consulta pedidos de venda' WSSYNTAX '/api/v3/salesorder' PRODUCES APPLICATION_JSON
END WSRESTFUL
//-------------------------------------------------------------------
/*/{Protheus.doc} GET / salesorder
Retorna a lista de pedidos.
@param cSearchKey , caracter, chave de pesquisa utilizada em diversos campos
Page , numerico, numero da pagina
PageSize , numerico, quantidade de registros por pagina
@return cResponse , caracter, JSON contendo a lista de pedidos
/*/
//-------------------------------------------------------------------
WSMETHOD GET salesorder WSRECEIVE cSearchKey, page, pageSize WSREST SalesOrder
Local aListSales := {}
Local aLast := {}
Local cQrySC5 := GetNextAlias()
Local cJsonCli := ''
Local cSearchKey := ''
Local cSearch := ''
Local cWhere := " AND SC5.C5_FILIAL = '"+xFilial('SC5')+"' AND SC6.C6_FILIAL = '"+xFilial('SC6')+"'"
Local cPedido := ''
Local lRet := .T.
Local nCount := 0
Local nStart := 1
Local nReg := 0
Local oJsonSales := JsonObject():New()
Default self:cSearchKey := ''
Default self:page := 1
Default self:pageSize := 100
//-------------------------------------------------------------------
// Tratativas para a chave de busca
//Existem outras maneira de trabalhar com filtro, por hora vou manter dessa forma
//-------------------------------------------------------------------
If !Empty(self:cSearchKey)
cSearch := AllTrim( Upper( Self:cSearchKey ) )
cWhere += " AND ( SC5.C5_NUM LIKE '%" + cSearch + "%' OR "
cWhere += " SC5.C5_CLIENTE LIKE '%" + cSearch + "%' OR"
cWhere += " SC6.C6_PRODUTO LIKE '%" + cSearch + "%' )"
//cWhere += " SA1.A1_NOME LIKE '%" + cSearch + "%' ) "
EndIf
cWhere := '%'+cWhere+'%'
//-------------------------------------------------------------------
// Query para selecionar pedidos
//-------------------------------------------------------------------
BeginSQL Alias cQrySC5
SELECT SC5.C5_CLIENTE,SC5.C5_LOJACLI,SC5.C5_CONDPAG,SC5.C5_TPFRETE,SC5.C5_MENNOTA,SC5.C5_NATUREZ,
SC5.C5_FILIAL ,SC5.C5_NUM,SC5.C5_LIBEROK,SC5.C5_NOTA,SC5.C5_BLQ,
C6_NUM,C6_ITEM,C6_PRODUTO,C6_DESCRI,C6_QTDVEN,C6_VALOR,C6_TES,C6_NOTA,C6_SERIE
FROM %Table:SC5% SC5
INNER JOIN SC6030 SC6 ON SC5.C5_NUM = SC6.C6_NUM
WHERE SC5.%NotDel%
AND SC6.%NotDel%
%exp:cWhere%
ORDER BY SC5.C5_NUM,SC6.C6_ITEM
EndSQL
//conout(cQrySC5)
If ( cQrySC5 )->( ! Eof() )
//-------------------------------------------------------------------
// Identifica a quantidade de registro no alias temporário
//-------------------------------------------------------------------
COUNT TO nRecord
//-------------------------------------------------------------------
// nStart -> primeiro registro da pagina
// nReg -> numero de registros do inicio da pagina ao fim do arquivo
//-------------------------------------------------------------------
If self:page > 1
nStart := ( ( self:page - 1 ) * self:pageSize ) + 1
nReg := nRecord - nStart + 1
Else
nReg := nRecord
EndIf
//-------------------------------------------------------------------
// Posiciona no primeiro registro.
//-------------------------------------------------------------------
( cQrySC5 )->( DBGoTop() )
//-------------------------------------------------------------------
// Valida a exitencia de mais paginas
//-------------------------------------------------------------------
If nReg > self:pageSize
oJsonSales['hasNext'] := .T.
Else
oJsonSales['hasNext'] := .F.
EndIf
Else
//-------------------------------------------------------------------
// Nao encontrou registros
//-------------------------------------------------------------------
oJsonSales['hasNext'] := .F.
EndIf
//-------------------------------------------------------------------
// Alimenta array de pedidos
//-------------------------------------------------------------------
While ( cQrySC5 )->( ! Eof() )
cPedido := ''
cPedido := (cQrySC5)->C5_NUM
nCount++
If nCount >= nStart
aAdd( aListSales , JsonObject():New() )
nPos := Len(aListSales)
aListSales[nPos]['NUM'] := (cQrySC5)->C5_NUM
aListSales[nPos]['CLIENTE'] := TRIM((cQrySC5)->C5_CLIENTE)
aListSales[nPos]['LOJACLI'] := TRIM((cQrySC5)->C5_LOJACLI)
aListSales[nPos]['CONDPAG'] := TRIM((cQrySC5)->C5_CONDPAG)
aListSales[nPos]['TPFRETE'] := TRIM((cQrySC5)->C5_TPFRETE)
aListSales[nPos]['MENNOTA'] := TRIM(EncodeUTF8((cQrySC5)->C5_MENNOTA))
aListSales[nPos]['NATUREZ'] := TRIM((cQrySC5)->C5_NATUREZ)
While (cPedido == (cQrySC5)->C5_NUM)
Aadd(aLast,JsonObject():new())
nPosItem := Len(aLast)
aLast[nPosItem]['NUM'] := (cQrySC5)->C5_NUM
aLast[nPosItem]['ITEM'] := (cQrySC5)->C6_ITEM
aLast[nPosItem]['PRODUT'] := TRIM((cQrySC5)->C6_PRODUTO)
aLast[nPosItem]['DESCRI'] := TRIM(EncodeUTF8(((cQrySC5)->C6_DESCRI)))
aLast[nPosItem]['QTDVEN'] := (cQrySC5)->C6_QTDVEN
aLast[nPosItem]['VALOR'] := (cQrySC5)->C6_VALOR
aLast[nPosItem]['TES'] := TRIM((cQrySC5)->C6_TES)
aLast[nPosItem]['NOTA'] := TRIM((cQrySC5)->C6_NOTA)
aLast[nPosItem]['SERIE'] := TRIM((cQrySC5)->C6_SERIE)
(cQrySC5)->(DBSkip())
End
aListSales[Len(aListSales)]['ITENS'] := aLast
aLast := {}
If Len(aListSales) >= self:pageSize
Exit
EndIf
//Se estiver buscando por paginas, sera skipado os registros até iniciar a pagina passada pelo parâmetro Page
Else
(cQrySC5)->(DBSkip())
EndIf
End
( cQrySC5 )->( DBCloseArea() )
oJsonSales['sales'] := aListSales
//-------------------------------------------------------------------
// Serializa objeto Json
//-------------------------------------------------------------------
cJsonCli:= FwJsonSerialize( oJsonSales )
//-------------------------------------------------------------------
// Elimina objeto da memoria
//-------------------------------------------------------------------
FreeObj(oJsonSales)
Self:SetResponse( cJsonCli ) //-- Seta resposta
Return( lRet )
Retorno
Para testar a API utilizo o Postman.


Fontes:https://centraldeatendimento.totvs.com/hc/pt-br/articles/360020555111-MP-ADVPL-Em-um-WS-REST-definir-a-quantidade-de-informa%C3%A7%C3%A3o-retornada-em-um-m%C3%A9todo-GET
https://tdn.totvs.com/display/tec/Classe+JsonObject
https://centraldeatendimento.totvs.com/hc/pt-br/articles/360021048151-MP-ADVPL-RESPOSTA-JSON-EM-OBJETO