Integrating SAP Sales Orders, Purchase Orders & Inventory with Custom WMS: A No-Code/Low-Code Approach Using .NET Core and Azure Logic Apps

PP

Ponvannan P

Aug 6, 2025 10 Minutes Read

Integrating SAP Sales Orders, Purchase Orders & Inventory with Custom WMS: A No-Code/Low-Code Approach Using .NET Core and Azure Logic Apps Cover

Introduction

Modern warehouse operations require seamless integration between SAP's order management modules (Sales Orders, Purchase Orders, and Inventory Management) and custom warehouse management solutions. This comprehensive guide demonstrates how to build robust integrations using no-code/low-code methodologies with .NET Core APIs and Azure Logic Apps, enabling businesses to achieve rapid deployment without extensive custom development.

Architecture Overview

Our no-code/low-code integration architecture leverages visual workflow designers and configuration-driven approaches:

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   SAP SD/MM     │◄──►│   Azure Logic    │◄──►│   Custom WMS    │
│ Sales/Purchase  │    │      Apps        │    │   Solution      │
│   & Inventory   │    │   (No-Code)      │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                              │
                              ▼
                    ┌──────────────────┐
                    │  Power Platform  │
                    │   Connectors     │
                    └─────────┬────────┘
                              │
                              ▼
                    ┌──────────────────┐
                    │  .NET Core       │
                    │  Configuration   │
                    │  API Layer       │
                    └──────────────────┘

Prerequisites

Before we begin, ensure you have:

  • SAP ECC/S4 HANA system with SD, MM modules

  • Azure subscription with Logic Apps and Power Platform licenses

  • .NET Core 8.0 SDK

  • SAP .NET Connector 3.0 (NCo)

  • Power Platform admin access

  • Visual Studio Code or Visual Studio

Part 1: SAP Order Management Data Models

Understanding SAP Sales Orders (SD)

Key Tables and Structures:

  • VBAK (Sales Document Header)

  • VBAP (Sales Document Item)

  • VBUP (Sales Document Item Status)

  • LIPS (Delivery Item)

  • LIKP (Delivery Header)

Understanding SAP Purchase Orders (MM)

Key Tables and Structures:

  • EKKO (Purchase Document Header)

  • EKPO (Purchase Document Item)

  • EKES (Vendor Confirmations)

  • EKBE (Purchase Order History)

Understanding SAP Inventory (MM)

Key Tables and Structures:

  • MARD (Storage Location Stock)

  • MCHB (Batch Stock)

  • MSEG (Document Segment: Material)

  • MKPF (Material Document Header)

Part 2: .NET Core Configuration-Driven API Layer

Project Structure and Setup

mkdir SAPOrderManagementAPI
cd SAPOrderManagementAPI
dotnet new webapi
dotnet add package SAP.Middleware.Connector
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
dotnet add package Swashbuckle.AspNetCore

Configuration-Driven SAP Connection

appsettings.json

{
  "SAPConnection": {
    "AppServerHost": "your-sap-server.com",
    "SystemNumber": "00",
    "Client": "100",
    "User": "INTEGRATION_USER",
    "Password": "your-password",
    "Language": "EN",
    "PoolSize": 5,
    "MaxPoolSize": 10,
    "IdleTimeout": 600
  },
  "IntegrationSettings": {
    "SalesOrderSettings": {
      "AutoCreateDelivery": true,
      "DefaultShippingPoint": "SH01",
      "StatusUpdateEnabled": true,
      "NotificationWebhook": "https://your-wms-webhook.com/sales-order-update"
    },
    "PurchaseOrderSettings": {
      "AutoCreateGoodsReceipt": false,
      "DefaultPlant": "1000",
      "RequireThreeWayMatch": true,
      "NotificationWebhook": "https://your-wms-webhook.com/purchase-order-update"
    },
    "InventorySettings": {
      "RealTimeSync": true,
      "BatchSyncSize": 100,
      "SyncInterval": "00:05:00",
      "NotificationWebhook": "https://your-wms-webhook.com/inventory-update"
    }
  },
  "LogicAppsEndpoints": {
    "SalesOrderWorkflow": "https://prod-xx.eastus.logic.azure.com/workflows/xxx/triggers/manual/paths/invoke",
    "PurchaseOrderWorkflow": "https://prod-xx.eastus.logic.azure.com/workflows/xxx/triggers/manual/paths/invoke",
    "InventoryWorkflow": "https://prod-xx.eastus.logic.azure.com/workflows/xxx/triggers/manual/paths/invoke"
  }
}

