Clipper On Line • Ver Tópico - Rotina de Extenso
Mudar para estilo Clássico
Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.
Postar uma resposta

Rotina de Extenso

21 Jul 2019 17:26

Refiz minha rotina de extenso de valor, mas ficou interessante.... parece mais .... humana
A parte do idioma em português é que complica a rotina mas tudo bem.

Como é que a gente faz o extenso de um valor?
primeira parte com a palavra reais, e segunda parte centavos
N reais e N centavos

Código:
FUNCTION ze_ExtensoDinheiro( nValor )

   LOCAL cTxt := "", cStrValor, nInteiro, nDecimal

   nValor    := Abs( nValor )
   cStrValor := Str( nValor, 18, 2 )
   nInteiro  := Val( Substr( cStrValor, 1, At( ".", cStrValor ) - 1 ) )
   nDecimal  := Val( Substr( cStrValor, At( ".", cStrValor ) + 1 ) )
   IF nInteiro != 0 .OR. nDecimal == 0
      cTxt += ze_ExtensoNumero( nInteiro ) + " " + iif( nInteiro == 1, "REAL", "REAIS" )
   ENDIF
   IF nDecimal != 0
      IF nInteiro != 0
         cTxt += " E "
      ENDIF
      cTxt += ze_ExtensoNumero( nDecimal ) + " " + iif( nDecimal == 1, "CENTAVO", "CENTAVOS" )
   ENDIF

   RETURN cTxt


E depois?
A gente separa em blocos de três, e acrescenta bilhão, milhão, mil, etc.
N bilhões, N milhões, N mil e N
O detalhe é que coloquei na rotina pra fazer de trás pra frente... vai fazendo até acabar.
E recursividade, sem FOR/NEXT.

Código:
STATIC FUNCTION ze_ExtensoNumero( nValor, nGrupo )

   LOCAL cTxt := "", cStrValor, nCentena, nResto, cTxtGrupo := "", lNegativo
   LOCAL aList := { "", "MIL", "MILHAO", "BILHAO", "TRILHAO", "QUATRILHAO", ;
      "QUINTILHAO", "SEPTILHAO", "OCTILHAO", "NONILHAO", "DECILHAO" }

   hb_Default( @nGrupo, 1 )
   lNegativo := ( nValor < 0 )
   nValor    := Abs( nValor )
   cStrValor := StrZero( nValor, 16 )
   nCentena  := Val( Right( cStrValor, 3 ) )
   nResto    := Val( Substr( cStrValor, 1, Len( cStrValor ) - 3 ) )
   IF nCentena != 0
      IF nCentena > 0
         cTxtGrupo := aList[ nGrupo ]
         IF nCentena > 1
            cTxtGrupo := StrTran( cTxtGrupo, "LHAO", "LHOES" )
         ENDIF
      ENDIF
      cTxt := ze_ExtensoCentena( nCentena ) + " " + cTxtGrupo
   ENDIF
   IF nResto != 0 .AND. nGrupo < Len( aList )
      cTxt := ze_ExtensoNumero( nResto, nGrupo + 1 ) + " E " + cTxt
   ENDIF
   IF nGrupo == 1
      IF nValor == 0
         cTxt := "ZERO"
      ENDIF
      cTxt := iif( lNegativo, "*NEGATIVO* ", "" ) + AllTrim( cTxt )
   ENDIF

   RETURN cTxt


Aí vém o grupo de 3 números... 999
Como a gente faz? a gente vê 100, 200, pode ser CEM ou CENTO, ou duzentos, trezentos, ... e alguma coisa
A rotina pega esse primeiro número, e se o que sobra for diferente de zero, chama a de 2 números... 99
exemplo... duzentos e N, ela preenche o duzentos e chama a rotina de dezena pra preencher o N

