Tipos de Action no RAP: O Guia Completo
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
liberarRemessahabilitada 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 |
|---|---|
| Declara o parâmetro de saída da action (é opcional — action sem result não devolve nada) |
| A cardinalidade do resultado por instância de entrada: cada instância processada devolve exatamente 1 resultado |
| 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 |
|---|---|---|
| Exatamente 1 resultado | Aprovar: devolve a própria instância atualizada |
| 0 ou 1 resultado | Buscar documento subsequente: pode não existir |
| 1 ou mais resultados | Explodir estrutura: devolve os componentes gerados |
| 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 atualizadaPor 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 usarREQUESTnoEXECUTEpara 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$selfali é implícito por natureza.$selfcomo 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
resultem factory action: o resultado volta pelo response parameterMAPPED, mapeando o%cidpara 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,massApprovee uma determination chamam a mesmainternal 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 internal6. 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 methodfinalizee pode ser chamada de uma determination on save;save(adjustnumbers)roda noadjust_numberse 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 comosave(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 |
|---|---|---|
| Copia a instância ativa para a tabela draft | Aceita feature/authorization control |
| Persiste o draft na tabela ativa e limpa o draft | Use sempre |
| Descarta o draft | — |
| Retoma draft com lock expirado | Executado automaticamente pelo framework |
| 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 |
|---|---|---|
| Feature control dinâmico — habilita/desabilita por instância ou globalmente | Só aprovar se status = 'O' |
| Controla o check de autorização: pula, delega ao update, ou substitui | Estorno exige o mesmo direito do update |
| Barra a operação antes de chegar ao buffer | Bloquear action em instância arquivada |
| Não trava a instância para a action | Action de log que não altera o registro |
| Alias exposto na metadata OData |
|
| 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 |
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 action — |
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
Action modifica, function lê. Não use action para consulta — e não tente modificar numa function (runtime error garantido no consumo).
Parâmetros sempre via abstract entity. É a recomendação oficial mesmo quando um elemento DDIC funcionaria — o contrato fica versionável e extensível.
result [1] $selfnas 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.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.
Factory action não tem result — o retorno é o MAPPED (
%cid→ chave). Não brigue com o framework.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.
Save action só para o que realmente é "hora do commit". Se cabe na interaction phase, é action comum.
Activate sempre
optimizedem BOs draft — a diferença de performance em determinations/validations é considerável.Nomeie pelo verbo de negócio (
approve,postToLedger) e useexternalse 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
Comentários 0
Ainda sem comentários
Seja o primeiro a comentar este artigo.
Entre na conversa
Faça login para deixar seu comentário neste artigo.