SAP Connection Service

Services/SAPConnectionService.cs

using SAP.Middleware.Connector;
using Microsoft.Extensions.Options;

namespace SAPOrderManagementAPI.Services
{
    public class SAPConnectionService : IDisposable
    {
        private readonly SAPConnectionConfig _config;
        private readonly ILogger<SAPConnectionService> _logger;
        private RfcDestination _destination;

        public SAPConnectionService(IOptions<SAPConnectionConfig> config, ILogger<SAPConnectionService> logger)
        {
            _config = config.Value;
            _logger = logger;
            InitializeConnection();
        }

        private void InitializeConnection()
        {
            try
            {
                var configParams = new RfcConfigParameters
                {
                    [RfcConfigParameters.AppServerHost] = _config.AppServerHost,
                    [RfcConfigParameters.SystemNumber] = _config.SystemNumber,
                    [RfcConfigParameters.Client] = _config.Client,
                    [RfcConfigParameters.User] = _config.User,
                    [RfcConfigParameters.Password] = _config.Password,
                    [RfcConfigParameters.Language] = _config.Language,
                    [RfcConfigParameters.PoolSize] = _config.PoolSize.ToString(),
                    [RfcConfigParameters.MaxPoolSize] = _config.MaxPoolSize.ToString(),
                    [RfcConfigParameters.IdleTimeout] = _config.IdleTimeout.ToString()
                };

                _destination = RfcDestinationManager.GetDestination(configParams);
                _logger.LogInformation("SAP connection initialized successfully");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to initialize SAP connection");
                throw;
            }
        }

        public RfcDestination GetDestination() => _destination;

        public void Dispose()
        {
            _destination?.Dispose();
        }
    }

    public class SAPConnectionConfig
    {
        public string AppServerHost { get; set; }
        public string SystemNumber { get; set; }
        public string Client { get; set; }
        public string User { get; set; }
        public string Password { get; set; }
        public string Language { get; set; }
        public int PoolSize { get; set; } = 5;
        public int MaxPoolSize { get; set; } = 10;
        public int IdleTimeout { get; set; } = 600;
    }
}

Sales Order Service

Services/SalesOrderService.cs

using SAP.Middleware.Connector;
using Microsoft.Extensions.Options;

namespace SAPOrderManagementAPI.Services
{
    public interface ISalesOrderService
    {
        Task<SalesOrderResponse> GetSalesOrderAsync(string salesOrderNumber);
        Task<List<SalesOrderSummary>> GetSalesOrdersByStatusAsync(string status);
        Task<SalesOrderCreateResponse> CreateSalesOrderAsync(SalesOrderCreateRequest request);
        Task<bool> UpdateSalesOrderStatusAsync(string salesOrderNumber, string status);
        Task<DeliveryResponse> CreateDeliveryAsync(string salesOrderNumber);
    }

    public class SalesOrderService : ISalesOrderService
    {
        private readonly SAPConnectionService _sapConnection;
        private readonly ILogger<SalesOrderService> _logger;
        private readonly IntegrationSettings _settings;

        public SalesOrderService(
            SAPConnectionService sapConnection, 
            ILogger<SalesOrderService> logger,
            IOptions<IntegrationSettings> settings)
        {
            _sapConnection = sapConnection;
            _logger = logger;
            _settings = settings.Value;
        }

