DNATech :: **“SX1 não tem WHEN… mas será que o problema é esse mesmo?” 🤔**

**“SX1 não tem WHEN… mas será que o problema é esse mesmo?” 🤔**

Se você trabalha com Protheus, já deve ter passado por isso:

Campo SX1 (`Pergunte / MV_PARxx`) com múltiplos valores:

```text
SP,TO
```

E no `VALID` você precisa saber:

* o usuário **adicionou**?
* ele **removeu**?
* ou **substituiu tudo**?

👉 E aí vem o problema clássico:
**o SX1 não tem WHEN**

---

## 💣 PROBLEMA

No `X1_VALID`, você recebe apenas o valor atual.

Sem contexto.

Sem histórico.

Sem saber o que o usuário fez.

---

## 🧠 MAS TEM UM DETALHE QUE MUITA GENTE IGNORA

A própria TOTVS já resolveu esse problema… parcialmente.

Quando o SX1 usa:

```text
X1_GSC = "R" (Range)
```

👉 o sistema já mantém estado
👉 já controla interação
👉 já resolve o “antes vs depois”

Ou seja…

> **o Protheus SABE lidar com isso**

---

## ⚠️ ONDE QUEBRA

Quando usamos:

```text
X1_GSC = "G" (Get)
```

👉 esse comportamento simplesmente não existe

Resultado:

* não há controle de estado
* não há inferência de alteração
* o `X1_VALID` vira um tiro no escuro

---

## 💡 SOLUÇÃO

Em vez de tentar adivinhar no `X1_VALID`, a ideia foi outra:

> **tratar como problema de estado (igual o Range já faz)**

---

## 🧠 COMO FUNCIONA

A solução faz uma **reconciliação de estado** baseada em:

* snapshot do valor anterior (armazenado em JSON)
* valor atual informado
* inferência de intenção

---

## ⚙️ REGRAS

```text
Se atual ⊆ anterior  -> REMOÇÃO / EDIÇÃO
Se atual ⊄ anterior  -> ADIÇÃO (merge incremental)
Se atual vazio       -> LIMPEZA TOTAL
```

---

## 🔧 IMPLEMENTAÇÃO

Estado mantido em memória:

```advpl
static __jMergeSX1SelState := JSONObject():New()
```

Métodos:

```advpl
MergeSX1SelState(cMVParameter, cToken)
MergeSX1SelStateUpdate(cMVParameter)
MergeSX1SelStatePurge(cMVParameter)
```

---

## 🧪 USO

```advpl
Pergunte(cPerg, .F.)

// Snapshot inicial (simulando WHEN)
MDSX1Tools():MergeSX1SelStateUpdate("MV_PAR07")

// X1_VALID
MDSX1Tools():MergeSX1SelState("MV_PAR07")

// Limpeza
MDSX1Tools():MergeSX1SelStatePurge("MV_PAR07")
```

---

## ✅ RESULTADO

```text
SP + TO  -> SP,TO
SP,TO -> TO
SP,TO -> "" 
```

---

## ⚠️ LIMITAÇÃO (sem mágica)

```text
Last = SP,TO
Atual = RJ
```

👉 Pode ser substituição
👉 Pode ser adição

Sem WHEN real → impossível saber 100%

(assumimos ADIÇÃO por padrão)

---

## 🚀 O PONTO PRINCIPAL

Isso aqui não é “gambiarra”.

É:

> **estender para o GET (G) um comportamento que já existe no RANGE (R)**

---

## 🧠 CONCLUSÃO

A TOTVS já mostrou o caminho com `X1_GSC = "R"`.

Essa solução só aplica o mesmo princípio onde ele faz falta:

👉 `X1_GSC = "G"` com múltiplos valores

##💡DNATech: 

