Página 4 de 7

Práticas que facilitam programar Clipper/Harbour

Enviado: 06 Nov 2015 14:55
por JoséQuintas
Complementando esse último trecho do post anterior:

Porque #define, não dá no mesmo?

Código: Selecionar todos

SEEK "PISCCC" + codigo

SEEK AUX_PISCCC + codigo
Considerando o #include e a compilação -w3 -es2

No primeiro caso, só vamos saber do erro quando o cliente avisar que deu errado.

No segundo caso, o compilador já rejeita o fonte, porque já enxergou que está errado.

Práticas que facilitam programar Clipper/Harbour

Enviado: 08 Nov 2015 01:05
por Nascimento
muito boas essas aulas amigo parabens vai abrindo nossa visão sobre como melhorar nossas tecnicas de programaçao

Práticas que facilitam programar Clipper/Harbour

Enviado: 20 Nov 2015 22:08
por JoséQuintas
No meu aplicativo tenho lá a estrutura do DBF.
Ao carregar o sistema, ele verifica se a estrutura está ok.
Então, agora acrescente o campo do CEST nos produtos:

Código: Selecionar todos

      { "IECEST",    "C", 8 }, ;
Pronto, quando o cliente atualizar versão, vai ser criado o campo no arquivo DBF.

E não é só isso, no programa principal tenho lá a versão dos DBFs:

Código: Selecionar todos

   AppVersaoDbf( 20151105 )
Quando o cliente atualizar versão, se esse número for diferente do que ele usa, ao executar pela primeira vez, isso faz com que o aplicativo saiba que haverá mudança de estrutura.

Então o aplicativo primeiro "expulsa" os usuários do aplicativo, pra poder fazer o backup e depois atualizar estrutura.
Enquanto não conseguir fazer isso, ninguém mais usa o aplicativo.

Então faz backup, atualiza estrutura (já eliminando índices do arquivo modificado), e já cria os índices para garantir que se existem novos índices em novos campos, já sejam criados.

No MySQL já comecei a criar esse tipo de coisa, mas não tão flexível quanto as rotinas pra DBF.

Isso facilita muito.
Nada de mexer ou ajustar DBF em cliente manualmente.
É atualizar versão e tudo é ajustado automaticamente.

Comecei com isso quando tinha vários aplicativos diferentes, cada cliente com o seu.
Cada cliente com estrutura de arquivo diferente, telas de cadastro diferentes, etc.
Agrupei todos os EXEs em um, agrupei definições de estrutura, coloquei essa atualização automática.
Com o tempo, fui padronizando todos.
Deixava para o aplicativo modificar estruturas, fazer conversões aonde necessário, etc.
Até mesmo se precisar liberar uma opção pra determinado usuário, de determinada empresa, consigo acrescentar para o aplicativo fazer automático.

Isso resume tudo a falar para o cliente: atualize o aplicativo e já fica resolvido.

Lógico, já aconteceram imprevistos no passado.
Quando isso ocorreu, bastou utilizar o backup que o sistema havia feito.
E já alterar nos fontes para que não acontecesse novamente.

Lembro de uma única vez onde a estrutura no EXE foi problema:
Era um 486 SX, com problemas de mau contato, e o EXE se alterava ao ser carregado, considerando estruturas diferentes, e convertia sem precisar, modificando estruturas.
Esse tipo de coisa não se esquece...
Chamou a atenção o monitor piscando, então parecia normal, um monitor com problema.
Na prática era a placa mãe do computador com problema que fazia o monitor piscar.
Já não lembro em que ano foi, mas 486 SX é de muito tempo atrás.

Nesse caso, foi só retornar o backup que o aplicativo fez antes de atualizar.

De lá pra cá, já não lembro mais quando precisei de um backup.
Mas isso continua sendo feito.

Práticas que facilitam programar Clipper/Harbour

Enviado: 21 Nov 2015 11:25
por asimoes
Excelentes informações Quintas,

Com a relação ao dbf, o único problema de acrecentar um campo novo em um dbf, que provavelmente será populado a partir da atualização do sistema, é o passado, registros com conteúdo vazio, seria muito interessante se houvesse um campo tipo varchar no dbf.

Práticas que facilitam programar Clipper/Harbour

Enviado: 21 Nov 2015 17:40
por JoséQuintas
O mais parecido a varchar seria o campo memo, mas ocupando no mínimo 10 caracteres.
Na SIXCDX havia alguns recursos a mais aproveitando o campo memo.