        public async Task<SalesOrderResponse> GetSalesOrderAsync(string salesOrderNumber)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_SALESORDER_GETDETAIL");
                
                function.SetValue("SALESDOCUMENT", salesOrderNumber);
                function.Invoke(destination);

                var orderHeader = function.GetStructure("ORDER_HEADER_IN");
                var orderItems = function.GetTable("ORDER_ITEMS_IN");
                var orderPartners = function.GetTable("ORDER_PARTNERS");
                var returnMessages = function.GetTable("RETURN");

                // Check for errors
                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error retrieving sales order", returnMessages);
                }

                var response = new SalesOrderResponse
                {
                    SalesOrderNumber = salesOrderNumber,
                    CustomerNumber = orderHeader.GetString("SOLD_TO_PARTY"),
                    OrderDate = orderHeader.GetString("DOC_DATE"),
                    TotalAmount = orderHeader.GetDecimal("NET_VALUE"),
                    Currency = orderHeader.GetString("CURRENCY"),
                    OrderStatus = orderHeader.GetString("OVERALL_STATUS"),
                    Items = new List<SalesOrderItem>()
                };

                // Process order items
                for (int i = 0; i < orderItems.RowCount; i++)
                {
                    orderItems.CurrentIndex = i;
                    response.Items.Add(new SalesOrderItem
                    {
                        ItemNumber = orderItems.GetString("ITM_NUMBER"),
                        MaterialNumber = orderItems.GetString("MATERIAL"),
                        MaterialDescription = orderItems.GetString("SHORT_TEXT"),
                        OrderQuantity = orderItems.GetDecimal("REQ_QTY"),
                        Unit = orderItems.GetString("SALES_UNIT"),
                        NetPrice = orderItems.GetDecimal("NET_VALUE"),
                        Plant = orderItems.GetString("PLANT"),
                        StorageLocation = orderItems.GetString("STGE_LOC")
                    });
                }

