Dados capturados de sites

Fórum sobre a linguagem CA-Clipper.

Moderador: Moderadores

Ladinilson Sousa
Usuário Nível 1
Usuário Nível 1
Mensagens: 35
Registrado em: 09 Fev 2015 11:41
Localização: Belém/PA

Dados capturados de sites

Mensagem por Ladinilson Sousa »

Boa tarde meus caros,

Recentemente acrescentei em meu sistema a rotina de "pegar" dados do site da receita tanto os dados pesquisados pelo cnpj ou cpf. Tenho em outro cliente um desafio que não sei se é possível para pegar as informações dentro do site de processos.

Pergunto a vocês se tem uma classe, dll, rotina ou uma documentação seja paga ou não, que explana este assunto de modo que qualquer (digo assim pelo domínio caso fosse) site facilitando e acredito, de uma grande contribuição para todos pois meu cliente trabalha com o copiar as informações do site e colar nos campos.

Segue abaixo o programa Receita.prg que não é de minha autoria e esta em Fivewin a quem possa interessar.

Código: Selecionar todos

#include "FiveWin.Ch"
CLASS Receita

	DATA cUrls INIT ""
	DATA cUrlRet INIT ""
	DATA cSeekUrl INIT ""

	DATA cRetorno  INIT ""
	DATA lDownload INIT .F.
	DATA lError    INIT .F.
	DATA cError    INIT ""
	DATA nTimeOut  INIT 240					// 4 minutos
	
	DATA tCnpjCpf

	METHOD New() CONSTRUCTOR
	METHOD Consulta( lConsulta )
	METHOD DownloadComplete( oActived )
	METHOD PreencheCampos()	

	METHOD GetHash()
	METHOD NewHash()
	METHOD GroupBy( cStart, cEnd, cString, lTags, lBreak )
	METHOD StripHTML( cHTML )
	METHOD RemoveChar( cString, aChar )

	METHOD Clear() INLINE DeleteUrlCacheEntry( ::cUrls )

ENDCLASS

METHOD New() CLASS Receita
RETURN Self

