CRM and CX Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
Michael_W141
Product and Topic Expert
Product and Topic Expert
1,330

My colleagues Yugandhar, Shrinivas, and Kiruthika prepared a prompt for Generative AI that helps in converting ABAP user exits (custom pricing routines) to the required format for SAP Variant Configuration and Pricing. We are exited to share this prompt here that aims to assist you effectively in the conversion process, ensuring that the generated code aligns with your needs.

Important Notes:

  • Local Extensions: This prompt specifically focuses on converting ABAP pricing user exits to local extensions.
  • Validation by Experts: The generated code should always be validated by pricing experts. While the prompt acts as a co-pilot, it might not generate 100% accurate code. Your expertise is crucial in ensuring that the final output meets all necessary standards and requirements.
  • Prompt Execution: You can execute this prompt using the SAP AI Launchpad in BTP or Open AI ChatGPT. Below are the detailed steps and instructions.
  • Caution: Be mindful of sending sensitive or proprietary source code to generative AI platforms. Always adhere to your organization's data security policies and guidelines.

 

Steps to Use the Prompt:

1. Subscribe to SAP AI Launchpad in BTP:

2. Open the AI Launchpad:

  • Access the AI Launchpad and select any model, for example, “gemini-1.5-pro” or "gpt-4-32k". Gemini worked best in our tests!

3. Enter the Prompt:

  • Enter the provided prompt into the selected model in the AI Launchpad. Ensure you add the complete ABAP code to be converted inside the curly braces at the end of the prompt.

4. Execute the Prompt:

  • Execute the "Run" command to generate the converted code.

 

Example of how to use it with ABAP code:

If you have a specific ABAP code to be converted, insert it inside the curly braces at the end of the prompt. For instance:

### Complete ABAP Code to be Converted:

{ form frm_kondi_wert_006.
if xkomv-koaid = 'A'.
xkwert = xkomv-kwert - komp-netwr - komp-mwsbp.
endif.
endform. }

Additional Instructions:

  • Ensure that the ABAP code is correctly formatted and complete within curly braces: { <ABAP code> }
  • If there are any function modules called within the custom exit implementation, ensure to include the source code of those function modules as well. This is necessary to achieve a complete and accurate conversion.
  • Review the generated code for accuracy and completeness.
  • Collaborate with pricing experts to validate and finalize the output.
  • Amend the resulting code with comments and log statements as needed. For more details, refer to the Extension Guide for SAP Variant Configuration and Pricing.
  • The complete text within the following box is the prompt and must be provided in its entirety for the conversion to work properly.

By following these detailed instructions, you can effectively convert ABAP pricing user exits to the required format for the Pricing service.

Prompt to perform the conversion: 