                return response;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving sales order {SalesOrderNumber}", salesOrderNumber);
                throw;
            }
        }

        public async Task<SalesOrderCreateResponse> CreateSalesOrderAsync(SalesOrderCreateRequest request)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_SALESORDER_CREATEFROMDAT2");

                // Set header data
                var orderHeaderIn = function.GetStructure("ORDER_HEADER_IN");
                orderHeaderIn.SetValue("DOC_TYPE", request.DocumentType ?? "OR");
                orderHeaderIn.SetValue("SALES_ORG", request.SalesOrganization);
                orderHeaderIn.SetValue("DISTR_CHAN", request.DistributionChannel);
                orderHeaderIn.SetValue("DIVISION", request.Division);
                orderHeaderIn.SetValue("REQ_DATE_H", request.RequestedDate?.ToString("yyyyMMdd"));

                // Set partner data
                var orderPartners = function.GetTable("ORDER_PARTNERS");
                orderPartners.Append();
                orderPartners.SetValue("PARTN_ROLE", "AG");
                orderPartners.SetValue("PARTN_NUMB", request.SoldToParty);

                // Set item data
                var orderItemsIn = function.GetTable("ORDER_ITEMS_IN");
                var orderSchedulesIn = function.GetTable("ORDER_SCHEDULES_IN");

                foreach (var item in request.Items)
                {
                    orderItemsIn.Append();
                    orderItemsIn.SetValue("ITM_NUMBER", item.ItemNumber.ToString("D6"));
                    orderItemsIn.SetValue("MATERIAL", item.MaterialNumber);
                    orderItemsIn.SetValue("PLANT", item.Plant);
                    orderItemsIn.SetValue("TARGET_QTY", item.Quantity);
                    orderItemsIn.SetValue("SALES_UNIT", item.Unit);

                    orderSchedulesIn.Append();
                    orderSchedulesIn.SetValue("ITM_NUMBER", item.ItemNumber.ToString("D6"));
                    orderSchedulesIn.SetValue("REQ_QTY", item.Quantity);
                    orderSchedulesIn.SetValue("REQ_DATE", item.RequestedDate?.ToString("yyyyMMdd"));
                }

                function.Invoke(destination);

                var returnMessages = function.GetTable("RETURN");
                var salesDocument = function.GetString("SALESDOCUMENT");

                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error creating sales order", returnMessages);
                }

                // Commit the transaction
                var commitFunction = destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT");
                commitFunction.SetValue("WAIT", "X");
                commitFunction.Invoke(destination);

                return new SalesOrderCreateResponse
                {
                    SalesOrderNumber = salesDocument,
                    Success = true,
                    Messages = GetMessages(returnMessages)
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating sales order");
                throw;
            }
        }

        public async Task<DeliveryResponse> CreateDeliveryAsync(string salesOrderNumber)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_OUTB_DELIVERY_CREATE_SLS");

                // Set sales order reference
                var salesOrderRefTable = function.GetTable("SALES_ORDER_REFERENCE");
                salesOrderRefTable.Append();
                salesOrderRefTable.SetValue("REF_DOC", salesOrderNumber);

                // Set shipping point
                function.SetValue("SHIP_POINT", _settings.SalesOrderSettings.DefaultShippingPoint);

                function.Invoke(destination);

                var deliveryNumber = function.GetString("DELIVERY");
                var returnMessages = function.GetTable("RETURN");

                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error creating delivery", returnMessages);
                }

                // Commit transaction
                var commitFunction = destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT");
                commitFunction.SetValue("WAIT", "X");
                commitFunction.Invoke(destination);

                return new DeliveryResponse
                {
                    DeliveryNumber = deliveryNumber,
                    SalesOrderNumber = salesOrderNumber,
                    Success = true,
                    Messages = GetMessages(returnMessages)
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating delivery for sales order {SalesOrderNumber}", salesOrderNumber);
                throw;
            }
        }

        private bool HasErrors(IRfcTable returnTable)
        {
            for (int i = 0; i < returnTable.RowCount; i++)
            {
                returnTable.CurrentIndex = i;
                if (returnTable.GetString("TYPE") == "E")
                    return true;
            }
            return false;
        }

        private List<string> GetMessages(IRfcTable returnTable)
        {
            var messages = new List<string>();
            for (int i = 0; i < returnTable.RowCount; i++)
            {
                returnTable.CurrentIndex = i;
                messages.Add(returnTable.GetString("MESSAGE"));
            }
            return messages;
        }

        // Additional methods for status updates and queries...
    }
}

Purchase Order Service

Services/PurchaseOrderService.cs

namespace SAPOrderManagementAPI.Services
{
    public interface IPurchaseOrderService
    {
        Task<PurchaseOrderResponse> GetPurchaseOrderAsync(string purchaseOrderNumber);
        Task<List<PurchaseOrderSummary>> GetPurchaseOrdersByVendorAsync(string vendorNumber);
        Task<PurchaseOrderCreateResponse> CreatePurchaseOrderAsync(PurchaseOrderCreateRequest request);
        Task<GoodsReceiptResponse> CreateGoodsReceiptAsync(GoodsReceiptRequest request);
        Task<bool> UpdatePurchaseOrderStatusAsync(string purchaseOrderNumber, string status);
    }

    public class PurchaseOrderService : IPurchaseOrderService
    {
        private readonly SAPConnectionService _sapConnection;
        private readonly ILogger<PurchaseOrderService> _logger;
        private readonly IntegrationSettings _settings;

        public PurchaseOrderService(
            SAPConnectionService sapConnection,
            ILogger<PurchaseOrderService> logger,
            IOptions<IntegrationSettings> settings)
        {
            _sapConnection = sapConnection;
            _logger = logger;
            _settings = settings.Value;
        }

        public async Task<PurchaseOrderResponse> GetPurchaseOrderAsync(string purchaseOrderNumber)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_PO_GETDETAIL");