METHOD Consulta( cCnpjCpf ) CLASS Receita 
LOCAL oDlgSint, oActiveX
LOCAL nTimer

	IF IsInternet()

		::Clear()
  
	ELSE

		MsgStop( "Não é Possível Encontrar os Dados no Receita !" + CRLF + CRLF + "Aguarde a Conexão com a Internet.", "Erro de Conexão" )
		RETURN {}

	ENDIF

	cCnpjCpf := STRTRAN(cCnpjCpf,".","")
	cCnpjCpf := STRTRAN(cCnpjCpf,"/","")
	cCnpjCpf := STRTRAN(cCnpjCpf,"-","")
	cCnpjCpf := ALLTRIM(cCnpjCpf)

	::tCnpjCpf := IF( LEN( ALLTRIM(cCnpjCpf) ) >= 14, "CNPJ", "CPF" )

   IF ::tCnpjCpf == "CNPJ"

		::cUrls := "http://www.receita.fazenda.gov.br/PessoaJuridica/CNPJ/cnpjreva/Cnpjreva_Solicitacao2.asp"
		::cUrlRet := "http://www.receita.fazenda.gov.br/PessoaJuridica/CNPJ/cnpjreva/Cnpjreva_Comprovante.asp"
		::cSeekUrl := "REPÚBLICA FEDERATIVA DO BRASIL"

	ELSE

		::cUrls := "http://www.situacaocadastral.com.br/"  //http://www.receita.fazenda.gov.br/aplicacoes/atcta/cpf/consultapublica.asp"
		::cUrlRet := "http://www.situacaocadastral.com.br/"  //http://www.receita.fazenda.gov.br/aplicacoes/atcta/cpf/ConsultaPublicaExibir.asp"
		::cSeekUrl := "Comprovante de Situação Cadastral no CPF"

	ENDIF

	DEFINE DIALOG oDlgSint FROM 0,0 TO 600,900 PIXEL
		oDlgSint:lHelpIcon := .F.
	ACTIVATE DIALOG oDlgSint NOWAIT CENTERED 

	oActiveX := TActivex():New( oDlgSint, "Shell.Explorer", 0, 0, 900, 600 )
	oActiveX:bOnEvent := {|cEv| IF( cEv == "DownloadComplete", ::DownloadComplete( oActiveX ), NIL ) }
   oActivex:Silent := .T.
   
	#ifndef __HARBOUR__
		oActiveX:Silent( .T. )
	#endif

   oDlgSint:oClient := oActiveX
	nTimer := Seconds()

   oActiveX:Do( "Navigate2", ::cUrls )

	while oActiveX:ReadyState <> 4 ; SysRefresh() ; ENDDO

	oActiveXdo := oActiveX:Document()                                    

	TRY

	   IF ::tCnpjCpf == "CNPJ"
			oActiveXdo:All:Item("cnpj",0):Value := cCnpjCpf
			oActiveXdo:All:Item("txtTexto_captcha_serpro_gov_br",0):Focus()
		ELSE
			oActiveXdo:All:Item("doc",0):Value := cCnpjCpf
			//se tiver a data de nascimento basta descomentar a linha abaixo
			//oActiveXdo:All:Item("txtDataNascimento",0):Value := ""
			oActiveXdo:All:Item("consultar",0):Focus() 
			oActiveXdo:All:Item("consultar",0):Click() 			                                        
		ENDIF

	CATCH
	
	END
	

	while !::lDownload

		IF !IsWindowVisible( oDlgSint:hWnd )

			::lDownload := .T.
			::lError    := .T.
			::cError    :=  "Consulta Cancelada !"

		ELSEIF !IsInternet()
			
			::lDownload := .T.
			::lError    := .T.
			::cError    := "Perda de Conexão com a Internet !"

		ELSEIF ( Seconds() - nTimer ) >= ::nTimeOut

			::lDownload := .T.
			::lError    := .T.
			::cError    := "Tempo Limite Esgotado !"

		ENDIF

		SysRefresh()

	ENDDO
	
	oDlgSint:End()
	SysRefresh()
	
	IF ::lDownload .AND. !::lError                                     

	 	MsgInfo( Hb_DumpVar( ::GetHash() ), "Consulta Com Sucesso !" )

		IF ::PreencheCampos( ::GetHash() )
         ::Clear()
  		ELSE
  			RETURN .F.
		ENDIF
	 	
	ELSE

    //  MsgStop( "Não Foi Possível Encontrar os Dados no Receita !" + CRLF + CRLF + ::cError, "Erro de Pesquisa" )
		::Clear()

	ENDIF

RETURN {}

METHOD DownloadComplete( oActived ) CLASS Receita
LOCAL lRetVal := .F.

	IF ::cUrlRet $ ALLTRIM(oActived:LocationURL)

		while oActived:Busy ; SysWait(0.5) ; ENDDO

		IF ::cSeekUrl $ oActived:Document:Body:InnerHtml

			TRY

				::cRetorno := oActived:Document:Body:InnerHtml 

			CATCH
		
				::lError := .T.
				::cError := "Falha no Recebimento dos Dados !"
		
			END

			oActived:oWnd:Hide()
			::lDownload := .T.

		ENDIF

	ELSEIF AT( "não+foi+encontrado", oActived:LocationURL ) > 0

		::cError    := "O CNPJ não foi Encontrado na Base de Dados do Receita."
		::lError    := .T.
		::lDownload := .T.

	ELSEIF AT( "incorreto", oActived:LocationURL ) > 0        

		::cError    := "O C.N.P.J. Não é Válido !"
		::lError    := .T.
		::lDownload := .T.

	ENDIF

RETURN NIL

