@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'RAP Facade: Bonus Payment main item'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZDemo_BonusPaymentItem
as select from I_OperationalAcctgDocItem
association [1] to I_OperationalAcctgDocItem as _AcctDocItem on _AcctDocItem.CompanyCode = $projection.CompanyCode
and _AcctDocItem.FiscalYear = $projection.FiscalYear
and _AcctDocItem.AccountingDocument = $projection.AccountingDocument
and _AcctDocItem.AccountingDocumentItem = $projection.MainItem
{
key CompanyCode, //Access control
key FiscalYear,
key AccountingDocument,
min( AccountingDocumentItem ) as MainItem,
_AcctDocItem
}
// Assumption:
// every bonus payment has exactly 1 debit item with the total bonus amount
// there may be multiple credit items
where
DebitCreditCode = 'S' //Debit line
group by
CompanyCode,
FiscalYear,
AccountingDocument
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'RAP Facade: Bonus Payment'
define root view entity ZDemo_BonusPayment
as select from I_AccountingDocument
//Main item
association [0..1] to ZDemo_BonusPaymentItem as _MainItem on _MainItem.CompanyCode = $projection.CompanyCode
and _MainItem.FiscalYear = $projection.FiscalYear
and _MainItem.AccountingDocument = $projection.AccountingDocument
{
key CompanyCode, // Access control
key FiscalYear,
key AccountingDocument,
//main attributes
DocumentDate,
PostingDate,
// main item figures - currency relationship inherited
_MainItem._AcctDocItem.CompanyCodeCurrency as Currency,
_MainItem._AcctDocItem.AmountInCompanyCodeCurrency as Amount,
//Administrative data
CreationTime as CreatedAt,
AccountingDocumentCreationDate as CreatedOn,
AccountingDocCreatedByUser as CreatedBy,
//Document reference used for employee field
DocumentReferenceID as BonusRecipient
}
where
AccountingDocumentType = 'WA'
and IsReversed != 'X'
managed implementation in class zbp_demo_bonuspayment unique;
strict ( 2 );
define behavior for ZDemo_BonusPayment alias BonusPayment
with unmanaged save
late numbering // <<<< must have when working with the BAPI
lock master
authorization master ( instance ) // based on company code field
etag master CreatedAt
{
field ( readonly ) AccountingDocument, FiscalYear; //Key fields filled by API
field ( readonly : update ) CompanyCode; //Key fields provided from external
field ( readonly ) CreatedAt, CreatedBy, CreatedOn; //Admin fields
create;
delete; //Cancellation - Not implemented yet
}
PRIVATE SECTION.
DATA:
"! <p class="shorttext synchronized" lang="en">Configuration</p>
"! <p>hard coded just for demo purposes only </p>
"! <p>Use a configuration table or BRF+ rule to fill
"! the values in a real use case </p>
BEGIN OF ms_config,
company_code TYPE bapiache09-comp_code VALUE '1010',
document_type TYPE bapiache09-doc_type VALUE 'WA',
BEGIN OF debit,
transaction TYPE bapiacgl09-acct_key VALUE 'BSX',
glaccount TYPE bapiacgl09-gl_account VALUE '0054070000',
END OF debit,
BEGIN OF credit,
transaction TYPE bapiacgl09-acct_key VALUE 'GBB',
glaccount TYPE bapiacgl09-gl_account VALUE '0013600000',
END OF credit,
END OF ms_config ##no_text.
TYPES:
"! <p class="shorttext synchronized" lang="en">Accounting document request</p>
BEGIN OF ty_s_bapi_request,
pid TYPE abp_behv_pid,
header TYPE bapiache09,
amounts TYPE STANDARD TABLE OF bapiaccr09 WITH DEFAULT KEY,
accounts TYPE STANDARD TABLE OF bapiacgl09 WITH DEFAULT KEY,
END OF ty_s_bapi_request.
METHOD fill_header.
"basics
CLEAR es_header.
es_header-comp_code = is_bonus-CompanyCode.
es_header-fisc_year = is_bonus-FiscalYear.
"dates
es_header-doc_date = is_bonus-DocumentDate.
es_header-pstng_date = is_bonus-PostingDate.
"document references
es_header-ref_doc_no = is_bonus-BonusRecipient.
"determination of internal attributes
det_header_defaults( EXPORTING is_bonus = is_bonus
CHANGING cs_header = es_header
cs_reported = cs_reported ).
ENDMETHOD.
"there is no re-usable structure to split this key
TYPES:
BEGIN OF ty_s_acc_doc_key,
belnr TYPE belnr_d,
bukrs TYPE bukrs,
gjahr TYPE gjahr,
END OF ty_s_acc_doc_key.
TYPES:
"! <p class="shorttext synchronized" lang="en">Accounting document result</p>
BEGIN OF ty_s_bapi_result,
pid TYPE abp_behv_pid,
temp_key TYPE ty_s_acc_doc_key,
key TYPE awkey,
msg TYPE STANDARD TABLE OF bapiret2 WITH DEFAULT KEY,
END OF ty_s_bapi_result.
METHOD adjust_numbers.
DATA lt_bapi_res TYPE SORTED TABLE OF ty_s_bapi_result WITH UNIQUE KEY pid.
DATA lt_bapi_req TYPE SORTED TABLE OF ty_s_bapi_request WITH UNIQUE KEY pid.
"get all records from buffer
READ ENTITY IN LOCAL MODE ZDemo_BonusPayment\\BonusPayment
ALL FIELDS WITH VALUE #( FOR ls_Payment IN mapped-bonuspayment ( %pky = ls_payment-%pre ) )
RESULT DATA(lt_payments).
"prepare the create requests
LOOP AT mapped-bonuspayment ASSIGNING FIELD-SYMBOL(<ls_bonus_key>).
TRY.
"get the record from transaction buffer for this key
DATA(lr_bonus) = REF #( lt_payments[ KEY pid COMPONENTS %pid = <ls_bonus_key>-%pid
%key = <ls_bonus_key>-%tmp ] ).
"new BAPI request
DATA(ls_bapi_request) = VALUE ty_s_bapi_request( pid = lr_bonus->%pid ).
"fill header
fill_header(
EXPORTING is_bonus = lr_bonus->*
IMPORTING es_header = ls_bapi_request-header ).
"fill amounts and account information
"Every payment is a combination of a debit and credit item
fill_amounts(
EXPORTING is_bonus_payment = lr_bonus->%data
is_header = ls_bapi_request-header
CHANGING ct_amounts = ls_bapi_request-currency_amount
ct_accounts = ls_bapi_request-accountgl ).
"call the BAPI
DATA(ls_bapi_result) = VALUE ty_s_bapi_result( pid = ls_bapi_request-pid ).
CALL FUNCTION 'BAPI_ACC_DOCUMENT_POST'
EXPORTING
documentheader = ls_bapi_request-header
IMPORTING
obj_key = ls_bapi_result-acct_doc_key
TABLES
accountgl = ls_bapi_request-accountgl
currencyamount = ls_bapi_request-currency_amount
return = ls_bapi_result-msg.
"handle errors
handle_bapi_result( EXPORTING is_result = ls_bapi_result
CHANGING cs_reported = reported ).
"split key string
DATA(ls_acc_doc_key) = CONV ty_s_acc_doc_key( ls_bapi_result-acct_doc_key ).
<ls_bonus_key>-companycode = ls_acc_doc_key-bukrs.
<ls_bonus_key>-fiscalyear = ls_acc_doc_key-gjahr.
<ls_bonus_key>-accountingdocument = ls_acc_doc_key-belnr.
CATCH zcx_demo_rap_facade_web_api INTO DATA(lx_bapi_error).
"log error reason
APPEND VALUE #( %pky = <ls_bonus_key>-%pre
%msg = lx_bapi_error ) TO reported-bonuspayment.
"BAPI call or preparation failed
INSERT VALUE #( %pky = <ls_bonus_key>-%pre
%create = if_abap_behv=>mk-on
) INTO TABLE failed-bonuspayment.
ENDTRY.
ENDLOOP.
ENDMETHOD.
@EndUserText.label: 'RAP facade demo: bonus payment'
define service ZDEMO_BONUS_PAYMENT {
expose ZDemo_BonusPayment as BonusPayments;
}
{
"Currency": "EUR",
"Amount": 100.00,
"BonusRecipient": "Jan Sample"
}
{
"@odata.context": "$metadata#BonusPayments/$entity",
"@odata.metadataEtag": "W/\"20230731082241\"",
"@odata.etag": "W/\"SADL-303832333039~082309\"",
"CompanyCode": "1010",
"FiscalYear": "2023",
"AccountingDocument": "4900010896",
"DocumentDate": "2023-07-31",
"PostingDate": "2023-07-31",
"Currency": "EUR",
"Amount": 100,
"CreatedAt": "08:23:09",
"CreatedOn": "2023-07-31",
"CreatedBy": "HIDDEN",
"BonusRecipient": "Jan Sample",
"SAP__Messages": []
}
"! <p class="shorttext synchronized" lang="en">RAP Facade: Bonus payment test run</p>
CLASS zcl_demo_rap_facade_test DEFINITION PUBLIC FINAL CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun .
ENDCLASS.
METHOD if_oo_adt_classrun~main.
TYPES ty_s_bonus_data TYPE STRUCTURE FOR CREATE zdemo_bonuspayment\\BonusPayment.
"fill minimal data
DATA(ls_bonus) = VALUE ty_s_bonus_data(
%cid = 'CREATE_MINIMAL_001'
CompanyCode = '1010'
PostingDate = sy-datum "optional
amount = 500
Currency = 'EUR'
%control = VALUE #(
CompanyCode = if_abap_behv=>mk-on
PostingDate = if_abap_behv=>mk-on
amount = if_abap_behv=>mk-on
Currency = if_abap_behv=>mk-on
)
) ##no_text.
"run
MODIFY ENTITY zdemo_bonuspayment\\BonusPayment
CREATE FROM VALUE #( ( ls_bonus ) )
MAPPED DATA(ls_create_mapped)
FAILED DATA(ls_create_failed)
REPORTED DATA(ls_create_reported).
"check for problems
IF ls_create_failed IS NOT INITIAL.
LOOP AT ls_create_reported-bonuspayment ASSIGNING FIELD-SYMBOL(<ls_create_msg>).
out->write( <ls_create_msg>-%msg->if_message~get_text( ) ).
ENDLOOP.
out->write( 'Creation failed'(e01) ).
RETURN.
ENDIF.
"save
COMMIT ENTITIES BEGIN
RESPONSE OF zdemo_bonuspayment
FAILED DATA(ls_save_failed)
REPORTED DATA(ls_save_reported).
"check for problems
IF ls_save_failed IS NOT INITIAL.
LOOP AT ls_save_reported-bonuspayment ASSIGNING FIELD-SYMBOL(<ls_save_msg>).
out->write( <ls_save_msg>-%msg->if_message~get_text( ) ).
ENDLOOP.
out->write( 'Saving failed'(e01) ).
RETURN.
ENDIF.
LOOP AT ls_create_mapped-bonuspayment ASSIGNING FIELD-SYMBOL(<ls_temp_key>).
"get the final keys from late numbering
CONVERT KEY OF zdemo_bonuspayment\\BonusPayment
FROM TEMPORARY VALUE #( %pid = <ls_temp_key>-%pid
%tmp = <ls_temp_key>-%key ) TO DATA(ls_acc_doc_key).
"check we have a new document key
IF ls_acc_doc_key-AccountingDocument IS INITIAL.
out->write( 'Saving did not generate AccountingDocument key'(e02) ).
RETURN.
ELSE.
out->write( |Generated Accounting Document key { ls_acc_doc_key-AccountingDocument }| ).
ENDIF.
ENDLOOP.
COMMIT ENTITIES END.
"check for problems
IF ls_save_failed IS NOT INITIAL.
out->write( 'Saving failed'(e03) ).
RETURN.
ENDIF.
"re-read new record from DB
READ ENTITY zdemo_bonuspayment\\BonusPayment
FROM VALUE #( ( CORRESPONDING #( ls_acc_doc_key ) ) )
RESULT DATA(lt_new_payment_data).
"check for problems
IF lt_new_payment_data IS INITIAL.
out->write( 'Re-read accounting document failed'(e04) ).
ENDIF.
ENDMETHOD.
@EndUserText.label: 'Demo payment: read access control'
@MappingRole: true
//Inheriting read access from accouting document by company code
define role ZDEMO_BONUSPAYMENT {
grant select on ZDemo_BonusPayment
where inheriting conditions from entity I_AccountingDocument;
grant select on ZDemo_BonusPaymentItem
where inheriting conditions from entity I_AccountingDocument;
}
"! <p class="shorttext synchronized" lang="en">RAP demo: error messages</p>
CLASS zcx_demo_rap_facade_web_api DEFINITION
PUBLIC
INHERITING FROM cx_static_check
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_abap_behv_message .
CONSTANTS:
"! <p class="shorttext synchronized" lang="en">Unexpected technical error</p>
BEGIN OF c_tech_error,
msgid TYPE symsgid VALUE 'ZDEMO_RAP_FACADE_WEB',
msgno TYPE symsgno VALUE '002',
attr1 TYPE scx_attrname VALUE '',
attr2 TYPE scx_attrname VALUE '',
attr3 TYPE scx_attrname VALUE '',
attr4 TYPE scx_attrname VALUE '',
END OF c_tech_error ##no_text.
CONSTANTS:
"! <p class="shorttext synchronized" lang="en">Document already exists</p>
BEGIN OF c_already_exists,
msgid TYPE symsgid VALUE 'ZDEMO_RAP_FACADE_WEB',
msgno TYPE symsgno VALUE '001',
attr1 TYPE scx_attrname VALUE 'MV_ACCOUNTING_DOCUMENT',
attr2 TYPE scx_attrname VALUE 'MV_COMPANY_CODE',
attr3 TYPE scx_attrname VALUE 'MV_FISCAL_YEAR',
attr4 TYPE scx_attrname VALUE '',
END OF c_already_exists ##no_text.
"! <p class="shorttext synchronized" lang="en">Company code</p>
DATA mv_company_code TYPE ZDemo_BonusPayment-CompanyCode.
"! <p class="shorttext synchronized" lang="en">Fiscal year</p>
DATA mv_Fiscal_Year TYPE ZDemo_BonusPayment-FiscalYear.
"! <p class="shorttext synchronized" lang="en">Document number</p>
DATA mv_Accounting_Document TYPE ZDemo_BonusPayment-AccountingDocument.
"! <p class="shorttext synchronized" lang="en">CONSTRUCTOR</p>
METHODS constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
severity TYPE if_abap_behv_message=>t_severity DEFAULT if_abap_behv_message=>severity-error
mv_company_code TYPE zdemo_bonuspayment-companycode OPTIONAL
mv_fiscal_year TYPE zdemo_bonuspayment-fiscalyear OPTIONAL
mv_accounting_document TYPE zdemo_bonuspayment-accountingdocument OPTIONAL.
ENDCLASS.
CLASS zcx_demo_rap_facade_web_api IMPLEMENTATION.
METHOD constructor ##ADT_SUPPRESS_GENERATION.
super->constructor( previous = previous ).
me->mv_company_code = mv_company_code.
me->mv_fiscal_year = mv_fiscal_year.
me->mv_accounting_document = mv_accounting_document.
"RAP message severity
me->if_abap_behv_message~m_severity = severity. //<<<<< manually added!
CLEAR me->textid.
IF textid IS INITIAL.
if_t100_message~t100key = if_t100_message=>default_textid.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
ENDCLASS.
METHOD det_header_defaults.
"values from configuration - hard coded just for demo purposes!
IF cs_header-comp_code IS INITIAL.
cs_header-comp_code = ms_config-company_code.
ENDIF.
cs_header-doc_type = ms_config-document_type.
cs_header-header_txt = |Bonus payout ARE { cs_header-fisc_year } for { cs_header-ref_doc_no }| ##no_text. "demo only
cs_header-username = sy-uname.
IF cs_header-doc_date IS INITIAL. "fallback
cs_header-doc_date = sy-datum.
ENDIF.
IF cs_header-pstng_date IS INITIAL. "fallback
cs_header-pstng_date = sy-datum.
ENDIF.
"determine fiscal year if not provided
IF cs_header-fisc_year IS INITIAL.
DATA(ls_msg) = VALUE bapiret1( ).
CALL FUNCTION 'BAPI_COMPANYCODE_GET_PERIOD'
EXPORTING
companycodeid = cs_header-comp_code
posting_date = cs_header-pstng_date
IMPORTING
fiscal_year = cs_header-fisc_year
return = ls_msg.
"handle errors
IF ls_msg-type CA c_msg_type_error.
RAISE EXCEPTION new_msg_from_bapi( CORRESPONDING #( ls_msg ) ).
ENDIF.
ENDIF.
ENDMETHOD.
"! <p class="shorttext synchronized" lang="en">Late messages</p>
TYPES ty_s_reported TYPE RESPONSE FOR REPORTED LATE zdemo_bonuspayment.
"! <p class="shorttext synchronized" lang="en">Late errors</p>
TYPES ty_s_failed TYPE RESPONSE FOR FAILED LATE zdemo_bonuspayment.
"! <p class="shorttext synchronized" lang="en">Handle BAPI exceptions + messages</p>
"! @parameter is_result | BAPI result
METHODS handle_bapi_result
IMPORTING
is_result TYPE bapiret2_tab
CHANGING
cs_reported TYPE ty_s_reported
cs_failed TYPE ty_s_failed.
METHOD handle_bapi_result.
"process all messages
LOOP AT is_result-msg ASSIGNING FIELD-SYMBOL(<ls_bapi_msg>).
"Convert all messages
DATA(lo_behv_msg) = new_msg_from_bapi( <ls_bapi_msg> ).
"Stop on error
IF <ls_bapi_msg>-type CA c_msg_type_error.
DATA(lx_error_msg) = lo_behv_msg.
EXIT.
ELSE.
APPEND VALUE #( %pid = is_result-pid
%msg = lo_behv_msg ) TO cs_reported-bonuspayment.
ENDIF.
ENDLOOP.
"check a key was generated
IF lx_error_msg IS NOT BOUND
AND is_result-acct_doc_key IS INITIAL.
"Unexpected technical error
RAISE EXCEPTION TYPE zcx_demo_rap_facade_web_api
EXPORTING
textid = zcx_demo_rap_facade_web_api=>c_tech_error.
ENDIF.
"If there was an error, raise it
IF lx_error_msg IS BOUND.
RAISE EXCEPTION lx_error_msg.
ENDIF.
ENDMETHOD.
"! <p class="shorttext synchronized" lang="en">New RAP message from bapiret</p>
"! @parameter is_message | BAPIRET message
"! @parameter ro_msg | RAP behavior message
METHODS new_msg_from_bapi
IMPORTING is_message TYPE bapiret2
RETURNING VALUE(ro_msg) TYPE REF TO zcx_demo_rap_facade_web_api.
METHOD new_msg_from_bapi.
CHECK is_message IS NOT INITIAL.
"determine severity
DATA(lv_msg_type) = COND #( WHEN is_message-type IS NOT INITIAL THEN is_message-type
ELSE if_abap_behv_message=>severity-error ).
"set system variables
MESSAGE ID is_message-id TYPE lv_msg_type NUMBER is_message-number
WITH is_message-message_v1 is_message-message_v2 is_message-message_v3 is_message-message_v4
INTO DATA(lv_msg).
TRY.
"raise message from system variables
RAISE EXCEPTION TYPE zcx_demo_rap_facade_web_api USING MESSAGE.
CATCH zcx_demo_rap_facade_web_api INTO ro_msg ##no_handler.
ENDTRY.
ENDMETHOD.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
26 | |
21 | |
19 | |
13 | |
10 | |
9 | |
8 | |
8 | |
7 | |
7 |