Olá pessoal…
Hoje vou mostrar uma rotina desenvolvida para converter valores gerados pelo Soma1 (como por exemplo, após o 99, vir o 9A, depois o 9B, 9C, 9D até chegar no ZZ).
Foram desenvolvidas duas rotinas, a primeira converte 100% dos casos, porém é bem mais demorada, pois é feito um While, percorrendo até chegar no valor do soma1.
A segunda, pelos testes realizados, converte 100% quando o valor tem 1 posição ou duas (ou seja, no tamanho máximo de “ZZ”), a partir de 3 posições, o fator fica impreciso no valor de AB0, pois não consegui chegar em um fator de conversão exato.
Abaixo o código desenvolvido com as duas funções.
//Bibliotecas
#Include "Protheus.ch"
/*/{Protheus.doc} zCnvSoma1
Função de conversão do Soma1
@type function
@author Atilio
@since 03/08/2016
@version 1.0
@param cValor, character, Valor do Soma1
@return nValConv, Valor convertido
@example
u_zCnvSoma1("ZZZ")
/*/
User Function zCnvSoma1(cValor)
Local aArea := GetArea()
Local nValConv := 0
Local cAtual := ""
Default cValor := "0"
//Definindo o atual como 0
cAtual := StrZero(0, Len(cValor))
cAtual := StrTran(cAtual, '0', '9')
//Enquanto o valor atual for diferente do parâmetro
While cAtual != cValor
nValConv++
cAtual := Soma1(cAtual)
EndDo
RestArea(aArea)
Return nValConv
/*/{Protheus.doc} zCnvTipo2
Função para conversão do Soma1 (sem utilizar o While)
@type function
@author Atilio
@since 03/08/2016
@version 1.0
@param cNumero, character, Valor do Soma1
@return nValor, Valor Convertido
@example
u_zCnvSoma1("ZZ")
@obs Essa função não foi finalizada 100%!
Não foi encontrado um fator exato de conversão, em testes realizados, com valores de tamanho 1 e 2, a função funciona
perfeitamente (por exemplo, A0, ZZ, Z, M9, etc), porém para valores de tamanho 3 para cima não.
Para valores de tamanho 1, a rotina funciona
Para valores de tamanho 2, a rotina funciona
Para valores de tamanho 3, a inconsistência começa partir do AB0 (após o AAZ) - 2357 registros
Para valores de tamanho 4, a inconsistência começa partir do 9AB0 (após o 9AAZ) - 11356 registros
/*/
User Function zCnvTipo2(cNumero)
Local aArea := GetArea()
Local nValor := 0
Local lSoNumero := .T.
Local nAtual := 0
Local cAscii := ""
Local nPosIni := 0
Local cCaract := ""
Local nValAux := 0
Local cZeros := ""
Local cAscii := ""
cNumero := Upper(cNumero)
//Percorre os valores
For nAtual := 1 To Len(cNumero)
cCaract := SubStr(cNumero, nAtual, 1)
//Se tiver alguma letra no numero
If cCaract $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
If nPosIni == 0
nPosIni := nAtual
EndIf
lSoNumero := .F.
Exit
EndIf
Next
//Se tiver somente numero, converte com Val
If lSoNumero
nValor := Val(cNumero)
Else
nValor := 0
//Percorre os valores
For nAtual := 1 To Len(cNumero)
cCaract := SubStr(cNumero, nAtual, 1)
cZeros := Replicate("0", Len(cNumero)-nAtual)
//Se tiver alguma letra no numero
If cCaract $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
cAscii := cValToChar(Asc(cCaract) - 64 + 9)
//Se for a partir da segunda posição e não for a última
If nAtual > nPosIni .And. nAtual != Len(cNumero)
nValAux := Val(cAscii + cZeros) + Iif(nAtual != Len(cNumero), 26 * (Asc(cCaract) - 64), 0)
nValAux *= Val(cAscii)
nValAux += (26 + Val(cAscii))
nValor += nValAux
Else
nValor += Val(cAscii + cZeros) + Iif(nAtual != Len(cNumero), 26 * (Asc(cCaract) - 64), 0)
EndIf
//Se for somente números
Else
//Se for a partir da segunda posição e não for a última
If nAtual > nPosIni .And. nAtual != Len(cNumero)
nValor += Val(cCaract + cZeros) + (36 * 26) + (26*Val(cCaract))
Else
nValor += Val(cCaract + cZeros)
EndIf
EndIf
Next
EndIf
RestArea(aArea)
Return nValor
Bom pessoal, por hoje é só.
Abraços e até a próxima.