METHOD GetHash() CLASS Receita
LOCAL hHash := ::NewHash()
LOCAL cHTML := STRTRAN( STRTRAN( ::StripHTML( ::cRetorno ), CHR(13) ), CHR(10) )


	IF ::tCnpjCpf == "CPF"

		hHash["Nome"]        := SUBS( ::GroupBy("Nome da Pessoa Física: ", " Situação Cadastral: ", cHtml ), 1, AT( "Data de Nascimento:", ::GroupBy("Nome da Pessoa Física: "," Situação Cadastral: ",cHtml) ) - 1 )
		hHash["Nascimento"]  := ::GroupBy( "Data de Nascimento: ", "Situação Cadastral: ", cHtml )
		hHash["CPF"]         := ::GroupBy( "No do CPF: ", "Nome da Pessoa Física:", cHtml )
		hHash["Situacao"]    := ::GroupBy( "Situação Cadastral: ", "Digito", cHtml )
		hHash["Digito"]      := STRTRAN( ::GroupBy("Verificador: ", "Comprovante emitido", cHtml), CRLF )
		hHash["EmitidoHr"]   := ::GroupBy( "Comprovante emitido às: ", " do dia ", cHtml )
		hHash["EmitidoDt"]   := ::GroupBy( " do dia ", " (hora e data de Brasília).", cHtml )
		hHash["Comprovante"] := ::GroupBy( "controle do comprovante: ", " A autenticidade deste comprovante", cHtml )

	ELSEIF ::tCnpjCpf == "CNPJ"

		hHash["CNPJ"]       := SUBS( ::GroupBy( "NÚMERO DE INSCRIÇÃO ", "COMPROVANTE DE INSCRIÇÃO E DE SITUAÇÃO CADASTRAL", cHtml), 0, 18 )
		hHash["Tipo"]       := SUBS( ::GroupBy( "NÚMERO DE INSCRIÇÃO ", "COMPROVANTE DE INSCRIÇÃO E DE SITUAÇÃO CADASTRAL", cHtml), 19, LEN( ::GroupBy( "NÚMERO DE INSCRIÇÃO ", "COMPROVANTE DE INSCRIÇÃO E DE SITUAÇÃO CADASTRAL", cHtml ) ) -18 )
		hHash["DtAbertura"] := ::GroupBy( "DATA DE ABERTURA ", " Fim Linha NÚMERO DE INSCRIÇÃO", cHtml )
		hHash["NomeEmpresarial"] := ::GroupBy( "NOME EMPRESARIALNOME EMPRESARIAL", "Fim Linha NOME EMPRESARIAL", cHtml )
		hHash["NomeFantasia"] := ::GroupBy( "TÍTULO DO ESTABELECIMENTO (NOME DE FANTASIA)", "Fim Linha ESTABELECIMENTO", cHtml )
		hHash["AtividadeEconomicaPrincipal"] := ::GroupBy( "CÓDIGO E DESCRIÇÃO DA ATIVIDADE ECONÔMICA PRINCIPAL ", "Fim Linha ATIVIDADE ECONOMICA", cHtml )
		hHash["AtividadeEconomicaSecundarias"] := ::GroupBy( "CÓDIGO E DESCRIÇÃO DAS ATIVIDADES ECONÔMICAS SECUNDÁRIAS ", " Fim Linha ATIVIDADE ECONOMICA SECUNDARIA", cHtml )
		hHash["CodigoEDestricaoDaNaturezaJuridica"] := ::GroupBy( "CÓDIGO E DESCRIÇÃO DA NATUREZA JURÍDICA ", " Fim Linha NATUREZA JURÍDICA", cHtml )
		hHash["SituacaoCadastral"] := ::GroupBy( "CADASTRALSITUAÇÃO CADASTRAL ", "DATA DA SITUAÇÃO CADASTRAL ", cHtml )
		hHash["DtSituacaoCadastral"] := ::GroupBy( "DATA DA SITUAÇÃO CADASTRAL ", " Fim Linha SITUACAO CADASTRAL", cHtml )
		hHash["MotivoDeSituacaoCadastral"] := ::GroupBy( "Início Linha MOTIVO DE SITUAÇÃO CADASTRALMOTIVO DE SITUAÇÃO CADASTRAL ", "Fim Linha MOTIVO DE SITUAÇÃO CADASTRAL", cHtml )
		hHash["SituacaoEspecial"] :=  ::GroupBy( "Início Linha SITUAÇÃO ESPECIALSITUAÇÃO ESPECIAL ", "DATA DA SITUAÇÃO ESPECIAL", cHtml )
		hHash["EndLogradouro"] := SUBS( ::GroupBy( "Início Linha LOGRADOUROLOGRADOURO","COMPLEMENTO",cHtml), 1, AT( "NÚMERO", ::GroupBy("Início Linha LOGRADOUROLOGRADOURO","COMPLEMENTO",cHtml) ) - 1 )
		hHash["EndNumero"] := TOKEN( ::GroupBy( "Início Linha LOGRADOUROLOGRADOURO", "COMPLEMENTO", cHtml ), "NUMERO" )
		hHash["EndComplemento"] := ::GroupBy( "COMPLEMENTO ", " Fim Linha LOGRADOURO", cHtml )
		hHash["EndCep"] := ::GroupBy( "Início Linha CEPCEP", "BAIRRO/DISTRITO", cHtml )
		hHash["EndBairro"] := ::GroupBy( "BAIRRO/DISTRITO", "MUNICÍPIO ", cHtml )
		hHash["EndMunicipio"] := ::GroupBy( "MUNICÍPIO", "UF ", cHtml )
		hHash["EndUf"] := ::GroupBy( "UF ", " Fim Linha CEPInício Linha SITUAÇÃO CADASTRAL", cHtml )
		hHash["EmitidoDt" ] := ::GroupBy( "Emitido no dia ", " às ", cHtml )
		hHash["EmitidoHr" ] := ::GroupBy( " às ", " (data e hora de Brasília).", cHtml )

	ENDIF

	HEval( hHash, {|cField,uVar| IF( VALTYPE(uVar) == "C", hHash[cField] := ALLTRIM(uVar), ) } )