```advpl
#include "totvs.ch"
#include "parmtype.ch"

static __jMergeSX1SelState:=JSONObject():New() as json

/*
            _  _                  _
 ___ __  __/ || |_   ___    ___  | | ___
/ __|\ \/ /| || __| / _ \  / _ \ | |/ __|
\__ \ >  < | || |_ | (_) || (_) || |\__ \
|___//_/\_\|_| \__| \___/  \___/ |_||___/
class SX1Tools

The source code `SX1Tools.tlpp` is based on a more detailed and feature-rich
source code, `dna.tech.sx1.tools.tlpp`, and implements part of the functionalities
present in the latter.

Released to Public Domain.
-----------------------------------------------------------------------
Biblioteca utilitária para manipulação de parâmetros SX1 com suporte a
seleção múltipla simulando comportamento de WHEN via X1_VALID quando o
valor de X1_GSC=="G".

-----------------------------------------------------------------------
PROBLEMA
-----------------------------------------------------------------------
Campos SX1 (Pergunte/MV_PARxx) não possuem evento WHEN para controle
de estado inicial. Em cenários de seleção múltipla (ex: "SP,TO"),
não é possível distinguir diretamente no VALID se o usuário:

    - adicionou novos valores
    - removeu valores existentes
    - substituiu completamente a seleção

Isso causa comportamento incorreto ao tentar acumular ou limpar valores.

-----------------------------------------------------------------------
SOLUÇÃO
-----------------------------------------------------------------------
Esta implementação realiza uma reconciliação de estado baseada em:

    - snapshot do valor anterior (armazenado em JSON)
    - valor atual informado pelo usuário
    - inferência de intenção via comparação (subset vs merge)

Regras aplicadas:

    * Se atual $ anterior  -> REMOÇÃO / EDIÇÃO
    * Se atual !$ anterior -> ADIÇÃO (merge incremental)
    * Se atual vazio       -> LIMPEZA TOTAL

O controle de estado é mantido em memória através de:

    static __jMergeSX1SelState := JSONObject():New()

permitindo suporte a múltiplos parâmetros simultaneamente.

-----------------------------------------------------------------------
MÉTODOS DISPONÍVEIS
-----------------------------------------------------------------------

MergeSX1SelState(cMVParameter, cToken) --> lSuccess
    Realiza o merge inteligente da seleção múltipla.

    @param cMVParameter  Nome do parâmetro SX1 (ex: "MV_PAR07")
    @param cToken        Separador (default: ",")

MergeSX1SelStateUpdate(cMVParameter) --> lSuccess
    Inicializa o snapshot do estado atual antes do uso.

MergeSX1SelStatePurge(cMVParameter) --> lSuccess
    Remove o snapshot da memória após o uso.

-----------------------------------------------------------------------
EXEMPLO DE USO
-----------------------------------------------------------------------

    Pergunte(cPerg, .F.)

    // Inicializa snapshot
    SX1Tools():MergeSX1SelStateUpdate("MV_PAR07")

    // Durante validação (ex: VALID do campo)
    SX1Tools():MergeSX1SelState("MV_PAR07")

    // Finaliza e limpa memória
    SX1Tools():MergeSX1SelStatePurge("MV_PAR07")

-----------------------------------------------------------------------
COMPORTAMENTO ESPERADO
-----------------------------------------------------------------------

    Last = "SP"
    Atual = "TO"
    -> Resultado: "SP,TO"

    Last = "SP,TO"
    Atual = "TO"
    -> Resultado: "TO"

    Last = "SP,TO"
    Atual = ""
    -> Resultado: ""

-----------------------------------------------------------------------
LIMITAÇÕES
-----------------------------------------------------------------------

    Cenários ambíguos não podem ser resolvidos com 100% de precisão
    sem evento WHEN real, por exemplo:

        Last = "SP,TO"
        Atual = "RJ"

    Neste caso, o comportamento padrão é assumir ADIÇÃO.

-----------------------------------------------------------------------
OBSERVAÇÕES
-----------------------------------------------------------------------

    - Entrada é normalizada (Trim + Upper)
    - Duplicidades são removidas automaticamente
    - Compatível com múltiplos parâmetros simultâneos
    - Não depende de eventos de foco (WHEN)

-----------------------------------------------------------------------
PALAVRAS-CHAVE
-----------------------------------------------------------------------

    SX1, VALID, WHEN, MULTISELECT, MERGE, PROTHEUS, ADVPL
-----------------------------------------------------------------------
*/
class SX1Tools

    static method MergeSX1SelState(cMVParameter as character,cToken as character) as logical
    static method MergeSX1SelStateUpdate(cMVParameter as character) as logical
    static method MergeSX1SelStatePurge(cMVParameter as character) as logical

end class

static method MergeSX1SelState(cMVParameter,cToken) class SX1Tools

    local aOld:={} as array
    local aNew:={} as array
    local aFinal:={} as array

    local cAtual:="" as character
    local cFinal:="" as character

    local lIsSubset:=.T. as logical

    paramtype 1 var cMVParameter as character
    paramtype 2 var cToken as character optional default ","

    if (Type(cMVParameter)!="C")
        return(.F.)
    endif

    if (!__jMergeSX1SelState:HasProperty(cMVParameter))
        SX1Tools():MergeSX1SelStateUpdate(cMVParameter)
    endif

    // ================================
    // NORMALIZA ENTRADA
    // ================================
    cAtual:=Upper(AllTrim(&(cMVParameter)))
    __jMergeSX1SelState[cMVParameter]:=AllTrim(__jMergeSX1SelState[cMVParameter])

    // ================================
    // CONVERTE PARA ARRAY
    // ================================
    aOld:=if(Empty(__jMergeSX1SelState[cMVParameter]),{},StrToKArr2(__jMergeSX1SelState[cMVParameter],cToken,.T.))
    aNew:=if(Empty(cAtual),{},StrToKArr2(cAtual,cToken,.T.))

    // ================================
    // CASO: LIMPOU TUDO
    // ================================
    if (Empty(aNew))
        aFinal:={}
    else
        // ============================
        // DETECTA SUBSET (remocao)
        // ============================
        aEval(aNew,{|e|;
            if((aScan(aOld,e)==0);
                ,lIsSubset:=.F.;
                ,nil;
            );
        })

        // ============================
        // DECISAO PRINCIPAL
        // ============================
        if (lIsSubset)
            // REMOCAO / EDICAO
            aFinal:=aClone(aNew)
        else
            // ADICAO
            aFinal:=aClone(aOld)
            aEval(aNew,{|e|;
                if((!Empty(e)).and.(aScan(aFinal,e)==0);
                    ,aAdd(aFinal,e);
                    ,nil;
                );
            })
        endif

    endif

    // ================================
    // REMOVE DUPLICADOS + MONTA STRING
    // ================================
    cFinal:=""

    aEval(aFinal,{|e|;
        if((!Empty(e)).and.!(e$cFinal);
            ,cFinal+=e+cToken;
            ,nil;
        );
    })

    if (Right(cFinal,1)==cToken)
        cFinal:=SubStr(cFinal,1,Len(cFinal)-1)
    endif

    // ================================
    // ATUALIZA CAMPO E ESTADO
    // ================================
    &(cMVParameter):=PadR(cFinal,99)
    __jMergeSX1SelState[cMVParameter]:=cFinal

    FWFreeArray(@aOld)
    FWFreeArray(@aNew)
    FWFreeArray(@aFinal)

return(.T.)

static method MergeSX1SelStateUpdate(cMVParameter) class SX1Tools
    paramtype 1 var cMVParameter as character
    if (Type(cMVParameter)=="C")
        __jMergeSX1SelState[cMVParameter]:=&(cMVParameter)
        return(.T.)
    endif
return(.F.)

static method MergeSX1SelStatePurge(cMVParameter) class SX1Tools
    paramtype 1 var cMVParameter as character
    if (__jMergeSX1SelState:HasProperty(cMVParameter))
        __jMergeSX1SelState:DelName(cMVParameter)
        return(.T.)
    endif
return(.F.)

```

---

## 💬 TL;DR

* SX1 não tem WHEN ✔
* Range (R) resolve isso ✔
* Get (G) não resolve ❌
* Essa solução preenche essa lacuna ✔

---

## 🏷️ #Protheus #ADVPL #SX1 #ERP #Arquitetura #Desenvolvimento #DNATech

---

Comentários

Postagens mais visitadas