Blog SAPienteSAPiente
RAP

Tipos de Action no RAP: O Guia Completo

SAPiente3 de jul. de 2026· 13 min read

Action não é tudo igual — e escolher errado custa caro

Todo mundo que começa em RAP aprende a criar um botão "Aprovar" com uma action. Mas aí o requisito muda: "quero copiar o pedido", "quero disparar isso sem selecionar linha nenhuma", "isso aqui só pode rodar por dentro do BO", "preciso executar isso duas vezes na mesma requisição"... e de repente você descobre que action é só a ponta do iceberg.

A definição oficial: actions são operações não-standard de um RAP BO — tudo que não é o CRUD de fábrica — implementadas em métodos handler FOR MODIFY no behavior pool. A documentação divide o mundo das actions em três grandes famílias, cada uma com variações:

Família

Propósito

Variações

Non-factory actions

Modificar instâncias existentes

instance (default), static, repeatable, internal

Factory actions

Criar novas instâncias

instance factory, static factory, default factory

Save actions

Rodar somente na save sequence

save(finalize), save(adjustnumbers)

E orbitando as três famílias, dois parentes que merecem capítulo próprio: as determine actions (disparam determinations/validations sob demanda) e as draft actions (Edit, Activate, Discard, Resume, Prepare). Vamos passar por todas — com código e caso de uso de cada uma.

A regra de ouro antes de tudo: action modifica estado. Se a sua "action" só lê, calcula e devolve — sem alterar nada — o artefato certo é uma function (FOR READ, sem lock). E não é só convenção: se você tentar modificar dados dentro de uma function, não há erro de sintaxe, mas ocorre runtime error quando um consumidor a acessa.

1. Instance action: a action "de fábrica"

É o default. Sem nenhuma palavra-chave adicional, uma action é vinculada a uma instância do BO e altera o estado dela. Ela recebe as chaves das instâncias selecionadas e, por padrão, trava (lock) cada instância durante a execução.

Casos de uso:

  • Aprovar/rejeitar requisição: o aprovador seleciona 5 linhas na List Report, clica "Aprovar", a action muda o status das 5 e a UI atualiza.

  • Liberar remessa bloqueada: action liberarRemessa habilitada só quando o status é "bloqueado" (feature control).

  • Estornar documento: action que valida se o documento é estornável, grava o estorno e amarra a referência.

define behavior for ZR_PurchaseReq alias PurchReq
{
  ...
  action ( features : instance ) approve result [1] $self;
  action ( features : instance ) reject  parameter ZD_RejectReason result [1] $self;
}
METHOD approve.
  " 1. atualiza o status via EML em modo local
  MODIFY ENTITIES OF ZR_PurchaseReq IN LOCAL MODE
    ENTITY PurchReq
      UPDATE FIELDS ( OverallStatus ApprovedBy ApprovedAt )
      WITH VALUE #( FOR key IN keys
                    ( %tky          = key-%tky
                      OverallStatus = 'A'
                      ApprovedBy    = cl_abap_context_info=>get_user_technical_name( )
                      ApprovedAt    = cl_abap_context_info=>get_system_date( ) ) ).

  " 2. lê o resultado do buffer para preencher o result
  READ ENTITIES OF ZR_PurchaseReq IN LOCAL MODE
    ENTITY PurchReq ALL FIELDS WITH CORRESPONDING #( keys )
    RESULT DATA(reqs).

  result = VALUE #( FOR req IN reqs
                    ( %tky   = req-%tky
                      %param = req ) ).
ENDMETHOD.

E o feature control que desabilita o botão quando o status não permite — em vez de deixar o usuário clicar e tomar erro:

METHOD get_instance_features.
  READ ENTITIES OF ZR_PurchaseReq IN LOCAL MODE
    ENTITY PurchReq FIELDS ( OverallStatus ) WITH CORRESPONDING #( keys )
    RESULT DATA(reqs).

  result = VALUE #( FOR req IN reqs
    ( %tky              = req-%tky
      %action-approve   = COND #( WHEN req-OverallStatus = 'O'   " aberto
                                  THEN if_abap_behv=>fc-o-enabled
                                  ELSE if_abap_behv=>fc-o-disabled )
      %action-reject    = COND #( WHEN req-OverallStatus = 'O'
                                  THEN if_abap_behv=>fc-o-enabled
                                  ELSE if_abap_behv=>fc-o-disabled ) ) ).