RETURN hHash


METHOD PreencheCampos( hHash ) CLASS Receita
	IF ::tCnpjCpf == "CPF"

		IF !( "REGULAR" $ hHash["SituacaoCadastral"] )
			MsgAlert("Pessoa Com Cadastro Irregular !" + CRLF + CRLF + "Verificar o Cadastro",{"&Ok"},{"Gravar1"},"Erro de Pesquisa")
			RETURN .F.
		ENDIF

  	   vrazao	:= hHash["Nome"] ; grazao:Refresh()
	   vnome 	:= hHash["Nome"] ; oNome:Refresh() 
	   vcgc  	:= hHash["CPF"]  ; ocgc:Refresh()  
	  	/*                  
		Clientes->cgc_cpf := hHash["CPF"] ; oCgc:Refresh()
		Clientes->razao := hHash["Nome"] ; oNom:Refresh()
		Clientes->descricao := hHash["Nome"] ; oApe:Refresh()
		*/
	ELSE

		IF "Baixado" $ hHash["SituacaoCadastral"] .OR. "BAIXADA" $ hHash["SituacaoCadastral"] .OR. "NÃO HABILITADO" $ hHash["SituacaoCadastral"]
			MsgAlert("Empresa Com Cadastro com Baixa !" + CRLF + CRLF + "Verificar o Cadastro",{"&Ok"},{"Gravar1"},"Erro de Pesquisa")
			RETURN .F.
		ENDIF
		
		mTipo      := "Juridica"                                                                                        ;ComTipo:REFRESH()
		vrazao     := LEFT( hHash["NomeEmpresarial"]    + SPACE( LEN(Clientes->razao) ), LEN(Clientes->razao) )                   ; oNome:Refresh()
		vnome      := LEFT( hHash["NomeFantasia"] + SPACE( LEN(Clientes->descricao) ), LEN(Clientes->descricao) )    ; grazao:Refresh()
		vcgc       := LEFT( hHash["CNPJ"]     + SPACE( LEN(TIRAPONTO(Clientes->cgc_cpf)) ), LEN(TIRAPONTO(Clientes->cgc_cpf)) )            
		vie        := LEFT( ["Tipo"]       + SPACE( LEN(TIRAPONTO(Clientes->insc_rg)) ), LEN(TIRAPONTO(Clientes->insc_rg)) )                
		vcgc       := TIRAPONTO(vcgc)              ; ocgc:Refresh()
		vie        := TIRAPONTO(vie)               ; oIe:Refresh()

		vendereco  := LEFT( hHash["EndLogradouro"]    + SPACE( LEN(Clientes->ende) ), LEN(Clientes->ende) )                   ; getend:Refresh()      
		vende_num  := LEFT( hHash["EndNumero"]      + SPACE( LEN(Clientes->ende_num) ), LEN(Clientes->ende_num) )         ; getnum:Refresh()          
		vperimetro := LEFT( hHash["EndComplemento"] + SPACE( LEN(Clientes->perimetro) ), LEN(Clientes->perimetro) ) ; getperim:Refresh()         
		vbairro    := LEFT( hHash["EndBairro"]      + SPACE( LEN(Clientes->bairro) ), LEN(Clientes->bairro) )               ; getbairr:Refresh()      
      vcidade    := LEFT( hHash["EndMunicipio"]      + SPACE( LEN(Clientes->cidade) ), LEN(Clientes->cidade) )               ; getcidad:Refresh()      
		vuf        := hHash["EndUf"]                                                                               ; getuf:Refresh()         
		vcep       := hHash["EndCep"]                                                                               ; getcep:Refresh() 
		
		OK_CGC(vcgc,ocgc)  
		                                                                     
		/*
		IF (::oDbfRet:cAlias)->( FieldPos( "ATV" ) ) # 0
			IF ::cUF == "ES"
				::oDbfRet:atv := "HABILITADO" $ hHash["ATIVO"] ; oAtv:Refresh()
			ELSEIF ::cUF == "MG"
				::oDbfRet:atv := hHash["ATIVO"] == "Habilitado Ativo" ; oAtv:Refresh()
			ELSEIF ::cUF # "RS"
				::oDbfRet:atv := hHash["ATIVO"] $ "HABILITADOAtivo.ATIVA.Habilitado.HABILITADO.ATIVO" ; oAtv:Refresh()
			ELSE
				::oDbfRet:atv := "HABILITADO" $ hHash["ATIVO"] ; oAtv:Refresh()
			ENDIF
		ENDIF

		IF oClassVar:nRegTri == 1
			IF ::oDbfRet:dbOpen()
				IF (::oDbfRet:cAlias)->( FieldPos("RPA") ) > 0
					IF ::cUF == "AL"
						::oDbfRet:rpa := "inscrição gera crédito " $ hHash["REGIME"] ; oRpa:Refresh()
					ELSEIF ::cUF == "BA"
						::oDbfRet:rpa := "C/CORRENTE FISCAL" $ hHash["REGIME"] ; oRpa:Refresh()
					ELSEIF ::cUF == "RO"
						::oDbfRet:rpa := "REGIME" $ hHash["REGIME"] ; oRpa:Refresh()
					ELSEIF ::cUF == "RS"
						::oDbfRet:rpa := "GERAL" $ hHash["REGIME"] ; oRpa:Refresh()
					ELSEIF ::cUF == "ES"
						::oDbfRet:rpa := "ORDINARIO" $ hHash["REGIME"] ; oRpa:Refresh()
					ELSE
						::oDbfRet:rpa := hHash["REGIME"] $ "NORMAL.Não.Normal.Regime Normal" ; oRpa:Refresh()
					ENDIF
				ENDIF
				::oDbfRet:dbClose()
			ENDIF
		ENDIF
		*/
	ENDIF

