Pular para o conteúdo principal

Postagem em destaque

🚀 Oferecendo Serviços Remotos de Desenvolvedor AdvPL e Mais 🖥️

🚀 Oferecendo Serviços Remotos de Desenvolvedor AdvPL e Mais 🖥️ Olá pessoal, Espero que este post encontre todos vocês bem! É com grande entusiasmo que compartilho que estou expandindo meus serviços como Desenvolvedor AdvPL para novos desafios e colaborações. Com mais de duas décadas de experiência sólida, minha jornada profissional tem sido enriquecedora, com a oportunidade de participar de projetos empolgantes ao longo dos anos. Agora, estou ansioso para trazer minha experiência e habilidades para novas equipes e projetos, trabalhando de forma remota. Minha expertise abrange não apenas AdvPL, mas também outras tecnologias-chave, incluindo JS, SQL, Infraestrutura e Otimização de Processos. Acredito que essa combinação de conhecimentos me permite oferecer soluções abrangentes e eficazes para uma variedade de necessidades de desenvolvimento. Acredito que a tecnologia tem o poder de transformar negócios e impulsionar o sucesso, e estou comprometido em ajudar meus clientes a alcançar seu

BlackTDN :: SIGATRM :: Interface de Lançamento de Treinamentos usando tela MVC "só com GRID"

 Introdução:

Neste artigo, apresentaremos a implementação de uma interface de lançamento de treinamentos usando a tela MVC com foco exclusivo em um componente do tipo GRID. Essa interface foi desenvolvida com o objetivo de simplificar o lançamento de treinamentos e permitir a duplicação de linhas de forma fácil e intuitiva.


Descrição da necessidade:

A necessidade era criar uma interface de lançamento de treinamentos que fosse simples e permitisse a duplicação de linhas. A ideia era desenvolver uma solução eficiente, que proporcionasse uma experiência agradável ao usuário, sem a necessidade de telas complexas ou muitos campos para preencher.


Implementação:

Para atender a essa necessidade, foi implementada a Interface de Lançamento de Treinamentos usando tela MVC "só com GRID". A estrutura MVC (Model-View-Controller) foi utilizada para separar as responsabilidades da interface, facilitando o desenvolvimento e a manutenção do código.


O código fonte abaixo representa a implementação dessa interface:

    

#include "totvs.ch"
#include "fwmvcdef.ch"

#define MVC_TITLE " :: Inserção de Treinamento"

/*
    X3_VALID
        RA6_ENTIDA => NaoVazio() .And. ExistCpo("RA0") .And. IF(FWISINCALLSTACK("U_INSTRM"),.t.,Tr010Desc(.F.))
        RA6_CURSO => NaoVazio() .And. ExistCpo("RA1") .And. IF(FWISINCALLSTACK("U_INSTRM"),.t.,Tr010Desc(.F.))
    X3_RELACAO
        RA6_DESC => IF(FWISINCALLSTACK("U_INSTRM"),SPACE(GETSX3CACHE("RA6_DESC","X3_TAMANHO")),Tr010Desc(.T.))
*/

function U_INSTRM() as variant
    local cSvFilAnt:=&("cFilAnt")
    local xRet as variant
    private cCadastro:=MVC_TITLE as character
    private INCLUI:=.T. as logical
    xRet:=FWExecView(MVC_TITLE,"VIEWDEF.INSERTTRAINING",MODEL_OPERATION_INSERT)
    SetFilAnt(cSvFilAnt)
return(xRet)