You are an expert at converting ABAP code to corresponding javascript function. You have the following lookup table which tells you the corresponding json for a given ABAP variable:
[Start of data set mapping]
[
  { 
    "abap": "2", 
    "json": "json.documentInput.documentCurrency.numberOfDecimals" 
  }, 
  {
    "abap": "KOMK-WAERK",
    "json": "json.documentInput.documentCurrency.unit"
  },
  {
    "abap": "KOMK-HWAER",
    "json": "json.documentInput.local currency.unit"
  },
  {
    "abap": "KOMP-KZNEP",
    "json": "json.documentInput.itemInput.exclusionIndicator"
  },
  {
    "abap": "KOMP-BRGEW",
    "json": "json.documentInput.itemInput.grossWeight"
  },
  {
    "abap": "KOMP-GEWEI",
    "json": "json.documentInput.itemInput.grossWeight.internalUnit"
  },
  {
    "abap": "KOMP-NETPR",
    "json": "json.documentInput.itemInput.netPrice"
  },
  {
    "abap": "KOMP-NETWR",
    "json": "json.documentInput.itemInput.netValue"
  },
  {
    "abap": "KOMV-KSTBS or KOMV-KZBZG",
    "json": "json.documentInput.itemInputconditions.scaleBaseType"
  },
  {
    "abap": "KOMP-NTGEW",
    "json": "json.documentInput.itemInput.netWeight"
  },
  {
    "abap": "KOMP-MGAME",
    "json": "json.documentInput.itemInput.quantity.value"
  },
  {
    "abap": "KOMP-VRKME",
    "json": "json.documentInput.itemInput.quantity.internalUnit"
  },
  {
    "abap": "KOMP-KOWRR",
    "json": "json.documentInput.itemInput.statistical"
  },
  {
    "abap": "KOMP-KZWI*",
    "json": "json.documentInput.itemInput.subTotals"
  },
  {
    "abap": "KOMP-MWSBP",
    "json": "json.documentInput.itemInput.taxValue"
  },
  {
    "abap": "KOMP-VOLUM",
    "json": "json.documentInput.itemInput.volume.value"
  },
  {
    "abap": "KOMP-VOLEH",
    "json": "json.documentInput.itemInput.volume.internalUnit"
  },
  {
    "abap": "xkomv-KRECH",
    "json": "json.documentInput.pricingCondition.calculationType"
  },
  {
    "abap": "xkomv-KAWRT",
    "json": "json.documentInput.pricingCondition.conditionBase"
  },
  {
    "abap": "xkomv-KOAID",
    "json": "json.documentInput.pricingCondition.conditionClass"
  },
  {
    "abap": "xkomv-KBETR",
    "json": "json.documentInput.pricingCondition.conditionRate.value"
  },
  {
    "abap": "xkomv-KSCHL",
    "json": "json.documentInput.pricingCondition.conditionType"
  },
  {
    "abap": "xkomv-KMEIN",
    "json": "json.documentInput.pricingCondition.conditionUnit.internalUnit"
  },
  {
    "abap": "xkomv-KPEIN",
    "json": "json.documentInput.pricingCondition.conditionUnit.value"
  },
  {
    "abap": "xkomv-KWERT",
    "json": "json.documentInput.pricingCondition.conditionValue"
  },
  {
    "abap": "xkomv-ZAEHK",
    "json": "json.documentInput.pricingCondition.counter"
  },
  {
    "abap": "xkomv-KFAKTOR1",
    "json": "json.documentInput.pricingCondition.durationFactor"
  },
  {
    "abap": "xkomv-KINAK",
    "json": "json.documentInput.pricingCondition.inactiveFlag"
  },
  {
    "abap": "xkomv-KHERK",
    "json": "json.documentInput.pricingCondition.origin"
  },
  {
    "abap": "xkomv-KNUMH",
    "json": "json.documentInput.pricingCondition.recordId"
  },
  {
    "abap": "xkomv-KSTBS or xkomv-KZBZG",
    "json": "json.documentInput.pricingCondition.scaleBaseType"
  },
  {
    "abap": "xkomv-KSTAT",
    "json": "json.documentInput.pricingCondition.statistical"
  },
  {
    "abap": "xkomv-STUNR",
    "json": "json.documentInput.pricingCondition.stepNumber"
  },
  {
    "abap": "xkomv-KDUPL",
    "json": "json.documentInput.pricingCondition.structureCondition"
  },
  {
    "abap": "xkomv-KVARC",
    "json": "json.documentInput.pricingCondition.variantCondition"
  },
  {
    "abap": "xkomv-KFAKTOR",
    "json": "json.documentInput.pricingCondition.variantConditionFactor"
  },
  {
    "abap": "xkomv-VARCOND",
    "json": "json.documentInput.pricingCondition.variantConditionKey"
  }
]
[End of data set mapping]
[Start of input Json Structure]
{
  "action": "PROCESS_FORMULA",
  "documentInput": {
    "documentCurrency": {
      "numberOfDecimals": 0,
      "unit": "string"
    },
    "itemInput": {
      "attributes": [
        {
          "name": "string",
          "values": [
            "string"
          ]
        }
      ],
      "conditions": [
        {
          "conditionBase": 0,
          "conditionRate": {
            "internalUnit": "string",
            "unit": "string",
            "value": 0
          },
          "conditionType": "string",
          "conditionUnit": {
            "internalUnit": "string",
            "unit": "string",
            "value": 0
          },
          "conditionValue": 0,
          "durationFactor": 0,
          "origin": "string",
          "purpose": "string",
          "recordId": "string",
          "scaleBaseType": "string",
          "statistical": true,
          "variantConditionFactor": 0,
          "variantConditionKey": "string"
        }
      ],
      "exclusionIndicator": "$",
      "grossWeight": {
        "internalUnit": "string",
        "unit": "string",
        "value": 0
      },
      "lastPriceCondition": {
        "calculationType": "string",
        "conditionBase": 0,
        "conditionControl": "string",
        "conditionRate": {
          "internalUnit": "string",
          "unit": "string",
          "value": 0
        },
        "conditionType": "string",
        "conditionUnit": {
          "internalUnit": "string",
          "unit": "string",
          "value": 0
        },
        "conditionValue": 0,
        "counter": 0,
        "factor": 0,
        "manuallyChanged": true,
        "quantity": {
          "internalUnit": "string",
          "unit": "string",
          "value": 0
        },
        "stepNumber": 0,
        "variantConditionFactor": 0
      },
      "netPrice": 0,
      "netValue": 0,
      "netWeight": {
        "internalUnit": "string",
        "unit": "string",
        "value": 0
      },
      "quantity": {
        "internalUnit": "string",
        "unit": "string",
        "value": 0
      },
      "statistical": true,
      "subTotals": [
        {
          "flag": "string",
          "value": 0
        }
      ],
      "taxValue": 0,
      "volume": {
        "internalUnit": "string",
        "unit": "string",
        "value": 0
      }
    },
    "localCurrency": {
      "numberOfDecimals": 0,
      "unit": "string"
    },
    "pricingCondition": {
      "calculationType": "string",
      "conditionBase": 0,
      "conditionClass": "string",
      "conditionRate": {
        "internalUnit": "string",
        "unit": "string",
        "value": 0
      },
      "conditionType": "string",
      "conditionUnit": {
        "internalUnit": "string",
        "unit": "string",
        "value": 0
      },
      "conditionValue": 0,
      "counter": 0,
      "durationFactor": 0,
      "inactiveFlag": "string",
      "origin": "string",
      "purpose": "string",
      "recordId": "string",
      "scaleBaseType": "string",
      "statistical": true,
      "stepNumber": 0,
      "structureCondition": "string",
      "variantCondition": true,
      "variantConditionFactor": 0,
      "variantConditionKey": "string"
    }
  }
}
[End of input Json Structure]
Your task is to create the javascript function exactly as shown in the example below. Observe that the ABAP variables are not directly replaced with the json equivalents, instead, a js variable has been created to hold the value of the json and then that has been used for better code readability.
If the mapping of KOMK-* or KOMP-* ABAP fields is missing from the data set given above, it should read the values[0] from the json.documentInput.itemInput.attributes[] from the input json structure defined above with the name KOMK-* or  KOMP-*
The value for KOMP-KZWI* should be read from the json.documentInput.itemInput.subTotals array for the respective key.
The value for xwork* should be read from the json.documentInput.itemInput.subTotals array for the respective key. For example, xworkd the subtotal key which should be used is "D".
 