Mas registro de tamanho variável tem seus prós e contras.
Lembro de ter lido que mesmo no MySQL, registros de tamanho fixo deixam as coisas mais rápidas.
Só imaginar que sendo fixo, a posição do milésimo registro seria algo como tamanho * 1000, o que agiliza posicionamentos.

Ainda sobre o campo novo, em alguns casos a conversão grava valor no campo novo.
Tem casos aonde é possível fazer isso, e tem casos aonde é necessário um conteúdo pra que novas rotinas/relatórios funcionem.

Práticas que facilitam programar Clipper/Harbour

Enviado: 28 Nov 2015 11:08
por JoséQuintas
Tenho no aplicativo uma variável pública que guarda o nome do módulo em uso: m_Prog
Deixo os relatórios com muitas opções disponíveis, pra alterar o comportamento dos relatórios.
Aconteceu agora de precisar limitar opções no relatório, porque tem opções que só interessam ao gerente.

O nome do módulo é PNOT0100.
Pra facilitar esse controle, acabei criando um módulo novo.

Código: Selecionar todos

PROCEDURE PNOT0101
   DO PNOT0100
   RETURN
Sim, só isso mesmo no módulo novo.

Já no PNOT0100, acrescentei algumas coisas simples:

Código: Selecionar todos

   acTxtOpcoes := { "Sem Rentabilidade" }
   IF m_Prog == "PNOT0100"
      AAdd( acTxtOpcoes, "Com Rentabilidade" )
   ENDIF
Resultado:
Se chamar o módulo PNOT0100, o usuário pode escolher se imprime o cálculo de rentabilidade
Se chamar o módulo PNOT0101, não há opção de escolha.

Como todo controle de módulos é por senha, então é liberar PNOT0101 para o usuário comum, e PNOT0100 para o gerente.

Melhor do que criar outro programa com opções diferentes, e aumentar o trabalho na hora de fazer ajustes..

m_Prog é uma variável pública, preenchida pelo programa de menu, ao escolher uma opção.
Continuo usando em multithread, sem problemas.

Solução simples, apenas fiz uso de uma variável do aplicativo, sem precisar nada avançado ou complicado.

Práticas que facilitam programar Clipper/Harbour

Enviado: 28 Nov 2015 11:53
por JoséQuintas
Uma tela com configuração ilimitada de parâmetros, por exemplo uma digitação no caixa.
Parece algo complicado, não?
Vamos a um cadastro simples, chama-lo de TIPOLANC:

Código: Selecionar todos

CODIGO, N, 1
DESCRICAO, C, 30
PARAMETROS,C,100
A tela de digitação de lançamentos do caixa, simples também:

Código: Selecionar todos

Tipo de lançamento....:
Histórico....:
Valor...:
Vamos supor que dependendo do tipo de lançamento, queira informações diferentes.
Pode querer código de cliente, número de docto, número de banco, etc.
Quer que fique tudo automático, configurável.

Agora que tal este fonte:

Código: Selecionar todos

@ 1, 0 SAY "Tipo de lançamento...:" GET nTipoLancto VALID ValidaTipoLancto( nTipoLancto )
READ
SELECT TIPOLANC
SEEK nTipoLancto
@ 2, 0 SAY "Histórico.....:" GET cHistorico PICTURE "@!"
IF "DIGITACLIENTE" $ tipolanc->Parametro
   @ Row() + 1, 0 SAY "cliente..:" GET nCliente PICTURE "999999" VALID ValidaCliente( @nCliente )
ENDIF
IF "DIGITADOCTO" $ tipolanc->Parametro
   @ Row() + 1, 0 SAY "Documento..:" GET nDocto PICTURE "999999" VALID ValidaDocto( @nDocto )
ENDIF
IF "DIGITABANCO" $ tipolanc->Parametro
   @ Row() + 1, 0 SAY "banco...:" GET nBanco PICTURE "999999" VALID ValidaBanco( @nBanco )
ENDIF
@ Row() + 1, 0 SAY "Valor...:" GET nValor PICTURE "999999.99"
READ

cDescricao := Trim( cDescricao )
IF nBanco != 0
   SELECT banco
  SEEK nBanco
   cDescricao := " no banco " + banco->Nome
ENDIF
IF nCliente != 0
   SELECT cliente
   SEEK nCliente
   cDescricao += " do cliente " + cliente->Nome