RETURN .T.

METHOD NewHash() CLASS Receita
LOCAL hHash := Hash()

	HSetAACompatibility( hHash, .T. )
	HSetCaseMatch( hHash, .F. )

RETURN hHash

METHOD GroupBy( cStart, cEnd, cString, lTags, lBreak ) CLASS Receita
LOCAL nBegin, nEnd
LOCAL cFound := "", nAT

	DEFAULT lTags:=.F., lBreak:=.F.

	nBegin := AT( cStart, cString )

	IF nBegin == 0 ; RETURN cFound ; ENDIF

	IF !lTags ; nBegin += LEN(cStart) ; ENDIF

	IF cEnd # NIL
             
		nEnd := AT( cEnd, cString )
		IF nEnd == 0
			RETURN cFound
		ELSE
			nEnd := nEnd - nBegin
		ENDIF

		IF lTags ; nEnd += LEN(cEnd) ; ENDIF

	ELSE

		IF lBreak

			nAt := AT( CRLF, SUBS( cString, nBegin, LEN(cString) ) )
			IF nAt > 0
				nEnd := nAt - 1
				If nEnd < LEN(cString) ; nEnd := LEN(cString)+1 ; ENDIF
			ELSE
				nEnd := LEN(cString)
			ENDIF

		ELSE

			nEnd := LEN(cString)

		ENDIF

	ENDIF

	cFound := SUBS( cString, nBegin, nEnd )

	IF lBreak

		IF lTags

			cFound:=StrTran(cFound,CRLF)

		ELSE

			IF cEnd # NIL

				nAt := At(CRLF,cFound)
				IF nAt > 0
					cFound := LEFT(cFound,nAT)
				ENDIF

			ENDIF

		ENDIF

	ENDIF