ENDMETHOD.

Para expor como botão no Fiori Elements, a metadata extension:

@UI.lineItem: [ { position: 10 },
                { type: #FOR_ACTION, dataAction: 'approve', label: 'Aprovar' },
                { type: #FOR_ACTION, dataAction: 'reject',  label: 'Rejeitar' } ]
PurchaseRequisition;

Com parâmetro de entrada

Quando o usuário precisa informar algo (um percentual, um motivo), você tipa o parâmetro com uma CDS abstract entity e acessa os valores via %param. No Fiori, isso gera automaticamente o popup de entrada:

@EndUserText.label: 'Motivo da rejeição'
define abstract entity ZD_RejectReason
{
  ReasonCode : abap.char(2);
  ReasonText : abap.char(80);
}
METHOD reject.
  MODIFY ENTITIES OF ZR_PurchaseReq IN LOCAL MODE
    ENTITY PurchReq
      UPDATE FIELDS ( OverallStatus RejectReason RejectText )
      WITH VALUE #( FOR key IN keys
                    ( %tky          = key-%tky
                      OverallStatus = 'X'
                      RejectReason  = key-%param-ReasonCode
                      RejectText    = key-%param-ReasonText ) ).
  ...
ENDMETHOD.

Bônus de UX que pouca gente usa: uma default function acoplada à action pré-preenche o popup (ex.: sugerir o motivo mais comum):

action reject parameter ZD_RejectReason result [1] $self
{ default function getRejectDefaults; }

Decifrando o result [1] $self

Esse trecho aparece em quase toda action e pouca gente para pra ler o que ele diz. São três peças:

Peça

Significado

result

Declara o parâmetro de saída da action (é opcional — action sem result não devolve nada)

[1]

A cardinalidade do resultado por instância de entrada: cada instância processada devolve exatamente 1 resultado

$self

O tipo do resultado é a própria entidade em que a action foi definida — "devolvo a mim mesmo, atualizado"

As cardinalidades possíveis e quando cada uma faz sentido:

Cardinalidade

Cada instância de entrada devolve...

Exemplo

[1]

Exatamente 1 resultado

Aprovar: devolve a própria instância atualizada

[0..1]

0 ou 1 resultado

Buscar documento subsequente: pode não existir

[1..*]

1 ou mais resultados

Explodir estrutura: devolve os componentes gerados

[0..*]

0 ou mais resultados

Gerar parcelas: pode gerar zero, uma ou N

E o $self não é a única opção de tipo. O resultado pode ser:

action approve       result [1] $self;              " a própria entidade
action getFollowUp   result [0..1] entity ZR_Invoice; " OUTRA entidade do BO
action simulateTax   result [1] ZD_TaxResult;        " abstract entity (estrutura)

No behavior pool, o result entra na assinatura do método (tipo TABLE FOR ACTION RESULT) e você o preenche com %tky + %param — onde %param carrega os dados do resultado:

METHODS approve FOR MODIFY
  IMPORTING keys FOR ACTION PurchReq~approve RESULT result.

" na implementação, após o MODIFY + READ do buffer:
result = VALUE #( FOR req IN reqs
                  ( %tky   = req-%tky
                    %param = req ) ).   " %param = a instância atualizada

Por que o result [1] $self importa tanto no Fiori: é ele que faz a UI atualizar a linha/objeto na hora após o clique, sem refresh manual. E o contrário também vale: se você declara o result mas esquece de preenchê-lo na implementação, o Fiori Elements navega para um resultado vazio — o usuário clica em "Aprovar" e cai numa tela em branco. Declarou, preencheu.

Três detalhes finos da documentação para fechar o assunto:

  • result selective: com essa adição na BDEF, o consumidor EML pode usar REQUEST no EXECUTE para pedir só parte do resultado — útil quando o result é pesado e nem todo consumidor precisa de tudo.

  • Factory action não tem result — o retorno dela é sempre a instância criada, via MAPPED (%cid → chave). O $self ali é implícito por natureza.

  • $self como input parameter é outra história: só é permitido em actions/functions static — e em BDEF strict mode nem isso. Num instance action, compila sem warning e dá runtime error quando o consumidor acessa. Não confunda os dois lados.

2. Static action: sem instância nenhuma

A palavra-chave static desliga o vínculo com instância: a action passa a se relacionar com a entidade como um todo — e, por definição, é non-locking, já que não há instância para travar.

Casos de uso:

  • Reprocessar todos os pendentes: botão na List Report que não depende de seleção — varre o que está com erro e reprocessa.

  • Sincronizar cadastro com sistema externo: dispara a rotina de sincronização do conjunto inteiro.

  • Gerar lote/período: "Gerar parcelas do mês", "Abrir período" — operações que criam contexto, não mexem em linha específica.

static action reprocessPending;
METHOD reprocessPending.
  " sem instância: seleciona o universo de trabalho na própria implementação
  SELECT FROM zpurchreq
    FIELDS req_uuid
    WHERE overall_status = 'E'          " com erro
    INTO TABLE @DATA(pending_keys).

  MODIFY ENTITIES OF ZR_PurchaseReq IN LOCAL MODE
    ENTITY PurchReq
      EXECUTE resubmit                   " reusa uma internal action (seção 5)
      FROM VALUE #( FOR p IN pending_keys ( ReqUUID = p-req_uuid ) )
    FAILED DATA(failed) REPORTED DATA(reported).
ENDMETHOD.

No consumo via EML, static action usa %cid em vez de chave:

MODIFY ENTITIES OF ZR_PurchaseReq
  ENTITY PurchReq
    EXECUTE reprocessPending FROM VALUE #( ( %cid = 'cid1' ) )
  FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.

Cuidado com o lock:none (e a pegadinha do "non-locking"): uma instance action pode ser declarada com lock:none para não travar a instância — mas isso vale só para a action em si. Se dentro da implementação você fizer um MODIFY, a instância é travada normalmente pelo modify. Não existe mágica de "alterar sem lock".

3. Repeatable action: a mesma instância, duas vezes na mesma requisição

Regra pouco conhecida do framework: uma action comum não pode ser executada duas vezes na mesma instância dentro da mesma requisição EML/OData — se tentar, runtime error. A adição repeatable existe exatamente para liberar isso.

Casos de uso:

  • Descontos cumulativos via API: o consumidor externo manda, num único batch OData, dois descontos para o mesmo item (promoção + cupom).

  • Lançamentos parciais: registrar duas medições/apontamentos da mesma ordem numa única chamada.

  • Orquestração interna: um fluxo do próprio BO precisa aplicar a mesma operação N vezes na mesma instância antes do commit.

repeatable action applyDiscount parameter ZD_Discount result [1] $self;
" agora isso é válido dentro de UM único MODIFY:
MODIFY ENTITIES OF ZR_SalesItem
  ENTITY Item
    EXECUTE applyDiscount FROM VALUE #(
      ( ItemUUID = lv_uuid  %param-discount_percent = 10 )   " promoção
      ( ItemUUID = lv_uuid  %param-discount_percent = 5 ) )  " mesmo item: cupom
  RESULT DATA(result) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.