When the ABAP code contains the read table xkomv or iterating over xkomv or xkomv[] in java script the iteration should happen on json.documentInput.itemInput.conditions , in other cases the reference to xkmov and komt1 refers to json.documentInput.pricingCondition.  The data set fields of json.documentInput.itemInput.conditions match with the fields of json.documentInput.pricingCondition.

For database operations, assume the presence of a database client object 'db' supporting the following methods:

* `db.select()`: Initiates building a SELECT query and returns a query builder object.
* `db.from(tableName)`: Specifies the table for the SELECT query.
* `db.columns(column1, column2, ...)`: Selects columns for the query.
* `db.where(condition)`: Adds a WHERE clause to the query.
* `db.and(condition1, condition2, ...)`: Combines conditions with logical AND.
* `db.or(condition1, condition2, ...)`: Combines conditions with logical OR.
* `db.eq(column, value)`: Creates an equality condition for the WHERE clause.
* `db.in(column, values): Creates an IN condition for the WHERE clause, checking if the column's value is within the provided list of values.
* `db.like(column, value): Creates a LIKE condition for the WHERE clause, allowing pattern matching using wildcards (% for multiple characters, _ for a single character).
* `db.string(value)`: Ensures that a value is treated as a string literal.
* `db.build()`: Finalizes the query builder and returns the SQL query string.
* `db.execute(sqlQuery)`: Executes the provided SQL query.
* `dbResult.getRowCount()`: Returns the number of rows in the result set.
* `dbResult.getColumnCount()`: Returns the number of columns in the result set.
* `dbResult.get(rowIndex, columnIndex)`: Retrieves the value at the specified row and column index.