Código:
STATIC FUNCTION ze_ExtensoCentena( nValor )

   LOCAL aList := { "CENTO", "DUZENTOS", "TREZENTOS", "QUATROCENTOS", ;
      "QUINHENTOS", "SEISCENTOS", "SETECENTOS", "OITOCENTOS", ;
      "NOVECENTOS" }
   LOCAL nCentena, nDezena, cTxt := ""

   nCentena := Int( nValor / 100 )
   nDezena  := Mod( nValor, 100 )
   IF nValor > 0
      IF nCentena == 1 .AND. nDezena == 0
         cTxt += "CEM"
      ELSE
         IF nCentena != 0
            cTxt += aList[ nCentena ]
         ENDIF
         IF nDezena != 0
            IF nCentena != 0
               cTxt += " E "
            ENDIF
            cTxt += ze_ExtensoDezena( nDezena )
         ENDIF
      ENDIF
   ENDIF

   RETURN cTxt


Aí vém a dezena... se for até 19 tem descrição própria, senão é vinte e n, trinta e N, etc.
Ela preenche a primeira parte e chama a rotina de unidade para o N

Código:
STATIC FUNCTION ze_ExtensoDezena( nValor )

   LOCAL aList := { "DEZ", "VINTE", "TRINTA", "QUARENTA", "CINQUENTA", "SESSENTA", ;
      "SETENTA", "OITENTA", "NOVENTA" }
   LOCAL cTxt := "", nDezena, nUnidade

   IF nValor > 0
      nDezena := Int( nValor / 10 )
      nUnidade := Mod( nValor, 10 )
      IF nValor < 20
         cTxt += ze_ExtensoUnidade( nValor )
      ELSE
         cTxt += aList[ nDezena ]
         IF nUnidade != 0
            cTxt += " E " + ze_ExtensoUnidade( nUnidade )
         ENDIF
      ENDIF
   ENDIF

   RETURN cTxt


E finalmente a unidade.

Código:
STATIC FUNCTION ze_ExtensoUnidade( nValor )

   LOCAL aList := { "UM", "DOIS", "TRES", "QUATRO", "CINCO", "SEIS", ;
      "SETE", "OITO", "NOVE", "DEZ", "ONZE", "DOZE", "TREZE", ;
      "QUATORZE", "QUINZE", "DEZESSEIS", "DEZESSETE", "DEZOITO", ;
      "DEZENOVE" }

   RETURN aList[ nValor ]


O que achei interessante é que parece o jeito "humano" de fazer.
Foi mais como ensinar o jeito humano ao computador, do que traduzir o jeito humano para o jeito do computador.

E é nisso que muito programador apanha.
O que inventam pra facilitar programação é justamente pra trazer esse jeito humano para o fonte.
Só que o programador fica pensando em computador, e não em humanos.

Achei legal o resultado. A parte mais chata, ou com mais código fonte, é mesmo acertar a parte de português.

milhão ou milhões? bom... se maior que 1 é milhões.
olhem lá o fonte, tá igual o texto... se maior que 1 é ..lhões.
Código:
         IF nCentena > 1
            cTxtGrupo := StrTran( cTxtGrupo, "LHAO", "LHOES" )
         ENDIF

Rotina de Extenso

21 Jul 2019 17:29

E a parte legal de separar os números, é que dá pra usar numa rotina de extenso de data, por exemplo.

Código:
   ELSEIF ValType( xValue ) == "D"
      IF ! xFull
         cTxt := StrZero( Day( xValue ), 2 ) + " DE " + ze_ExtensoMes( xValue ) + " DE " + StrZero( Year( xValue ), 4 )
      ELSE
         cTxt := ze_ExtensoNumero( Day( xValue ) )
         cTxt += " DE " + ze_ExtensoMes( xValue ) + " DE "
         cTxt += ze_ExtensoNumero( Year( xValue ) )
         DO WHILE Space(2) $ cTxt
            cTxt := StrTran( cTxt, Space(2), Space(1) )
         ENDDO
      ENDIF
   ENDIF
Postar uma resposta