static function ModelDef() as object

    local aFieldsDet as array

    local bLOkVld as codeblock
    local bTOkVld as codeblock

    local cFieldsDet as character
    
    local oModel as object
    local oHeader as object
    local oDetail as object

    aFieldsDet:=getFieldsDet()

    oModel:=MPFormModel():New("INSERTTRAINING",{||.T.}/*bPre*/,{||.T.}/*bPos*/,/*bCommit*/,{||.T.}/*bCancel*/)
    oModel:SetDescription(MVC_TITLE)

    oModel:bCommit:={|oModel|ZZ4TTSCommit(oModel)}

    oHeader:=FWFormModelStruct():New()
    oHeader:addTable("",{"C_FAKE"},"::"/*MVC_TITLE*/,{||""})
    oHeader:addField("FAKE","FAKE","C_FAKE","C",1)

    oModel:addFields("INSERTTRAINING_MASTER",/*cOwner*/,oHeader,/*bPre*/,/*bPost*/,{|oMdl|{""}})
    
    bDetail:={|cField|(cFieldsDet:=Upper(allTrim(cField)),(aScan(aFieldsDet,{|cField|(Upper(allTrim(cField))==cFieldsDet)})>0))}
    oDetail:=FWFormStruct(1,"RA4",bDetail)

    bLOkVld:={|oGrid|GridVldLOK(oGrid,oModel,aFieldsDet)}
    bTOkVld:={|oGrid|GridVldTOK(oGrid,oModel,aFieldsDet)}

    oModel:AddGrid("INSERTTRAINING_DETAIL","INSERTTRAINING_MASTER",oDetail,{||.T.},bLOkVld,{||.T.},bTOkVld,{||.T.})
    
    oModel:GetModel("INSERTTRAINING_DETAIL"):SetUniqueLine({"RA4_FILIAL","RA4_MAT","RA4_CURSO","RA4_DATAIN"})
    oModel:GetModel("INSERTTRAINING_DETAIL"):SetDescription(MVC_TITLE)
    oModel:GetModel("INSERTTRAINING_DETAIL"):SetUseOldGrid(.T.)

    oModel:setActivate({|oModel|onActivate(oModel)})

    return(oModel)

static function ViewDef() as object

    local aFieldsDet as array
    local aFieldsSXB as array

    local bDetail as codeblock
    local bGDSeek as codeblock
    local bReplicateLine as codeblock

    local cField as character
    local cFieldsDet as character

    local nATSXB as numeric

    local oView as object
    local oModel as object
    local oDetail as object
    local oHeader as object

    aFieldsDet:=getFieldsDet()
    
    aFieldsSXB:=Array(0)
    aAdd(aFieldsSXB,{"RA4_CURSO","U__RA1"})
    aAdd(aFieldsSXB,{"RA4_ENTIDA","U__RA6"})

    oHeader:=FWFormViewStruct():New()
    oHeader:addField("C_FAKE","01","FAKE","FAKE",/*aHelp*/,"C")

    bDetail:={|cField|(cFieldsDet:=Upper(allTrim(cField)),(aScan(aFieldsDet,{|cField|(Upper(allTrim(cField))==cFieldsDet)})>0))}
    oDetail:=FWFormStruct(2,"RA4",bDetail)

    oDetail:addField("RA4_FILIAL","00","Filial","Filial do sistema",/*aHelp*/,"C")
    
    //Redefinir as consultas F3
    aEval(;
        oDetail:GetFields(),{|x|;
            cField:=x[MVC_VIEW_IDFIELD],;
            nATSXB:=aScan(aFieldsSXB,{|x|x[1]==cField}),;
            x[MVC_VIEW_LOOKUP]:=if(nATSXB>0,aFieldsSXB[nATSXB][2],getSX3Cache(x[MVC_VIEW_IDFIELD],"X3_F3"));
        })

    oModel:=FWLoadModel("INSERTTRAINING")

    oView:=FWFormView():New()
    oView:SetModel(oModel)

    oView:AddField("INSERTTRAINING_MASTER",oHeader,"INSERTTRAINING_MASTER")
    oView:AddGrid("INSERTTRAINING_DETAIL",oDetail,"INSERTTRAINING_DETAIL")

    oView:CreateHorizontalBox("INSERTTRAINING_BOX_MASTER",0)
    oView:CreateHorizontalBox("INSERTTRAINING_BOX_DETAIL",100)

    oView:SetOwnerView("INSERTTRAINING_MASTER","INSERTTRAINING_BOX_MASTER")
    oView:SetOwnerView("INSERTTRAINING_DETAIL","INSERTTRAINING_BOX_DETAIL")

    oView:EnableControlBar(.F.)
    oView:lForceSetOwner:=.T.

    bReplicateLine:={||GDReplicateLine(oModel),SetKey(VK_F5,bReplicateLine)}
    SetKey(VK_F5,bReplicateLine)
    oView:AddUserButton("Duplicar Linha ","",bReplicateLine)

    bGDSeek:={||GDSeek(nil,OemtoAnsi("Pesquisar nos Detalhes"),nil,nil,.T.,oModel:GetModel("INSERTTRAINING_DETAIL"))}
    oView:AddUserButton("Pesquisar Detalhes","",bGDSeek)

    return(oView)