ENDIF
IF nDocto != 0
   cDescricao += " docto " + Str( nDocto )
ENDIF
Pronto.
Agora só cadastrar lá no TIPOLANC, por exemplo:
1, Depósito no Banco, "DIGITABANCO"
2, Recebido de cliente, "DIGITACLIENTE"
3, Emissão de cheque, "DIGITABANCO,DIGITADOCTO"

Para cada tipo de lançamento, um comportamento diferente.

Agora só expandir isso pra pedidos, nota fiscal, impostos, etc. e teremos tudo se auto-configurando.
Recursos avançados? Sim, o CÉREBRO, atende todas as rotinas.
E o limite é a imaginação.

Práticas que facilitam programar Clipper/Harbour

Enviado: 29 Nov 2015 15:36
por JoséQuintas
Certas coisas são pessoais, aqui um exemplo pessoal que usei.
Crio uma lista de códigos pra pesquisar no MySQL:

Desta forma ficaria errado:
"... WHERE OR CODI=1 OR CODI=2 OR CODI=3"

Código: Selecionar todos

cSql := "... WHERE "
FOR nCont = 1 TO 3
   cSql += " OR CODI=" + Str( nCont )
NEXCT
Desta forma fica correto, não colocando OR no primeiro, mas achei que visualmente o fonte não ficou bom.
"... WHERE CODI=1 OR CODI=2 OR CODI=3"

Código: Selecionar todos

cSql := "... WHERE "
FOR nCont = 1 TO 3
   cSql += iif( nCont == 1, "", " OR" ) + " CODI=" + Str( nCont )
NEXT
Acabei preferindo assim:

Código: Selecionar todos

cSql := "... WHERE 1=2"
FOR nCont = 1 TO 3
   cSql += " OR CODI=" + Str( nCont )
NEXT
Acaba sendo o mesmo resultado, já que 1=2 não seleciona nada.
"... WHERE 1=2 OR CODI=1 OR CODI=2 OR CODI=3"
Achei mais interessante desse jeito, pro fonte ficar mais simples.
Quem vai ter que enxergar o fonte sou eu, e não o compilador, então escolhi o que seria melhor pra mim.
Questão pessoal apenas.

Muitas vezes o comando não se limita a isso, então tem horas que convém facilitar.


Por falar nisso, uma dica interessante:
Para o SQL não importa CR+LF, ele é desprezado.
Mas se tiver que conferir alguma coisa, separar em linhas ajuda, por exemplo pra conferir na tela.

Código: Selecionar todos

MsgBox( cSql )
"SELECT
ENDERECO
FROM IMOVEL
WHERE
( 1=2 OR CODI=1 OR CODI=2 )"
Lembrem-se sempre disso:
O fonte não se limita ao que é melhor para o compilador, ou a ganhar segundos de execução ou alguns bytes de memória.
Cada um tem que encontrar sua forma confortável de trabalhar com o fonte.
Mexer nos fontes tem que ser algo confortável, e não algo terrível.

Práticas que facilitam programar Clipper/Harbour

Enviado: 29 Nov 2015 21:00
por JoséQuintas
Tempos atrás peguei um aplicativo pra converter.
A primeira coisa era referente a impressão.

Num primeiro momento alterei tudo pra:

Código: Selecionar todos

PrintBegin()
... // relatório
PrintEnd()
A situação curiosa foi esta:

Ao invés de trocentos relatórios pendentes pra resolver, fiquei apenas com 2 funções pra resolver.
Ao ajustar as funções, tudo resolvido.

Práticas que facilitam programar Clipper/Harbour

Enviado: 29 Nov 2015 21:16
por Toledo
O que pode ajudar é arquivo CH:

Código: Selecionar todos

#command SET PRINTER TO                => ;
               Set_PrinterOFF()

#command SET PRINTER TO <(file)> [<add: ADDITIVE>]    => ;
               Set_PrinterON( <(file)>, <.add.> )
Assim nem precisa alterar os PRG dos relatórios.

Abraços,

Práticas que facilitam programar Clipper/Harbour

Enviado: 29 Nov 2015 22:57
por JoséQuintas
Bem pensado, mas seria no SET DEVICE TO PRINT, que é o mais comum.

Práticas que facilitam programar Clipper/Harbour

Enviado: 12 Jan 2016 14:52
por JoséQuintas
Usando um termo que usei num post recente:

Dividir rotinas por "assunto" / funcionalidade.

Código: Selecionar todos