RETURN cFound

METHOD StripHTML( cHTML ) CLASS Receita
LOCAL cString:= ::RemoveChar( cHTML, { " ", "<!-- ", " -->", "<--", "-->", "||", "\/", "/\" } )
LOCAL aMatch := HB_RegExAll( "\<[^\>]*\>", cString )

	AEVAL( aMatch, {|a| cString := STRTRAN( cString, a[1] ) } )

RETURN cString

METHOD RemoveChar( cString, aChar ) CLASS Receita
	AEVAL( aChar, {|cChar| cString := STRTRAN( cString, cChar ) } )
RETURN cString


DLL STATIC FUNCTION DeleteUrlCacheEntry(lpszUrlName AS STRING) AS LONG PASCAL FROM "DeleteUrlCacheEntryA" LIB "wininet.dll"

/*
http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/cnpjreva_solicitacao.asp
http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/cnpjreva_solicitacao.asp
AC 07358761021670 <- o retorno agora é em pdf
AL 24487886000108
AM 00280273000137
AP 00361512000183 <- o retorno agora é em pdf
AP 23080484000121 <- o retorno agora é em pdf
BA 01718478000114
CE 01591524000167
DF 32440901003025 <- o retorno não consegue pegar os dados
ES 28053619001740
GO 04823792000155 <- erro (DOS Error -2147352567) WINOLE/1007   (0x80004005): INNERHTML
MA 03062748000107
MG 16716417000195
MS 07835806000220
MT 03790841000138
PA 63878250000149
PB 41137225000171
PE 23637697006738 <- não consegue pegar o retorno
PI 03751615000148 <- não consegue pegar o retorno
PR 01275430000189 <- não acessou o site
RJ 33252156000119
RN 33000167000969
RO 09502005000197
RR 02315090000135 <- retorno sempre dando que não existe
RS 87821278000108
SC 80659238000162 <- não acessou o site
SE 01491090000123
SP 02790893000222
TO 17262213011200 <- não acessou o site
*/


E nos sites, podemos acessar seus conteúdos na opção "Mais Ferramentas/Ferramentas do desenvolvedor" e "Inspecionar" no ítem desejado igual imagem abaixo...

http://ap.imagensbrasil.org/image/EfrvC6

E se caso já tenha alguns tópicos aqui, por favor me direcionar!

Obrigado
Avatar do usuário
fladimir
Colaborador
Colaborador
Mensagens: 2445
Registrado em: 15 Nov 2006 20:21

Dados capturados de sites

Mensagem por fladimir »

Amigo, algo especifico sobre o assunto em si não sei, mas aki no fórum tem bastante coisa pra te auxiliar a vc montar sua própria classe / rotina para tal.

O próprio código q vc postou já da uma boa base para fazer.

Por exemplo tem este tópico q monstra um exemplo da contrib sobre o assunto.
Sun Tzu há mais de três mil anos cita nas epígrafes de seu livro “A Arte da Guerra“:

“Concentre-se nos pontos fortes, reconheça as fraquezas, agarre as oportunidades e proteja-se contra as ameaças”.
“Se não é vantajoso, nunca envie suas tropas; se não lhe rende ganhos, nunca utilize seus homens; se não é uma situação perigosa, nunca lute uma batalha precipitada”
.


Até 2017    Desktop Console [ Legado ] Harbour | MinGW | DBF | CDX | FastReport | MySQL


Novos Projetos:

   Desktop Visual           Windev Desktop
   Celular Android/iOS   Windev Mobile
   WEB                            Windev Web


Sejamos gratos a Deus.
Ladinilson Sousa
Usuário Nível 1
Usuário Nível 1
Mensagens: 35
Registrado em: 09 Fev 2015 11:41
Localização: Belém/PA

Dados capturados de sites

Mensagem por Ladinilson Sousa »

Hum, deve ser algo parecido mas não tão específico.
Avatar do usuário
Daniel
Usuário Nível 3
Usuário Nível 3
Mensagens: 373
Registrado em: 13 Ago 2003 22:42
Localização: Apucarana - PR

Dados capturados de sites

Mensagem por Daniel »

Daniel

Harbour + Minigui + dbfcdx
Marinas-Gui Pena que parou o suporte
Responder