Example DB query:

1. db.select()
        .columns("KBOBJNAME")
        .from("COMM_CFGKB")
        .where(
            db.like("KBOBJNAME", db.string("CPQ_IQA_MATERIAL_"))
        )
        .build();
   
2. db.select().from('T683S')
                .columns('*')
                .where(db.and(db.eq('KAPPL', db.string(kappl)),
                              db.eq('KVEWE', db.string('A')),
                              db.eq('KALSM', db.string(kalsm)),
                              db.eq('KSCHL', db.string(kschl))))
                .build();

You can use these methods to construct and execute SQL queries within your Javascript code.

There is an exception for one Table i.e TABLE "T001R or too1r" ignore the case sensitive.
   Whenever the table T001R is used in ABAP code, it must always include a condition where the field BURKS matches the companyCode variable and WAERS matches the documentCurrencyUnit variable.
You can use meaningful variable names in the JavaScript code.

Always enclose the generated database queries within the try and catch block with proper error handling  , use sap.log().error(‘Error’) instead of using   console.error("Error").

EXAMPLE without Database queries:
Input Abap code:
 
FORM FRM_KONDI_WERT_980.
 
IF KOMP-KZNEP = '$' AND  xkomv-kbetr >0.
  XKOMV-KINAK = 'Z'.
ENDIF.
 
READ TABLE xkomv INTO ls_za01 WITH KEY kschl = 'ZA01'.
READ TABLE xkomv INTO ls_mwsi WITH KEY kschl = 'MWSI'.
XKWERT = KOMP-KZWI1 - ls_za01-KWERT - ls_mwsi-KWERT.
 
ENDFORM.
 
 
OUTPUT JAVASCRIPT CODE:
 
function VAL_980(pricingInput) {
    var json = JSON.parse(pricingInput);
    var response;
    if (json.action === 'COLLECT_ATTRIBUTES') {
        response = VAL_980_collect_attributes();
    }
    if (json.action === 'PROCESS_FORMULA') {
        response = VAL_980_process_formula(json);
    }
    return JSON.stringify(response);
}
function VAL_980_process_formula(json) {
	var conditionOutput = {};
    var exclusionIndicator = json.documentInput.itemInput.exclusionIndicator;
    if (exclusionIndicator == '$' && json.documentInput.pricingCondition.conditionRate.value >0) {
        conditionOutput.inactiveFlag = 'Z';
    }
	var za01 = json.documentInput.itemInput.conditions.find(condition => condition.conditionType === 'ZA01');
	var mwsi = json.documentInput.itemInput.conditions.find(condition => condition.conditionType === 'MWSI');
	var subTotals = json.documentInput.itemInput.subTotals;
	var subtotal1 = subTotals.find(subtotal => subtotal.flag === '1');
	var result = subtotal1.value - za01.conditionValue - mwsi.conditionValue;
	var response = { result: result, condition: conditionOutput };
    return response;
}
function VAL_980_collect_attributes() {
	var response={
	  extendedInput: {
      documentInput: {
        itemInput: {
          conditions: {
            filter: {
              conditionType: ['ZA01','MSWI']
            },
            projection: ['conditionType','conditionValue']
          },
          projection: ['exclusionIndicator','conditions','subTotals']
        },
        pricingCondition: {
          projection: ['conditionRate']
        }
      }
    },
    result: []
  };
  return response;
}
Study the above example logic carefully and give the javascript functions for the following input ABAP
 
[start of output JSON structure]
below is the Response JSON Structure. Lets name it as response.
{
  "condition": {
    "conditionRate": {
      "value": 0
    },
    "inactiveFlag": "string",
    "statistical": boolean
  },
  "extendedInput": {
    "documentInput": {
      "itemInput": {
        "conditions": {
          "filter": {
            "conditionType": [
              "string"
            ]
          },
          "projection": [
            "string"
          ]
        },
        "projection": [
          "string"
        ]
      },
      "pricingCondition": {
        "projection": [
          "string"
        ]
      }
    }
  },
  "item": {
    "exclusionIndicator": "string",
    "subtotals": [
      {
        "flag": "string",
        "value": 0
      }
    ]
  },
  "message": "string",
  "result": {}
}
[end of output JSON structure]
 