static function GDReplicateLine(oModel) as numeric

    local aFieldsDet as array
    local aNoReplicate as array

    local cField as character
    
    local nField as numeric
    local nFields as numeric

    local nLines as numeric
    local nLineAT as numeric
    local nGDReplicateLine:=(-1) as numeric

    local oModelGrid as object
    local oFWViewActive as object

    local xValue as variant

    begin sequence

        if (!FWISInCallStack("U_INSTRM"))
            break
        endif

        oModelGrid:=oModel:GetModel("INSERTTRAINING_DETAIL")
        
        nLines:=oModelGrid:Length()
        nLineAT:=oModelGrid:GetLine()
        
        nGDReplicateLine:=oModelGrid:AddLIne()
        
        if (nGDReplicateLine>nLines)
            aNoReplicate:=array(0)
            aAdd(aNoReplicate,"RA4_MAT")
            aAdd(aNoReplicate,"RA4_NOME")
            aAdd(aNoReplicate,"RA4_UNOMEL")
            aAdd(aNoReplicate,"RA4_DESCCU")    
            aAdd(aNoReplicate,"RA4_DESCEN")
            aFieldsDet:=getFieldsDet()    
            nFields:=Len(aFieldsDet)
            for nField:=1 to nFields
                cField:=aFieldsDet[nField]
                if (aScan(aNoReplicate,{|fld|(fld==cField)})>0)
                    loop
                endif
                xValue:=oModelGrid:GetValue(cField,nLineAT)
                oModelGrid:GoLine(nGDReplicateLine)
                oModelGrid:SetValue(cField,xValue,.T.)
            next nField
            FWFreeArray(@aFieldsDet)
            FWFreeArray(@aNoReplicate)
            oFWViewActive:=FWViewActive()
            oFWViewActive:Refresh()
        endif

    end sequence

return(nGDReplicateLine)

static function MenuDef() as array
    
    local aRotina:=array(0) as array

    ADD OPTION aRotina TITLE "Incluir" ACTION "VIEWDEF.INSERTTRAINING" OPERATION MODEL_OPERATION_INSERT ACCESS 0 //OPERATION 3

    return(aRotina)