Se o seu suporte já viu o dump de "action executada mais de uma vez na mesma instância", o remédio é este. A adição também existe para functions (repeatable function).

4. Factory actions: actions que criam instâncias

Enquanto non-factory actions modificam, factory actions criam instâncias do BO — inclusive com filhas na mesma tacada. Três regras rígidas:

  • Uma factory action sempre produz exatamente uma nova instância — por isso a cardinalidade é obrigatória e sempre [1];

  • Não existe result em factory action: o resultado volta pelo response parameter MAPPED, mapeando o %cid para a chave da instância criada;

  • Parâmetro de entrada é permitido; output, não.

4a. Instance factory action: criar a partir de uma existente

Casos de uso:

  • Botão "Copiar": duplicar um contrato/pedido herdando os valores e resetando status, datas e chave.

  • Documento subsequente: "Criar follow-up" — gerar a devolução a partir da venda, o aditivo a partir do contrato, levando as filhas junto.

  • Nova versão: versionar um registro congelando o original e criando a v2 editável.

factory action copyContract [1];
METHOD copyContract.
  " 1. lê a instância de origem (cabeçalho + itens)
  READ ENTITIES OF ZR_Contract IN LOCAL MODE
    ENTITY Contract
      ALL FIELDS WITH CORRESPONDING #( keys )
      RESULT DATA(contracts)
    ENTITY Contract BY \_Item
      ALL FIELDS WITH CORRESPONDING #( keys )
      RESULT DATA(items).

  " 2. cria a cópia: cabeçalho com deep create dos itens (CREATE BY)
  MODIFY ENTITIES OF ZR_Contract IN LOCAL MODE
    ENTITY Contract
      CREATE FIELDS ( PartnerID ValidFrom ValidTo OverallStatus )
      WITH VALUE #( FOR contract IN contracts
        ( %cid          = keys[ KEY entity %key = contract-%key ]-%cid
          %data         = CORRESPONDING #( contract EXCEPT ContractID )
          ValidFrom     = cl_abap_context_info=>get_system_date( )
          OverallStatus = 'O' ) )
      CREATE BY \_Item
      FIELDS ( Material Quantity Price )
      WITH VALUE #( FOR contract IN contracts
        ( %cid_ref = keys[ KEY entity %key = contract-%key ]-%cid
          %target  = VALUE #( FOR item IN items
                              WHERE ( ContractUUID = contract-ContractUUID )
                              ( %cid  = |ITEM{ sy-tabix }|
                                %data = CORRESPONDING #( item EXCEPT ItemUUID ) ) ) ) )
    MAPPED DATA(mapped_create).

  " 3. devolve as chaves criadas — é ASSIM que factory action "retorna"
  mapped-contract = mapped_create-contract.
ENDMETHOD.

4b. Static factory action: criar do zero com defaults

Casos de uso:

  • Criar a partir de template: o usuário informa o código do template e a factory monta o objeto completo.

  • Application Job que cria instâncias em background: o job chama a static factory action via EML — toda a lógica de criação fica dentro do BO, e UI, API e job criam instâncias idênticas.

  • Criação parametrizada via Web API: o consumidor externo não preenche 40 campos; manda 3 parâmetros e a factory deriva o resto.

static factory action createFromTemplate parameter ZD_Template [1];
METHOD createFromTemplate.
  " carrega o template informado no parâmetro
  SELECT SINGLE * FROM ztemplate
    WHERE template_id = @( keys[ 1 ]-%param-TemplateID )
    INTO @DATA(ls_template).

  MODIFY ENTITIES OF ZR_Contract IN LOCAL MODE
    ENTITY Contract
      CREATE FIELDS ( PartnerID PaymentTerms ValidFrom ValidTo OverallStatus )
      WITH VALUE #( ( %cid          = keys[ 1 ]-%cid
                      PartnerID     = keys[ 1 ]-%param-PartnerID
                      PaymentTerms  = ls_template-payment_terms
                      ValidFrom     = cl_abap_context_info=>get_system_date( )
                      ValidTo       = cl_abap_context_info=>get_system_date( ) + ls_template-duration_days
                      OverallStatus = 'O' ) )
    MAPPED mapped.
