sap abap - ERP Q&A https://www.erpqna.com/tag/sap-abap/ Trending SAP Career News and Guidelines Mon, 24 Nov 2025 09:56:56 +0000 en-US hourly 1 https://wordpress.org/?v=6.9 https://www.erpqna.com/wp-content/uploads/2021/11/cropped-erpqna-32x32.png sap abap - ERP Q&A https://www.erpqna.com/tag/sap-abap/ 32 32 Building a RAG Bot on SAP BTP With Hana Cloud Vector Engine and AI Core https://www.erpqna.com/building-a-rag-bot-on-sap-btp-with-hana-cloud-vector-engine-and-ai-core/?utm_source=rss&utm_medium=rss&utm_campaign=building-a-rag-bot-on-sap-btp-with-hana-cloud-vector-engine-and-ai-core Sat, 30 Aug 2025 12:16:00 +0000 https://www.erpqna.com/?p=93497 Retrieval-Augmented Generation (RAG) is a method for improving the output of large language models (LLMs). While LLMs are trained on vast amounts of data, they may lack access to proprietary or enterprise-specific information behind paywalls, or other custom data needed to generate accurate results. RAG supplements the LLM’s knowledge by retrieving relevant external information, making […]

The post Building a RAG Bot on SAP BTP With Hana Cloud Vector Engine and AI Core appeared first on ERP Q&A.

]]>
Retrieval-Augmented Generation (RAG) is a method for improving the output of large language models (LLMs). While LLMs are trained on vast amounts of data, they may lack access to proprietary or enterprise-specific information behind paywalls, or other custom data needed to generate accurate results. RAG supplements the LLM’s knowledge by retrieving relevant external information, making its outputs more accurate, relevant, and useful in a cost-effective way.

Key Challenges of LLMs Solved by RAG Bots

  • Presenting False Information: LLMs can hallucinate when they lack the correct answer, often providing inaccurate responses that may appear convincing, which can lead to wrong decisions.
  • Outdated Responses: LLMs are essentially prediction machines, generating answers based on their training data. As a result, they may provide outdated information if it falls beyond their knowledge cutoff.
  • Terminology Differences: Natural languages like English, Spanish, or French are subjective, and the same term can have multiple meanings. LLMs may misunderstand context, leading to incorrect answers.

RAG bots address these challenges by supplementing LLMs with relevant, up-to-date information, making them a cost-effective and reliable way to enhance AI outputs.

To complete this project we will need

  • Data not already accessible to the LLM: There’s no point in using widely known content, like Shakespeare’s plays, as the LLM likely already knows it.
  • A Vector Database: In this example, we’ll use SAP HANA Vector Engine. Many options exist, including AWS vector engines and open-source alternatives. choose the one that fits your needs.
  • A Python Environment: All code will be implemented in Python.
  • An Embedding Model: Here, we use OpenAI’s embedding model, but any freely available model from Hugging Face or similar sources works.

What is a vector database? why do i need it? how does it differ from a real database?

Vector databases store data as mathematical representations called embeddings. Imagine visiting an ice cream shop you’ve been to a hundred times. On your 101st visit, a super-intelligent shopkeeper could instantly guess your preferred flavor based on your previous questions. That’s similar to how a vector database works.

Each piece of data is stored as an embedding, and when you ask a question, it’s converted into an embedding as well. The database then finds the part of your data that is most similar to your query. For example, if your question is meant to return the number 6, and the 12th paragraph of the 200th PDF is embedded as 5.8989898, the database will return it because it’s the closest match.

It’s crucial to embed information carefully. Using inaccurate data or poor embedding methods can result in irrelevant or excessive information being retrieved.

What are embeddings?

An embedding is a vector representation of data generated by an embedding model. In our setup, the model creates embeddings for your data, which are then stored in the HANA vector database. When you ask a question, it is also converted into an embedding, and the database compares it against the stored embeddings to find the most relevant matches.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

What vector embeddings looks like

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

start:

For my data i used this . It is a long 2000 page pdf on aws lambda.

First install these dependencies in your terminal

python-dotenv==1.0.1 # For reading environment variables stored in .env file
langchain==0.2.2
langchain-community==0.2.3
unstructured==0.14.4 # Document loading
# onnxruntime==1.17.1 # chromadb dependency: on Mac use `conda install onnxruntime -c conda-forge`
# For Windows users, install Microsoft Visual C++ Build Tools first
# install onnxruntime before installing `chromadb`

Then import the following before proceeding

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

Step 1: Document Loading

At this point we have our database ready and we have set up our aicore model appropriately; now we can import our document.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

Step 2: Splitting the documents

This is a crucial stage because how you split and chunk your documents directly affects the quality of results. Dividing documents into manageable segments allows the system to efficiently create accurate embeddings. When queried, these embeddings produce more precise responses.

Each chunk can also be enriched with metadata, such as page numbers or chapter titles. This helps locate the source if a deeper look is needed. Chunking must be done thoughtfully by headings, topics, or other logical divisions to ensure the system captures the content effectively.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

Step 3: Connect to database:

At this stage, we are ready to generate the embeddings and connect them to our HANA database or any other vector database we choose to use.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

There are many resources on how to connect to hana, so for now i will leave this here.

Step 4: Access Embedding Model:

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

As explained earlier, an embedding model transforms complex, high-dimensional data into numerical values that machines can easily understand. This makes the data easier to process and allows ML models to identify patterns and relationships.

A vector is a one-dimensional representation of data containing multiple values. For example, a weather vector might be [2, 32, 1000], representing rain (2 = yes), temperature (32°F), and location (1000 = a specific county). Vectors capture relevant information depending on the type of data being represented.

Since this is a text embedding model, it can only create embeddings for textual data. This is important if you want to embed image files etc.

Step 5: Adding Data into HANA.

First, create the schema. You can do this by running a SQL query with the desired schema. This schema will hold the table where all your embeddings are stored.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

This code will create the table in your database, load your document, split it into chunks, and generate embeddings for each chunk.

When querying the data, your query is first transformed into an embedding and placed in the vector space. The vector database then returns the document chunks that are most similar to your query. This is why chunking is important: it ensures that the system can match your prompt accurately, typically using cosine similarity to find the closest matches.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

After integrating everything into a clean Streamlit UI, here is the final result.

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit

SAP BTP ABAP Environment, SAP AI Core, SAP BTP Cockpit
Rating: 5 / 5 (2 votes)

The post Building a RAG Bot on SAP BTP With Hana Cloud Vector Engine and AI Core appeared first on ERP Q&A.

]]>
Handle Asynchronous task in background job from a stateless UI application using RAP business object https://www.erpqna.com/handle-asynchronous-task-in-background-job-from-a-stateless-ui-application-using-rap-business-object/?utm_source=rss&utm_medium=rss&utm_campaign=handle-asynchronous-task-in-background-job-from-a-stateless-ui-application-using-rap-business-object Tue, 18 Mar 2025 12:09:01 +0000 https://www.erpqna.com/?p=91078 Requirement: An application built using BAS on BTP for an RAP business object has a requirement of triggering an asynchronous task in the background when a button is clicked. Example: ( this example will be used to explain the implementation ) when the button is clicked, the user uploads an excel file with data and […]

The post Handle Asynchronous task in background job from a stateless UI application using RAP business object appeared first on ERP Q&A.

]]>
Requirement: An application built using BAS on BTP for an RAP business object has a requirement of triggering an asynchronous task in the background when a button is clicked. Example: ( this example will be used to explain the implementation ) when the button is clicked, the user uploads an excel file with data and on click of OK, the data needs to be saved but as it is a large amount of data it will take some time, so the UI shouldn’t be waiting for a response from the backend.

Challenge: background processing framework has been introduced by SAP from Cloud 2311 version. Without this framework, it is not possible to trigger an asynchronous task from a stateless UI.

Solution: To simulate similar functionality we can use existing ABAP artifacts like RFC and submitting program in background job.

Overview: In this example, I have implemented the following:

  • a new action with parameters is defined in the behavior definition. The key of the RAP object will hold the keys of the table that need to be changed. The new values to be updated are passed by the UI as parameters to the action
action (features : instance) upload_file  parameter ZSD_ABS_CONTRACTUAL_INDATA;
  • an RFC function module is created that accepts the data from the file in the form of an internal table
  • Above RFC function module is called from the RAP action implementation ( with the addition destination ‘NONE’ as otherwise commit cannot be done from the FM to schedule the job ).
METHOD upload_file.

    DATA: gt_return   TYPE STANDARD TABLE OF zsds_soitem_message,
          lt_sch_data TYPE zsdt_soschdata.

    IF NOT keys[] IS INITIAL.
      LOOP AT keys INTO DATA(ls_keys).
        APPEND INITIAL LINE TO lt_sch_data ASSIGNING FIELD-SYMBOL(<ls_sch_data>).
        <ls_sch_data>-vbeln = ls_keys-salesdocument.
        <ls_sch_data>-posnr = ls_keys-salesdocumentitem.
        <ls_sch_data>-del_dt = COND #( WHEN ls_keys-%param-newcontractualdt IS NOT INITIAL
                                       THEN ls_keys-%param-newcontractualdt ).
        <ls_sch_data>-email = cond #( WHEN ls_keys-%param-email IS NOT INITIAL
                                       THEN ls_keys-%param-email ).

      ENDLOOP.

      CALL FUNCTION 'ZSD_DEL_DATE_UPD_FILE' DESTINATION 'NONE'
        EXPORTING
          it_sch_data   = lt_sch_data
        EXCEPTIONS
          error_message = 99.

    ENDIF.

  ENDMETHOD.
  • The function module calls the ‘JOB_OPEN’ and ‘JOB_CLOSE’ function modules to submit another program via a background job.
  • The program runs asynchronously as a job and updates the data and also sends an email with the results to the email-id ( the parameter email in the previous screenshot )
  • Email functionality has been implemented so the user is informed of the success or failure of the task as the UI doesn’t wait for a response and simply displays a message that the data will be updated in background.
  • The button and the popup that allows the user to upload an excel file:

Conclusion: The above approach can be used to call an asynchronous task from an RAP object for OP SAP versions. With SAP Cloud 2311, it will also be possible to update the UI once the asynchronous task is completed using event driven actions.

Rating: 5 / 5 (1 votes)

The post Handle Asynchronous task in background job from a stateless UI application using RAP business object appeared first on ERP Q&A.

]]>
Writing Clean Code and Best Practices in SAP ABAP https://www.erpqna.com/writing-clean-code-and-best-practices-in-sap-abap/?utm_source=rss&utm_medium=rss&utm_campaign=writing-clean-code-and-best-practices-in-sap-abap Wed, 15 Jan 2025 09:20:59 +0000 https://www.erpqna.com/?p=90059 Clean code is not just about writing code that works; it’s about writing code that is readable, maintainable, and efficient. In the context of SAP ABAP, adhering to clean code principles and best practices can significantly enhance the quality of your programs. Here are some key points to keep in mind: 1. Use Meaningful Names […]

The post Writing Clean Code and Best Practices in SAP ABAP appeared first on ERP Q&A.

]]>
Clean code is not just about writing code that works; it’s about writing code that is readable, maintainable, and efficient. In the context of SAP ABAP, adhering to clean code principles and best practices can significantly enhance the quality of your programs. Here are some key points to keep in mind:

1. Use Meaningful Names

Choose clear, descriptive names for variables, methods, and classes. Avoid abbreviations and aim for names that convey the purpose and intent of the code.

2. Write Small and Focused Methods

Keep your methods small and focused on a single task. This makes the code easier to understand, test, and maintain. Aim for methods that do one thing and do it well.

3. Avoid Hardcoding Values

Hardcoding values makes the code inflexible and harder to maintain. Use constants or configuration tables instead, so changes can be made easily without modifying the code.

4. Comment Judiciously

Write comments that explain why a piece of code exists, not what it does. Good code should be self-explanatory, but comments can provide context and rationale for complex logic.

5. Follow Consistent Formatting

Maintain a consistent coding style and format throughout your ABAP programs. This includes indentation, spacing, and naming conventions. Consistent formatting improves readability and helps in code reviews.

6. Use Modularization Techniques

Break down complex processes into smaller, reusable components using subroutines, function modules, or methods. Modularization promotes code reuse and simplifies debugging.

7. Handle Exceptions Properly

Implement proper error handling using TRY-CATCH blocks. Ensure that your code gracefully handles exceptions and provides meaningful error messages to users.

8. Optimize Database Access

