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