static function GridVldLOK(oGrid as object,oModel as object,aFieldsDet as array) as logical

    local cField as character
    local cKeySeek as character
    
    local lLinhaOK:=.T. as logical

    local nATRow as numeric

    local nField as numeric
    local nFields as numeric

    local nRA4Order as numeric
    local nRA6Order as numeric

    nFields:=Len(aFieldsDet)

    nATRow:=oGrid:GetLine()

    begin sequence
        
        if (oGrid:IsDeleted())
            break
        endif
        
        for nField:=1 to nFields
            cField:=aFieldsDet[nField]
            lLinhaOK:=(!empty(oGrid:GetValue(cField)))
            if (!lLinhaOK)
                Help(nil,nil,"OBRIGAT",nil,PadR("Verifique o preenchimento do(s) campo(s) ("+cField+") Linha ("+cValToChar(nATRow)+")",255),1,0)
                break
            endif
        next nField
        
        cKeySeek:=FWFldGet("RA4_FILIAL",nATRow,oModel,.F.)
        cKeySeek+=FWFldGet("RA4_MAT",nATRow,oModel,.F.)

        SRA->(dbSetOrder(1))
        lLinhaOK:=SRA->(MsSeek(cKeySeek,.F.))
        if (!lLinhaOK)
            cField:="RA4_FILIAL,RA4_MAT"
            Help(nil,nil,"OBRIGAT",nil,PadR("Verifique o preenchimento do(s) campo(s) ("+cField+") Linha ("+cValToChar(nATRow)+")",255),1,0)
            break
        endif

        nRA4Order:=RetOrder("RA4","RA4_FILIAL+RA4_MAT+RA4_CALEND+RA4_CURSO+RA4_TURMA+RA4_SINONI+DTOS(RA4_DATAIN)",.T.) 
        if (nRA4Order==0)
            nRA4Order:=RetOrder("RA4","RA4_FILIAL+RA4_MAT+RA4_CURSO")
            cKeySeek+=FWFldGet("RA4_CURSO",nATRow,oModel,.F.)
        else
            //RA4_FILIAL+RA4_MAT+RA4_CALEND+RA4_CURSO+RA4_TURMA+RA4_SINONI+DTOS(RA4_DATAIN)
            cKeySeek+=Space(GetSX3Cache("RA4_CALEND","X3_TAMANHO"))
            cKeySeek+=FWFldGet("RA4_CURSO",nATRow,oModel,.F.)
            cKeySeek+=Space(GetSX3Cache("RA4_TURMA","X3_TAMANHO"))
            cKeySeek+=Space(GetSX3Cache("RA4_SINONI","X3_TAMANHO"))
            cKeySeek+=DToS(FWFldGet("RA4_DATAIN",nATRow,oModel,.F.))
        endif

        RA4->(dbSetOrder(nRA4Order))
        lLinhaOK:=RA4->(!MsSeek(cKeySeek,.F.))
        if (!lLinhaOK)
            Help(nil,nil,"JAEXISTE",nil,PadR("Já Existe Informação deste curso para o Funcionário Linha ("+cValToChar(nATRow)+")",255),1,0)
            break
        endif

        lLinhaOK:=(FWFldGet("RA4_DATAIN",nATRow,oModel,.F.)<=FWFldGet("RA4_DATAFI",nATRow,oModel,.F.))
        if (!lLinhaOK)
            Help(nil,nil,"RA4_DATAFI",nil,PadR("Data Final maior que Data Inicial ("+cValToChar(nATRow)+")",255),1,0)
            break
        endif

        nRA6Order:=retOrder("RA6","RA6_FILIAL+RA6_ENTIDA+RA6_CURSO")
        RA6->(dbSetOrder(nRA6Order))
        cKeySeek:=xFilial("RA6",FWFldGet("RA4_FILIAL",nATRow,oModel,.F.))
        cKeySeek+=FWFldGet("RA4_ENTIDA",nATRow,oModel,.F.)
        cKeySeek+=FWFldGet("RA4_CURSO",nATRow,oModel,.F.)
        lLinhaOK:=RA6->(MsSeek(cKeySeek,.F.))
        if (!lLinhaOK)
            Help(nil/*cRotina*/,nil/*nLinha*/,"RA4_ENTIDA"/*cCampo*/,nil/*cNome*/,PadR("O Código da Entidade informada não é válido. "+GetSX3Cache("RA4_CURSO","X3_TITULO")+" não vinculado: ("+FWFldGet("RA4_CURSO",nATRow,oModel,.F.)+") ("+cValToChar(nATRow)+").",255)/*cMensagem*/,1/*nLinha1*/,0/*nColuna*/,/*lPop*/,/*hWnd*/,/*nHeight*/,/*nWidth*/,/*lGravaLog*/,{"Informe um Código de Entidade Válido."}/*aSoluc*/)
            break
        endif

    end sequence

    return(lLinhaOK)    

static function GridVldTOK(oGrid as object,oModel as object,aFieldsDet as array) as logical

    local lTudoOK as logical

    local nRow as numeric
    local nRows as numeric
    local nATRow as numeric
    
    nATRow:=oGrid:GetLine()
    nRows:=oGrid:Length()

    for nRow:=1 to nRows
        oGrid:GoLine(nRow)
        if (oGrid:IsDeleted())
            loop
        endif
        lTudoOK:=GridVldLOK(@oGrid,oModel,@aFieldsDet)
        if (!lTudoOK)
            exit
        endif
    next nRow

    if (!lTudoOK)
        oGrid:GoLine(nRow)
    else
        oGrid:GoLine(nATRow)
    endif

    return(lTudoOK)

static function onActivate(oModel) as variant
    if (oModel:GetOperation()==MODEL_OPERATION_INSERT)
        FwFldPut("C_FAKE","0",/*nLinha*/,oModel)
    endif
return    