ENDMETHOD.

4c. Default factory action: substituindo o Create do Fiori

Exatamente uma static factory action pode receber a adição default — e isso tem efeito direto nos frameworks consumidores: o Fiori Elements passa a usar a sua factory action como operação de criação padrão.

static default factory action createWithDefaults parameter ZD_Template [1];

Caso de uso: o botão "Criar" da List Report precisa abrir com um diálogo de parâmetros ou lógica de inicialização própria — em vez de esconder o create e treinar o usuário a achar outro botão, você troca o próprio Create pelo seu diálogo.

5. Internal action: só quem é de casa executa

A adição internal (disponível para actions, factory actions, save actions, determine actions e functions) torna a operação acessível apenas de dentro da implementação do próprio BO — ela não aparece no serviço OData nem pode ser chamada por consumidores EML externos. É o "método privado" do mundo BDEF.

Casos de uso:

  • Lógica compartilhada: approve, massApprove e uma determination chamam a mesma internal action resubmit — um único ponto de manutenção.

  • Passo intermediário protegido: a etapa "recalcular impostos" não pode ser disparada isolada de fora, só como parte de um fluxo maior.

  • API pública enxuta: o serviço OData expõe 3 actions; as outras 5 são engrenagem interna.

internal action resubmit;
" chamada de DENTRO do behavior pool (ex.: por outra action ou determination):
METHOD massApprove.
  MODIFY ENTITIES OF ZR_PurchaseReq IN LOCAL MODE
    ENTITY PurchReq
      EXECUTE resubmit FROM CORRESPONDING #( keys )
    FAILED DATA(failed).
