⚙️SAPTools
ABAP10 min read

SAP BAPI Reference: BAPI_SALESORDER_CREATEFROMDAT2, BAPI_PO_CREATE1 & BAPI_MATERIAL_SAVEDATA

A field-level guide to three of the most-used SAP BAPIs — creating sales orders, purchase orders, and material masters — with parameter structure, the RETURN table pattern, and commit handling.

Published August 20, 2026

Advertisement

SAP BAPI Reference: BAPI_SALESORDER_CREATEFROMDAT2, BAPI_PO_CREATE1 & BAPI_MATERIAL_SAVEDATA

Every SAP consultant eventually needs to create a sales order, a purchase order, or a material master from outside the standard transaction — a data migration, a custom interface, an RPA bot. These three BAPIs are the ones that come up again and again. Here's what actually matters when calling each one.

BAPI_SALESORDER_CREATEFROMDAT2 — Create Sales Order

Object: SalesOrder · Method: CreateFromData2 · Module: SD

Despite the name suggesting "from data version 2," this is still the current standard BAPI for creating sales orders programmatically (there's no "3"). The structure follows the header/items/partners pattern common to most creation BAPIs:

CALL FUNCTION 'BAPI_SALESORDER_CREATEFROMDAT2'
  EXPORTING
    order_header_in = ls_header       " Doc type, sales org, distr. channel, division
  TABLES
    order_items_in  = lt_items        " Material, quantity, plant per line
    order_partners  = lt_partners     " Sold-to (AG), ship-to (WE), etc.
    return          = lt_return.

Common gotcha: the BAPI does not commit automatically. Even on success, nothing is actually saved to the database until you explicitly call BAPI_TRANSACTION_COMMIT afterward — forget it, and the sales order number comes back in return but doesn't exist when you check VA03 five minutes later. Always check return for entries with TYPE = 'E' before committing; a mix of warnings (TYPE = 'W') and success messages is normal and doesn't mean the call failed.

BAPI_PO_CREATE1 — Create Purchase Order

Object: PurchaseOrder · Method: Create1 · Module: MM

Same shape, different domain — header/item/schedule-line structure for purchasing:

CALL FUNCTION 'BAPI_PO_CREATE1'
  EXPORTING
    poheader        = ls_header       " Vendor, purch. org, purch. group, doc type
    poheaderx       = ls_headerx      " X-structure: which header fields are "set"
  IMPORTING
    exppurchaseorder = lv_ebeln
  TABLES
    poitem          = lt_items
    poitemx         = lt_itemsx
    poschedule      = lt_schedule     " Delivery dates and quantities per item
    return          = lt_return.

Common gotcha: the X structures (poheaderx, poitemx) are the part people forget. BAPIs that support both create and change with the same function module use these "change indicator" structures to distinguish "field is empty" from "field should be cleared" — for a create, you still need to set the corresponding X field to 'X' for every field you populate in poheader/poitem, or the value gets silently ignored. This is the single most common reason a PO create "succeeds" (returns a number) with half the fields blank.

BAPI_MATERIAL_SAVEDATA — Create/Change Material Master

Object: Material · Method: SaveData · Module: MM

This one covers both creating a new material and updating an existing one — which view (Basic Data, Sales, MRP, Accounting...) gets touched depends entirely on which substructure you populate:

CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA'
  EXPORTING
    headdata        = ls_headdata     " Material number, industry sector, material type
    clientdata      = ls_clientdata   " MARA-level fields (base unit, material group...)
    clientdatax     = ls_clientdatax  " X-structure, same pattern as BAPI_PO_CREATE1
    plantdata       = ls_plantdata    " MARC-level fields (only if maintaining plant view)
    plantdatax      = ls_plantdatax
  TABLES
    materialdescription = lt_makt     " Material descriptions per language
    return               = lt_return.

Common gotcha: headdata-material is only required if you're creating a specific material number yourself — leave it blank with internal number range configured and the system assigns one, which then only appears inside return, not as a direct export parameter (unlike the sales order and PO BAPIs, which do return the new number directly). Missing this distinction is the most common cause of "the material got created but I don't know its number" in a migration script.

The Pattern That Applies to (Almost) All of Them

  • RETURN table, not exceptions. Unlike RFC-enabled function modules with declared EXCEPTIONS, BAPIs report success/failure through the return table with a TYPE field (Error, Warning, Success, Info, Abend). Check for TYPE = 'E' or 'A' before assuming success — a BAPI call returning sy-subrc = 0 tells you the function executed, not that the business operation succeeded.
  • No implicit commit. BAPI_TRANSACTION_COMMIT (and its counterpart BAPI_TRANSACTION_ROLLBACK) is a separate, explicit call. This is deliberate — it lets you call several BAPIs as one logical unit of work and commit once.
  • X-structures mean "this field is meaningful." Any BAPI with a change or dual create/change flavor uses this pattern; always check whether your target BAPI expects one before assuming a populated structure is enough.

Use the BAPI Explorer to browse these and 200+ others by business object, and the RFC Function Module Docs Generator if you need to scaffold your own RFC-enabled wrapper around one of these calls.

Topics:

bapi_salesorder_createfromdat2bapi_po_create1bapi_material_savedatasap bapi referencebapi return tablebapi commit