static function getFieldsDet() as aray

    local aFieldsDet as array

    aFieldsDet:=array(0)

    aAdd(aFieldsDet,"RA4_FILIAL")
    aAdd(aFieldsDet,"RA4_MAT")
    aAdd(aFieldsDet,"RA4_NOME")
    aAdd(aFieldsDet,"RA4_ULIDER")
    aAdd(aFieldsDet,"RA4_UNOMEL")
    aAdd(aFieldsDet,"RA4_CURSO")
    aAdd(aFieldsDet,"RA4_DESCCU")    
    aAdd(aFieldsDet,"RA4_ENTIDA")
    aAdd(aFieldsDet,"RA4_DESCEN")
    aAdd(aFieldsDet,"RA4_DURACA")
    aAdd(aFieldsDet,"RA4_UNDURA")
    aAdd(aFieldsDet,"RA4_HORAS")
    aAdd(aFieldsDet,"RA4_DATAIN")
    aAdd(aFieldsDet,"RA4_DATAFI")
    aAdd(aFieldsDet,"RA4_UMODAL")
    aAdd(aFieldsDet,"RA4_UAVREC")
    aAdd(aFieldsDet,"RA4_EFICSN")

return(aFieldsDet)

function u_INSERTTRAINING() as variant
    local aParameter as array
    local xRet:=.F. as variant
    begin sequence
        if (!type("ParamIXB")=="A")
            break
        endif
        aParameter:=&("ParamIXB")
        xRet:=INSERTTRAINING(@aParameter)
    end sequence
    return(xRet)

static function INSERTTRAINING(aParameter as array) as variant

    local cIDPonto as character
    local cIDModel as character

    local cObjMVCClassName as character

    local nParameters as numeric

    local oObjMVC as object

    local xRet:=.T. as variant

    begin sequence

        nParameters:=len(aParameter)
        
        oObjMVC:=aParameter[1]
        cObjMVCClassName:=oObjMVC:ClassName()
        
        cIDPonto:=aParameter[2]
        cIDModel:=aParameter[3]

        if (cIDPonto=="MODELPOS")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="FORMPOS")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="FORMLINEPRE")
            if ((nParameters>=5).and.(aParameter[5]=="DELETE"))
                xRet:=.T.
            endif
            break
        endif

        if (cIDPonto=="FORMLINEPOS")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="MODELCOMMITTTS")
            if ((cIDModel=="INSERTTRAINING_DETAIL").and.(cObjMVCClassName=="FWFORMGRID"))
                xRet:=.T.
            else
                xRet:=.T.
            endif
            break
        endif

        if (cIDPonto=="MODELCOMMITNTTS")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="FORMCOMMITTTSPRE")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="FORMCOMMITTTSPOS")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="MODELCANCEL")
            xRet:=.T.
            break
        endif

        if (cIDPonto=="BUTTONBAR")
            xRet:=array(0)
            break
        endif

    end sequence

    return(xRet)

static function SetFilAnt(cFil) as character
    local cSvFilAnt:=&("cFilAnt") as character
    if (valType("cFilAnt")=="C")
        cSvFilAnt:=&("cFilAnt")
        if (cFil!=cSvFilAnt)
            &("cFilAnt"):=cFil
            FWSM0Util():setSM0PositionBycFilAnt()
        endif
    endif
return(cSvFilAnt)

static function ZZ4TTSCommit(oModel) as logical

    local aSaveRows as array

    local cSvcArqTab:=&("cArqTab") as character

    local lZZ4TTSCommit as logical

    local nAT as numeric

    local oSaveModel as object

    aSaveRows:=FWSaveRows()

    oSaveModel:=FWModelActive(oModel)

    //Altera o Modo de Acesso da Tabela RA4 para garantir a Gravação das Filiais informadas pelo usuário
    //Desta Forma FWFormCommit não irá substituir a Filial Gravara
    nAT:=AT("RA4",&("cArqTab"))
    if (nAT>0)
        &("cArqTab"):=SubStr(&("cArqTab"),1,nAT+2)+"C"+SubStr(&("cArqTab"),nAT+4)
    else
        &("cArqTab")+="RA4"+"C"+"/"
    endif

    lZZ4TTSCommit:=FWFormCommit(oModel)

    &("cArqTab"):=cSvcArqTab

    FWModelActive(oSaveModel)

    FWRestRows(aSaveRows)

    return(lZZ4TTSCommit)

