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.