ENDMETHOD.

" de FORA do BO, a mesma chamada nem compila:
" EXECUTE resubmit ...  ->  erro de sintaxe: action is internal

6. Save actions: actions que só existem na save sequence

O tipo mais desconhecido da família. Uma save action só pode ser chamada durante o saver method especificado na declaração — qualquer chamada na interaction phase termina em short dump.

Casos de uso (e a razão de existir): save actions existem para a representação RAP de funcionalidades que não mantêm buffer transacional e, do ponto de vista do RAP, só têm fase de save:

  • Posting contábil: o lançamento no ledger só pode acontecer quando o documento está consolidado — no commit, nunca antes.

  • Número definitivo por number range: save(adjustnumbers) é o gancho para trocar a chave temporária pelo número oficial no último instante.

  • Chamada de BAPI/função legada que grava direto: o "fazer de verdade" é a própria gravação — então ele pertence à save sequence.

save(finalize)      action postToLedger;
save(adjustnumbers) action assignOfficialNumber;
" no saver class (CCIMP), a chamada acontece no método correspondente:
CLASS lsc_contract DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.
    METHODS finalize       REDEFINITION.
    METHODS adjust_numbers REDEFINITION.
ENDCLASS.

CLASS lsc_contract IMPLEMENTATION.
  METHOD finalize.
    MODIFY ENTITIES OF ZR_Contract IN LOCAL MODE
      ENTITY Contract
        EXECUTE postToLedger FROM lt_to_post   " OK: save(finalize) no finalize
      FAILED DATA(failed).
  ENDMETHOD.
ENDCLASS.

As regras que a documentação crava — cada uma vale um dump se ignorada:

  • save(finalize) roda no saver method finalize e pode ser chamada de uma determination on save;

  • save(adjustnumbers) roda no adjust_numbers e não pode ser chamada de determinations on save;

  • Save action não chama outra save action do mesmo saver method;

  • Se uma determination on save for executada via determine action (que roda na interaction phase!), uma save action dentro dela dá dump;

  • A sintaxe antiga save action Nome; sem saver method está obsoleta — é interpretada como save(finalize).

7. Determine action: determinations e validations sob demanda

Tecnicamente uma prima das actions: a determine action permite ao consumidor executar, sob demanda, um pacote de determinations e validations do BO. As triggers de cada uma são avaliadas e só rodam as que têm condição satisfeita — a menos que você force com (always).

Casos de uso:

  • Dados que entraram por fora do BO: migração ou carga via ABAP SQL gravou direto na tabela — as determinations nunca rodaram. A determine action aplica as regras retroativamente.

  • Revalidação em massa via API: depois de um upload, o consumidor chama a determine action para recalcular totais e validar consistência, sem duplicar lógica.

  • Reprocessamento de regras alteradas: a regra de cálculo mudou; em vez de um report paralelo, roda-se o pacote de determinations sobre o estoque de registros.