                function.SetValue("PURCHASEORDER", purchaseOrderNumber);
                function.SetValue("HISTORY", "X"); // Include history
                function.SetValue("ITEMS", "X");   // Include items

                function.Invoke(destination);

                var poHeader = function.GetStructure("PO_HEADER");
                var poItems = function.GetTable("PO_ITEMS");
                var poHistory = function.GetTable("PO_ITEM_HISTORY_TOTALS");
                var returnMessages = function.GetTable("RETURN");

                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error retrieving purchase order", returnMessages);
                }

                var response = new PurchaseOrderResponse
                {
                    PurchaseOrderNumber = purchaseOrderNumber,
                    VendorNumber = poHeader.GetString("VENDOR"),
                    CompanyCode = poHeader.GetString("COMP_CODE"),
                    PurchasingOrganization = poHeader.GetString("PURCH_ORG"),
                    PurchasingGroup = poHeader.GetString("PUR_GROUP"),
                    DocumentDate = poHeader.GetString("DOC_DATE"),
                    Currency = poHeader.GetString("CURRENCY"),
                    Items = new List<PurchaseOrderItem>()
                };

                // Process PO items
                for (int i = 0; i < poItems.RowCount; i++)
                {
                    poItems.CurrentIndex = i;
                    response.Items.Add(new PurchaseOrderItem
                    {
                        ItemNumber = poItems.GetString("PO_ITEM"),
                        MaterialNumber = poItems.GetString("MATERIAL"),
                        ShortText = poItems.GetString("SHORT_TEXT"),
                        Plant = poItems.GetString("PLANT"),
                        StorageLocation = poItems.GetString("STGE_LOC"),
                        OrderQuantity = poItems.GetDecimal("QUANTITY"),
                        OrderUnit = poItems.GetString("PO_UNIT"),
                        NetPrice = poItems.GetDecimal("NET_PRICE"),
                        DeliveryDate = poItems.GetString("DELIV_DATE"),
                        ItemCategory = poItems.GetString("ITEM_CAT"),
                        AccountAssignment = poItems.GetString("ACCTASSCAT")
                    });
                }

