A Wrapper of DIAPI with more extras such as UDF field mapping, easy access to UDO, SQL query, and more.
DIAPI must be installed on machine (tested on DIAPI 9 & 10 SAP HANA & SQL Server version) & Add COM reference into your C# project.
var provider = new SboProvider(new SboConfiguration()
{
ServerType = SboServerType.SapMsSql2019,
Server = "SERVER",
CompanyDatabase = "ACME_DB",
LicenseServer = "SERVER:30000",
User = "johndoe",
Password = "password"
});
var connected = provider.Connect();
// get business partner master data
var businessPartner = provider.GetBusinessObject<BusinessPartners>
(BoObjectTypes.oBusinessPartners);
// get goods receipt
var goodsReceipt = provider.GetBusinessObject<Documents>
(BoObjectTypes.oInventoryGenEntry);
Assign(IUdf source);
This method is applied to UserFields
data type.
Example of case :
Marketing Document in SAP B1 is any document relating to the planning, pricing, coordination, promotion, purchase, sale, or distribution of goods or services. So the UDF will be placed on related transaction interfaces e.g. SalesOrder, GoodsReceipt, Inventory Transfer, Goods Issue or Receipt, etc.
Now, we will manage those fields from Marketing Documents UDF into POCO class like this:
public class MarketingDocumentUdf : IUdf
{
[SboField("U_AnotherDate")]
public DateTime? AnotherDate { get; set; }
[SboField("U_AnotherNumber")]
public int? AnotherNumber { get; set; }
[SboField("U_AnotherText")]
public string? AnotherText { get; set; }
}
public class MarketingDocumentRowUdf : IUdf
{
[SboField("U_TestRow1")]
public string? TestRow { get; set; }
}
And below is how we implement those fields into the transaction
var docUdf = new MarketingDocumentUdf()
{
AnotherDate = DateTime.Today.AddDays(1),
AnotherNumber = 1000,
AnotherText = "test",
};
var goodsIssue = provider.GetBusinessObject<Documents>
(BoObjectTypes.oInventoryGenExit);
goodsIssue.UserFields.Assign(docUdf);
var goodsReceipt = provider.GetBusinessObject<Documents>
(BoObjectTypes.oInventoryGenEntry);
goodsReceipt.UserFields.Assign(docUdf);
goodsReceipt.Lines.UserFields.Assign(new MarketingDocumentRowUdf()
{
TestRow = "value"
});
As we can see, the variabledocUdf
is reusable and we can use into another document transaction.
Now, let's compare with native (origin) from DIAPI SDK:
var goodsIssue = (Documents)
company.GetBusinessObject(BoObjectTypes.oInventoryGenEntry);
goodsIssue.UserFields.Fields.Item("U_AnotherDate").Value = DateTime.Today.AddDays(1);
goodsIssue.UserFields.Fields.Item("U_AnotherNumber").Value = 1000;
goodsIssue.UserFields.Fields.Item("U_AnotherText").Value = "test";
var goodsReceipt = (Documents)
company.GetBusinessObject(BoObjectTypes.oInventoryGenEntry);
goodsReceipt.UserFields.Fields.Item("U_AnotherDate").Value = DateTime.Today.AddDays(1);
goodsReceipt.UserFields.Fields.Item("U_AnotherNumber").Value = 1000;
goodsReceipt.UserFields.Fields.Item("U_AnotherText").Value = "test";
goodsReceipt.Lines.UserFields.Fields.Item("U_TestRow1").Value = "row value";
// we need more effort to save GoodsIssue & Receipt documents at same time
// it's just about setting up the value of fields
You must set all property data type (has getter & setter) as
Nullable
.In .NET Framework :
public Nullable<int> Value { get; set; }
In .NET Core, simply put
?
sign after data type.
NULL
value of the property will be ignored, so we can insert/update data partially.
var udo = provider.GetUDO("REGISTERED_OBJECT_NAME");
UdoProperties properties = udo.Properties;
// here's the UdoProperties structure
class UdoProperties
{
public string Code { get; set; }
public string Name { get; set; }
public string TableName { get; set; }
public string LogTable { get; set; }
// UDO type, 1 = Master Data otherwise Document
public string Type { get; set; }
}
List<UdoChildTable> childs = udo.ChildTables;
-
var table1 = childs.Item(0); or var table1 = childs.Item("NAME_OF_CHILD_TABLE");
-
List<T> GetValues<T>() where T : GeneralDataRowField var values = table1.GetValues<YourCustomType>();
-
//add to existing GeneralDataCollection void AddList<T>(List<T> values) where T : GeneralDataRowField void Add<T>(T value) where T : GeneralDataRowField //update existing GeneralData in GeneralDataCollection UpdateList<T>(List<T> values) where T : GeneralDataRowField void Update<T>(T value) where T : GeneralDataRowField //remove existing GeneralData in GeneralDataCollection void RemoveAtLine(int lineId) void RemoveAll()
// it will returning DocEntry if UDO type is Document otherwise Code if Master Data
object Insert();
public void Update();
// cancel & close only available if UDO type is Document
public void Cancel();
public void Close();
public void Delete();
Let's try it on available User Table Item Sale
& has been registered as UDO named ITM_SALE
The first step, define those fields into POCO class:
public class ItemSale : GeneralDataField
{
// this is system field & defined as key
[SboField("Code"), SboPrimaryKey]
public string? Code { get; set; }
[SboField("U_ItemCode")]
public string? ItemCode { get; set; }
[SboField("U_Notes")]
public string? Notes { get; set; }
}
public class ItemSaleDiscount : GeneralDataRowField
{
[SboField("U_FromDate")]
public DateTime? StartDate { get; set; }
[SboField("U_Days")]
public int? AvailableDays { get; set; }
[SboField("U_Disc")]
public double? Discount { get; set; }
public ItemSaleDiscount(DateTime? startDate,
int? availableDays,
double? discount,
int? lineId = null)
{
StartDate = startDate;
AvailableDays = availableDays;
Discount = discount;
LineId = lineId;
}
}
object Insert();
It will returning the new inserted DocEntry
if UDO type is Document, otherwise the new inserted Code
if UDO type is Master Data.
// header
udo.Values = new ItemSale()
{
Code = "MyCode",
ItemCode = "123",
Notes = "test"
};
var discTable = udo.ChildTables.Item("ITM_SALE_DISC");
// add multiple values
discTable.AddList(new List<ItemSaleDiscount>()
{
new ItemSaleDiscount(DateTime.Now, 2, 35),
new ItemSaleDiscount(DateTime.Now.AddDays(1), 2, 30),
new ItemSaleDiscount(DateTime.Now.AddDays(1), 2, 20),
});
// add single value
discTable.Add(new ItemSaleDiscount(DateTime.Now, 2, 35));
udo.Insert();
udo.GetByParams<ItemSale>(new ItemSale()
{
Code = "MyCode"
});
var discTable = udo.ChildTables.Item("ITM_SALE_DISC");
// update multiple values in line 1 & 3
discTable.UpdateList(new List<ItemSaleDiscount>()
{
new ItemSaleDiscount(null, null, 25, 1),
new ItemSaleDiscount(null, null, 25, 3)
});
// update value in line 1
discTable.Update(new ItemSaleDiscount(null, null, 25, 1));
udo.Update();
udo.GetByParams<ItemSale>(new ItemSale()
{
Code = "MyCode"
}, SboRecordsetFillParam.FillIntoValues);
Examples :
With manual field mapping (using attribute) :
public class BusinessPartner
{
[SboField("CardCode")]
public string Code { get; set; }
[SboField("CardName")]
public string Name { get; set; }
}
var sql = "SELECT CardCode, CardName from OCRD";
var result = provider.SqlQuery<BusinessPartner>(sql, true);
// it will return List<BusinessPartner>
or, using automatic field mapping:
public class BusinessPartner
{
public string Code { get; set; }
public string Name { get; set; }
}
var sql = "SELECT CardCode AS Code, CardName from OCRD AS Name";
var result = provider.SqlQuery<BusinessPartner>(sql);