determine action recalcAndValidate
{
  determination setDocumentID;
  determination (always) calcTotalAmount;
  validation    Item~validateQuantity;     " inclui filhas!
}
" cenário: carga gravou direto no banco, agora aplicamos as regras do BO
MODIFY ENTITIES OF ZR_Contract
  ENTITY Contract
    EXECUTE recalcAndValidate
    FROM VALUE #( FOR uuid IN lt_migrated_uuids ( ContractUUID = uuid ) )
  MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
COMMIT ENTITIES.

" reported traz as mensagens das validations que falharam:
LOOP AT reported-contract INTO DATA(ls_msg).
  " logar / exibir ls_msg-%msg
ENDLOOP.

O extensible permite que extensões adicionem as próprias validations ao pacote (BOs draft). O primo draft dela é a draft determine action Prepare, que roda antes do Activate.

8. Draft actions: o quinteto do draft

Num BO draft-enabled com strict mode, elas são obrigatórias e explícitas na BDEF:

define behavior for ZR_Contract alias Contract
...
with draft
{
  ...
  draft action Edit;
  draft action Activate optimized;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare
  {
    validation validateDates;
    validation Item~validateQuantity;
  }
}

Draft action

O que faz

Detalhe que importa

Edit

Copia a instância ativa para a tabela draft

Aceita feature/authorization control

Activate

Persiste o draft na tabela ativa e limpa o draft

Use sempre optimized — reduz drasticamente execuções de determinations/validations

Discard

Descarta o draft

Resume

Retoma draft com lock expirado

Executado automaticamente pelo framework

Prepare

Determine action do draft (roda antes do Activate)

Onde entram as validations pré-ativação

Todas aceitam with additional implementation quando você precisa enfiar lógica própria no meio do fluxo padrão — aí o método correspondente vira um FOR MODIFY seu. Caso de uso típico: no Edit with additional implementation, registrar em log de auditoria quem entrou em edição.

As adições transversais (valem para quase todas)

Adição

Efeito

Exemplo

features: instance | global

Feature control dinâmico — habilita/desabilita por instância ou globalmente

Só aprovar se status = 'O'

authorization: none | update | global | instance

Controla o check de autorização: pula, delega ao update, ou substitui

Estorno exige o mesmo direito do update

precheck

Barra a operação antes de chegar ao buffer

Bloquear action em instância arquivada

lock: none

Não trava a instância para a action

Action de log que não altera o registro

external 'Nome'

Alias exposto na metadata OData

external 'Approve' num contrato em inglês

default function ...

Fornece defaults para os parâmetros no diálogo Fiori

Sugerir o motivo de rejeição mais comum

A tabela de decisão: qual action usar?

Requisito

Use

Mudar status/dados de registros selecionados

Instance action (com features:instance para habilitar por status)

Usuário precisa informar valores

Instance action com parameter (abstract entity) + default function

Operação sobre o conjunto, sem seleção

Static action

Mesma action, mesma instância, mesma requisição, N vezes

Repeatable action

Copiar/duplicar um registro (com filhas)

Instance factory action

Criar do zero com defaults/template/parâmetros

Static factory action

Substituir o Create padrão do Fiori

Static default factory action

Lógica interna reutilizada, invisível para fora

Internal action

Gancho de execução dentro da save sequence (posting, número definitivo)

Save actionsave(finalize) ou save(adjustnumbers)

Rodar determinations/validations sob demanda (dados que entraram por fora)

Determine action

Só ler/calcular, sem modificar nada

Function — não é action!

Boas práticas consolidadas

  1. Action modifica, function lê. Não use action para consulta — e não tente modificar numa function (runtime error garantido no consumo).

  2. Parâmetros sempre via abstract entity. É a recomendação oficial mesmo quando um elemento DDIC funcionaria — o contrato fica versionável e extensível.

  3. result [1] $self nas actions de status. Sem isso, a UI Fiori não atualiza a linha após o clique e o usuário dá F5 achando que não funcionou.

  4. Feature control em vez de mensagem de erro. Desabilitar o botão "Aprovar" para status inválido é melhor UX do que deixar clicar e devolver erro.

  5. Factory action não tem result — o retorno é o MAPPED (%cid → chave). Não brigue com o framework.

  6. Toda a lógica de criação dentro da factory action, nunca no consumidor (job, API): assim UI, EML e background criam instâncias idênticas.

  7. Save action só para o que realmente é "hora do commit". Se cabe na interaction phase, é action comum.

  8. Activate sempre optimized em BOs draft — a diferença de performance em determinations/validations é considerável.

  9. Nomeie pelo verbo de negócio (approve, postToLedger) e use external se o contrato OData pedir outro nome.