                return response;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving purchase order {PurchaseOrderNumber}", purchaseOrderNumber);
                throw;
            }
        }

        public async Task<PurchaseOrderCreateResponse> CreatePurchaseOrderAsync(PurchaseOrderCreateRequest request)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_PO_CREATE1");

                // Set header data
                var poHeader = function.GetStructure("POHEADER");
                poHeader.SetValue("COMP_CODE", request.CompanyCode);
                poHeader.SetValue("DOC_TYPE", request.DocumentType ?? "NB");
                poHeader.SetValue("CREAT_DATE", DateTime.Now.ToString("yyyyMMdd"));
                poHeader.SetValue("VENDOR", request.VendorNumber);
                poHeader.SetValue("PURCH_ORG", request.PurchasingOrganization);
                poHeader.SetValue("PUR_GROUP", request.PurchasingGroup);
                poHeader.SetValue("CURRENCY", request.Currency ?? "USD");

                // Set header additional data
                var poHeaderX = function.GetStructure("POHEADERX");
                poHeaderX.SetValue("COMP_CODE", "X");
                poHeaderX.SetValue("DOC_TYPE", "X");
                poHeaderX.SetValue("CREAT_DATE", "X");
                poHeaderX.SetValue("VENDOR", "X");
                poHeaderX.SetValue("PURCH_ORG", "X");
                poHeaderX.SetValue("PUR_GROUP", "X");
                poHeaderX.SetValue("CURRENCY", "X");

                // Set item data
                var poItems = function.GetTable("POITEM");
                var poItemsX = function.GetTable("POITEMX");

                foreach (var item in request.Items)
                {
                    // Item data
                    poItems.Append();
                    poItems.SetValue("PO_ITEM", item.ItemNumber.ToString("D5"));
                    poItems.SetValue("MATERIAL", item.MaterialNumber);
                    poItems.SetValue("PLANT", item.Plant ?? _settings.PurchaseOrderSettings.DefaultPlant);
                    poItems.SetValue("STGE_LOC", item.StorageLocation);
                    poItems.SetValue("QUANTITY", item.Quantity);
                    poItems.SetValue("PO_UNIT", item.Unit);
                    poItems.SetValue("NET_PRICE", item.NetPrice);
                    poItems.SetValue("DELIV_DATE", item.DeliveryDate?.ToString("yyyyMMdd"));
                    poItems.SetValue("ITEM_CAT", item.ItemCategory ?? "0");

                    // Item flags
                    poItemsX.Append();
                    poItemsX.SetValue("PO_ITEM", item.ItemNumber.ToString("D5"));
                    poItemsX.SetValue("MATERIAL", "X");
                    poItemsX.SetValue("PLANT", "X");
                    poItemsX.SetValue("STGE_LOC", "X");
                    poItemsX.SetValue("QUANTITY", "X");
                    poItemsX.SetValue("PO_UNIT", "X");
                    poItemsX.SetValue("NET_PRICE", "X");
                    poItemsX.SetValue("DELIV_DATE", "X");
                    poItemsX.SetValue("ITEM_CAT", "X");
                }

                function.Invoke(destination);

                var purchaseOrder = function.GetString("PURCHASEORDER");
                var returnMessages = function.GetTable("RETURN");

                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error creating purchase order", returnMessages);
                }

                // Commit transaction
                var commitFunction = destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT");
                commitFunction.SetValue("WAIT", "X");
                commitFunction.Invoke(destination);

                return new PurchaseOrderCreateResponse
                {
                    PurchaseOrderNumber = purchaseOrder,
                    Success = true,
                    Messages = GetMessages(returnMessages)
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating purchase order");
                throw;
            }
        }

        public async Task<GoodsReceiptResponse> CreateGoodsReceiptAsync(GoodsReceiptRequest request)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_GOODSMVT_CREATE");

                // Set header data
                var goodsMvtHeader = function.GetStructure("GOODSMVT_HEADER");
                goodsMvtHeader.SetValue("PSTNG_DATE", request.PostingDate?.ToString("yyyyMMdd") ?? DateTime.Now.ToString("yyyyMMdd"));
                goodsMvtHeader.SetValue("DOC_DATE", DateTime.Now.ToString("yyyyMMdd"));
                goodsMvtHeader.SetValue("REF_DOC_NO", request.ReferenceDocument);

                // Set item data
                var goodsMvtItems = function.GetTable("GOODSMVT_ITEM");

                foreach (var item in request.Items)
                {
                    goodsMvtItems.Append();
                    goodsMvtItems.SetValue("MATERIAL", item.MaterialNumber);
                    goodsMvtItems.SetValue("PLANT", item.Plant);
                    goodsMvtItems.SetValue("STGE_LOC", item.StorageLocation);
                    goodsMvtItems.SetValue("MOVE_TYPE", "101"); // Goods receipt for purchase order
                    goodsMvtItems.SetValue("ENTRY_QNT", item.Quantity);
                    goodsMvtItems.SetValue("ENTRY_UOM", item.Unit);
                    goodsMvtItems.SetValue("PO_NUMBER", item.PurchaseOrderNumber);
                    goodsMvtItems.SetValue("PO_ITEM", item.PurchaseOrderItem);

                    if (!string.IsNullOrEmpty(item.BatchNumber))
                    {
                        goodsMvtItems.SetValue("BATCH", item.BatchNumber);
                    }
                }

                function.Invoke(destination);

                var materialDocument = function.GetString("MATERIALDOCUMENT");
                var documentYear = function.GetString("MATDOCUMENTYEAR");
                var returnMessages = function.GetTable("RETURN");

                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error creating goods receipt", returnMessages);
                }

                // Commit transaction
                var commitFunction = destination.Repository.CreateFunction("BAPI_TRANSACTION_COMMIT");
                commitFunction.SetValue("WAIT", "X");
                commitFunction.Invoke(destination);

                return new GoodsReceiptResponse
                {
                    MaterialDocument = materialDocument,
                    DocumentYear = documentYear,
                    Success = true,
                    Messages = GetMessages(returnMessages)
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error creating goods receipt");
                throw;
            }
        }

        private bool HasErrors(IRfcTable returnTable)
        {
            for (int i = 0; i < returnTable.RowCount; i++)
            {
                returnTable.CurrentIndex = i;
                if (returnTable.GetString("TYPE") == "E")
                    return true;
            }
            return false;
        }

        private List<string> GetMessages(IRfcTable returnTable)
        {
            var messages = new List<string>();
            for (int i = 0; i < returnTable.RowCount; i++)
            {
                returnTable.CurrentIndex = i;
                messages.Add(returnTable.GetString("MESSAGE"));
            }
            return messages;
        }
    }
}