IF .NOT. AbreArquivos()
   RETURN
ENDIF
cOpcao := " "
DO WHILE .T.
   TelaCadastro()
   cOpcao := SelecionaOpcao()
   DO CASE
   CASE cOpcao == "I" // inclusão
      TelaInclusao()
   CASE cOpcao == "A" // alteração
      TelaAlteracao()
   ENDCASE
ENDDO
CLOSE DATABASES
Esse exemplo poderia ser de um cadastro, tela de pedidos, etc.
Assunto 1: abrir arquivos, está em AbrirArquivos()
Assunto 2: A tela com as informações está em TelaCadastro()
Assunto 3: A escolha de opções está em SelecionaOpcao()
Assunto 4: Inclusão em TelaInclusao()
Assunto 5: alteração em TelaAlteracao()
Assunto 6: tudo isso funcionando

A rotina que faz tudo tem o que precisa.
Ela chama a abrirarquivos() e só precisa saber se deu certo.
Ela chama a que coloca as informações na tela
E ela chama a que faz com o que o usuário escolha uma opção, só precisa saber o que foi que o usuário escolheu, não importa se é texto, gráfico, botões, setinha, etc.

Então, cada "assunto" tratado no seu lugar.

Tá muito dividido... vai ser difícil achar erro.

Ué... vamos pensar nos tipos de erro, e como localizar:

deu erro de arquivo não aberto.
Só olhar o bloco de abrir arquivo, tá lá bem identificado no fonte com o nome AbreArquivos()

deu erro que escolhe uma opção e faz outra
Conferir no fonte, se está usando a letra certa de retorno das opções.
E conferir no SelecionaOpcoes() se está selecionando certo.
Sö pode ser uma coisa ou outra.

deu erro durante a inclusão
olhar a rotina de inclusão.

Então, fonte maior ou não, tá até mais fácil chegar ao local do erro.
E está fácil alterar, porque a rotina só trata daquele "assunto".

Lógico, não vá fazer isso multiplicando variáveis públicas e private.
Procure deixar as variáveis de uma rotina dentro da rotina, LOCAL, não espalhar variáveis public/private pelas várias funções.
Cada "assunto" tem que tratar suas próprias variáveis.
Uma função recebe variáveis, e retorna variáveis. Use isto pra transferir informação entre as rotinas.
Sim, uma função normalmente só retorna uma variável, mas pode retornar array ou receber parâmetros por referência e modificá-los.
Sempre foi assim, há mais de 20 anos, nenhuma novidade.

Práticas que facilitam programar Clipper/Harbour

Enviado: 13 Jan 2016 20:23
por JoséQuintas
FOR EACH

O uso do FOR EACH é legal, deixa o fonte mais limpo.
Aqui padronizei como oElement quase sempre.

Num array, a variável do FOR EACH vai conter um elemento do array.

Compare:

Código: Selecionar todos

FOR nCont = 1 TO Len( oFrm:Visual:Botoes )
   ? oFrm:Visual:Botoes[ nCont, 1 ], oFrm:Visual:Botoes[ nCont, 2 ], oFrm:Visual:Botoes[ nCont, 3 ]
NEXT

Código: Selecionar todos

FOR EACH oElement IN oFrm:Visual:Botoes
   ? oElement[ 1 ], oElement[ 2 ], oElement[ 3 ]
NEXT
Em algo mais conhecido, em Directory():

Código: Selecionar todos

FOR EACH oElement IN Directory( "*.*" )
   ? oElement[ F_NAME ], ? oElement[ F_SIZE ]
NEXT
Em strings, cada variável do FOR EACH vai ser uma letra.

Código: Selecionar todos

FOR EACH cLetra IN cTexto
      ? cLetra
NEXT
compare

Código: Selecionar todos

FOR nCont = 1 TO Len( cTexto )
   ? Substr( cTexto, nCont, 1 )
NEXT

Práticas que facilitam programar Clipper/Harbour

Enviado: 14 Jan 2016 08:30
por asimoes
Quintas,

São recursos do harbour que facilitam a programação.

Práticas que facilitam programar Clipper/Harbour

Enviado: 14 Jan 2016 09:06
por asimoes
Quintas,

Como ficaria um FOR EACH para este caso:

Código: Selecionar todos

FOR nIdx := 1 to 25 STEP 2
    nLinha ++
    RestScreen(nIdx, 0, nIdx+1, 79, cTAbertura[nLinha])
    SysWait(.01)
NEXT