When sy_subrc is set to non-zero value set result in response structure to false else to true only in case of REQ formula.
map value assign to komp-kznep to item.exclusionIndicator in response structure
map value assign to xkomv-kstat to condition.statistical in response structure
map value assign to xkomv-kinak to condition.inactiveFlag in response structure
map value assign to xkomv-kbetr to condition.conditionRate.value in response structure
map value assign to xkwert  to result in response structure.
map value assign to xvakey to result in response structure.
Consider only the mapped fields in the response and remove other fields. Do not set the fields with the default value which are there in the ABAP code.
process_formula function just takes input json as input parameter. The response is created within the process_formula.
if you find in the abap code function name as 'FRM_KONDI_WERT_' or 'frm_kondi_wert_' take  javascript function name as  VAL_(abap code function number) for remaining two functions use the same function name
if you find in the abap code function name as 'FRM_KONDI_BASIS_' or 'frm_kondi_basis_'  take  javascript function name  BAS_(abap code function number)
if you find in the abap code function name as 'FRM_GRUPPENKEY_' or 'frm_gruppenkey_'  take  javascript function name  GRP_(abap code function number)
if you find in the abap code function name as 'FRM_STAFFELBAS_' or 'frm_staffelbas_' take  javascript function name  SCL_(abap code function number)
if you find in the abap  code function name as 'FRM_KOBED_' or 'frm_kobed_'  take  javascript function name  REQ_(abap code function number)

After generating the process_formula function take the process_formula code as input for the collect_attributes function. To be generated the collect_attributes function should be static.
 
Ensure the collect_attributes function adheres to the following specifications:
1. The function should only include logic related to collecting attribute information from the input JSON structure.
2. After generating the process_formula function, use its code as input for the collect_attributes function, and ensure that the collect_attributes function is static.
3. All fields read from the structure documentInput.itemInput.attributes[].name should be added to the result in the output structure. If no fields are found, set the result as an empty array. The fields generally start with at005-*, KOMP-*, KOMK-*, and et005-*.
4. Add all the subfields of the structure documentInput.itemInput to extendedInput->documentInput->itemInput->projection.
5. Add all the subfields of the structure documentInput.itemInput.conditions to extendedInput->documentInput->itemInput->conditions->projection. Also, add all the conditionType names read from the structure documentInput.itemInput.conditions to extendedInput->documentInput->itemInput->conditions->filter. If no subfields of the structure documentInput.itemInput.conditions are used, set extendedInput->documentInput->itemInput->conditions to null. Don't forget to add the 'conditionType' subfield to extendedInput->documentInput->itemInput->conditions->projection if it is used.
6. If extendedInput->documentInput->itemInput->conditions->filter->conditionType is empty ([]), set extendedInput->documentInput->itemInput->conditions->projection to [].
7. If extendedInput->documentInput->itemInput->conditions is not null, add the 'conditions' field to extendedInput->documentInput->itemInput->projection.
8. Add all the subfields of the structure documentInput.pricingCondition to extendedInput->documentInput->pricingCondition->projection.
9. If no subfields are used from the structure documentInput.itemInput and documentInput.pricingCondition, set extendedInput->documentInput->itemInput->projection and extendedInput->documentInput->pricingCondition->projection as [] arrays, respectively.
10. Do not mix subfields from the structure documentInput.pricingCondition and documentInput.itemInput.conditions.
11. Ensure that no subfield is missed and add it to its respective projection.
12. Only consider level 1 subfields. For example, if the field is documentInput.itemInput.conditions[?].conditionRate.value, then add the conditionRate field as projection for extendedInput->documentInput->itemInput->conditions->projection. The same applies to documentInput.pricingCondition.
13. The function should not contain any comments or explanatory text.
14. The function should perform its designated task efficiently and concisely without unnecessary additions.
15. Please follow all the above instructions mentioned above for ABAP code with and without Database queries.
Make sure that you only give the required javascript code. Do not add any clarifying information or comments either inside or outside the function. Make sure that the function does exactly what the ABAP code does.
code: {<ABAP userexit code should be given here>}

 

2 Comments