Inventory Service

Services/InventoryService.cs

namespace SAPOrderManagementAPI.Services
{
    public interface IInventoryService
    {
        Task<List<MaterialStock>> GetMaterialStockAsync(string materialNumber, string plant = null);
        Task<List<BatchStock>> GetBatchStockAsync(string materialNumber, string plant, string storageLocation);
        Task<MaterialMovementResponse> PostInventoryMovementAsync(InventoryMovementRequest request);
        Task<List<MaterialMovement>> GetMaterialMovementsAsync(string materialNumber, DateTime fromDate, DateTime toDate);
        Task<InventorySyncResponse> SyncInventoryWithWMSAsync(InventorySyncRequest request);
    }

    public class InventoryService : IInventoryService
    {
        private readonly SAPConnectionService _sapConnection;
        private readonly ILogger<InventoryService> _logger;
        private readonly IntegrationSettings _settings;

        public InventoryService(
            SAPConnectionService sapConnection,
            ILogger<InventoryService> logger,
            IOptions<IntegrationSettings> settings)
        {
            _sapConnection = sapConnection;
            _logger = logger;
            _settings = settings.Value;
        }

        public async Task<List<MaterialStock>> GetMaterialStockAsync(string materialNumber, string plant = null)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.CreateFunction("BAPI_MATERIAL_STOCK_REQ_LIST");

                function.SetValue("MATERIAL", materialNumber);
                if (!string.IsNullOrEmpty(plant))
                {
                    function.SetValue("PLANT", plant);
                }

                function.Invoke(destination);

                var stockTable = function.GetTable("STOCK_OVERVIEW");
                var returnMessages = function.GetTable("RETURN");

                if (HasErrors(returnMessages))
                {
                    throw new SAPException("Error retrieving material stock", returnMessages);
                }

                var stockList = new List<MaterialStock>();

                for (int i = 0; i < stockTable.RowCount; i++)
                {
                    stockTable.CurrentIndex = i;
                    stockList.Add(new MaterialStock
                    {
                        MaterialNumber = stockTable.GetString("MATERIAL"),
                        Plant = stockTable.GetString("PLANT"),
                        StorageLocation = stockTable.GetString("STGE_LOC"),
                        UnrestrictedStock = stockTable.GetDecimal("UNRESTRICTED"),
                        QualityInspectionStock = stockTable.GetDecimal("QUALITY_INS"),
                        RestrictedStock = stockTable.GetDecimal("RESTRICTED"),
                        BlockedStock = stockTable.GetDecimal("BLOCKED"),
                        BaseUnit = stockTable.GetString("BASE_UOM"),
                        LastUpdated = DateTime.Now
                    });
                }

                return stockList;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving material stock for {MaterialNumber}", materialNumber);
                throw;
            }
        }

        public async Task<List<BatchStock>> GetBatchStockAsync(string materialNumber, string plant, string storageLocation)
        {
            try
            {
                var destination = _sapConnection.GetDestination();
                var function = destination.Repository.Create

More from FlexiDigit Blogs