Hoje vamos mostrar como incluir e consultar pedidos de venda no Protheus utilizando JSON e REST.
Primeiramente jovens, o fonte foi criado pelo Rodrigo dos Santos, uma das lendas do Black TDN . Ele publicou o código no nosso grupo AdvPLers no Discord. Se quiserem acompanhar o trabalho dele, ele está organizando o GitHub, deem um Follow lá, o cara é fera – https://github.com/rodrigomicrosiga .
Basicamente, esse é um fonte onde existem dois métodos POST, um com FWJsonDeserialize e outro com o uso de JsonObject, onde é necessário informar uma string JSON, com a seguinte parametrização:
{
"Client":"000001",
"Store":"01",
"CustomerDelivery":"000001",
"DeliveryStore":"01",
"PaymentTerms":"000",
"MessageForNote":"Mensagem para nota",
"Nature":"0000000001",
"Items":
[
{
"Product":"000000000000001",
"SalesQuantity":1,
"SalesPrice":100,
"Value":100,
"InputTypeOutPut":"501"
},
{
"Product":"000000000000002",
"SalesQuantity":1,
"SalesPrice":100,
"Value":100,
"InputTypeOutPut":"501"
}
]
}
Além dos métodos POST, tem um método GET para buscar informações do pedido.
Abaixo o código fonte completo (foi criado ID para cada método, pois existem verbos repetidos, por exemplo, existem dois POST – tem uma limitação dessa de não poder declarar ID em binários antigos antes da 12.1.17):
#include 'totvs.ch'
#include 'restful.ch'
#DEFINE LOG_DIRECTORY "\log_integ"
#DEFINE TYPE_OF_NORMAL_REQUEST "N"
#DEFINE OPEN_REQUEST "pedido em aberto"
#DEFINE APPLICATION_CLOSED "pedido encerrado"
#DEFINE REQUEST_RELEASED "pedido liberado"
#DEFINE REQUEST_BLOCKED_BY_RULE "pedido bloqueado por regra"
#DEFINE REQUEST_BLOCKED_BY_FUNDS "pedido bloqueado por verba"
/*
API para inclusão e consulta de pedidos de venda
Autor: Rodrigo dos Santos ( www.blacktdn.com.br )
OBS.: Para o release 17 é necessário possuir LIB / binário que suporte ID na declaração do método
*/
WSRESTFUL SalesOrder DESCRIPTION 'API para manutenção de pedidos de venda' SECURITY 'MATA410' FORMAT APPLICATION_JSON
WSDATA RequestNumber As Character
WSMETHOD POST SalesOrderInclusion DESCRIPTION 'Inclusão de pedidos de venda' WSSYNTAX '/SalesOrder/SalesOrderInclusion' PRODUCES APPLICATION_JSON
WSMETHOD POST OrderInclusion DESCRIPTION 'Inclusão de pedidos de venda' WSSYNTAX '/SalesOrder/OrderInclusion' PRODUCES APPLICATION_JSON
WSMETHOD GET RequestQuery DESCRIPTION 'Consulta status do pedido de venda' WSSYNTAX '/SalesOrder{RequestNumber}' PRODUCES APPLICATION_JSON
ENDWSRESTFUL
/*
SalesOrderInclusion método POST utilizando jSonObject
Autor: Rodrigo dos Santos ( www.blacktdn.com.br )
*/
WSMETHOD POST SalesOrderInclusion WSRECEIVE WSRESTFUL SalesOrder
Local lRet := .T.
Local aArea := GetArea()
Local aCabec
Local aItens
Local aLinha
Local oJson
Local oItems
Local cJson := Self:GetContent()
Local cError
Local nX
Local nY
Private lMsErroAuto := .F.
Private lMsHelpAuto := .T.
Private lAutoErrNoFile := .T.
//Se não existir o diretório de logs dentro da Protheus Data, será criado
IF .NOT. ExistDir(LOG_DIRECTORY)
MakeDir(LOG_DIRETCTORY)
EndIF
//Definindo o conteúdo como JSON, e pegando o content e dando um parse para ver se a estrutura está ok
Self:SetContentType("application/json")
oJson := JsonObject():New()
cError := oJson:FromJson(cJson)
//Se tiver algum erro no Parse, encerra a execução
IF .NOT. Empty(cError)
SetRestFault(500,'Parser Json Error')
lRet := .F.
Else
//Se encontrar o cliente existente conforme dados do JSON
DbSelectArea('SA1')
SA1->(dbSetOrder(1))
IF (SA1->(dbSeek(FWxFilial("SA1")+PadR(oJson:GetJsonObject('Client'),TamSX3("A1_COD")[1])+PadR(oJson:GetJsonObject('Store'),TamSX3("A1_LOJA")[1]))))
aCabec := {}
aItens := {}
/* removido trecho abaixo pois o cliente afirma que existe geração de numeração automatica
cPedido := GetSXENum("SC5","C5_NUM")
aAdd(aCabec,{"C5_NUM", cPedido, NIL})
*/
aAdd(aCabec,{"C5_TIPO", TYPE_OF_NORMAL_REQUEST, NIL})
aAdd(aCabec,{"C5_CLIENTE", AllTrim(oJson:GetJsonObject('Client')), NIL})
aAdd(aCabec,{"C5_LOJACLI", AllTrim(oJson:GetJsonObject('Store')), NIL})
aAdd(aCabec,{"C5_LOJAENT", AllTrim(oJson:GetJsonObject('DeliveryStore')), NIL})
aAdd(aCabec,{"C5_CONDPAG", AllTrim(oJson:GetJsonObject('PaymentTerms')), NIL})
aAdd(aCabec,{"C5_MENNOTA", AllTrim(oJson:GetJsonObject('MessageForNote')), NIL})
aAdd(aCabec,{"C5_NATUREZ", AllTrim(oJson:GetJsonObject('Nature')), NIL})
//Busca os itens no JSON, percorre eles e adiciona no array da SC6
oItems := oJson:GetJsonObject('Items')
For nX := 1 To Len (oItems)
aLinha := {}
aAdd(aLinha,{"C6_ITEM", StrZero(nX,2), NIL})
aAdd(aLinha,{"C6_PRODUTO", AllTrim(oItems[nX]:GetJsonObject('Product')), NIL})
aAdd(aLinha,{"C6_QTDVEN", oItems[nX]:GetJsonObject('SalesQuantity'), NIL})
aAdd(aLinha,{"C6_PRCVEN", oItems[nX]:GetJsonObject('SalesPrice'), NIL})
aAdd(aLinha,{"C6_VALOR", oItems[nX]:GetJsonObject('Value'), NIL})
aAdd(aLinha,{"C6_TES", AllTrim(oItems[nX]:GetJsonObject('InputTypeOutPut')), NIL})
aAdd(aItens,aLinha)
Next nX
//Chama a inclusão automática de pedido de venda
MsExecAuto({|x, y, z| MATA410(x, y, z)},aCabec,aItens,3)
//Se houve erro, gera um arquivo de log dentro do diretório da protheus data
IF lMsErroAuto
/* removido trecho abaixo pois o cliente afirma que existe geração de numeração automatica
RollBackSX8()
*/
cArqLog := oJson:GetJsonObject('CLient')+oJson:GetJsonObject('Store')+"-"+ StrTran(Time(), ':', '-')+".log"
aLogAuto := {}
aLogAuto := GetAutoGrLog()
For nY := 1 To Len(aLogAuto)
cErro += aLogAuto[nY] + CRLF
Next nY
MemoWrite(LOG_DIRECTORY + cArqLog,cErro)
SetRestFault(500, cErro)
lRet := .F.
ELSE
/* removido trecho abaixo pois o cliente afirma que existe geração de numeração automatica
ConfirmSX8()
*/
cJsonRet := '{"Pedido gerado com sucesso":"'+SC5->C5_NUM+'"}'
Self:SetResponse(cJsonRet)
EndIF
ELSE
SetRestFault(500,EncodeUTF8("Cliente não encontrado"))
lRet := .F.
EndIF
EndIf
RestArea(aArea)
FreeObj(oJson)
Return(lRet)
/*
OrderInclusion método POST utilizando FWJsonDeserialize
Autor: Rodrigo dos Santos ( www.blacktdn.com.br )
*/
WSMETHOD POST OrderInclusion WSRECEIVE WSRESTFUL SalesOrder
Local cJson := Self:GetContent()
Local cJsonRet := ""
Local cArqLog := ""
Local cErro := ""
Local nX
Local nY
Local lRet := .T.
Local oParseJson := NIL
Local aArea := GetArea()
Local aCabec := {}
Local aItens := {}
Local aLinha := {}
Local aLogAuto := {}
Private lMsErroAuto := .F.
Private lMsHelpAuto := .T.
Private lAutoErrNoFile := .T.
//Se não existir o diretório de logs dentro da Protheus Data, será criado
IF .NOT. ExistDir(LOG_DIRECTORY)
MakeDir(LOG_DIRECTORY)
EndIF
//Definindo o conteúdo como JSON, e pegando o content e dando um parse para ver se a estrutura está ok
Self:SetContentType("application/json")
If FWJsonDeserialize(cJson,@oParseJson)
//Se encontrar o cliente existente conforme dados do JSON
DbSelectArea('SA1')
SA1->(dbSetOrder(1))
IF (SA1->(dbSeek(FWxFilial("SA1")+oParseJson:Client+oParseJson:Store)))
aCabec := {}
aItens := {}
/* removido trecho abaixo pois o cliente afirma que existe geração de numeração automatica
cPedido := GetSXENum("SC5","C5_NUM")
aAdd(aCabec,{"C5_NUM", cPedido, NIL})
*/
aAdd(aCabec,{"C5_TIPO", TYPE_OF_NORMAL_REQUEST, NIL})
aAdd(aCabec,{"C5_CLIENTE", AllTrim(oParseJson:Client), NIL})
aAdd(aCabec,{"C5_LOJACLI", AllTrim(oParseJson:Store), NIL})
aAdd(aCabec,{"C5_LOJAENT", AllTrim(oParseJson:DeliveryStore), NIL})
aAdd(aCabec,{"C5_CONDPAG", AllTrim(oParseJson:PaymentTerms), NIL})
aAdd(aCabec,{"C5_MENNOTA", AllTrim(oParseJson:MessageForNote), NIL})
aAdd(aCabec,{"C5_NATUREZ", AllTrim(oParseJson:Nature), NIL})
//Busca os itens no JSON, percorre eles e adiciona no array da SC6
For nX := 1 To Len (oParseJson:Items)
alinha := {}
aAdd(aLinha,{"C6_ITEM", StrZero(nX,2), NIL})
aAdd(aLinha,{"C6_PRODUTO", AllTrim(oParseJson:Items[nX]:Product), NIL})
aAdd(aLinha,{"C6_QTDVEN", oParseJson:Items[nX]:SalesQuantity, NIL})
aAdd(aLinha,{"C6_PRCVEN", oParseJson:Items[nX]:SalesPrice, NIL})
aAdd(aLinha,{"C6_VALOR", oParseJson:Items[nX]:Value, NIL})
aAdd(aLinha,{"C6_TES", AllTrim(oParseJson:Items[nX]:InputTypeOutPut), NIL})
aAdd(aItens,aLinha)
Next nX
//Chama a inclusão automática de pedido de venda
MsExecAuto({|x, y, z| MATA410(x, y, z)},aCabec,aItens,3)
//Se houve erro, gera um arquivo de log dentro do diretório da protheus data
IF lMsErroAuto
/* removido trecho abaixo pois o cliente afirma que existe geração de numeração automatica
RollBackSX8()
*/
cArqLog := oJson:GetJsonObject('CLient')+oJson:GetJsonObject('Store')+"-"+ StrTran(Time(), ':', '-')+".log"
aLogAuto := {}
aLogAuto := GetAutoGrLog()
For nY := 1 To Len(aLogAuto)
cErro += aLogAuto[nY] + CRLF
Next nY
MemoWrite(LOG_DIRECTORY + cArqLog,cErro)
SetRestFault(500,cErro)
lRet := .F.
ELSE
/* removido trecho abaixo pois o cliente afirma que existe geração de numeração automatica
ConfirmSX8()
*/
cJsonRet := '{"Pedido gerado com sucesso":"'+SC5->C5_NUM+'"}'
Self:SetResponse(cJsonRet)
EndIF
ELSE
SetRestFault(500,EncodeUTF8("Cliente não encontrado"))
lRet := .F.
EndIF
ELSE
SetRestFault(500,'Parser Json Error')
lRet := .F.
EndIf
RestArea(aArea)
Return(lRet)
/*
RequestQuery método GET
Autor: Rodrigo dos Santos ( www.blacktdn.com.br )
*/
WSMETHOD GET RequestQuery WSRECEIVE RequestNumber WSRESTFUL SalesOrder
Local lRet := .T.
Local cViewSC5 := GetNextAlias()
Local aData := {}
Local oData := NIL
/*
obrigatorio informar o numero do pedido
*/
IF Empty(Self:RequestNumber)
SetRestFault(500,EncodeUTF8('O parametro RequestNumber é obrigatório'))
lRet := .F.
Return(lRet)
EndIF
/*
montagem da consulta
*/
BeginSQL Alias cViewSC5
SELECT SC5.C5_FILIAL
,SC5.C5_NUM
,SC5.C5_LIBEROK
,SC5.C5_NOTA
,SC5.C5_BLQ
FROM %Table:SC5% SC5
WHERE SC5.C5_FILIAL = %FWxFilial:SC5%
AND SC5.C5_NUM = %Exp:AllTrim(Self:RequestNumber)%
AND SC5.%NotDel%
EndSQL
//Verifica se tem dados na query
dbSelectArea(cViewSC5)
(cViewSC5)->(dbGoTop())
IF (cViewSC5)->(.NOT. Eof())
//Enquanto houver dados na query
While (cViewSC5)->(.NOT. Eof())
//Cria um objeto JSON
oData := JsonObject():New()
//Se o pedido tiver em aberto
IF Empty((cViewSC5)->C5_LIBEROK) .AND. Empty((cViewSC5)->C5_NOTA) .AND. Empty((cViewSC5)->C5_BLQ)
oData['status'] := OPEN_REQUEST
//Se o pedido estiver encerrado
ELSEIF !Empty((cViewSC5)->C5_NOTA) .OR. (cViewSC5)->C5_LIBEROK=='E' .AND. Empty((cViewSC5)->C5_BLQ)
oData['status'] := APPLICATION_CLOSED
//Se o pedido estiver liberado
ELSEIF !Empty((cViewSC5)->C5_LIBEROK) .AND. Empty((cViewSC5)->C5_NOTA) .AND. Empty((cViewSC5)->C5_BLQ)
oData['status'] := REQUEST_RELEASED
//Se o pedido tiver bloqueio por regra
ELSEIF (cViewSC5)->C5_BLQ=='1'
oData['status'] := REQUEST_BLOCKED_BY_RULE
//Se o pedido tiver bloqueio por verba
ELSEIF (cViewSc5)->C5_BLQ=='2'
oData['status'] := REQUEST_BLOCKED_BY_FUNDS
EndIF
//Adiciona no array a informação e libera o objeto
aAdd(aData,oData)
FreeObj(oData)
(cViewSC5)->(dbSkip())
EndDo
//Define o retorno do método
Self:SetResponse(FwJsonSerialize(aData))
ELSE
SetRestFault(500,EncodeUTF8('Não existem dados para serem apresentados'))
lRet := .F.
EndIF
(cViewSC5)->(dbCloseArea())
Return(lRet)
Bom pessoal, por hoje é só.
Abraços e até a próxima.
Obrigado por compartilhar!
Só estou tendo um problema…
No método Post ele não respeita o método que chamo, a rotina sempre entra em SalesOrderInclusion
Boa tarde Ricardo.
Você teria o trecho de como você está utilizando?
Bom dia.
Muito boa essa materia.
Mas vcs teriam um fonte assim com json mas para incluir cadastro de clientes no protheus vindo do meu e-commerce?
Grato.
Boa noite Adalberto.
No caso você pode adaptar chamando a rotina da Mata030 ao invés da Mata410.
Nesse link tem um exemplo de ExecAuto: https://terminaldeinformacao.com/knowledgebase/execauto-mata030-mvc/
Abraços.
Boa tarde!
Estamos querendo realizar o cadastro automático de usuários no Protheus, via JSON, conforme documentação da TOTVS (https://tdn.totvs.com/pages/releaseview.action?pageId=274327398) porém sou novo em desenvolvimento e fiquei perdido, não entendi como posso realizar o cadastro via JSON sendo que o cadastro de usuários não tem uma “tabela” para realizarmos o “post”. Você teria algum exemplo ou alguma luz para ajudar?
Obrigado!
Bom dia Nicolas.
Eu nunca precisei criar usuários via WS. Mas parece que o exemplo esta bem completo. Na parte que esta escrito “Exemplo de requisição para a inclusão de usuário”, chegou a testar via Postman?
Outro ponto também, tente entrar no nosso fórum no Discord (link no cabeçalho do site), pode ser que lá alguém já tenha realizado essa operação.
Abraços.
Não consegui implentar, ele da erro na FWxFilial
Bom dia Reinaldo, tudo joia?
No caso, como ficou a configuração do seu AppServer?
Aqui tem um exemplo – https://terminaldeinformacao.com/2021/07/19/como-configurar-um-appserver-rest-no-protheus/