Existe a função DecodSoma1 que faz isso e é muito rápido.
Opa, valeu pelo adendo Gustavo, realmente não conhecia essa.
Pode ser que a rotina seja nova, criada após 2016 (ano que publiquei esse artigo).
Grande abraço.
Danilo, segue uma contribuição, pois já me servi várias vezes dos seus exemplos.
Não achei documentação sobre o DecodSoma1, mas ele não serviu no momento, pois não funcionou como eu esperava, ao passar uma string com 2 dígitos, não somou como no Soma1, talvez tenha algum parâmetro. Bem o Soma1, até que atenderia, mas não gostei da sequência dele após o 99, é usado de 9A a 9Z, porque não usar também para 00 a 0Z, 10 a 1Z…, foi o que fiz com as funções abaixo, assim somando os dígitos 0 a 9 e A a Z, temos 36, usando 2 dígitos, 36 X 36 = 1296 combinações, considerando o ’00’:
…
cSquenc := ’00’
cSquenc := fXSoma1(cSequenc)
…
//#############################################################################
// Função: fXSoma1 Autor: José Murilo: 13/10/2020
// Descrição: Fornecer sequência alfanumérica com os dígitos 0 a 9 e A a Z
//—————————————————————————–
// ASCII 048 a 057 números 0 – 9
// ASCII 065 a 090 letras A – Z maiúsculas
// A vantagem sobre o Soma1 é que podemos ter até 1296 combinações contra 1035,
// usando apenas 2 dígitos. 00 a 0Z, 10 a 1Z, 20 a 2Z, 30 a 3Z…
//#############################################################################
static function fXSoma1(pcNum)
Local cRet as character
Local aNum as array
aNum := {substr(pcNum,1,1), substr(pcNum,2,1)}
if aNum[2] == ‘Z’
aNum[1] := fXPrxDig(aNum[1])
aNum[2] := fXPrxDig(aNum[2])
else
aNum[2] := fXPrxDig(aNum[2])
endif
cRet := aNum[1] + aNum[2]
return(cRet)
//#############################################################################
// Função…: fXPrxDig Autor: José Murilo: 13/10/2020
// Descrição: Retorna o próximo dígito da sequência alfanumérica com os dígitos.
// 0 a 9 e A a Z
//—————————————————————————–
// ASCII 048 a 057 números 0 – 9
// ASCII 065 a 090 letras A – Z maiúsculas
//#############################################################################
static function fXPrxDig(pcDig)
Local cRet as character
if pcDig == ‘9’
cRet := ‘A’
elseif pcDig == ‘Z’
cRet := ‘0’
else
cRet := chr(asc(pcDig)+1)
endif
return(cRet)
Abraços, obrigado por compartilhar e parabéns pelo trabalho no blog.
Opa, grande José.
Obrigado pelo comentário e feedback jovem.
Pode apostar que sua contribuição será de grande valia.
Abração e obrigado mais uma vez.
“Futucando” alguns fontes antigos da LIB, encontrei a função RetAsc() que é usada nas rotinas que convertem esse tipo de campo nos grids:
Sintaxe: RetAsc(cString, nTamanho, lVolta) onde lVolta == .T. faz o processo contrário (numero para letra)
Opa, obrigado pelo comentário e contribuição jovem.
Grande abraço.