Minimize the number of database accesses and use efficient queries. Retrieve only the data you need and avoid nested SELECT statements. Use indexes and buffering where appropriate.

9. Avoid Using Obsolete Constructs

Stay updated with the latest ABAP syntax and features. Avoid using obsolete constructs and embrace modern ABAP features such as inline declarations, new string operations, and expressions.

10. Test Your Code Thoroughly

Write unit tests to validate your code’s functionality. Regular testing helps catch bugs early and ensures that your code behaves as expected in different scenarios.

11. Document Your Code

Provide clear and concise documentation for your programs. This includes explaining the purpose, usage, and any special considerations of your code. Good documentation aids future maintenance and onboarding of new developers.

12. Engage in Code Reviews

Participate in code reviews to share knowledge and ensure adherence to coding standards. Peer reviews help identify potential issues and foster a culture of continuous improvement.

    By following these clean code principles and best practices, you can create ABAP programs that are robust, efficient, and easy to maintain. Clean code is a key factor in the long-term success of any software project, and SAP ABAP is no exception.

    Rating: 3 / 5 (2 votes)

    The post Writing Clean Code and Best Practices in SAP ABAP appeared first on ERP Q&A.

    ]]>
    Calling Fragment Form & Send it as an attachment using ABAP Walk-Through (Dunning Example) https://www.erpqna.com/calling-fragment-form-send-it-as-an-attachment-using-abap-walk-through-dunning-example/?utm_source=rss&utm_medium=rss&utm_campaign=calling-fragment-form-send-it-as-an-attachment-using-abap-walk-through-dunning-example Wed, 16 Oct 2024 10:22:41 +0000 https://www.erpqna.com/?p=88299 This Blog is based on the Output Management Form and has all of its configuration completed Ex for dunning: use maintenance view: V_T047E using code: sm30 to switch between frameworks through drop-down list First: you can identify Fields for Key & Master Key internal tables for standard fragment form through putting a breaking point By […]

    The post Calling Fragment Form & Send it as an attachment using ABAP Walk-Through (Dunning Example) appeared first on ERP Q&A.

    ]]>
    This Blog is based on the Output Management Form and has all of its configuration completed

    Ex for dunning: use maintenance view: V_T047E using code: sm30 to switch between frameworks through drop-down list

    First: you can identify Fields for Key & Master Key internal tables for standard fragment form through putting a breaking point

    By using framework: sap cloud and using fragment form template and of course maintain brf+ configuration

    In class: CL_SOMU_FORM_SERVICES , Method : GET_DOCUMENT

    1. Put a break point in the first line and go back to the previous program to see internal tables passed to form calling for keys.
    2. Take screenshots for them, as you will be using them for the custom calling of the form as these fields are maintained according to configuration & master form template derivation.
    3. If you need the application ID Key, it’s a concatenation between multiple variables, You will find it in the call back class specific to a given object in the SPRO configuration

    SPRO -> CROSS APPLICATION COMPONENTS -> OUTPUT CONTROL (here you can find all of OM Configuration) -> DEFINE OUTPUT TYPE THEN SELECT THE ENTRY CORRESPONDING TO YOUR BRF+ ROLE and click details, you will find your call back class, in our class for dunning : CL_FIN_FO_DUNN_OUTPUT , all call back classes Implement same interface, then go into method : IF_APOC_COMMON_API~GET_FDP_PARAMETER

    Second step:

    Activate the legacy printout system and put a breakpoint inside the printing object whether it’s a program or function module, if you are using PDF, you can put a breakpoint in open job FM and trace back through the stack to the transaction screen printing program

    In our case for dunning: It prints using FM : “PRINT_DUNNING_NOTICE_PDF”, put a breakpoint there

    1. Start comparing the values of the key & master key internal table with local variables available to locate the objects that contain the data which will be passed to the fragment forms Key internal tables from the standard

    • You will find all the data u need in the objects because whether it is fragment, PDF or whatever technology, the system always collects the same data passed one way or the other

    2. After that start creating your custom code to call the fragment form

    • Consider the condition to identify which master form template to use.

    3. Create an enhancement in the standard program to defuse the standard code and prevent its execution, make sure that the standard code doesn’t update standard somewhere along the code as you need to confirm whether it happens through the OData service of fragment or not! ( because if not you will have to use the standard code that does that to make everything consistent )

    • then test the cycle end to end

    in our case , here is the example for the PO Form

    1. I created a custom program se38 to test using po form

    *&---------------------------------------------------------------------*
    *& Report ZDUMMY
    *&---------------------------------------------------------------------*
    *&
    *&---------------------------------------------------------------------*
    REPORT ZDUMMY.
    
    SELECTION-SCREEN BEGIN OF BLOCK b1.
      PARAMETERS: p_po_num TYPE ebeln MATCHCODE OBJECT h_ekko OBLIGATORY.
    SELECTION-SCREEN END OF BLOCK b1.
    
    INITIALIZATION.
    
    START-OF-SELECTION.
      DATA: lo_cl_somu_form_services TYPE REF TO cl_somu_form_services.
      DATA: lt_keys TYPE cl_somu_form_services=>ty_gt_key.
      DATA: ls_ekko TYPE ekko.
      DATA: lt_master_keys TYPE cl_somu_form_services=>ty_gt_key.
      DATA: lv_content TYPE  xstring.
      DATA: lv_xml TYPE xstring.
      DATA: lv_pages TYPE  fppagecount.
      DATA: lv_trace_string  TYPE string.
      DATA: lv_stop_processing TYPE  abap_bool.
      DATA: lt_message TYPE cl_somu_form_services=>ty_gt_message.
      DATA: lv_output_length TYPE i.
      DATA: lt_generated_pdf  TYPE STANDARD TABLE OF tbl1024 .
    
      FIELD-SYMBOLS: <ls_key> TYPE cl_somu_form_services=>ty_gs_key.
    
    
      SELECT SINGLE *
        FROM ekko
        INTO ls_ekko
        WHERE ebeln = p_po_num.
    
    
      " -------------- Values for the purchase order form ------------
      APPEND INITIAL LINE TO lt_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'PurchaseOrder'.
      <ls_key>-value = p_po_num.
    
      APPEND INITIAL LINE TO lt_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'SenderCountry'.
      <ls_key>-value = 'DE'.
    
      APPEND INITIAL LINE TO lt_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'PurchaseOrderChangeFlag'.
      <ls_key>-value = space.
    
      APPEND INITIAL LINE TO lt_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'Language'.
      <ls_key>-value = 'E'.
    
      APPEND INITIAL LINE TO lt_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'ReceiverPartnerNumber'.
      <ls_key>-value = ls_ekko-lifnr.
    
      " --------------------------- Values for the master form template ---------------------------
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'PrintFormDerivationRule'.
      <ls_key>-value = 'PURCHASE_ORDER_MASTER_FOR_COMPANY'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'WatermarkText'.
      <ls_key>-value = space.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'LocaleCountry'.
      <ls_key>-value = 'DE'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'LocaleLanguage'.
      <ls_key>-value = 'E'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'OutputControlApplicationObjectType'.
      <ls_key>-value = 'PURCHASE_ORDER'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'OutputControlApplicationObject'.
      <ls_key>-value = p_po_num.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'OutputRequestItem'.
      <ls_key>-value = '000001'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'OutputDocumentType'.
      <ls_key>-value = 'PURCHASE_ORDER'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'Recipient'.
      <ls_key>-value = ls_ekko-lifnr.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'RecipientRole'.
      <ls_key>-value = 'LF'.
    
      APPEND INITIAL LINE TO lt_master_keys ASSIGNING <ls_key>.
      <ls_key>-name = 'SenderCountry'.
      <ls_key>-value = 'DE'.
    
      lo_cl_somu_form_services = cl_somu_form_services=>get_instance( ).
    
    
      lo_cl_somu_form_services->get_document( 
       EXPORTING iv_master_form_name  = 'SOMU_FORM_MASTER_A4'
                 iv_form_name         = 'MM_PUR_PURCHASE_ORDER'
                 it_key               = lt_keys
                 it_master_key        = lt_master_keys
                 iv_form_language     = 'E'
                 iv_form_country      = 'DE'
      IMPORTING  ev_content          = lv_content
                 ev_xml               = lv_xml
                 ev_pages             = lv_pages
                 ev_trace_string      = lv_trace_string
                 ev_stop_processing   = lv_stop_processing
                 et_message           = lt_message ).
      IF sy-subrc <> 0.
      ENDIF.
    
    
      CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
        EXPORTING
          buffer        = lv_content
        IMPORTING
          output_length = lv_output_length
        TABLES
          binary_tab    = lt_generated_pdf.
    
    
      " Shows a preview of the generated PDF file in a popup window.
      CALL FUNCTION 'FDM_COLL_INV_PDF_SHOW'
        EXPORTING
          t_pdf = lt_generated_pdf.
    
    "=============================================================================
    
      "Object References
    DATA: lo_bcs         TYPE REF TO cl_bcs,
          lo_doc_bcs     TYPE REF TO cl_document_bcs,
          lo_recep       TYPE REF TO if_recipient_bcs,
          lo_sapuser_bcs TYPE REF TO cl_sapuser_bcs,
          lo_cx_bcx      TYPE REF TO cx_bcs,
          lv_string_text  TYPE string,
          lt_text           TYPE bcsy_text,
          lv_bin_filesize TYPE so_obj_len,
          lv_sent_to_all  TYPE os_boolean,
          lt_binary_content TYPE solix_tab.
    
      CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
        EXPORTING
          buffer        = lv_content
        IMPORTING
          output_length = lv_output_length
        TABLES
          binary_tab    = lt_binary_content[].
    
    lv_bin_filesize = lv_output_length.
    
      TRY.
    *     -------- create persistent send request ------------------------
        lo_bcs = cl_bcs=>create_persistent( ).
          "First line
          CONCATENATE 
                     'Dear Colleague' cl_abap_char_utilities=>newline 
          INTO lv_string_text.
        APPEND lv_string_text TO lt_text.
        CLEAR lv_string_text.
        "Second line
        CONCATENATE 'Please find attached a test smartform.'
         cl_abap_char_utilities=>newline INTO lv_string_text.
        APPEND lv_string_text TO lt_text.
        CLEAR lv_string_text.
        "Third line
        APPEND 'Best Regards,' TO lt_text.
        "Fourth line
        APPEND 'Systems Administrator.' TO lt_text.
    
    *---------------------------------------------------------------------
    *-----------------&      Create Document     *------------------------
    *---------------------------------------------------------------------
        lo_doc_bcs = cl_document_bcs=>create_document(
                        i_type    = 'RAW'
                        i_text    = lt_text[]
                        i_length  = '12'
                        i_subject = 'Test Email' ).   "Subject of the Email
    
    *---------------------------------------------------------------------
    *-----------------&   Add attachment to document     *----------------
    *---------------------------------------------------------------------
    *     BCS expects document content here e.g. from document upload
    *     binary_content = ...
        CALL METHOD lo_doc_bcs->add_attachment
          EXPORTING
            i_attachment_type    = 'PDF'
            i_attachment_size    = lv_bin_filesize
            i_attachment_subject = 'Test Email'
            i_att_content_hex    = lt_binary_content.
    
    *     add document to send request
        CALL METHOD lo_bcs->set_document( lo_doc_bcs ).
    
    *---------------------------------------------------------------------
    *------------------------&   Set Sender     *-------------------------
    *---------------------------------------------------------------------
    *    lo_sapuser_bcs = cl_sapuser_bcs=>create( sy-uname ).
    *    CALL METHOD lo_bcs->set_sender
    *      EXPORTING
    *        i_sender = lo_sapuser_bcs.
    
    lo_recep = 
    cl_cam_address_bcs=>create_internet_address('Receiving Email Address').
    
    "Add recipient with its respective attributes to send request
        CALL METHOD lo_bcs->add_recipient
          EXPORTING
            i_recipient = lo_recep
            i_express   = 'X'.
    
        CALL METHOD lo_bcs->set_send_immediately
          EXPORTING
            i_send_immediately = 'X'.
    
    *---------------------------------------------------------------------
    *-----------------&   Send the email    *-----------------------------
    *---------------------------------------------------------------------
        CALL METHOD lo_bcs->send(
          EXPORTING
            i_with_error_screen = 'X'
          RECEIVING
            result              = lv_sent_to_all ).
    
        IF lv_sent_to_all IS NOT INITIAL.
          COMMIT WORK.
        ENDIF.
    
    *---------------------------------------------------------------------
    *-----------------&   Exception Handling     *------------------------
    *---------------------------------------------------------------------
      CATCH cx_bcs INTO lo_cx_bcx.
        "Appropriate Exception Handling
        WRITE: 'Exception:', lo_cx_bcx->error_type.
    ENDTRY.

    PS. you can use same concept if you are creating a total custom fragment on your own but you need to

    • Create OData Model
    • Create Master Template using Maintain Form Template Fiori APP / SFP

    • Create Content Template using Maintain Form Template Fiori APP / SFP

    Start building Up your Solution, i haven’t tried build a full custom solution from A to Z but this is the main idea for it.

    Rating: 0 / 5 (0 votes)

    The post Calling Fragment Form & Send it as an attachment using ABAP Walk-Through (Dunning Example) appeared first on ERP Q&A.

    ]]>
    Generate Dynamic QR code from oDATA https://www.erpqna.com/generate-dynamic-qr-code-from-odata/?utm_source=rss&utm_medium=rss&utm_campaign=generate-dynamic-qr-code-from-odata Wed, 18 Jan 2023 11:31:14 +0000 https://www.erpqna.com/?p=71775 Introduction QR (Quick Response) codes are two-dimensional barcodes that are widely used for storing and sharing information. They are particularly useful because they can be read quickly and easily by a smartphone or other device equipped with a camera and a QR code reader app. If we can add a QR code to our app/oData […]

    The post Generate Dynamic QR code from oDATA appeared first on ERP Q&A.

    ]]>
    Introduction

    QR (Quick Response) codes are two-dimensional barcodes that are widely used for storing and sharing information. They are particularly useful because they can be read quickly and easily by a smartphone or other device equipped with a camera and a QR code reader app.

    If we can add a QR code to our app/oData call, it can open a wide area of fields for implementation.

    Step-by-Step Procedure:

    1. Create a page format as of QR size: If we do not create a page format then the QR will be shown in the whole A4 size page and a lot of space will be left empty. Use tcode SPAD for creating.
    2. Create a Barcode: We will use SE73 to create a barcode.
    3. Create a smartstyle: To create a character format for QR.
    4. Create a smartform: To display the
    5. Create a class: To convert text to xstring. This xstring will be passed to the oData call.
    6. Table Creation: Create a table to store the data to be displayed in form of a QR code via oData.
    7. Create oData Service: Finally, we will be creating an oData service.
    8. Publish and test the Odata service.

    Let’s start the development now in the steps mentioned above.

    STEP 1: Create a page format: Go to tcode SPAD.

    1.1 Click on the full administrator and go to the tab Device Types and click on display.

    Image 1- SPAD landing screen
    Image 2- SPAD Page Format

    1.2 Click on the pencil button to come to change mode and then click Create to create a new page format.

    Image 3- Create Page Format

    1.3 Save the page format with the below settings. One thing to notice is that depending on the data, the QR code can increase its size a bit as it has to hold more data in it. Play and change these settings as required.

    Image 4- Save Page Format

    1.4 Our first step completes here.

    STEP 2: Create a barcode.

    2.1 Go to tcode SE73 and create a new Barcode.

    Image 5 – SE73

    2.2 Create a new barcode.

    Image 6 – New Barcode

    2.3 Use the below setting to create the barcode.

    Image 7 – New Barcode Setting
    Image 8 – New Barcode Setting
    Image 9 – New Barcode Setting
    Image 10 – New Barcode Setting

    2.4 QR Code is created. Now it’s time to test the QR code. Place your cursor on the new barcode, in our case it is ZQRDISP, and hit F6 (execute barcode). I guess we all are excited to see the QR code in this development for the first time.

    Image 11 – Test Barcode From SE73

    2.5 Execute the report with any text.

    Image 12 – Execute Report

    2.6 Tada! Our QR code is ready.

    Image 13 – QR Code from SE73

    STEP 3 : Create Smartstyle

    Let’s create a smartstyle on the top of created barcode.

    3.1 Create a smartstyle with character format as the created barcode. Also, create a default paragraph to add to the header data.

    Image 14 – Smartsyles

    STEP 4: Create Smartform

    Let’s move on to create a smartform.

    4.1 This smartform will have an input parameter of type string and we will pass this parameter to the text element so that it can be displayed as a QR code. Add recently created smartsytle via the output options tab of the text element.

    Image 15 – Smartform

    4.2 Save and activate it.

    4.3 It’s time to execute the smartform, let’s see what the output looks like. Pass any text to input field. Voila, we got a QR code!🕺🕺🕺

    Image 16 – Smartform Output

    4.4 Do scan the QR code and it will take you to like to read another oData blog.

    STEP 5: Create Class

    5.1 Create a class that will take a string as an input. It will take that string and pass it to smartform, generate the QR from it, and then finally the QR will be converted to pdf xstring. As oData can not process images directly, we are converting it to xstring.

    Image 17 – Class

    5.2 Below is the snippet of the code. We are performing the below activities in the code:

    • Get Device type.
    • Set relevant output options of the smartform.
    • Call smartform.
    • Convert the smartform output to OTF format.
    • Pass back the OTF xstring as a returning parameter.
    METHOD display.
    
        DATA :
          lv_fm_name            TYPE rs38l_fnam,
          ls_output_options     TYPE ssfcompop,
          lv_language           TYPE tdspras,
          ls_control_parameters TYPE ssfctrlop,
          ls_output_data        TYPE ssfcrescl,
          lv_pdf_len            TYPE i,
          lv_pdf_xstring        TYPE xstring,
          lt_lines              TYPE TABLE OF tline,
          lv_devtype            TYPE rspoptype,
          lt_tstotf             TYPE tsfotf.
    
    * language
        lv_language = sy-langu.
        TRANSLATE lv_language TO UPPER CASE.
        ls_control_parameters-langu = lv_language.
    
    * set control parameters to get the output text format (OTF) from Smart Forms
        ls_control_parameters-no_dialog = 'X'.
        ls_control_parameters-getotf    = 'X'.
        ls_control_parameters-preview   = space. "No preview
    
    * get device type from language
        CALL FUNCTION 'SSF_GET_DEVICE_TYPE'
          EXPORTING
            i_language             = lv_language
    *       i_application          = 'SAPDEFAULT'
          IMPORTING
            e_devtype              = lv_devtype
          EXCEPTIONS
            no_language            = 1
            language_not_installed = 2
            no_devtype_found       = 3
            system_error           = 4
            OTHERS                 = 5.
    
    * set device type in output options
        ls_output_options-tdprinter = lv_devtype.
    
    * Set relevant output options
        ls_output_options-tdnewid  = 'X'.   "Print parameters,
        ls_output_options-tddelete = space. "Print parameters
    
        CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'
          EXPORTING
            formname           = 'ZQRDISPLAY'  "Smartform name
          IMPORTING
            fm_name            = lv_fm_name
          EXCEPTIONS
            no_form            = 1
            no_function_module = 2
            OTHERS             = 3.
    
    * Call Smartform generated FM
        CALL FUNCTION lv_fm_name
          EXPORTING
            control_parameters = ls_control_parameters
            output_options     = ls_output_options
            user_settings      = space
            gv_text            = iv_barcode_data
          IMPORTING
            job_output_info    = ls_output_data
          EXCEPTIONS
            formatting_error   = 1
            internal_error     = 2
            send_error         = 3
            user_canceled      = 4
            OTHERS             = 5.
    
        APPEND LINES OF ls_output_data-otfdata[] TO lt_tstotf[].
    
    * Convert to OTF
        CALL FUNCTION 'CONVERT_OTF'
          EXPORTING
            format                = 'PDF'
          IMPORTING
            bin_filesize          = lv_pdf_len
            bin_file              = lv_pdf_xstring       " binary file
          TABLES
            otf                   = lt_tstotf
            lines                 = lt_lines
          EXCEPTIONS
            err_max_linewidth     = 1
            err_format            = 2
            err_conv_not_possible = 3
            err_bad_otf           = 4
            OTHERS                = 5.
    
        IF sy-subrc = 0.
          rv_image_data = lv_pdf_xstring.
        ENDIF.
    
      ENDMETHOD.

    STEP 6: Create a Table

    6.1 This table will store the data information and also will be used while creating the oData service.

    Image 18 – Table

    STEP 7: Create an oData service

    7.1 Finally we reached the step where we will be creating the oData service. If it’s your first oData service then I would recommend visiting A Step by Step process to create Odata services.

    7.2 Once the oData service is created, import the table created.

    Image 19 – oData Service

    7.3 Do remember to check the checkbox “Media”.

    Image 20 – oData Media Check

    7.4 Now we are all set to generate the oData service. Once oData is generated, goto DPC extension class and redefine method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_STREAM.

    METHOD /iwbep/if_mgw_appl_srv_runtime~get_stream.
    
        DATA:
          lt_keys     TYPE /iwbep/t_mgw_tech_pairs,
          ls_key      TYPE /iwbep/s_mgw_tech_pair,
          lv_customer TYPE s_customer,
          lv_xstring  TYPE xstring,
          ls_stream   TYPE ty_s_media_resource.
    
        lt_keys = io_tech_request_context->get_keys( ).
    
    * docno is the specified key
        READ TABLE lt_keys WITH KEY name = 'DOCNO' INTO ls_key.
        IF sy-subrc IS INITIAL.
    
    * We will convert field LINK as QR code
          SELECT SINGLE link FROM zstoreqrdetails INTO @DATA(lv_link)
          WHERE docno = @ls_key-value.
    
    * Method display of Class zcl_qrcode will convert the data to QR to xstring
          lv_xstring = zcl_qrcode=>display( iv_barcode_data = lv_link ).
    
          ls_stream-value = lv_xstring.
    
          ls_stream-mime_type = 'application/pdf'.
    
    * Pass the QR's xstrinh back to oData
          copy_data_to_ref( EXPORTING is_data = ls_stream
                            CHANGING  cr_data = er_stream ).
    
        ENDIF.
    
      ENDMETHOD.

    7.5 In this step we will goto MPC extension class and redefine method DEFINE.

    METHOD define.
    
        DATA:
          lo_entity   TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
          lo_property TYPE REF TO /iwbep/if_mgw_odata_property.
    
        super->define( ).
    
    * QR_Detail is the entity name
        lo_entity = model->get_entity_type( iv_entity_name = 'QR_Detail' ).
    
        IF lo_entity IS BOUND.
    * Field Link will be converted to media
          lo_property = lo_entity->get_property( iv_property_name = 'Link' ).
          lo_property->set_as_content_type( ).
        ENDIF.
    
      ENDMETHOD.

    7.6 That’s it. We are done with the development. In the next step, we will test it.

    STEP 8: Testing

    8.1 Register your oData service.

    8.2 Once registered, let’s see if metadata is loaded.

    Image 21 – Metadata

    8.3 Guys, please open your QR scanners as we are now going to get them. Execute the below URL and we will get the QR code as output.

    /sap/opu/odata/sap/ZSHOW_QRINFO_SRV/QR_DetailSet('0001')/$value
    
    Image 22 – Test1

    8.4 Let’s execute another case.

    /sap/opu/odata/sap/ZSHOW_QRINFO_SRV/QR_DetailSet('0002')/$value
    
    Image 23 – Test2
    Rating: 0 / 5 (0 votes)

    The post Generate Dynamic QR code from oDATA appeared first on ERP Q&A.

    ]]>
    ABAP RESTful Application Programming Model (RAP) – ABAP Cloud https://www.erpqna.com/abap-restful-application-programming-model-rap-abap-cloud/?utm_source=rss&utm_medium=rss&utm_campaign=abap-restful-application-programming-model-rap-abap-cloud Tue, 27 Dec 2022 11:32:16 +0000 https://www.erpqna.com/?p=71336 Intro In this blog I am going to talk about RAP model – RESTful ABAP Programming Model. As described by SAP:- The ABAP RESTful Application Programming Model (in short RAP) defines the architecture for efficient end-to-end development of intrinsically SAP HANA-optimized OData services (such as Fiori apps). It supports the development of all types of […]

    The post ABAP RESTful Application Programming Model (RAP) – ABAP Cloud appeared first on ERP Q&A.

    ]]>
    Intro

    In this blog I am going to talk about RAP model – RESTful ABAP Programming Model. As described by SAP:-

    The ABAP RESTful Application Programming Model (in short RAP) defines the architecture for efficient end-to-end development of intrinsically SAP HANA-optimized OData services (such as Fiori apps). It supports the development of all types of Fiori applications as well as publishing Web APIs. It is based on technologies and frameworks such as Core Data Services (CDS) for defining semantically rich data models and a service model infrastructure for creating OData services with bindings to an OData protocol and ABAP-based application services for custom logic and SAPUI5-based user interfaces.

    We will try to understand it technically as we surf…

    Pre-requisite

    • Knowledge of CDS
    • Knowledge Fiori elements
    • knowledge of ABAP Cloud syntax
    • Access to BTP account (trial would do)
    • Eclipse Installation

    Evolution

    I am sure many of you would have seen the below picture. Let’s scan it one more time.

    Classic ABAP programming is something we have used since time immemorial and still lives to date but with technology changing every few years there was no way we could stick to the past.

    With advent of S/4HANA, Fiori adoption got a big kick start as the future UI technology (though it was available before S/4HANA 1511 was launched) and so did the adoption of ABAP programming model for SAP Fiori. Hence many of you would be aware of this architecture (middle box in the picture above) and would have used in your projects where you would have created a CDS view (basic, interface, Consumption – with Fiori elements) and used that CDS view in a SEGW gateway project and consumed that project in a Fiori App etc.

    Well, this works fine but with further technological shift happening towards Cloud, it’s also of importance to have a model which can be cloud ready. This is where the RAP model is findings its place (along with CAP but CAP model consumption is more NodeJS/Java driven whereas RAP is within the realms of ABAP). The core of data modelling for RAP remains CDS and the consumption is defined by a Behavior Definition + Behavior Implementation + Service Definition + Service Binding as shown in the Bigg Picture below.

    Service Architecture

    Where does the ABAP service resides – SaaS on BTP

    Which database it uses – SAP HANA embedded

    How the coding is done – Via Eclipse ADT

    What type of language – ABAP on Cloud

    I am not going to spend more time on these pictures as I feel as an ABAPer it’s better to understand concepts via the code and not slide decks. So, let’s get started with our Employee Data model for which we want to carry out CRUD operations on an Employee.

    BTP Account

    As a first Step get access to BTP trial account or any BTP account where you could access “Prepare an account for ABAP Trial”

    Click on Boosters ->Prepare an Account for ABAP Trial ->Start

    Once that is done you will get a service Key which you should download and keep.

    Eclipse

    You can download the latest Eclipse from https://www.eclipse.org/downloads/packages/

    And once done create an ABAP Cloud Project

    You can enter your service Key here

    Create Package

    Create a Package for your developments

    Create Table

    Once the package is created, create a new table for Employee

    Let’s call it ZTEST_EMPL

    The structure looks like

    @EndUserText.label : 'Employee Table'
    @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #RESTRICTED
    define table ztest_empl {
    
      key mandt       : abap.clnt not null;
      key id_int      : sysuuid_x16 not null;
      id_ext          : abap.char(10) not null;
      firstname       : abap.char(20);
      lastname        : abap.char(20);
      @Semantics.amount.currencyCode : 'ztest_empl.currency'
      salary          : abap.curr(10,2);
      currency        : abap.cuky;
      exp             : abap.int1;
      role            : abap.char(50);
      active          : abap_boolean;
      created_by      : syuname;
      created_at      : timestampl;
      last_changed_by : syuname;
      last_changed_at : timestampl;
    
    }

    The data element of id_int is of significance because it allows auto generation of id_int.

    Create Interface CDS

    As the core of RAP technologies is a CDS view, lets create one on top of our table

    @EndUserText.label: 'Interface view for Employee'
    define root view entity ZI_test_empl
      as select from ztest_empl
    {
    
      key id_int,
          id_ext,
          firstname,
          lastname,
          salary,
          currency,
          exp,
          role,
          active,
          @Semantics.user.createdBy: true
          created_by,
          @Semantics.systemDateTime.createdAt: true
          created_at,
          @Semantics.user.lastChangedBy: true
          last_changed_by,
          @Semantics.systemDateTime.lastChangedAt: true
          last_changed_at
    }

    Note that it’s a view entity hence does not have an SQL view annotation.

    Create Consumption CDS

    I will now create a root consumption view.

    @EndUserText.label: 'Consumption View'
    @Search.searchable: true
    @Metadata.allowExtensions: true
    define root view entity ZC_TEST_EMPL 
    as select from ZI_test_empl as Empl 
    {
          key id_int,
          id_ext,
          firstname,
          lastname,
          salary,
          currency,
          exp,
          role,
          active,
          @Semantics.user.createdBy: true
          created_by,
          @Semantics.systemDateTime.createdAt: true
          created_at,
          @Semantics.user.lastChangedBy: true
          last_changed_by,
          @Semantics.systemDateTime.lastChangedAt: true
          last_changed_at 
    }

    Disclaimer:

    Please note that I have not created a Projection view although the picture under Evolution section suggests creating a project view. I am trying to see if I could work without it technically. You could create a Projection view and expose whatever is required to be exposed in the corresponding behavior definition. My behavior Implementation is directly on Consumption view and not on interface view. Although Projection view would provide advantages in terms of whatever you want to project, I am trying to see if there is a technical limitation if I don’t follow that path.

    Create Metadata extension

    Create a metadata extension on the Consumption view so that Fiori annotations could be segregated.

    @Metadata.layer: #CORE
    @UI: {
      headerInfo: { typeName: 'Employee',
                    typeNamePlural: 'Employees',
                    title: { type: #STANDARD, label: 'Employee', value: 'id_ext' }  },
                    presentationVariant: [{ sortOrder: [{ by: 'firstname', direction:  #ASC }] }] }
    annotate view ZC_TEST_EMPL with
    {
      @UI.facet: [ { id:              'Employee',
                     purpose:         #STANDARD,
                     type:            #IDENTIFICATION_REFERENCE,
                     label:           'Employee',
                     position:        10 } ]
      @UI.hidden: true
      id_int;
      @UI: {
      lineItem:       [ { position: 10, importance: #HIGH } ],
      identification: [ { position: 10, label: 'ID [1,...,99999999]' } ] }
      @EndUserText.label: 'Employee Id'
      id_ext;
      @UI: {
          lineItem:       [ { position: 20, importance: #HIGH } ],
          identification: [ { position: 20, label: 'First Name' } ],
          selectionField: [{ position : 10 }]}
      @EndUserText.label: 'First name'
      @Search.defaultSearchElement: true
      firstname;
      @UI: {
          lineItem:       [ { position: 30, importance: #HIGH } ],
          identification: [ { position: 30, label: 'Last Name' } ] }
      @EndUserText.label: 'Last Name'
      lastname;
      @UI: {
                lineItem:       [ { position: 40, importance: #HIGH } ],
                identification: [ { position: 40, label: 'Salary' } ] }
      @EndUserText.label: 'Salary'
      salary;
      @UI: {
                lineItem:       [ { position: 50, importance: #HIGH } ],
                identification: [ { position: 50, label: 'Currency' } ] }
      currency;
    
      @UI: {
                lineItem:       [ { position: 60, importance: #HIGH } ],
                identification: [ { position: 60, label: 'Experience' } ] }
      @EndUserText.label: 'Experience'
      exp;
    
      @UI: {
                lineItem:       [ { position: 70, importance: #HIGH } ],
                identification: [ { position: 70, label: 'Role' } ] }
      @EndUserText.label: 'Role'
      role;
    
      @UI: {
                lineItem:       [ { position: 80 }, { type: #FOR_ACTION, dataAction: 'setActive', label: 'Set Active' } ],
                identification: [ { position: 80 }, { type: #FOR_ACTION, dataAction: 'setActive', label: 'Set Active' } ] }
      @EndUserText.label: 'Active ?'
      active;
    
      @UI.hidden: true
      created_by;
      @UI.hidden: true
      created_at;
      @UI.hidden: true
      last_changed_by;
      @UI.hidden: true
      last_changed_at;
    
    }
    

    Note the method associated with dataAction = ‘setActive’ would be used in Behavior Definition & Implementation.

    Behavior Definition

    This is listing of all the behaviors – Basically meaning how my entity would behave.

    • What is allowed – CUD ?
    • What is read only?
    • What function imports ( actions ), calculation and validations I can do ?
    managed implementation in class zbp_c_test_empl unique;
    
    define behavior for ZC_TEST_EMPL alias Empl
    persistent table ZTEST_EMPL
    lock master
    //authorization master ( instance )
    //etag master <field_name>
    {
    
    // CUD operations come by default
      create;
      update;
      delete;
    
    // manage numburing, read only and mandatory behaviours of the entity
     field ( numbering : managed, readonly ) id_int;
     field ( readonly ) active, salary, currency;
     field ( readonly ) created_by, created_at, last_changed_by, last_changed_at;
     field ( mandatory ) firstname, lastname;
    
    // Function import to set the Employee to active/inactive
    action ( features : instance ) setActive result [1] $self;
    //determination of Salary based on Experience and Role
    determination calcSalary on save { field exp, role; }
    //validate various fields user is going to enter on the UI
    validation validateFirstName on save { field firstname; }
    
    
    }

    Let’s look at this one by one

    • Managed: This means frameworks is going to take care of the CUD operations. You could have unmanaged as well if you are looking to port some code from ABAP and want to change the default CUD operations.
    • Implementation in class: Please note that Behavior definition is just the definition (like you have interfaces in ABAP which need to be implemented using a class implementation ). The implementation needs to be done in the class zbp_c_test_empl.
    • Unique: The mandatory addition unique defines that each operation can be implemented exactly once.
    • define behavior for: Defines the behavior for a RAP business object
    • persistent table: DDIC database table a RAP BO is based on. The data on the persistent table is processed by RAP BO operations.
    • Lock master: Specifies the RAP locking mechanism for a RAP BO entity. The RAP locking mechanism prevents simultaneous modification access to data on the database by more than one user. Whenever a lock is requested for a specific entity instance, its lock master and all lock-dependent entity instances are locked for editing by a different user.
    • Create, Update, Delete: Create, update, and delete are standard operations. They are also known as CRUD operations, which is an acronym for create, read, update, delete. The read operation is always implicitly enabled for each entity listed in a CDS behavior definition (BDEF) and it mustn’t be declared explicitly.
    • field (numbering : managed, readonly ): The numbering should be managed by RAP framework and not by the user and it is Read only which means it can’t be changed manually.
    • field (mandatory): Defines that it is mandatory to enter values into the specified fields before persisting them on the database. These fields are marked as mandatory ( Adds a *) on the user interface. However, there is no runtime check for mandatory fields and no runtime error occurs if a mandatory field is not filled.
    • field (readonly) active, salary, currency: Salary, currency & active fields can’t be changed by users. Salary + Currency would be determined based on the years of experience & role and active field will be set by action called setActive.
    • action (features : instance ) setActive result [1] $self: RAP actions are non-standard RAP BO operations that modify the state of an entity instance. This is like a function import in traditional SEGW gateway project. The custom logic must be implemented in the RAP handler method FOR MODIFY. Set in metadata extension file.
    • determination calcSalary on save { field exp, role; } : Based on exp and role, calculate the salary when save is hit.
    • validation validateFirstName on save { field firstname; } : Validate the field firstname entered on the UI when save is hit.

    Behavior Implementations

    The Definition needs to be implemented in a class using custom logic using EML (entity manipulation language)

    METHOD setActive.: This method sets the active flag to X when the UI button is hit. The UI button is set in the metadata extension and linked to this method.

    MODIFY ENTITIES OF zc_test_empl IN LOCAL MODE
        ENTITY Empl
        UPDATE FIELDS ( active )
        WITH VALUE #(
        FOR wa IN keys (    %tky = wa-%tky
            active = abap_true ) )
           FAILED failed
           REPORTED reported.
    
        " Fill the response table
        READ ENTITIES OF zc_test_empl IN LOCAL MODE
        ENTITY Empl
        ALL FIELDS WITH CORRESPONDING #( keys )
        RESULT DATA(lt_empl).
    
        result = VALUE #( FOR wa_empl IN lt_empl
                            ( %tky   = wa_empl-%tky
                              %param = wa_empl ) ).
     ENDMETHOD.

    What it does?

    1. Modify the CDS entity and set the field active = abap_true.
    2. Read the entity back
    3. Set the result : %param is a component group in BDEF derived types. It is used for the result parameter in the context of action and function %param must be filled by the action.

    METHOD calcSalary.

    METHOD calcSalary.
        READ ENTITIES OF zc_test_empl IN LOCAL MODE
         ENTITY Empl
         FIELDS ( exp role ) WITH CORRESPONDING #( keys )
         RESULT DATA(lt_empl).
        LOOP AT lt_empl ASSIGNING FIELD-SYMBOL(<fs_empl>).
          IF <fs_empl>-role = 'Director' AND
             <fs_empl>-exp >= '20'.
            <fs_empl>-salary = '50000000'.
            <fs_empl>-currency = 'INR'.
          ELSEIF <fs_empl>-role = 'Director' AND
             <fs_empl>-exp < '20'.
            <fs_empl>-salary = '45000000'.
            <fs_empl>-currency = 'INR'.
          ELSE.
            <fs_empl>-salary = '2000000'.
            <fs_empl>-currency = 'INR'.
          ENDIF.
    
          MODIFY ENTITIES OF zc_test_empl IN LOCAL MODE
          ENTITY Empl
          UPDATE FIELDS ( salary currency  )
          WITH VALUE #(
          (  %tky = <fs_empl>-%tky
          salary = <fs_empl>-salary
          currency =  <fs_empl>-currency ) ).
        ENDLOOP.
      ENDMETHOD.

    What it does?

    1. Read the entity for the entered key and gets the data into internal table lt_empl.
    2. Loops over lt_empl and based on a logic sets the salary back to the entity using MODIFY.

    METHOD validateFirstName.

    METHOD validateFirstName.
    
        READ ENTITIES OF zc_test_empl IN LOCAL MODE
        ENTITY Empl
        FIELDS ( firstname ) WITH CORRESPONDING #( keys )
        RESULT DATA(lt_empl).
    
        READ TABLE lt_empl ASSIGNING FIELD-SYMBOL(<fs_empl>) INDEX 1.
        CHECK sy-subrc EQ 0.
        IF <fs_empl>-firstname CA '0123456789'.
          APPEND VALUE #( %tky = <fs_empl>-%tky ) TO failed-empl.
          APPEND VALUE #(  %tky = <fs_empl>-%tky
                            %msg      = new_message( id       = 'ZEMPL_MSG'
                                                      number   = '000'
                                                      v1       = <fs_empl>-firstname
                                                      severity = if_abap_behv_message=>severity-error ) )
                                                      TO reported-Empl.
        ENDIF.
      ENDMETHOD.

    What it does?

    1. Read the entity into an internal table lt_empl.
    2. Read the table and check if the name contains a number. If it does, an error message is raised.

    Service Definition

    Right click on Consumption view and create Service Definition

    Expose the Consumption view as a service

    Service Binding

    Right click on Service Definition and create Service Binding

    UI

    When you click on preview in the previous step, you will see the below screen.

    Now click on Create Button. Here you can enter ID, First Name, Last Name, Experience and Role.

    Salary will be calculated based on Experience and Role. Active flag can be set once everything is ok.

    Enter the values and hit Create

    The following screen appears.

    Now click back and you may see the record is populated

    You may select the record and use Set Active button to activate it. Once done the screen looks like below

    Rating: 5 / 5 (1 votes)

    The post ABAP RESTful Application Programming Model (RAP) – ABAP Cloud appeared first on ERP Q&A.

    ]]>
    Generate Custom Supplier Certificates via Generic Withholding Tax in SAP Document and Reporting Compliance(formerly known as Advanced Compliance Reporting – ACR) https://www.erpqna.com/generate-custom-supplier-certificates-via-generic-withholding-tax-in-sap-document-and-reporting-complianceformerly-known-as-advanced-compliance-reporting-acr/?utm_source=rss&utm_medium=rss&utm_campaign=generate-custom-supplier-certificates-via-generic-withholding-tax-in-sap-document-and-reporting-complianceformerly-known-as-advanced-compliance-reporting-acr Fri, 19 Aug 2022 10:43:39 +0000 https://www.erpqna.com/?p=67046 Generic withholding tax reporting helps you configure based on your country/region-specific or company-specific requirements. The report identifies withholding tax-relevant transactions for the reporting period and generates an ALV output that lists these transactions and groups them based on organizational unit, business partner, tax type, and tax code. This report can be used to report only […]

    The post Generate Custom Supplier Certificates via Generic Withholding Tax in SAP Document and Reporting Compliance(formerly known as Advanced Compliance Reporting – ACR) appeared first on ERP Q&A.

    ]]>
    Generic withholding tax reporting helps you configure based on your country/region-specific or company-specific requirements.

    The report identifies withholding tax-relevant transactions for the reporting period and generates an ALV output that lists these transactions and groups them based on organizational unit, business partner, tax type, and tax code.

    This report can be used to report only extended withholding tax documents or line items. However, withholding tax certificate cannot be generated with the standard solution.

    In this blog post, I will cover a simple way to extend the generic withholding tax report (GENC_WHT) and add new correspondence section to generate withholding tax certificate.

    1. Go to Tcode SEGW and create SAP Gateway Service. For this example, we will refer Mexico Gateway Service. Gateway service FDP_FIWTMX_CERT (which is available from SAP S/4HANA 1809 OP Onwards).

    Figure 1: Mexico Gateway Service

    2. Expand the SAP Gateway Service and go through the Data Model, Associations, Entity Sets and Association Sets. In Entity Type – Query Node will be used for passing filter from DRC(Formerly known as ACR ). In case of Mexico we will generate the certificate for every Unique Supplier, company code, language and Withholding tax Income Type

    Figure 2: Query Node – to be exposed in Document and Reporting Compliance

    3. Navigate to Runtime Artifacts and Use class CL_FDP_FIWTMX_CERT_DPC_EXT to populate data in the certificate

    Figure 3: SAP Gateway Artifacts

    4. Implement the method MXWHTHEADERSET_GET_ENTITY to populate data in the certificate

    Figure 4: Sample Data provider Implementation

    5. Note the Service name and maintain it in the Adobe Form.

    Figure 5: SAP Gateway Service – to be consumed in Adobe Forms

    6. Go to Tcode SFP, enter the form name as FIWTMX_CERT to refer already developed Mexico Certificate.

    Figure 6: Form Builder

    7. The Form should be of type XFA2, maintain the SAP Gateway Service Name FDP_FIWTMX_CERT_SRV.

    Figure 7: Mexico Adobe Form for reference

    8. Go to Utilities and download the form layout. The layout should be downloaded with XDP extensions.

    Figure 8: Download Form layout

    9. Extend the generic withholding tax reporting (GENC_WHT_V0)

    Figure 9: Extend Generic Withholding Tax Reporting

    10. Create a New document in the correspondence section in your report definition and provide the following details.

    i. Correspondence ID – ZCORSP
    ii. Form Template – FIWTMX_CERT
    iii. Query ID – GET_SUMMARY_BP_ITEMS
    iv. Correspondence Name – Mexico Supplier Certificate
    v. Output Type – ACCT_WHT(Specifically created for Withholding tax certificate)

    Figure 10: Correspondence Section

    11. Click on the correspondence line and map the query columns. This unique combination will decide on number of output forms in the correspondence section.

    Figure 11: Map Query columns to be passed to SAP Gateway Service

    12. Download XSD as shown below(The report should be open in Edit Mode)

    Figure 12: Download Schema

    13. After downloading the XSD, open Adobe LiveCycle Designer. Open the earlier downloaded layout for Form and perform mapping

    Figure 13: Adobe Form Layout in Adobe LiveCycle Designer

    14. Right click on the data connection and add New data connection, choose XML schema and upload the XSD(which was downloaded from Extended Report Definition).

    Figure 14: Import Schema
    Figure 15: Map fields from the Imported Schema

    15. Perform the mapping and upload the layout in Tcode SFP and activate the Form.

    Figure 16: Upload the Form and Activate

    16. Go to SPRO and navigate to path below, click on New Entry and maintain the custom form details: Cross-Application Components > Output Control > Assign Form Templates

    Figure 17: Maintain Custom Form Details

    17. Extend generic reporting category and maintain the extended report definition in the activity section.

    Figure 18: Extended Generic Withholding Tax Report Category

    18. Go to Run Compliance section and enter the report name

    Figure 19: Run Compliance Report

    19. Choose the extended reporting activity and execute the report

    Figure 20: Execute the Extended Activity

    20. You can view the correspondence form in the business partner Reporting Section

    Figure 21: Partner Correspondence

    21. Once you click on the display Icon, it will trigger the class CL_FDP_FIWTMX_CERT_DPC_EXT and the form will be populated

    Figure 22: Generated Partner Certificate

    Summary:

    Figure 23: Summary
    Rating: 0 / 5 (0 votes)

    The post Generate Custom Supplier Certificates via Generic Withholding Tax in SAP Document and Reporting Compliance(formerly known as Advanced Compliance Reporting – ACR) appeared first on ERP Q&A.

    ]]>
    SAP Password reset tool using Azure Logic App, SAP PO/Integration Suite and ABAP https://www.erpqna.com/sap-password-reset-tool-using-azure-logic-app-sap-po-integration-suite-and-abap/?utm_source=rss&utm_medium=rss&utm_campaign=sap-password-reset-tool-using-azure-logic-app-sap-po-integration-suite-and-abap Wed, 02 Mar 2022 09:05:56 +0000 https://www.erpqna.com/?p=60319 Introduction Recently while working on Azure Logic App, I felt we can make use of Office 365 email connector to automate a few manual processes. I thought, why not create a password reset tool? So, I designed a Logic App that picks up email from a specific folder(outlook) and passes on the information to SAP […]

    The post SAP Password reset tool using Azure Logic App, SAP PO/Integration Suite and ABAP appeared first on ERP Q&A.

    ]]>
    Introduction

    Recently while working on Azure Logic App, I felt we can make use of Office 365 email connector to automate a few manual processes.

    I thought, why not create a password reset tool?

    So, I designed a Logic App that picks up email from a specific folder(outlook) and passes on the information to SAP ECC6 via SAP PO, finally using the ABAP program user password is reset/unlocked and messages are returned back to the sender by the Logic App.

    Implementation

    Develop a logic app in Azure that is connected with an outlook account(in the real world email must be a service account) and look for new emails.

    Outlook connector configuration
    • Emails are looked at under the Password Reset folder every 10 seconds, so any new email that comes in will be immediately picked up.

    Make sure the email body is in a specific format (end-users need to be trained about the email body format), otherwise parsing at the logic app will fail and the correct format will be returned back the sender.

    Default email format(JSON)

    {
    
    “User”: {
    
    “Client”: “230”,
    
    “UNAME”: “MOHAMM4A”,
    
    “email”: ”
    
    “action” : “unlock”
    
    }
    
    }
    Email body parsing

    whenever an email trigger invokes, it will have a unique Message-Id. using message-id reply is sent back to the same email sender.

    As soon as the parsing completes, a proper JSON structure request will be created and hit the SAP PO REST adapter endpoint using HTTP connector in Logic App.

    HTTP connector

    SAP PO or Integration Suite configuration:

    In our DEV SAP ECC6, we have 2 clients(100 and 230), so the end-user needs to specify the proper client in the email body. once the payload is received in SAP PO it will be converted to XML by the REST adapter.

    1. Develop ID objects in SAP PO with one REST sender adapter and 2 SOAP adapters(XI protocol) for two clients.

    SAP PO ID objects

    2. In the ICO, I have added conditions in the receiver determination so that based on the client its business system can be invoked for a password reset.

    ICO – Receiver determination

    3. Create ESR objects, thus the structure appears in SPROXY for ABAP coding.

    ESR objects

    4. Mapping in ESR is straightforward (one-to-one mapping).

    Message Mapping

    We are done with the SAP PO configuration.

    If anyone is using SAP Integration suite, we can completely ignore Azure Logic App. It is a known thing that not everyone will be using Azure in their organization, hence we will be performing all the operations in SAP.

    SAP Integration Suite IFlow

    In my case I have used XI adapter. cloud connector was already in place, so I directly hit my ECC system.

    I reused the same SAP PO generated Service Interface in ECC.

    It is also possible to connect via SOAMANAGER.

    IFlow can be found at my Git Repository.

    Password reset IFlow – GitHub

    Sender MAIL adapter is connected to my email account via IMAP and polls from the folder CPI at an interval of 10 seconds.

    sender mail adapter

    Once we have an email in the folder. It will pick, process it and change the email to read from unread.

    For the purpose of sending the sender’s email ID along with the request payload for unlocking/reset, I’ve extracted the adapter header parameter(FROM) using a groovy script and passed it to ECC by forming a payload in the content modifier.

    Finally, the response from ECC must be returned to the sender via receiver email adapter.

    Receiver email configuration

    SAP ABAP development:-

    1. Generate proxy for the service provider in t-code SPROXY.

    Service Proxy Proxy

    2. Once we generate a proxy, An ABAP class will be generated.

    Auto-generated ABAP class

    3. We can either code in the same method or create a separate class where all the operations can be performed. I have created a separate class*(SE24) and did all the validations and password reset/unlock operation.

    4. The most important thing is, how are we getting user details from SAP? to do this we can make use of standard BAPI: BAPI_USER_GET_DETAIL where we pass user-id(UNAME from email) as an exporting parameter and return user address details and SCN(secure network communication data )

    BAPI_USER_GET_DETAIL

    ls_address-e_mail will be holding the email address of the user in SAP.

    the returned email(BAPI returned) will be validated against the sender’s email address (coming from Logic App or SAP Integration Suite).

    if the email-id in SAP doesn’t match with the email sender’s email-id, then a reply will be sent back to the sender.

    Otherwise, based on the action(UNLOCK/RESET) program will either reset or unlock the user’s password

    5. The user details can be seen in transaction code SU01.

    6. I have added a few validations within the ABAP code before resetting/unlocking the user.

    There would be situations when the BASIS team will lock all the users during system maintenance, during such situations users shouldn’t be allowed to unlock. (if the status of usr02-uflag is 32 & 66 then it is locked by the administrator)

    person who leaves the organization should not be allowed to reset/unlock. (usr02-class will hold user class)

    email-sender needs to specify the correct user-id in the email body.

    7. After validating and getting details from SAP, we can go ahead and reset/unlock users based on their choice.

    8. Finally, using standard BAPI: BAPI_USER_UNLOCK user can be unlocked.

    BAPI_USER_UNLOCK – To unlock user

    9. If the choice is to reset, we can use BAPI: BAPI_USER_CHANGE.

    I have used FM-GENERAL_GET_RANDOM_STRING to generate a random 8 character password and concatenated “@1” to it strong.

    By passing a new string and username into FM-BAPI_USER_CHANGE, the password will be reset, and the response will be returned back to SAP PO –> Logic App –> email.

    1. We are done with the ABAP development.

    Note: Validations are based on my own understanding post discussing with the security team, you can add as many validations you want. Also, If you have any suggestions regarding validations please write in the comment section.

    Now, It’s time to test our tool

    Test:1 – If is user tries to reset his password

    A new password is generated and email is received as an email reply

    Test 2: If the user tries to unlock.

    User was not locked in SAP, so a proper response was returned back to the sender

    Test 3: Few more validations.

    Invalid action and invalid email validations

    Test 4: In case the email body is not correct, the default email template will be returned as an email response.

    Invalid email body

    Monitoring:-

    Azure Logic App

    Logic App
    Invalid JSON
    HTTP connector

    SAP PO:

    SAP PO Message Monitor

    SAP ECC6 Logs:

    For the audit log purpose, I have added logs in ABAP SLG1 for each password reset request.

    Logs in ECC can be checked in the SLG1 transaction code.

    SLG1 log

    SAP Integration Suite:

    SAP IS Message Log
    Email picked and responded by SAP Integration Suite
    Rating: 0 / 5 (0 votes)

    The post SAP Password reset tool using Azure Logic App, SAP PO/Integration Suite and ABAP appeared first on ERP Q&A.

    ]]>
    Invoking Static BOPF Action From Custom Pop up in Fiori Elements App https://www.erpqna.com/invoking-static-bopf-action-from-custom-pop-up-in-fiori-elements-app/?utm_source=rss&utm_medium=rss&utm_campaign=invoking-static-bopf-action-from-custom-pop-up-in-fiori-elements-app Wed, 09 Feb 2022 11:31:08 +0000 https://www.erpqna.com/?p=59764 Introduction: Many times while working with Transactional List Report Apps developed using SAP Fiori Elements we might have requirements to get dynamic input from the end user, like providing a popup where user makes selection and then we need to take actions accordingly. In order to develop transactional App SAP provided us 2 programming model […]

    The post Invoking Static BOPF Action From Custom Pop up in Fiori Elements App appeared first on ERP Q&A.

    ]]>
    Introduction:

    Many times while working with Transactional List Report Apps developed using SAP Fiori Elements we might have requirements to get dynamic input from the end user, like providing a popup where user makes selection and then we need to take actions accordingly.

    In order to develop transactional App SAP provided us 2 programming model which are as follows.

    1. ABAP Programming model for Fiori ( Developed using Business Object Processing Framework-BOPF)
    2. RestFul Application Programming Model(RAP)

    In this blog we will see how to invoke API Actions using ABAP Programming model for Fiori

    Scenario:

    Let us consider an e.g., of Sales Order processing, where the end user after entering Item details needs an option to select additional Charges.

    The additional charges could be combination of one or more of the below charges.

    1. Freight Charges
    2. Miscellaneous charges
    3. Other Charges

    So below screen shot shows that the user can enter Additional Charges from the popup by pressing the Additional charges Button and in the backend, we need to add these charges to the Total of gross amount for the item selected and the value should be reflected in the front-end Fiori App.

    In order to achieve this, we will follow below steps as mentioned in Flowchart.

    So let us see each step.

    Implementation Steps:

    Step 1: Creating Sales Order Header and Item Database table

    We create Custom Sales header Table ZSOH, where we use SoKey which is 16-digit GUID and primary Key for table. The same will be used to generate Draft key in Draft Table.

    @EndUserText.label : 'Sales Order Header'
    @AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #ALLOWED
    define table zsoh {
      @AbapCatalog.foreignKey.screenCheck : false
      key client : mandt not null
        with foreign key [0..*,1] t000
          where mandt = zsoh.client;
      key sokey  : snwd_node_key not null;
      soid       : snwd_so_id;
      bpid       : snwd_partner_id;
      include /bobf/s_lib_admin_data;
    
    }

    Next we create Item table ZSOI where we use SOITMKEY as 16 digit GUID and we include fields to get product id, Gross Amount, Currency and Additional Charges as shown below.

    @EndUserText.label : 'SO Item Table'
    @AbapCatalog.enhancementCategory : #EXTENSIBLE_ANY
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #ALLOWED
    define table zsoi {
      @AbapCatalog.foreignKey.screenCheck : false
      key client   : mandt not null
        with foreign key [0..*,1] t000
          where mandt = zsoi.client;
      key soitmkey : snwd_node_key not null;
      @AbapCatalog.foreignKey.screenCheck : false
      sokey        : snwd_node_key
        with foreign key [0..*,1] zsoh
          where sokey = zsoi.sokey;
      soitm        : snwd_so_item_pos;
      product      : snwd_product_id;
      @Semantics.amount.currencyCode : 'zsoi.currencycode'
      grossamount  : snwd_ttl_gross_amount;
      currencycode : snwd_curr_code;
      quantity     : int4;
      @Semantics.amount.currencyCode : 'zsoi.currencycode'
      addcharge    : snwd_ttl_gross_amount;
      include /bobf/s_lib_admin_data;
    
    }

    Step 2: Create Interface View for Header and Item Tables, also generate the BOPF object and Draft table using @ObjectModel Annotation.

    We Create ZI_SOH interface view to generate BOPF object and we also mark the Create, Update and Delete Enabled as true. We define ZSOH_DRAFT as Draft Table. Below is the code for the same

    @AbapCatalog.sqlViewName: 'ZISOH'
    @AbapCatalog.compiler.compareFilter: true
    @AbapCatalog.preserveKey: true
    @AccessControl.authorizationCheck: #CHECK
    @EndUserText.label: 'Interface view for BO with Draft'
    @ObjectModel: {
        createEnabled: true,
        updateEnabled: true,
        deleteEnabled: true,
        draftEnabled: true,    
        modelCategory:#BUSINESS_OBJECT ,    
        semanticKey: ['Soid'],   
        compositionRoot: true,
        transactionalProcessingEnabled: true,        
        writeDraftPersistence: 'ZSOH_DRAFT',  
        writeActivePersistence: 'ZSOH',
        entityChangeStateId: 'Lchg_Date_Time'    
    }
    define view ZI_SOH as select from ZSOH as soh
    association [1..*] to ZI_SOI as _itm on $projection.Sokey = _itm.Sokey
    association [0..1] to SEPM_I_BusinessPartner as _bpa on $projection.Bpid = _bpa.BusinessPartner
    {
    @ObjectModel.readOnly: true    
        key soh.sokey as Sokey,
    @Search.defaultSearchElement: true    
        soh.soid as Soid,
    @ObjectModel.foreignKey.association: '_bpa'    
        soh.bpid as Bpid,
    @Semantics.systemDateTime.createdAt: true    
        soh.crea_date_time as Crea_Date_Time,
    @Semantics.user.createdBy: true    
        soh.crea_uname as Crea_Uname,
    @Semantics.systemDateTime.lastChangedAt: true    
        soh.lchg_date_time as Lchg_Date_Time,
    @Semantics.user.lastChangedBy: true
        soh.lchg_uname as Lchg_Uname,
    @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]    
        _itm    
    }

    Similarly we create Item Interface view as shown below and use ZSOI_DRAFT as draft table

    @AbapCatalog.sqlViewName: 'ZISOI'
    @AbapCatalog.compiler.compareFilter: true
    @AbapCatalog.preserveKey: true
    @AccessControl.authorizationCheck: #CHECK
    @EndUserText.label: 'Sales Item BO Draft'
    @ObjectModel: {
        createEnabled: true,
        updateEnabled: true,
        deleteEnabled: true,          
        semanticKey: ['Sokey','Soitm'],
        writeDraftPersistence: 'ZSOI_DRAFT',
        writeActivePersistence: 'ZSOI'    
    }
    define view ZI_SOI as select from zsoi as soi
    association [1..1] to ZI_SOH           as _soh  on $projection.Sokey = _soh.Sokey 
    association [0..1] to SEPM_I_Product_E as _pd   on $projection.Product = _pd.Product 
    association [0..1] to SEPM_I_Currency  as _curr on $projection.Currencycode = _curr.Currency         
    {     
        key soi.soitmkey as Soitmkey,    
        soi.sokey as Sokey,
    @ObjectModel.readOnly: true
        soi.soitm as Soitm,
    @ObjectModel.foreignKey.association: '_pd'   
        soi.product as Product,
    @ObjectModel.readOnly: true
    @Semantics.amount.currencyCode: 'Currencycode'
        soi.grossamount as Grossamount,    
    @ObjectModel.foreignKey.association: '_curr'  
    @Semantics.currencyCode: true
        soi.currencycode as Currencycode,
        soi.quantity as Quantity,  
    @Semantics.amount.currencyCode: 'Currencycode'
    @ObjectModel.readOnly: true
        soi.addcharge as AddCharge,  
    @ObjectModel.association.type: [#TO_COMPOSITION_PARENT,#TO_COMPOSITION_ROOT]    
        _soh,    
        _pd,
        _curr
    
    }

    We get the BOPF generated below is the screen shot for the same with Parent Child Relationship.

    Step 3: Create Consumption View, in which we use UI annotations to create Fiori Elements App.

    We Create 2 Consumption CDS view ZC_SOH and ZC_SOI and use UI annotations so that it will create all the required controls in fiori elements app.

    @AbapCatalog.sqlViewName: 'ZCSOH'
    @AbapCatalog.compiler.compareFilter: true
    @AbapCatalog.preserveKey: true
    @AccessControl.authorizationCheck: #CHECK
    @EndUserText.label: 'Consumption SO Hdr BO Draft'
    @ObjectModel: {
        createEnabled: true,
        updateEnabled: true,
        deleteEnabled: true,
        draftEnabled: true,
        
        semanticKey: ['Soid'],   
        compositionRoot: true,  
        transactionalProcessingDelegated: true   
    }
    @UI.headerInfo: {
        typeName: 'Sales Order',
        typeNamePlural: 'Sales Orders',    
        title: {
            type: #STANDARD,        
            value: 'Bpid'       
        },
        description: {
            type: #STANDARD,        
            value: 'Soid'        
        }
    }
    @VDM.viewType: #CONSUMPTION
    @OData.publish: true
    define view ZC_SOH as select from ZI_SOH 
    association [0..*] to ZC_SOI as _itm on $projection.Sokey = _itm.Sokey
    association [0..1] to SEPM_I_BusinessPartner as _bpa on $projection.Bpid = _bpa.BusinessPartner
    {
    @UI.facet: [
    {   
        id: 'Hdr',
        purpose: #HEADER,    
        position: 10,   
        type: #FIELDGROUP_REFERENCE,
        targetQualifier: 'HdrInfo'
    },
    {   
        id: 'SoHdr',
        purpose: #STANDARD,    
        position: 10,   
        label: 'Header Details',
        type: #IDENTIFICATION_REFERENCE    
    },
    {   
        id: 'SoItm',
        purpose: #STANDARD,    
        position: 20,   
        label: 'Item Details',
        type: #LINEITEM_REFERENCE,
        targetElement: '_itm'
    }]
    @UI.hidden: true
        key Sokey,
    @UI: {
        lineItem: [ { position: 10, label: 'Sales Order ID', importance: #HIGH } ],
        selectionField: [ { position: 10 } ],
        identification:[ { position: 10 } ]
        }    
    @UI.fieldGroup: [{ qualifier: 'HdrInfo' , position: 10 }]  
    @ObjectModel.readOnly: true  
        Soid,
    @UI: {
        lineItem: [ { position: 20, label: 'Customer', importance: #HIGH } ],
        identification: [{ position: 20 }]
        }    
        Bpid,    
        Crea_Date_Time,
        Crea_Uname,
        Lchg_Date_Time,
        Lchg_Uname,
        /* Associations */ 
        @ObjectModel.association.type: [#TO_COMPOSITION_CHILD]     
        _itm,
        _bpa
    }
    @AbapCatalog.sqlViewName: 'ZCSOI'
    @AbapCatalog.compiler.compareFilter: true
    @AbapCatalog.preserveKey: true
    @AccessControl.authorizationCheck: #CHECK
    @EndUserText.label: 'consumption view for BOD SOI'
    @ObjectModel: 
    {
        createEnabled: true,
        updateEnabled: true,
        deleteEnabled: true,      
        semanticKey: ['Sokey', 'Soitm']
      }
    @UI: {
        headerInfo: {
            typeName: 'Sales Order Item',
            typeNamePlural: 'Sales Order Items',
            title: { type: #STANDARD, value: '_soh.Bpid' },
            description:{ type: #STANDARD, value: '_soh.Soid'  }
        }
     }
      
    define view ZC_SOI as select from ZI_SOI as soi
    association [1..1] to ZC_SOH as _soh
        on $projection.Sokey = _soh.Sokey 
    association [0..1] to SEPM_I_Product_E as _pd
        on $projection.Product = _pd.Product 
    association [0..1] to SEPM_I_Currency as _curr 
        on $projection.Currencycode = _curr.Currency    
    {
    @UI.facet: [{    
        id: 'Item',
        purpose: #STANDARD,    
        position: 10,   
        label: 'Item Details',
        type: #IDENTIFICATION_REFERENCE   
    },
    {    
        id: 'IHdr',
        purpose: #HEADER,    
        position: 10,       
        type: #FIELDGROUP_REFERENCE,
        targetQualifier: 'IHdrItm'
    }]
    @UI.hidden: true
        key soi.Soitmkey as Soitmkey,
    @UI.hidden: true    
        soi.Sokey as Sokey,
    @UI.fieldGroup: [{ qualifier: 'IHdrItm' , position: 10 }]    
    @UI.lineItem: [{position: 10 , importance: #HIGH}]  
    @UI.identification: [{position: 10 }]   
    @ObjectModel.readOnly: true    
        soi.Soitm as Soitm,
    @UI.lineItem: [{position: 20 , importance: #HIGH}]  
    @UI.identification: [{position: 20 , importance: #HIGH }] 
    @ObjectModel.foreignKey.association: '_pd'   
        soi.Product as Product,
    @UI.lineItem: [{position: 40 ,importance: #HIGH}]
    @ObjectModel.readOnly: true
    @Semantics.amount.currencyCode: 'Currencycode'    
        soi.Grossamount as Grossamount,
    @ObjectModel.foreignKey.association: '_curr'  
    @Semantics.currencyCode: true    
        soi.Currencycode as Currencycode,
    @UI.lineItem: [{position: 30 , label : 'Quantity', importance: #HIGH}]    
    @UI.identification: [{position: 40, label : 'Quantity' }]    
        soi.Quantity as Quantity,
    @UI.lineItem: [{position: 40, importance: #HIGH, label: 'Additional Charges' }]
    @ObjectModel.readOnly: true
        soi.AddCharge,    
        /* Associations */
        _curr,
        _pd,
    @ObjectModel.association.type: [#TO_COMPOSITION_PARENT,#TO_COMPOSITION_ROOT]    
        _soh       
    }

    Step 4: Create 2 determinations in BOPF as follows

    1. Generate the Sales order number in Sales order Header Node:

    This Determination will generate Sales Order number for create Scenario. Below is the code.

    Below is the code implemented to generate the SO Id number.

    class ZCL_I_D_CALC_SO_ID definition
      public
      inheriting from /BOBF/CL_LIB_D_SUPERCL_SIMPLE
      final
      create public .
    
    public section.
    
      methods /BOBF/IF_FRW_DETERMINATION~EXECUTE
        redefinition .
    protected section.
    private section.
    ENDCLASS.
    
    
    CLASS ZCL_I_D_CALC_SO_ID IMPLEMENTATION.
    
    
      method /BOBF/IF_FRW_DETERMINATION~EXECUTE.
      DATA: itab TYPE ZTISOH.
    
        CALL METHOD io_read->retrieve
          EXPORTING
            iv_node = is_ctx-node_key
            it_key  = it_key
          IMPORTING
            et_data = itab.
        LOOP AT itab REFERENCE INTO DATA(lr_tab).
          IF lr_tab->soid IS INITIAL.
            CALL FUNCTION 'NUMBER_GET_NEXT'
              EXPORTING
                nr_range_nr             = '01'
                object                  = 'RV_BELEG'
              IMPORTING
                number                  = lr_tab->soid
              EXCEPTIONS
                interval_not_found      = 1
                number_range_not_intern = 2
                object_not_found        = 3
                quantity_is_0           = 4
                quantity_is_not_1       = 5
                interval_overflow       = 6
                buffer_overflow         = 7
                OTHERS                  = 8.
            IF sy-subrc <> 0.
    *             MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
    *               WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
            ENDIF.
            CALL METHOD io_modify->update
              EXPORTING
                iv_node           = is_ctx-node_key
                iv_key            = lr_tab->key
                is_data           =  lr_tab
                it_changed_fields =
                VALUE #( ( ZIF_I_SOH_C=>sc_node_attribute-zi_soh-soid ) )
              .
    *        CATCH /bobf/cx_frw_contrct_violation.
          ENDIF.
        ENDLOOP.
    
      endmethod.
    ENDCLASS.

    We now implement code to generate item number and also, we implement code to set the gross item = product price * Quantity entered by the user.

    Below is the code for the same.

    CLASS zcl_i_d_calc_soitm_cal_gross DEFINITION
      PUBLIC
      INHERITING FROM /bobf/cl_lib_d_supercl_simple
      FINAL
      CREATE PUBLIC .
    
    PUBLIC SECTION.
    
      METHODS /bobf/if_frw_determination~execute
        REDEFINITION .
    PROTECTED SECTION.
    PRIVATE SECTION.
    ENDCLASS.
    
    
    
    CLASS zcl_i_d_calc_soitm_cal_gross IMPLEMENTATION.
    
    
      METHOD /bobf/if_frw_determination~execute.
      DATA:
        itab TYPE ztisoi,
        itab1 TYPE ztisoh,
        li_key TYPE /bobf/t_frw_key.
    *   We Retrive the Items of the Sales Order
        CALL METHOD io_read->retrieve
          EXPORTING
            iv_node                 = is_ctx-node_key
            it_key                  = it_key
          IMPORTING
            et_data                 = itab.
    
      DATA(lw_itm) = VALUE #( itab[ soitm = space ] OPTIONAL ).
      READ TABLE itab WITH KEY soitm = space TRANSPORTING NO FIELDS.
    * Get the parent node to get the sales order number because Item does not have Sales order number
      IF sy-subrc = 0.
      CALL METHOD io_read->retrieve_by_association
        EXPORTING
          iv_node                 = zif_i_soh_c=>sc_node-zi_soi
          it_key                  = it_key
          iv_association          = zif_i_soh_c=>sc_association-zi_soi-to_root
          iv_fill_data            = abap_true
        IMPORTING
          et_data                 = itab1
          et_target_key           = li_key  .
    
      READ TABLE itab1 INTO DATA(lw_tab1) INDEX 1.
      SELECT SINGLE sokey FROM zsoh INTO @DATA(lv_sokey) WHERE soid = @lw_tab1-soid.
      IF sy-subrc <> 0.
        SELECT SINGLE sokey FROM ZSOH_draft INTO lv_sokey WHERE soid = lw_tab1-soid.
      ENDIF.
    
      WITH +both AS (  SELECT soitm FROM zsoi WHERE sokey = @lv_sokey
        UNION ALL
        SELECT soitm FROM zsoi_draft WHERE sokey = @lv_sokey )
            SELECT SINGLE
            FROM +both
            FIELDS MAX( soitm ) AS salesorderitem
        INTO @DATA(lv_max_salesorderitem).
    
    
        " If there are no entries, set a start value
        IF lv_max_salesorderitem IS INITIAL.
            lv_max_salesorderitem = '0000000000'.
        ENDIF.
        ENDIF.
    
    *   Get the Item and also Gross amount
        LOOP AT itab REFERENCE INTO DATA(lr_tab).
        IF lr_tab->soitm IS INITIAL.
          lr_tab->soitm = lv_max_salesorderitem = lv_max_salesorderitem + 10.
          lr_tab->soitm = |{ lr_tab->soitm ALPHA = IN }|.
        ENDIF.
    
    
        if lr_tab->quantity IS NOT INITIAL AND lr_tab->product IS NOT INITIAL.
          SELECT SINGLE * FROM snwd_pd INTO @Data(lw_snwd_pd) WHERE product_id = @lr_tab->product.
    *     We add the Add Charge + quantity * price
          lr_tab->grossamount = ( lr_tab->quantity * lw_snwd_pd-price ) + lr_tab->addcharge.
          lr_tab->currencycode = lw_snwd_pd-currency_code.
        ENDIF.
        CALL METHOD io_modify->update
          EXPORTING
            iv_node           = is_ctx-node_key
            iv_key            = lr_tab->key
    *        iv_root_key       =
            is_data           = lr_tab
            it_changed_fields =
            VALUE #( ( zif_i_soh_c=>sc_node_attribute-zi_soi-soitm )
                     ( zif_i_soh_c=>sc_node_attribute-zi_soi-grossamount )
                     ( zif_i_soh_c=>sc_node_attribute-zi_soi-currencycode ) ).
    *    CATCH /bobf/cx_frw_contrct_violation.
    
       ENDLOOP.
    
      ENDMETHOD.
    ENDCLASS.

    We now also activate the OData Service using /IWFND/MAINT_SERVICE TCODE in SAP.

    Step 5: Create List Report Elements App

    1. Open VS Code and use command pallet to create Fiori Elements App
    2. View->Command Pallet ->Open Template Wizard
    3. Select Fiori Elements List Report Floor plan, select your S/4 HANA system and give the Service name and project Details.

    Next We preview the app

    We click Create button on item to create Items.

    In the next step we will add Additional Charges Button to allow users to enter additional charges. In order to achieve the same we will be using Extension and Fiori Free Style coding.

    Step 6: Using Extension to Create an Additional Charges Button

    In order to get additional charges from the user we will perform below steps

    1. Use Extension to create Additional Charges Button
    2. Create a Fragment for creating Table where user will Enter Details
    3. Invoke the Fragment from the Controller.

    Create Additional Charges Button we will use Guided Development tool to add Custom button.

    View->Command Pallet->Open Guided Development

    We give below Details in step 1 and click on Insert Snippet Button so that it creates ObjectPageExt.controller

    Page Object Page
    Function Name   getAdditionalCharge 

     

    In Step 2 we give below Details and click on Insert Snippet, so that we get the details updated in Manifest JSON.

    Entity Set ZC_SOH
    Action Position Section
    Facets Annotation Term ID Item Details
    Action ID idGetAddChrg
    Button Text Additional Charge
    Row Selection Yes

     

    Below Code Gets generated in Manifest JSON.

    We can see that ext folder is created inside that we get ObjectPageExt.controller.controller.js file as shown below. We will implement code for Invoking Pop up later. In the next step we will create fragment.

    Step 7: Create Fragment which we will invoke it from the Extension Function.

    1. This fragment will have responsive table with multiselect option
    2. Two Buttons Copy and Cancel Action.
    3. We do Aggregation Binding on the table using Named JSON Model “Charge”

    Below is the XML code for the fragment.

    <core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc"
    	 height="100%">
    	<Dialog id="idExtraCharges" title="Other Charges" class="sapUiNoContentPadding">
    		<content>
    			<VBox id="idChargeVbox" class="sapUiSmallMarginBeginEnd">
    				<Table id="idChargeTable" inset="false" items="{Charge>/ExtraChrg}" mode="MultiSelect">
    					<columns>
    						<Column width="12em">
    							<Text text="Charge Type"/>
    						</Column>
    						<Column minScreenWidth="Tablet" demandPopin="true">
    							<Text text="Amount"/>
    						</Column>
    						<Column minScreenWidth="Desktop" demandPopin="true" hAlign="End">
    							<Text text="Currency"/>
    						</Column>
    					</columns>
    					<items>
    						<ColumnListItem vAlign="Middle">
    							<cells>
    								<Text text="{Charge>ChargeType}"/>
    								<Input value="{Charge>Amount}"/>								
    							</cells>
    						</ColumnListItem>
    					</items>
    				</Table>
    			</VBox>
    		</content>
    		<buttons>
    			<Button id="btncopy" text="Copy" press="onActionCopy" />
    			<Button id="btncancel" text="Cancel" press="onActionCancel"/>
    		</buttons>
    	</Dialog>
    </core:FragmentDefinition>

    Step 8: Implement Logic in Controller to Trigger Fragment as Pop up.

    Next we implement the ObjectPageExt Controller.

    onInit Method: In this method we define the JSON Model “Charge” which will have 2 fields Charge Type and Amount.Since we have 3 types of Charges we will define Freight Charge, Miscellaneous Charge and Other Charge.Amount would be 0 we will bind fill it from Popup Table. Below is the Code for the same

    getAdditionalCharge: In this method we validate if the user is in Edit or Create mode, if not we give error or trigger Pop up. We also get the Details of the line item selected because we will need to send this details to backend to modify values.

    In the Pop up we had 2 Fragment we have 2 Buttons Copy and Cancel, when the user clicks Cancel we should come out of the popup and do nothing so below is the code to trigger

    Close Pop up.

    When the user clicks on Copy, we need to get the details of charges entered by the user and then we need to invoke the BOPF Action from backend, we will come back to this later once we finish the next step of creating Static Action.

    Step 9: Create Static BOPF Action

    We will create Static Action in BOPF to get the Additional Charges modify the Additional charges amount. We will do below Steps. We create static action in the Item node in BOPF as shown below

    We then define the structure for ZADD_CHRG in SAP, this will have 2 parameters Draft ID and Total Coming from Front end system.

    We now implement the changes in class ZCL_I_A_SET_ADD_CHRG, because we need to update the additional charge. Below is the code, we get the parameter from the front end, then we add up the gross amount and update the BO.

    class ZCL_I_A_SET_ADD_CHRG definition
      public
      inheriting from /BOBF/CL_LIB_A_SUPERCL_SIMPLE
      final
      create public .
    
    public section.
    
      methods /BOBF/IF_FRW_ACTION~EXECUTE
        redefinition .
    protected section.
    private section.
    ENDCLASS.
    
    
    CLASS ZCL_I_A_SET_ADD_CHRG IMPLEMENTATION.
    
    
      method /BOBF/IF_FRW_ACTION~EXECUTE.
      DATA : ls_parameters TYPE ZADD_CHRG,
             lt_key        TYPE /bobf/t_frw_key,
             ls_key        TYPE /bobf/s_frw_key,
             itab          TYPE ZTISOI.
    
        "Retrive Source data from Frontend
        DATA(ls_parameters_ptr) = CAST zadd_chrg( is_parameters ).
    
        MOVE ls_parameters_ptr->* TO ls_parameters.
        ls_key-key = ls_parameters-draftuuid.
        APPEND ls_key TO lt_key.
    
        call METHOD io_read->retrieve
          EXPORTING
            iv_node                 = ZIF_I_SOH_C=>sc_node-zi_soi
            it_key                  = lt_key
          IMPORTING
            et_data                 =  itab.
    
       loop at itab REFERENCE INTO data(lr_tab).
        lr_tab->addcharge   = ls_parameters-amount + lr_tab->addcharge.
        lr_tab->grossamount = lr_tab->grossamount + ls_parameters-amount.
    
        call METHOD io_modify->update
          EXPORTING
            iv_node           = ZIF_I_SOH_C=>sc_node-zi_soi
            iv_key            = lr_tab->key
            is_data           = lr_tab
            it_changed_fields =
            VALUE #( ( ZIF_I_SOH_C=>sc_node_attribute-zi_soi-grossamount )
                     ( ZIF_I_SOH_C=>sc_node_attribute-zi_soi-addcharge )
                     ( ZIF_I_SOH_C=>sc_node_attribute-zi_soi-currencycode ) ).
         ev_static_action_failed = abap_false.
       ENDLOOP.
    
    
      endmethod.
    ENDCLASS.

    It is to be noted that whenever we create Action in BOPF it creates a Function Group in OData Service as shown below in Metadata.

    Step 10: Invoking the Action from Front End

    We will now implement the final changes in fiori app to trigger the backend static action ZC_SOISet_add_chrg for this we will use Invoke API.

    Below is the code for the same.

    Below is the final code of the Controller.

    sap.ui.controller("fe.so.fesaleorder.ext.controller.ObjectPageExt", {
        onInit: function () {
    
            var oJson = new sap.ui.model.json.JSONModel({
                "ExtraChrg": []
            });
    
            var oExtraChrg = [];
            var oCharges = {
                "ChargeType": "Freight",
                "Amount": 0,
            };
    
            oExtraChrg.push(JSON.parse(JSON.stringify(oCharges)));
    
            oCharges.ChargeType = "Miscellaneous Charge";
            oExtraChrg.push(JSON.parse(JSON.stringify(oCharges)));
    
            oCharges.ChargeType = "Other Charge";
            oExtraChrg.push(JSON.parse(JSON.stringify(oCharges)));
            this.getOwnerComponent().setModel(oJson, "Charge");
            this.getOwnerComponent().getModel("Charge").setProperty("/ExtraChrg", oExtraChrg);
    
        },
    
        getAdditionalCharge: function (oEvent) {
            debugger;
            // Below is how we get the ID of the Item table
            this._oTable = this.getView().byId(oEvent.getSource().getParent().getParent().getParent().getId()).getTable();
            this._oDetails = this.getView().getModel().getProperty(this._oTable.getSelectedContextPaths()[0]);
            var u = oEvent.getSource().getModel("ui").getData();
            if (u.editable === true || u.createMode === true) {
                if (!this._DialogGenerate) {
                    this._DialogGenerate = sap.ui.xmlfragment("fe.so.fesaleorder.ext.fragment.extraCharge", this);
                    this.getView().addDependent(this._DialogGenerate);
                }
                this._DialogGenerate.open();
            } else {
                // var i = this._oResourceBundle.getText('@copyInfo');
                sap.m.MessageBox.information("Changes Possible only in Create and Edit Mode");
            }
        },
    
        onActionCancel: function () {
            this._DialogGenerate.close();
            this._DialogGenerate.destroy();
            this.getView().removeDependent(this._DialogGenerate);
            this._DialogGenerate = null;
        },    
    
        onActionCopy: function (oEvent) {
            var oPromise;
            var that = this;
            var oApi = this.extensionAPI;
            var oParameter = {};
            //    Get Selected row in item table
            var ochargeDet = sap.ui.getCore().byId("idChargeTable").getSelectedContextPaths();
            var oTot = 0;
            for (var i = 0; i < ochargeDet.length; i++) {
                var oCharges = this.getOwnerComponent().getModel("Charge").getProperty(ochargeDet[i]);
                oTot = parseFloat(oCharges.Amount) + oTot;
            }        
            // ***************Implment Invoke Action after Creating Static BOPF Action*********
            oParameter = {
                "Draftuuid": this._oDetails.Soitmkey,
                "Amount": oTot,
                "Currency" :  'EUR'
            };
            oPromise = this.extensionAPI.invokeActions("ZC_SOH_CDS.ZC_SOH_CDS_Entities/ZC_SOISet_add_chrg", [],
                oParameter);
            oPromise.then(function (r) {
                that.getView().getModel().refresh(true, false, "CopyRefresh");
                sap.m.MessageToast.show("Charges Added Successfully to Item" + that._oDetails.Soitm);
    
            });
            oPromise.catch(function (r) {
                var E = jQuery.parseJSON(r[0].error.response.responseText);
                sap.m.MessageBox.error(E.error.message.value);
            });
    
            this.onActionCancel();
        }
    
    });

    Let us test the application as follows.

    We click on Create Sales Order

    We create item

    Click on Addtional Charges Button by selecting item.

    Rating: 0 / 5 (0 votes)

    The post Invoking Static BOPF Action From Custom Pop up in Fiori Elements App appeared first on ERP Q&A.

    ]]>
    Output Management and In-App Extension for Account Statement forms in SAP S/4 HANA https://www.erpqna.com/output-management-and-in-app-extension-for-account-statement-forms-in-sap-s-4-hana/?utm_source=rss&utm_medium=rss&utm_campaign=output-management-and-in-app-extension-for-account-statement-forms-in-sap-s-4-hana Tue, 08 Feb 2022 12:01:07 +0000 https://www.erpqna.com/?p=59712 The below blog focusses on the Output Management Configuration and execution of Account Statement forms. Steps to be followed for configuration : Form Name : FIN_FO_CORR_ACC_STAT ODATA Service : FDP_FFO_CORR_ACC_STAT_SRV Configure the below set of Views. Note: This can be also done via SPRO. V_T048B : Allocate Program for Automatic Correspondence V_T048I : Call Options […]

    The post Output Management and In-App Extension for Account Statement forms in SAP S/4 HANA appeared first on ERP Q&A.

    ]]>
    The below blog focusses on the Output Management Configuration and execution of Account Statement forms.

    Steps to be followed for configuration :

    Form Name : FIN_FO_CORR_ACC_STAT

    ODATA Service : FDP_FFO_CORR_ACC_STAT_SRV

    Configure the below set of Views.

    Note: This can be also done via SPRO.

    V_T048B : Allocate Program for Automatic Correspondence

    V_T048I : Call Options of Correspondence Types

    V_T048O : Allocate Output Management for Automatic Correspondence

    APOC_C_FORMV : Form Template

    APOC_C_EMAILTV : Email Template

    APOC_C_CHANNELV : Channel Print/Email

    V_FIN_CORR_VRNT : Define Custom Variants for Correspondence Types

    V_FIN_CORR_TYPE : Assign Labels and Company Codes

    Once the above configuration is completed the Form can be tested by the below two ways :

    • In case of Fore ground processing or testing use the below Fiori application

    Create Correspondence

    https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps(‘F0744A’)/S21OP
    
    
    Create Correspondence Fiori App

    Now Click on the preview you can see the below Standard OM form in the output

    Print Preview

    Email sending functionality is also possible using send email button :

    Email Sending Functionality

    For the background processing use the below Job scheduling Fiori application Schedule Accounts Payable Jobs as shown below :

    https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps(‘F2257’)/S21OP
    
    

    Click on Create a new job

    Select the template Correspondence : Account Statement

    Set the parameters like company code and Customers

    Also provide the variant name which is configured above

    If you want both print and email options use the below check boxes

    Once the job is scheduled you can find the logs in the below apps by clicking on the log or Results button

    In-App Extensions of the Forms and Email templates

    Open the Maintain Form Template Fiori application and select the Form template
    Download the Form Template :

    Now Open the Adobe Life cycle designer and open the XDP file as shown below and make small changes like changing logo or adding new fields or fragments

    Upload the changed template to the custom form template and change the configuration with the custom form name and generate the print preview

    Similarly Use the In-App Extension to change the email templates as shown below :

    Open the Custom Email template and copy the standard email template

    Dynamic variables shown in the above screenshot is available in the below CDS view and the view can be extended in case more variables are required for the same.

    Variables
    CDS Views

    Now change the email template by changing the email subject adding additional variables in the email body, Configure the custom email template and test the same.

    Custom Email template
    Custom Template configuration
    Custom Template

    Thus we can change the form layout and also the email template to adapt the same to custom functionality

    Rating: 0 / 5 (0 votes)

    The post Output Management and In-App Extension for Account Statement forms in SAP S/4 HANA appeared first on ERP Q&A.

    ]]>