Página 11 de 35

Meu modo de trabalho

Enviado: 27 Nov 2019 11:07
por JoséQuintas

Código: Selecionar todos

   WITH OBJECT cnMySql
      :cSql := "SELECT ..."
...
      oTBrowse := { ;
         { "PEDIDO",    { || :String( "PEDIDO", 6 ) } }, ;
         { "DT.EMIS.",  { || :Date( "PDDATEMI" ) } }, ;
         { "TRANSAÇÃO", { || ReturnValue( "", Encontra( :String( "PDTRANSA", 6 ), "jptransa", "numlan" ) ) + Pad( jptransa->trDescri, 12 ) } }, ;
...
Nesse browse, o JPTRANSA está em DBF... sem problemas.
Mas com a mudança do JPTRANSA disponível em MySQL... fim dos DBFs pra esse browse e fim de pesquisas adicionais no programa pro browse.

Meu modo de trabalho

Enviado: 01 Dez 2019 23:19
por JoséQuintas
UMA das mudanças de hoje:

Código: Selecionar todos

STATIC FUNCTION MostraUltimaCompra( cItem )

   LOCAL nSelect, cOrdSetFocus, cSetColor

   IF AppUserLevel() != 0
      RETURN NIL
   ENDIF
   nSelect := Select()
   SELECT jpestoque
   cOrdSetFocus := ordSetFocus( "jpestoq3" )
   SEEK cItem + "X" SOFTSEEK
   SKIP -1
   DO WHILE jpestoque->esItem == cItem .AND. ! Bof()
      IF Val( jpestoque->esPedido ) == 0
         SKIP -1
         LOOP
      ENDIF
      Encontra( jpestoque->esTransa, "jptransa", "numlan" )
      IF "COMPRA" $ jptransa->trReacao
         EXIT
      ENDIF
      SKIP -1
   ENDDO

   cSetColor := SetColor()
   IF Date() - jpestoque->esDatLan > 90 .OR. jpestoque->esItem != cItem .OR. jpestoque->esTipLan != "2"
      SetColor( SetColorAlerta() )
   ENDIF
   @ Row() + 2, 5     SAY Space( 80 )
   IF jpestoque->esItem == cItem .AND. jpestoque->esTipLan == "2"
      Encontra( jpestoque->esCliFor, "jpcadastro", "numlan" )
      @ Row(), 5         SAY "Última Entrada:" + DToC( jpestoque->esDatLan )
      @ Row(), Col() + 2 SAY "Qtd:" + LTrim( Str( jpestoque->esQtde ) )
      @ Row(), Col() + 2 SAY "Vl:" + LTrim( Transform( jpestoque->esValor, PicVal( 14, 2 ) ) )
      @ Row() + 1, 5     SAY Space( 80 )
      @ Row(), 5         SAY "Pedido:" + jpestoque->esPedido
      @ Row(), Col() + 2 SAY "Forn:" + jpcadastro->cdNome
   ENDIF
   @ Row() + 1, 5 SAY "Custo (Tabela): " + LTrim( Transform( CustoItem( cItem ), PicVal( 14, 2 ) ) ) + " Qt.Disp: " + Str( jpitem->ieQtd1 + jpitem->ieQtd2 + jpitem->ieQtd3 + jpitem->ieQtd4 - jpitem->ieRes1, 8 )
   SetColor( cSetColor )
   ordSetFocus( cOrdSetFocus )
   SELECT ( nSelect )

   RETURN NIL

Código: Selecionar todos

STATIC FUNCTION MostraUltimaCompra( cItem )

   LOCAL cSetColor, cnMySql := ADOClass():New( AppConexao() )

   IF AppUserLevel() != 0
      RETURN NIL
   ENDIF
   WITH OBJECT cnMySql
      :cSql := "SELECT ESDATLAN, ESCLIFOR, ESQTDE, ESVALOR, ESPEDIDO, JPCADASTRO.CDNOME AS NOME, " + ;
         "JPITEM.IEQTD1 + JPITEM.IEQTD2 + JPITEM.IEQTD3 + JPITEM.IEQTD4 - JPITEM.IERES1 AS QTDE " + ;
         "FROM JPESTOQUE " + ;
         "LEFT JOIN JPTRANSA ON JPESTOQUE.ESTRANSA=JPTRANSA.IDTRANSA " + ;
         "LEFT JOIN JPCADASTRO ON JPESTOQUE.ESCLIFOR=JPCADASTRO.IDCADASTRO " + ;
         "LEFT JOIN JPITEM ON JPESTOQUE.ESITEM=JPITEM.IDITEM " + ;
         "WHERE ESITEM=" + StringSql( cItem ) + " AND ESTIPLAN='2' AND JPTRANSA.TRREACAO LIKE '%COMPRA%' " + ;
         "ORDER BY ESDATLAN DESC LIMIT 1"
      :Execute()
      cSetColor := SetColor()
      IF Date() - :Date( "ESDATLAN" ) > 90
         SetColor( SetColorAlerta() )
      ENDIF
      @ Row() + 2, 5     SAY Space( 80 )
      @ Row(), 5         SAY "Última Entrada:" + DToC( :Date( "ESDATLAN" ) )
      @ Row(), Col() + 2 SAY "Qtd:" + LTrim( Str( :Number( "ESQTDE" ) ) )
      @ Row(), Col() + 2 SAY "Vl:" + LTrim( Transform( :Number( "ESVALOR" ), PicVal( 14, 2 ) ) )
      @ Row() + 1, 5     SAY Space( 80 )
      @ Row(), 5         SAY "Pedido:" + :String( "ESPEDIDO", 6 )
      @ Row(), Col() + 2 SAY "Forn:" + :String( "NOME" )
      @ Row() + 1, 5 SAY "Custo (Tabela): " + LTrim( Transform( CustoItem( cItem ), PicVal( 14, 5 ) ) ) + " Qt.Disp: " + Str( :Number( "QTDE" ), 8 )
      SetColor( cSetColor )
      :CloseRecordset()
   ENDWITH

   RETURN NIL
Deixando de lado o que mais poderia ser melhorado....

1. Antes utilizava os DBFs jpitem, jptransa, jpestoque, jpcadastro agora NENHUM
2. Antes o processamento era local, agora no servidor
3. No tráfego de rede, ao invés de 4 DBFs + 4 CDXs + vários registros de movimentação... agora é apenas a informação que interessa
4. Nada de área em uso, escolher índice, etc. etc. essas coisas de DBF
5. A rotina pode ser chamada de qualquer lugar, não depende mais de arquivo em uso, nem de qualquer coisa externa

E.... no futuro... no uso de alguma GUI... menos tranqueira pra se preocupar.

Essa rotina é um bom exemplo de que SQL NÃO TEM NADA A VER COM DBF. E que aproveitar fonte de DBF é apenas temporário.

Novamente.... chamando a atenção:

Usar RDDSQL, SQLMIX, etc, deixa aproveitar todo fonte de DBF.
Não é por trabalhar igual DBF que não precisa mexer nos fontes: se usar SQL igual DBF... não vai estar tirando nenhum proveito do SQL.
Essas RDDs são pra manter compatibilidade, NÃO há problema nisso, só não pode esquecer de usar os recursos do servidor.

Se não me engano elas deixam usar USE ( "SELECT * FROM SERVIDOR..." )...
Então dá pra tirar proveito de SQL mesmo usando as RDDs, elas permitiriam alterar pra um único comando.

Nota:

Com certeza nada é automático.
Apanhei muito nesse comando SQL hoje, porque estava usando ".AND.", e isso com pontos é do Harbour não do SQL ... kkkk
Estou dizendo isso pra destacar que tem problemas igual com DBF, se colocarmos errado no fonte também não funciona, mas... igual fazemos com DBF, é ir ajustando até resolver.

Graças à gravação dupla, DBF + MySQL, continuo podendo alterar os fontes aos poucos, e melhorando a cada dia.
Ainda não eliminei nenhum dos DBFs com gravação duplicada no MySQL, porque tem muito fonte que faz leitura deles.
Mas os clientes continuam notando aumento de velocidade em diversas rotinas.

Destaque pra isso:
Mesmo quem usa via terminal service, também tá ficando mais rápido, e nesse caso é comparando DBF LOCAL com MySQL.
Para os que usam via rede então.... vixe

Meu modo de trabalho

Enviado: 03 Dez 2019 13:25
por Itamar M. Lins Jr.
Ola!
Usar RDDSQL, SQLMIX, etc, deixa aproveitar todo fonte de DBF.
Não é por trabalhar igual DBF que não precisa mexer nos fontes: se usar SQL igual DBF... não vai estar tirando nenhum proveito do SQL.
Essa afirmação, não está de acordo com uso do SQLMIX, pois não é a mesma abordagem.

SQLMIX = 100% comandos SQL, para ler e gravar os dados.

Os comandos DBF são apenas para manipular os resultados.
É a mesma coisa de quem usa ADO e para quem usa acesso nativo, precisa manipular arrays...

Saudações,
Itamar M. Lins Jr.

Meu modo de trabalho

Enviado: 03 Dez 2019 14:07
por JoséQuintas
Itamar M. Lins Jr. escreveu:Essa afirmação, não está de acordo com uso do SQLMIX, pois não é a mesma abordagem.
SQLMIX = 100% comandos SQL, para ler e gravar os dados.
Não lembro se é o SQLMIX que dá essa opção: USE (ctable from conexão)
Ou... USE ( "SELECT * FROM FINANCEIRO" )

É sério:
Tem usuário que tá fazendo isso, ou quase isso.
Tá trazendo tudo pra selecionar apenas uma informação.
Acha que SQL é igual DBF e precisa continuar fazendo processamento local.

É para isso que estou chamando atenção, e não depende do que foi escolhido pra conectar, e sim com aproveitar o que o servidor pode fazer.

Meu modo de trabalho

Enviado: 03 Dez 2019 16:13
por JoséQuintas
Pensei que aquele era um exemplo.... até chegar neste:
TODO esse fonte vai pro lixo, vai virar um único comando SQL.

Código: Selecionar todos

   wSave()
   oStru := {  ;
      { "LOCADOR",     "N", 4, 0 }, ;
      { "LOCATARIO",   "N", 7, 0 }, ;
      { "ENDERECO",    "C", 35, 0 }, ;
      { "RECIBO",      "N", 6, 0 }, ;
      { "DATA",        "D", 8, 0 }, ;
      { "CIC",         "C", 18, 0 }, ;
      { "NOME",        "C", 35, 0 }, ;
      { "VALOR",       "N", 12, 2 }, ;
      { "VALORCOMIS",  "N", 12, 2 }, ;
      { "RATEIO",      "N", 6, 2 }, ;
      { "MES",         "C", 6, 0 } }

   cTmpFile  := MyTempFile( "DBF" )
   cTmpFile2 := MyTempFile( "CDX" )
   SELECT 0
   dbCreate( cTmpFile, oStru )
   USE ( cTmpFile ) EXCLUSIVE ALIAS simulado

   SELECT templocador
   nTotal := LastRec()
   GrafTempo( "Gerando dados para relatório" )
   GOTO TOP
   DO WHILE ! Eof()
      GrafTempo( nAtual++, nTotal )
      WITH OBJECT cnMySql
         cnMySql:cSql := "SELECT COD, LOCOD, IFDATPAG, IFDATDIM, CIC, RECIBO, VALOR, VALORCOMIS FROM INFORME WHERE LOCOD=" + ;
            NumberSql( templocador->Locador ) + " AND ANOBASE = " + StringSql( Str( Year( dDataReferencia ), 4 ) ) + ;
            " ORDER BY LOCOD, COD"
         cnMySql:Execute()
         DO WHILE :Number( "LOCOD" ) == templocador->Locador .AND. ! :Eof()
            DO CASE
            CASE Year( :Date( "IFDATDIM" ) ) != Year( dDataReferencia )
               :MoveNext()
               LOOP
            CASE :Number( "COD" ) == 0
               :MoveNext()
               LOOP
            CASE Month( :Date( "IFDATDIM" ) ) != Month( dDataReferencia )
               :MoveNext()
               LOOP
            ENDCASE
            cnMySqlHLDIMRAT:cSql := "SELECT * FROM HLDIMRAT WHERE LOCADOR=" + NumberSql( templocador->Locador ) + " AND LOCATARIO=" + NumberSql( :Number( "COD" ) )
            cnMySqlHLDIMRAT:Execute()
            DO WHILE ! cnMySqlHLDIMRAT:Eof()
               SELECT simulado
               APPEND BLANK
               REPLACE ;
                  simulado->Locador     WITH templocador->Locador, ;
                  simulado->Locatario   WITH :Number( "LOCOD" ), ;
                  simulado->Endereco    WITH "", ;
                  simulado->DATA        WITH :Date( "IFDATPAG" ), ;
                  simulado->Cic         WITH cnMySqlHLDIMRAT:String( "CIC" ), ;
                  simulado->Nome        WITH cnMySqlHLDIMRAT:String( "NOME" ), ;
                  simulado->Rateio      WITH cnMySqlHLDIMRAT:Number( "RATEIO" ), ;
                  simulado->Recibo      WITH :Number( "RECIBO" ), ;
                  simulado->Valor       WITH :Number( "VALOR" ) * cnMySqlHLDIMRAT:Number( "RATEIO" ) / 100, ;
                  simulado->ValorComis  WITH :Number( "VALORCOMIS" ) * cnMySqlHLDIMRAT:Number( "RATEIO" ) / 100, ;
                  simulado->Mes         WITH Left( Dtos( :Date( "IFDATPAG" ) ), 6 )
               cnMySqlHLDIMRAT:MoveNext()
            ENDDO
            cnMySqlHLDIMRAT:CloseRecordset()
            :MoveNext()
         ENDDO
         :CloseRecordset()
         SELECT templocador
      ENDWITH
      SKIP
   ENDDO

Meu modo de trabalho

Enviado: 04 Dez 2019 12:17
por JoséQuintas
Atualização de hoje interessante:

Código: Selecionar todos

FUNCTION ze_Update2019()

   SayScroll( "Verificando atualizações 2019" )
   IF AppVersaoDbfAnt() < 20191203;   Update1203();  ENDIF // antes das demais, arquivo desativado
   IF AppVersaoDbfAnt() < 20191022.1; Update1022A(); ENDIF
...
   IF AppVersaoDbfAnt() < 20191127;   Update1127(); ENDIF
   IF AppVersaoDbfAnt() < 20191204.1; Update1204A(); ENDIF
...
STATIC FUNCTION Update1203() // Antes das demais

   LOCAL aStruList := { ;
      { "IDCLISTA",   "C", 6 }, ;
      { "CSDESCRI",   "C", 80 }, ;
      { "CSBLOQUEIO", "C", 1 }, ;
      { "CSINFINC",   "C", 80 }, ;
      { "CSINFALT",   "C", 80 } }

   IF AppVersaoDbfAnt() < 20191101
      AAdd( aStruList, { "CSNUMLAN",   "C", 6 } )
   ENDIF

   SayScroll( "JPCLISTA, verificando atualizações" )

   IF ! ValidaStru( "jpclista", aStruList )
      MsgStop( "JPCLISTA não disponível!" )
      QUIT
   ENDIF
   IF AppVersaoDbfAnt() >= 20130201
      RETURN NIL
   ENDIF
   IF ! UseSoDbf( "jpclista", .T. )
      QUIT
   ENDIF
   IF Eof()
      RecAppend()
      REPLACE ;
         jpclista->idCliSta WITH StrZero( 1, 6 ), ;
         jpclista->csDescri WITH "GERAL"
      RecUnlock()
   ENDIF
   CLOSE DATABASES

   RETURN NIL

STATIC FUNCTION Update1204A()

   fErase( "jpclista.dbf" )
   fErase( "jpclista.cdx" )

   RETURN NIL
Atualização fora de ordem?
Criar pra apagar?

NÃO vai precisar mais do DBF a partir de hoje.
Mas... até a versão de ontem precisava.
Então... clientes atrasados vão atualizar versão, vai checar estrutura do DBF, atualizar para o MySQL e... no final apagar.
É que pras conversões de datas anteriores funcionarem precisa da estrutura correta, então precisa testar estrutura antes das datas anteriores.
Clientes com versão em dia... até vai testar estrutura, mas só precisa apagar.
Seja como for, depois da versão atual vai ser apagado, e não vai mais ser testado.

É o primeiro DBF apagado depois da gravação dupla.
É o status de cliente, converti pra facilitar o browse de clientes.
Como era usado em poucos fontes, foi rápido pra eliminar de vez.

Meu modo de trabalho

Enviado: 05 Dez 2019 16:44
por JoséQuintas
Ainda bem que estou atualizando aos poucos, porque estou cometendo muitos erros....

o de hoje:
mysql.png
usar alias de DBF em MySQL... aí não dá kkkkk

Meu modo de trabalho

Enviado: 05 Dez 2019 17:23
por JoséQuintas
enquanto isso...

Código: Selecionar todos

   WITH OBJECT cnMySql
      :cSql := "SELECT LPAD( IDPEDIDO, 6, '0' ) AS PEDIDO, PDDATEMI, PDCLIFOR," + ;
         " LEFT( JPCADASTRO.CDNOME, 30 ) AS NOME, LEFT( JPTRANSA.TRDESCRI, 12 ) AS TRANSACAO, " + ;
         " PDVALNOT, PDCONF, PDSTATUS, JPNOTFIS.NFNOTFIS, RIGHT( JPNOTFIS.NFFILIAL, 2 ) AS FILIAL " + ;
         " FROM JPPEDIDO " + ;
         " LEFT JOIN JPCADASTRO ON JPPEDIDO.PDCLIFOR = JPCADASTRO.IDCADASTRO " + ;
         " LEFT JOIN JPNOTFIS ON JPPEDIDO.IDPEDIDO = JPNOTFIS.NFPEDIDO " + ;
         " LEFT JOIN JPTRANSA ON JPPEDIDO.PDTRANSA = JPTRANSA.IDTRANSA " + ;
         " WHERE 1=1 " + cFiltro + " ORDER BY PEDIDO DESC LIMIT 2000"
      :Execute()
Deu certo, mas não implementei ainda o seguinte:
Os arquivos de NFE, de certa forma, não tem vínculo com o aplicativo.
Mas... guardo com CNPJ emitente/destinatário e nota fiscal.

Não ficou demorado, porque já tem índice, acrescentar nesse trem:

Código: Selecionar todos

"LEFT JOIN JPNFEKEY ON KKEMINFE=" + StringSql( jpempresa->emCnpj ) + ;
"AND KKDESNFE=JPCADASTRO.CDCNPJ AND KKNOTFIS=JPNOTFIS.NFNOTFIS"
O trem é doido.
consulta de pedidos...
... que busca os 2.000 pedidos mais recentes
... que busca o cadastro do cliente a partir do código nos pedidos
... que busca a transação a partir do código nos pedidos
... que busca a nota fiscal a partir do número de pedido
... que busca a nota eletrônica a partir do CNPJ no cadastro + nota em notas fiscais + CNPJ da empresa
Em 1 segundo o browse tá na tela.
A partir daí, qualquer filtro é instantâneo.

Feliz com o resultado das mudanças.

Se pensar que isso é recurso básico, de muitos anos atrás....
Só resta recuperar o tempo perdido com DBFs...

Meu modo de trabalho

Enviado: 07 Dez 2019 23:47
por JoséQuintas
O SQL cada vez mais tomando conta...

Código: Selecionar todos

SELECT SUM( IF( ESTIPLAN = '1', -ESQTDE, ESQTDE ) ) AS SALDO,
ESITEM AS ITEM, JPITEM.IEDESCRI AS DESCRICAO, JPITEM.IEUNID AS UNIDADE,
JPITEM.IEPROGRU AS GRUPO, JPITEM.IENCM AS NCM, JPITEM.IECEST AS CEST
FROM JPESTOQUE
LEFT JOIN JPITEM ON JPESTOQUE.ESITEM=JPITEM.IDITEM
WHERE ESDATLAN <= '2019-01-01'
GROUP BY ITEM
HAVING SALDO > 0
ORDER BY ITEM

Meu modo de trabalho

Enviado: 16 Dez 2019 04:14
por JoséQuintas
Primeiro relatório mais.... "sofisticado".
Ainda ajustando...
Caminhando pra apagar o estoque em DBF...

Código: Selecionar todos

         IF ConfirmaImpressao()
            WITH OBJECT cnMySql
               :cSql := "SELECT IDESTOQUE, ESITEM, ESCLIFOR, ESDATLAN, ESPEDIDO, ESNUMDOC, ESOBS, ESTIPLAN, ESNUMDEP, " + ;
                  iif( nOpcEmbalagem == 1, "ESQTDE", "ESQTDE / IF( JPITEM.IEQTDCOM < 1, 1, JPITEM.IEQTDCOM )" ) + " AS QTDE, " + ;
                  " JPAUXILIAR.AXDESCRI AS LOCDESCRICAO," + ;
                  " JPITEM.IEDESCRI AS ITEDESCRICAO, " + ;
                  " IF( JPITEM.IEQTDCOM < 1, 1, JPITEM.IEQTDCOM ) AS CONVERSAO" + ;
                  " FROM JPESTOQUE" + ;
                  " LEFT JOIN JPITEM ON ESITEM=JPITEM.IDITEM" + ;
                  " LEFT JOIN JPAUXILIAR ON AXTABELA=" + StringSql( AUX_PROLOC ) + " AND AXCODIGO=JPITEM.IEPROLOC" + ;
                  " WHERE 1=1"
               IF nOpcData == 2
                  :cSql += " AND ESDATLAN < " + DateSql( m_Dataf )
               ENDIF
               IF nOpcCliente == 2
                  :cSql += " AND ESCLIFOR != " + StringSql( mClientei )
               ENDIF
               IF nOpcItem == 2
                  :cSql += " AND ESITEM >= " + StringSql( cItemi )  + " AND ESITEM <= " + StringSql( cItemf )
               ENDIF
               IF nOpcTipoEst != 1
                  :cSql += " AND JPITEM.IETIPO = " + StringSql( Substr( "*SNIAU", nOpcTipoEst, 1 ) )
               ENDIF
               IF nOpcProDep != 1
                  :cSql += " AND JPITEM.IEPRODEP = " + StringSql( mieProDep )
               ENDIF
               IF nOpcProSec != 1
                  :cSql += " AND JPITEM.IEPROSEC = " + StringSql( mieProSec )
               ENDIF
               IF nOpcProGru != 1
                  :cSql += " AND JPITEM.IEPROGRU = " + StringSql( mieProGru )
               ENDIF
               IF nOpcAnp == 3
                  :cSql += " AND JPITEM.IEANP = " + StringSql( cieAnp )
               ELSEIF nOpcAnp == 2
                  :cSql += " AND JPITEM.IEANP IN ( " + ProdutoAnp( "", "", .T. ) + " )"
               ENDIF
               IF nOpcDeposito != 1
                  :cSql += " AND ESNUMDEP=" + StringSql( Str( nOpcDeposito - 1, 1 ) )
               ENDIF
               IF nOpcTransf == 2 .OR. nOpcTransf == 3
                  :cSql += " AND"
                  IF nOpcTransf == 3
                     :cSql += " NOT"
                  ENDIF
                  :cSql += "( ESCFOP LIKE '%905' OR ESCFOP LIKE '%663' OR ESCFOP LIKE '%664' OR ESCFOP LIKE '%906' )"
               ENDIF
               IF nOpcOrdem == 1
                  :cSql += " ORDER BY ESITEM, ESDATLAN, ESTIPLAN DESC"
               ELSE
                  :cSql += " ORDER BY LOCDESCRICAO, ITEDESCRICAO, ESITEM, ESDATLAN, ESTIPLAN DESC"
               ENDIF
               :Execute()
            ENDWITH

Meu modo de trabalho

Enviado: 16 Dez 2019 12:27
por JoséQuintas
Esta parte é legal, aqui já com correção:

Código: Selecionar todos

   iif( nOpcEmbalagem == 1, "ESQTDE", "ESQTDE * IF( JPITEM.IEQTDCOM < 1, 1, JPITEM.IEQTDCOM )" ) + " AS QTDE, " + ;
O que acontece: pode ser caixa com 5 latas de um litro, caixa com 24 latas de meio litro, etc.
Caso o usuário queira em litros ao invés de caixas, esta parte faz a conversão.
Só sobra mesmo para o relatório fazer a impressão.

Pois é... gostando cada vez mais desse troço... que existe há dezenas de anos e eu não usava...

Meu modo de trabalho

Enviado: 16 Dez 2019 14:08
por JoséQuintas
Convém destacar pontos importantes nisso:

Pra quem estranha, seria igual um SET FILTER, mas que no DBF é lento e no SQL é rápido, o WHERE equivale ao SET FILTER TO

E da mesma forma, o ponto interessante é o seguinte:

É comum encontrar

Código: Selecionar todos

SET FILTER TO CODCLI=&variável .AND. DEPTO=&depto
Isso deixa preso à variável.

Pode ser interessante fazer:

Código: Selecionar todos

cFiltro := [CODCLI=] + Str( variável ) + [ .AND. DEPTO=] + Str( Depto )
SET FILTER TO &cFiltro
Qual a diferença?
No primeiro caso, a macro no meio do filtro deixa dependente das variáveis existirem.
No segundo caso, a macro é diretamente no texto, e não depende mais nem da própria variável.
Por não depender de variável, o filtro vai continuar funcionando mesmo que troque de módulo.

Poderia não ser num SET FILTER, mas em algo como:

Código: Selecionar todos

DO WHILE ! Eof()
   IF ! &( cCondicao )
      SKIP
     LOOP
   ENDIF
Isso permitiria um relatório genérico onde só precisaria passar um texto de filtro.
Se for igual mostrei acima, o filtro não depende de variável nenhuma, e vai funcionar em qualquer lugar.
No SQL somos obrigados a usar assim, acabamos usando "esse jeito", mas ele vale pra qualquer situação.

É como eu sempre digo:
Em tudo dá pra aprender alguma coisa.
Na correria, às vezes não percebemos essas "coisinhas".

Também é bom pra mostrar que no final tudo é a mesma coisa.
Não se trata de ser diferente, se trata de acostumarmos a fazer de um jeito, mesmo podendo fazer de outro.
Às vezes porque queremos deixar mais rápido, mas às vezes só porque virou mania mesmo.

É só questão de ir trocando as manias antigas por manias novas...

Também uma coisa que comento sempre: EVITAR USAR MACRO
Os dois casos acima usam macro, mas um deles de certa forma usa mais do que deveria.

Meu modo de trabalho

Enviado: 21 Dez 2019 17:09
por JoséQuintas
Outro SELECT grande.

Código: Selecionar todos

   WITH OBJECT cnMySql
      :cSql := "SELECT LPAD( IDIMPOSTO, 6, '0' ) AS ID, IMTRANSA, IMTRIUF, IMTRICAD, IMTRIPRO, IMCFOP, " + ;
         " IMCFOP, IMIIALI, IMIPIALI, IMIPIICM, IMIPSALI, IMICMCST, IMICMRED, " + ;
         " IMICMALI, IMICMCST, IMICMRED, IMICMALI, IMICSALI, IMFCPALI, IMSUBIVA, " + ;
         " IMSUBRED, IMSUBALI, IMISSALI, IMPISCST, IMPISALI, IMPISENQ, IMCOFCST, " + ;
         " IMCOFALI, IMCOFENQ, IMLEIS" + ;
         " LEFT( AUXTRIUF.AXDESCRI, 15 )  AS TRIUFDES, " + ;
         " LEFT( AUXTRICAD.AXDESCRI, 15 ) AS TRICADDES, " + ;
         " LEFT( AUXTRIPRO.AXDESCRI, 15 ) AS TRIPRODES, " + ;
         " LEFT( AUXIPICST.AXDESCRI, 15 ) AS IPICSTDES, " + ;
         " LEFT( AUXPISCST.AXDESCRI, 15 ) AS PISCSTDES, " + ;
         " LEFT( AUXCOFCST.AXDESCRI, 15 ) AS COFCSTDES, " + ;
         " LEFT( JPTRANSA.TRDESCRI, 15 ) AS TRANSADES, " + ;
         " FROM JPIMPOSTO" + ;
         " LEFT JOIN JPTRANSA ON JPTRANSA.IDTRANSA=JPIMPOSTO.IMTRANSA" + ;
         " LEFT JOIN JPAUXILIAR AS AUXTRIUF  ON AUXTRIUF.AXTABELA="  + StringSql( AUX_TRIUF ) + " AND AUXTRIUF.AXCODIGO=JPIMPOSTO.IMTRIUF" + ;
         " LEFT JOIN JPAUXILIAR AS AUXTRICAD ON AUXTRICAD.AXTABELA=" + StringSql( AUX_TRICAD ) + " AND AUXTRICAD.AXCODIGO=JPIMPOSTO.IMTRICAD" + ;
         " LEFT JOIN JPAUXILIAR AS AUXTRIPRO ON AUXTRIPRO.AXTABELA=" + StringSql( AUX_TRIPRO ) + " AND AUXTRIPRO.AXCODIGO=JPIMPOSTO.IMTRIPRO" + ;
         " LEFT JOIN JPAUXILIAR AS AUXIPICST ON AUXIPICST.AXTABELA=" + StringSql( AUX_IPICST ) + " AND AUXIPICST.AXCODIGO=JPIMPOSTO.IMIPICST" + ;
         " LEFT JOIN JPAUXILIAS AS AUXICMCST ON AUXICMCST.AXTABELA=" + StringSql( AUX_ICMCST ) + " AND AUXICMCST.AXCODIGO=JPIMPOSTO.IMICMCST" + ;
         " LEFT JOIN JPAUXILIAR AS AUXPISCST ON AUXPISCST.AXTABELA=" + StringSql( AUX_PISCST ) + " AND AUXPISCST.AXCODIGO=JPIMPOSTO.IMPISCST" + ;
         " LEFT JOIN JPAUXILIAR AS AUXCOFCST ON AUXCOFCST.AXTABELA=" + StringSql( AUX_PISCST ) + " AND AUXCOFCST.AXCODIGO=JPIMPOSTO.IMCOFCST" + ;
         " ORDER BY TRANSADES"
      :Execute()
É um SELECT simples, apenas pegar códigos e descrições, mas de várias tabelas, e até de uma mesma tabela várias vezes.
A única coisa diferente, neste momento, é que JPAUXILIAR atende várias tabelas diferentes, e precisa ser indicada de forma especial.
Ainda não testei, preciso terminar o fonte, que ainda depende do DBF, mas já apaguei antes de terminar os testes... rs

É direto na versão em uso: só volta a funcionar depois que eu terminar a eliminação de JPIMPOSTO.DBF.

Meu modo de trabalho

Enviado: 21 Dez 2019 21:42
por JoséQuintas
versão final:

Código: Selecionar todos

METHOD GridSelection() CLASS JPIMPOSTOClass

   LOCAL oTBrowse, cnMySql := ADOClass():New( AppConexao() )

   WITH OBJECT cnMySql
      :cSql := "SELECT LPAD( IDIMPOSTO, 6, '0' ) AS ID, IMTRANSA, IMTRIUF, IMTRICAD, IMTRIPRO, IMCFOP, " + ;
         " IMCFOP, IMIIALI, IMIPICST, IMIPIALI, IMIPIICM, IMIPSALI, IMICMCST, IMICMRED, " + ;
         " IMICMALI, IMICMCST, IMICMRED, IMICMALI, IMICSALI, IMFCPALI, IMSUBIVA, " + ;
         " IMSUBRED, IMSUBALI, IMISSALI, IMPISCST, IMPISALI, IMPISENQ, IMCOFCST, " + ;
         " IMCOFALI, IMCOFENQ, IMLEIS, " + ;
         " LEFT( AUXTRIUF.AXDESCRI, 15 )  AS TRIUFDES, " + ;
         " LEFT( AUXTRICAD.AXDESCRI, 15 ) AS TRICADDES, " + ;
         " LEFT( AUXTRIPRO.AXDESCRI, 15 ) AS TRIPRODES, " + ;
         " LEFT( AUXIPICST.AXDESCRI, 15 ) AS IPICSTDES, " + ;
         " LEFT( AUXICMCST.AXDESCRI, 15 ) AS ICMCSTDES, " + ;
         " LEFT( AUXPISCST.AXDESCRI, 15 ) AS PISCSTDES, " + ;
         " LEFT( AUXCOFCST.AXDESCRI, 15 ) AS COFCSTDES, " + ;
         " LEFT( JPTRANSA.TRDESCRI, 15 ) AS TRANSADES " + ;
         " FROM JPIMPOSTO" + ;
         " LEFT JOIN JPTRANSA ON JPTRANSA.IDTRANSA=JPIMPOSTO.IMTRANSA" + ;
         " LEFT JOIN JPAUXILIAR AS AUXTRIUF  ON AUXTRIUF.AXTABELA="  + StringSql( AUX_TRIUF ) + " AND AUXTRIUF.AXCODIGO=JPIMPOSTO.IMTRIUF" + ;
         " LEFT JOIN JPAUXILIAR AS AUXTRICAD ON AUXTRICAD.AXTABELA=" + StringSql( AUX_TRICAD ) + " AND AUXTRICAD.AXCODIGO=JPIMPOSTO.IMTRICAD" + ;
         " LEFT JOIN JPAUXILIAR AS AUXTRIPRO ON AUXTRIPRO.AXTABELA=" + StringSql( AUX_TRIPRO ) + " AND AUXTRIPRO.AXCODIGO=JPIMPOSTO.IMTRIPRO" + ;
         " LEFT JOIN JPAUXILIAR AS AUXIPICST ON AUXIPICST.AXTABELA=" + StringSql( AUX_IPICST ) + " AND AUXIPICST.AXCODIGO=JPIMPOSTO.IMIPICST" + ;
         " LEFT JOIN JPAUXILIAR AS AUXICMCST ON AUXICMCST.AXTABELA=" + StringSql( AUX_ICMCST ) + " AND AUXICMCST.AXCODIGO=SUBSTR( JPIMPOSTO.IMICMCST, 2 )" + ;
         " LEFT JOIN JPAUXILIAR AS AUXPISCST ON AUXPISCST.AXTABELA=" + StringSql( AUX_PISCST ) + " AND AUXPISCST.AXCODIGO=JPIMPOSTO.IMPISCST" + ;
         " LEFT JOIN JPAUXILIAR AS AUXCOFCST ON AUXCOFCST.AXTABELA=" + StringSql( AUX_PISCST ) + " AND AUXCOFCST.AXCODIGO=JPIMPOSTO.IMCOFCST" + ;
         " ORDER BY TRANSADES"
      :Execute()
      oTBrowse := { ;
         { "N.Lanç",     { || :String( "ID", 6 ) } }, ;
         { "Transação",  { || :String( "IMTRANSA", 6 ) + " " + :String( "TRANSADES", 15 ) } }, ;
         { "Trib.UF",    { || :String( "IMTRIUF", 6 )  + " " + :String( "TRIUFDES", 15 ) } }, ;
         { "Trib.Cad",   { || :String( "IMTRICAD", 6 ) + " " + :String( "TRICADDES", , 15 ) } }, ;
         { "Trib.Prod",  { || :String( "IMTRIPRO", 6 ) + " " + :String( "TRIPRODES", 15 ) } }, ;
         { "CFOP",       { || :String( "IMCFOP", 6 ) } }, ;
         { "II.Alíq",    { || Str( :Number( "IMIIALI" ), 6, 2 ) } }, ;
         { "IPI CST",    { || :String( "IMIPICST", 2 ) + " "  + :String( "IPICSTDES", 15 ) } }, ;
         { "IPI Alíq",   { || Str( :Number( "IMIPIALI" ), 6, 2 ) } }, ;
         { "IPI ICM",    { || Str( :Number( "IMIPIICM" ), 6, 2 ) } }, ;
         { "IPI Simp",   { || Str( :Number( "IMIPSALI" ), 6, 2 ) } }, ;
         { "ICMS CST",   { || :String( "IMICMCST", 4 ) + " " + :String( "ICMCSTDES", 15 ) } }, ;
         { "ICMS Red",   { || Str( :Number( "IMICMRED" ), 6, 2 ) } }, ;
         { "ICMS Alíq",  { || Str( :Number( "IMICMALI" ), 6, 2 ) } }, ;
         { "ICMS Simp",  { || Str( :Number( "IMICSALI" ), 9, 5 ) } }, ;
         { "FCP Aliq",   { || Str( :Number( "IMFCPALI" ), 6, 2 ) } }, ;
         { "ST IVA",     { || Str( :Number( "IMSUBIVA" ), 6, 2 ) } }, ;
         { "ST Red",     { || Str( :Number( "IMSUBRED" ), 6, 2 ) } }, ;
         { "ST Alíq",    { || Str( :Number( "IMSUBALI" ), 6, 2 ) } }, ;
         { "ISS Alíq",   { || Str( :Number( "IMISSALI" ), 6, 2 ) } }, ;
         { "PIS CST",    { || :String( "IMPISCST", 2 ) + " " + :String( "PISCSTDES", 15 ) } }, ;
         { "PIS Alíq",   { || Str( :Number( "IMPISALI" ), 6, 2 ) } }, ;
         { "PIS Enq",    { || :String( "IMPISENQ", 3 ) } }, ;
         { "Cofins CST", { || :String( "IMCOFCST", 2 ) + " " + :String( "COFCSTDES", , 15 ) } }, ;
         { "Cof Alíq",   { || Str( :Number( "IMCOFALI" ), 6, 2 ) } }, ;
         { "Cof Enq",    { || :String( "IMCOFENQ", 3 ) } }, ;
         { "Leis",       { || :String( "IMLEIS", 70 ) } } }
      BrowseADO( cnMySql, oTBrowse, "TRANSADES", { || :String( "ID", 6 ) } )
      :CloseRecordset()
   ENDWITH

   RETURN NIL
o browse, apenas 3 partes dele
parte1.png
parte2.png
parte3.png
Trouxe tudo de todas as tabelas.

Meu modo de trabalho

Enviado: 21 Dez 2019 21:48
por JoséQuintas
O fonte pra DBF era menor, não tinha o comando SQL, mas dependia de todos os arquivos abertos.

Código: Selecionar todos

METHOD GridSelection() CLASS JPIMPOSTOClass

   LOCAL nSelect := Select(), oTBrowse

   SELECT jpimposto
   oTBrowse := { ;
      { "N.Lanç",     { || jpimposto->idImposto } }, ;
      { "Transação",  { || jpimposto->imTransa + iif( Encontra( jpimposto->imTransa, "jptransa", "numlan" ), "", "" ) + " " + Left( jptransa->trDescri, 15 ) } }, ;
      { "Trib.UF",    { || jpimposto->imTriUf + " " + Left( AUXTRIUFClass():Descricao( jpimposto->imTriUf ), 15 ) } }, ;
      { "Trib.Cad",   { || jpimposto->imTriCad + " " + Left( AUXTRICADClass():Descricao( jpimposto->imTriCad ), 15 ) } }, ;
      { "Trib.Prod",  { || jpimposto->imTriPro + " " + Left( AUXTRIPROClass():Descricao( jpimposto->imTriPro ), 15 ) } }, ;
      { "CFOP",       { || jpimposto->imCfOp } }, ;
      { "II.Alíq",    { || jpimposto->imIIAli } }, ;
      { "IPI CST",    { || jpimposto->imIpiCst + " "  + Left( AUXIPICSTClass():Descricao( jpimposto->imIpiCst ), 15 ) } }, ;
      { "IPI Alíq",   { || jpimposto->imIpiAli } }, ;
      { "IPI ICM",    { || jpimposto->imIpiIcm } }, ;
      { "IPI Simp",   { || jpimposto->imIpSAli } }, ;
      { "ICMS CST",   { || jpimposto->imIcmCst + " " + Left( AUXICMCSTClass():Descricao( Pad( Substr( jpimposto->imIcmCst, 2 ), 3 ) ), 15 ) } }, ;
      { "ICMS Red",   { || jpimposto->imIcmRed } }, ;
      { "ICMS Alíq",  { || jpimposto->imIcmAli } }, ;
      { "ICMS Simp",  { || jpimposto->imIcsAli } }, ;
      { "FCP Aliq",   { || jpimposto->imFcpAli } }, ;
      { "ST IVA",     { || jpimposto->imSubIva } }, ;
      { "ST Red",     { || jpimposto->imSubRed } }, ;
      { "ST Alíq",    { || jpimposto->imSubAli } }, ;
      { "ISS Alíq",   { || jpimposto->imIssAli } }, ;
      { "PIS CST",    { || jpimposto->imPisCst + " " + Left( AUXPISCSTClass():Descricao( jpimposto->imPisCst ),15 ) } }, ;
      { "PIS Alíq",   { || jpimposto->imPisAli } }, ;
      { "PIS Enq",    { || jpimposto->imPisEnq } }, ;
      { "Cofins CST", { || jpimposto->imCofCst + " " + Left( AUXPISCSTClass():Descricao( jpimposto->imCofCst ), 15 ) } }, ;
      { "Cof Alíq",   { || jpimposto->imCofAli } }, ;
      { "Cof Enq",    { || jpimposto->imCofEnq } }, ;
      { "Leis",       { || jpimposto->imLeis } } }
   FazBrowse( oTBrowse )
   IF LastKey() != K_ESC .AND. ! Eof()
      KEYBOARD jpimposto->idImposto + Chr( K_ENTER )
   ENDIF
   SELECT ( nSelect )

   RETURN NIL
Apesar de tudo, o que dá pra notar entre os dois?
O comando SQL coloca o trabalho pesado sendo feito no servidor, nas bases locais para o servidor.
O aplicativo apenas mostra o que veio do servidor e nada mais.