Perguntas frequentes

O que significa result [1] $self?

É o parâmetro de saída da action: [1] diz que cada instância de entrada devolve exatamente um resultado, e $self diz que o tipo do resultado é a própria entidade da action — ela devolve a si mesma, atualizada. É o que faz a UI Fiori refletir a mudança na hora. O tipo também pode ser outra entidade (entity ZR_Invoice) ou uma abstract entity, e as cardinalidades possíveis são [1], [0..1], [1..*] e [0..*].

Qual a diferença entre action e function no RAP?

Action modifica o estado do BO (handler FOR MODIFY, com lock por padrão); function apenas retorna informação (handler FOR READ, sem lock, output obrigatório). Modificar dados dentro de uma function gera runtime error quando um consumidor a acessa.

Por que minha factory action não aceita result?

Porque o retorno dela é padronizado: toda factory action produz exatamente uma instância (cardinalidade [1] obrigatória) e devolve a chave criada no response parameter MAPPED, via mapeamento %cid → chave.

Posso chamar uma internal action de outro BO?

Não — internal restringe o acesso à implementação do próprio BO. Se outro BO precisa da lógica, ou a action deixa de ser internal, ou a lógica vai para uma classe auxiliar compartilhada.

Quando o dump de "action executada mais de uma vez" acontece?

Quando uma action sem a adição repeatable é executada mais de uma vez na mesma instância dentro da mesma requisição EML/OData. Se o cenário é legítimo (batch de API, descontos cumulativos), declare-a como repeatable action.

Save action funciona em managed BO?

Save actions existem para representar funcionalidade que, do ponto de vista do RAP, só tem fase de save (tipicamente legado sem buffer transacional — cenários unmanaged/managed com additional save). E lembre das restrições: só o saver method declarado pode chamá-la, e save(adjustnumbers) não pode ser chamada de determination on save.

Determine action roda na interaction phase ou na save?

Na interaction phase — mesmo que execute determinations "on save". É por isso que uma save action chamada dentro de uma determination on save disparada via determine action gera short dump: o contexto de save não existe ali.

Conclusão

O leque de actions do RAP é um espectro de intenções: instance para o verbo de negócio sobre registros, static para o conjunto, repeatable para repetição legítima na mesma requisição, factory (instance/static/default) para criação com lógica própria, internal para encapsular, save para o gancho do commit, determine para reaplicar regras sob demanda — e function quando a resposta certa nem é uma action. Escolher o tipo certo não é estética: é lock correto, contrato OData limpo, UI que reage e dump que não acontece. Da próxima vez que o requisito chegar, passe pela tabela de decisão antes de digitar action.

Referências oficiais

  • RAP - action — ABAP Keyword Documentation

  • RAP - action, Non-Factory — ABAP Keyword Documentation

  • RAP - action, factory — ABAP Keyword Documentation

  • RAP - save action — ABAP Keyword Documentation

  • RAP - determine action — ABAP Keyword Documentation

  • RAP - draft action — ABAP Keyword Documentation

  • RAP - function — ABAP Keyword Documentation

  • RAP100 — Instance Action — SAP Developers

  • RAP100 — Factory Action — SAP Developers

  • ABAP Cheat Sheet — RAP BDL — SAP-samples

TagsADTActionsBDEFClean CoreDesenvolvedorEMLFiori Elements
Avalie este conteúdo
para avaliar
SAPiente
@sapiente

Blog sobre SAP, ABAP, BTP, Fiori e tudo que envolve o ecossistema SAP.

Ver perfil →
FacebookInstagramYouTubeTwitter

Comentários 0

Entre na conversa

Faça login para deixar seu comentário neste artigo.

Ainda sem comentários

Seja o primeiro a comentar este artigo.