function u_RA1SXBFilter()
return(SXBINSERTTRAINING():RA1Filter())

function u_RA6SXBFilter()
return(SXBINSERTTRAINING():RA6Filter())

static procedure __Dummy()

    if (.F.)
        __Dummy()
        MODELDEF()
        VIEWDEF()
        MENUDEF()
    endif

    return

    
O Exemplo completo poderá ser obtido aqui (GitHub)

Conclusão:
A Interface de Lançamento de Treinamentos usando tela MVC "só com GRID" oferece uma solução simplificada e eficiente para o lançamento de treinamentos. A utilização do componente GRID proporciona uma experiência intuitiva ao usuário, permitindo a duplicação de linhas com facilidade. A estrutura MVC garante a separação das responsabilidades e facilita a manutenção do código.

Com essa interface, é possível agilizar o processo de lançamento de treinamentos, proporcionando uma melhor experiência para os usuários e aumentando a produtividade da equipe responsável.

Comentários

Postagens mais visitadas deste blog

BlackTDN :: RLeg ~ Desvendando a Função ParamBox

Para quem precisar desenvolver uma interface de entrada de dados, coisa rápida, e não quer ter aquele trabalhão danado que todos já sabemos, o Protheus tem uma função que ajuda muito, é uma interface semelhante a função Pergunte, porém com muito mais opção de objeto de entrada de dados, alias até colocar o scrollbox desta interface com todos os objetos em outra MsDialog ou Wizard é simples. Vejam o exemplo abaixo, boa sorte! Rleg. //---------------------------------------------------------- // Função exemplo utilizando a função ParamBox() //---------------------------------------------------------- User Function xParamBox() Local aRet := {} Local aParamBox := {} Local aCombo := {"Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"} Local i := 0 Private cCadastro := "xParambox" // ---------------

Protheus :: Chamando Funções do Menu Diretamente e sem a Necessidade de Login

Ferne$ perguntou: "...é possível abrir alguma rotina do sistema sem solicitar login ao usuário, como por exemplo a rotina MATA010..." Sim Ferne$, é possível sim. Abaixo um Exemplo para a Chamada à função MATA010 sem a necessidade de Login no sistema. #INCLUDE "PROTHEUS.CH" #INCLUDE "TBICONN.CH" /*/ Funcao: MATA010Ex Data: 30/04/2011 Autor: Marinaldo de Jesus Descricao: Executar a Funcao MATA010 diretamente sem a necessidade de LOGIN no Protheus Sintaxe: 1 ) U_MATA010Ex ( Chamada diretamente na Tela de Entrada do Sistema ) ; ou 2 ) totvsclient.exe -q -p=u_MATA010Ex -a=01;01 -c=rnp_local -e=rnp -m -l ( Chamada Via Linha de Comando ) /*/ User Function MATA010Ex( cEmpFil ) Local aEmpFil Local bWindowInit := { || __Execute( "MATA010()" , "xxxxxxxxxxxxxxxxxxxx" , "MATA010" , "SIGAFAT" , "SIGAFAT", 1 , .T. ) } Local cEmp Local cFil Local cMod Local cModName := "SIGAFAT" DEFA

BlackTDN :: Customizando a interface de Login no Protheus e by You

A publicação “ BlackTDN :: By You e sua nova tela de login ”  de nosso amigo OBona deu o que falar e, em função disso, esse que a muito não vos escreve resolveu criar uma versão onde será possível personalizar, “por completo”, a tela de login no Protheus/by You. Considerando que OBona já havia “mapeado, identificado e customizado” as imagens peguei-as emprestadas para o exemplo que se segue: O primeiro passo para a customização “total” da interface de login do Protheus/by You será implementar o “Ponto de Entrada” ChgPrDir (Diretório de impressão) . Usaremos esse PE juntamente como programa U_FindMsObject.prg (apresentado pela primeira vez em: Protheus :: ADVPL : The Container : Presents Pandora's box ). Diferente do exemplo proposto por OBona, que substitui, durante o processo de compilação, as imagens padrões do sistema (excluindo-as) por imagens customizadas (com o mesmo nome) este novo exemplo mantém, no RPO, as imagens padrões adicionando novas imagens customizadas que serã