Skip to main content

SAP Business One

This guide walks you through the steps required to connect Docflo.ai to your SAP Business One system, enabling seamless document integration and automated data exchange.

πŸ“‹ Prerequisites:
  • SAP Business One system with DI API access
  • Visual Studio or development environment for .NET
  • Docflo.ai account with API access
  • Administrative access to Business One server
  • SSL certificate management capabilities

πŸš€ Integration Steps​

Follow these steps to establish a secure connection between Docflo.ai and your SAP Business One system:

Step 1: Import SSL Certificate Chain​

  1. Download our SSL certificate chain (link)
  2. Import the certificate to the Windows Certificate Store on your Business One server
  3. Add the certificate to Trusted Root Certification Authorities
  4. Ensure the certificate is properly validated for HTTPS connections

Step 2: Configure Business One Service Layer (Optional)​

If using Service Layer for REST API access:

  1. Enable Service Layer in your Business One installation
  2. Configure HTTPS with proper SSL certificates
  3. Set up authentication (Basic or Windows Authentication)
  4. Test the Service Layer endpoint connectivity

Step 3: Generate API Credentials​

  1. Go to the "Integrations" section in your Docflo.ai platform
  2. Create an API key for SAP Business One integration
  3. Copy the API key and store it securely
  4. Copy the tenant ID as well - you'll need both for the integration

Step 4: Create Business One Integration Service​

Create a Windows Service or scheduled application using the DI API. Here's a sample C# implementation:

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SAPbobsCOM;

namespace DocfloBusinessOneIntegration
{
public class DocfloIntegrationService
{
private Company _company;
private readonly string _docfloApiUrl;
private readonly string _tenantId;
private readonly string _apiKey;
private readonly HttpClient _httpClient;

public DocfloIntegrationService(string docfloApiUrl, string tenantId, string apiKey)
{
_docfloApiUrl = docfloApiUrl;
_tenantId = tenantId;
_apiKey = apiKey;
_httpClient = new HttpClient();

// Set default headers
_httpClient.DefaultRequestHeaders.Add("x-tenant-id", _tenantId);
_httpClient.DefaultRequestHeaders.Add("apiKey", _apiKey);
}

public bool ConnectToBusinessOne(string server, string companyDB, string username, string password)
{
try
{
_company = new Company();
_company.Server = server;
_company.CompanyDB = companyDB;
_company.UserName = username;
_company.Password = password;
_company.DbServerType = BoDataServerTypes.dst_MSSQL2019; // Adjust as needed

int result = _company.Connect();
if (result != 0)
{
string error = _company.GetLastErrorDescription();
throw new Exception($"Failed to connect to Business One: {error}");
}

return true;
}
catch (Exception ex)
{
Console.WriteLine($"Connection error: {ex.Message}");
return false;
}
}

public async Task<List<DocfloDocument>> GetDocfloDocuments(string documentType, bool includeResults = true)
{
try
{
string url = $"{_docfloApiUrl}/docs/v1/document?type={documentType}&includeResults={includeResults}";

HttpResponseMessage response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();

string jsonContent = await response.Content.ReadAsStringAsync();
var docfloResponse = JsonConvert.DeserializeObject<DocfloResponse>(jsonContent);

return docfloResponse.Data;
}
catch (Exception ex)
{
Console.WriteLine($"Error fetching Docflo documents: {ex.Message}");
return new List<DocfloDocument>();
}
}

public bool CreateInvoice(DocfloDocument document)
{
try
{
Documents invoice = (Documents)_company.GetBusinessObject(BoObjectTypes.oInvoices);

// Extract data from Docflo document results
var extractedData = ExtractInvoiceData(document);

// Set customer information
invoice.CardCode = extractedData.CustomerCode;
invoice.CardName = extractedData.CustomerName;

// Set invoice dates
invoice.DocDate = extractedData.InvoiceDate ?? DateTime.Now;
invoice.DocDueDate = extractedData.DueDate ?? DateTime.Now.AddDays(30);

// Set invoice number if available
if (!string.IsNullOrEmpty(extractedData.InvoiceNumber))
{
invoice.NumAtCard = extractedData.InvoiceNumber;
}

// Set tax information
if (!string.IsNullOrEmpty(extractedData.TaxCode))
{
invoice.DocTaxID = extractedData.TaxCode;
}

// Add line items
bool firstLine = true;
foreach (var item in extractedData.LineItems)
{
if (!firstLine)
{
invoice.Lines.Add();
}

invoice.Lines.ItemDescription = item.Description;
invoice.Lines.Quantity = item.Quantity;
invoice.Lines.Price = item.UnitPrice;
invoice.Lines.LineTotal = item.LineTotal;

// Set item code if available
if (!string.IsNullOrEmpty(item.ItemCode))
{
invoice.Lines.ItemCode = item.ItemCode;
}

// Set tax code if available
if (!string.IsNullOrEmpty(item.TaxCode))
{
invoice.Lines.TaxCode = item.TaxCode;
}

firstLine = false;
}

// Set totals
if (extractedData.SubTotal > 0)
{
invoice.DocTotal = extractedData.Total;
}

// Add custom field for Docflo document ID
invoice.UserFields.Fields.Item("U_DocfloID").Value = document.Id;

int result = invoice.Add();
if (result != 0)
{
string error = _company.GetLastErrorDescription();
Console.WriteLine($"Error creating Invoice: {error}");
return false;
}

Console.WriteLine($"Invoice created successfully for document {document.Id}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Exception creating Invoice: {ex.Message}");
return false;
}
}

private InvoiceData ExtractInvoiceData(DocfloDocument document)
{
var data = new InvoiceData();
data.LineItems = new List<InvoiceLineItem>();

// Extract data from Docflo results
if (document.DocfloResults?.ModelFields?.Items?.Value != null)
{
foreach (var item in document.DocfloResults.ModelFields.Items.Value)
{
var description = item.ValueObject?.Description;
if (description != null)
{
switch (description.Type?.ToLower())
{
case "customer_code":
case "vendor_code":
data.CustomerCode = description.ValueString;
break;
case "customer_name":
case "vendor_name":
case "company_name":
data.CustomerName = description.ValueString;
break;
case "invoice_number":
case "document_number":
data.InvoiceNumber = description.ValueString;
break;
case "invoice_date":
case "document_date":
if (DateTime.TryParse(description.ValueString, out DateTime invoiceDate))
{
data.InvoiceDate = invoiceDate;
}
break;
case "due_date":
case "payment_date":
if (DateTime.TryParse(description.ValueString, out DateTime dueDate))
{
data.DueDate = dueDate;
}
break;
case "tax_code":
case "vat_code":
data.TaxCode = description.ValueString;
break;
case "subtotal":
case "net_amount":
if (double.TryParse(description.ValueString, out double subtotal))
{
data.SubTotal = subtotal;
}
break;
case "total":
case "total_amount":
case "gross_amount":
if (double.TryParse(description.ValueString, out double total))
{
data.Total = total;
}
break;
case "item_description":
case "product_description":
// Create new line item
var lineItem = new InvoiceLineItem
{
Description = description.ValueString,
Quantity = 1, // Default quantity
UnitPrice = 0, // Default price
LineTotal = 0 // Default total
};
data.LineItems.Add(lineItem);
break;
case "quantity":
// Update last line item quantity
if (data.LineItems.Count > 0 && double.TryParse(description.ValueString, out double quantity))
{
data.LineItems[data.LineItems.Count - 1].Quantity = quantity;
}
break;
case "unit_price":
case "price":
// Update last line item price
if (data.LineItems.Count > 0 && double.TryParse(description.ValueString, out double unitPrice))
{
data.LineItems[data.LineItems.Count - 1].UnitPrice = unitPrice;
}
break;
case "line_total":
case "amount":
// Update last line item total
if (data.LineItems.Count > 0 && double.TryParse(description.ValueString, out double lineTotal))
{
data.LineItems[data.LineItems.Count - 1].LineTotal = lineTotal;
}
break;
}
}
}
}

// Generate customer code if not provided
if (string.IsNullOrEmpty(data.CustomerCode) && !string.IsNullOrEmpty(data.CustomerName))
{
data.CustomerCode = GenerateCardCode(data.CustomerName);
}

return data;
}

private string GenerateCardCode(string name)
{
if (string.IsNullOrEmpty(name))
return "CUST" + DateTime.Now.Ticks.ToString().Substring(0, 6);

// Generate card code from name
string cardCode = name.Replace(" ", "").ToUpper();
if (cardCode.Length > 15)
cardCode = cardCode.Substring(0, 15);

return cardCode;
}

public void Disconnect()
{
if (_company != null && _company.Connected)
{
_company.Disconnect();
}
_httpClient?.Dispose();
}
}

// Data models
public class DocfloResponse
{
[JsonProperty("data")]
public List<DocfloDocument> Data { get; set; }
}

public class DocfloDocument
{
[JsonProperty("_id")]
public string Id { get; set; }

[JsonProperty("sourceId")]
public string SourceId { get; set; }

[JsonProperty("sourceDesc")]
public string SourceDesc { get; set; }

[JsonProperty("status")]
public string Status { get; set; }

[JsonProperty("numOfPages")]
public string NumOfPages { get; set; }

[JsonProperty("createdAt")]
public string CreatedAt { get; set; }

[JsonProperty("docflo_results")]
public DocfloResults DocfloResults { get; set; }
}

public class DocfloResults
{
[JsonProperty("modelFields")]
public ModelFields ModelFields { get; set; }
}

public class ModelFields
{
[JsonProperty("Items")]
public ItemsField Items { get; set; }
}

public class ItemsField
{
[JsonProperty("value")]
public List<Item> Value { get; set; }

[JsonProperty("type")]
public string Type { get; set; }
}

public class Item
{
[JsonProperty("type")]
public string Type { get; set; }

[JsonProperty("valueObject")]
public ValueObject ValueObject { get; set; }
}

public class ValueObject
{
[JsonProperty("Description")]
public Description Description { get; set; }
}

public class Description
{
[JsonProperty("type")]
public string Type { get; set; }

[JsonProperty("valueString")]
public string ValueString { get; set; }

[JsonProperty("content")]
public string Content { get; set; }
}

public class InvoiceData
{
public string CustomerCode { get; set; }
public string CustomerName { get; set; }
public string InvoiceNumber { get; set; }
public DateTime? InvoiceDate { get; set; }
public DateTime? DueDate { get; set; }
public string TaxCode { get; set; }
public double SubTotal { get; set; }
public double Total { get; set; }
public List<InvoiceLineItem> LineItems { get; set; }
}

public class InvoiceLineItem
{
public string ItemCode { get; set; }
public string Description { get; set; }
public double Quantity { get; set; }
public double UnitPrice { get; set; }
public double LineTotal { get; set; }
public string TaxCode { get; set; }
}

// Main program example
class Program
{
static async Task Main(string[] args)
{
var service = new DocfloIntegrationService(
"https://api.docflo.ai", // Replace with actual API URL
"your-tenant-id", // Replace with your tenant ID
"your-api-key" // Replace with your API key
);

// Connect to Business One
bool connected = service.ConnectToBusinessOne(
"your-server", // Business One server
"your-company-db", // Company database
"username", // Username
"password" // Password
);

if (connected)
{
// Get documents from Docflo
var documents = await service.GetDocfloDocuments("invoice", true);

foreach (var document in documents)
{
// Process each document based on type
if (document.Status == "APPROVED")
{
// Create Invoice from processed document
service.CreateInvoice(document);
}
}

service.Disconnect();
}
}
}
}

πŸ”§ Configuration Requirements​

NuGet Packages Required​

Add these NuGet packages to your project:

<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />

Business One DI API Reference​

Add reference to the SAP Business One DI API:

  • SAPbobsCOM.dll (usually located in C:\Program Files (x86)\SAP\SAP Business One DI API\COM DLL)

Custom Fields Setup​

Create this custom field in Business One:

  1. Invoices (OINV):
    • Field Name: U_DocfloID
    • Type: Text
    • Size: 50
    • Description: Docflo Document ID for tracking

Application Configuration​

Create an app.config file with your settings:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="DocfloApiUrl" value="https://api.docflo.ai" />
<add key="TenantId" value="your-tenant-id" />
<add key="ApiKey" value="your-api-key" />
<add key="B1Server" value="your-b1-server" />
<add key="B1CompanyDB" value="your-company-database" />
<add key="B1Username" value="your-username" />
<add key="B1Password" value="your-password" />
</appSettings>
</configuration>

πŸš€ Deployment Options​

Option 1: Windows Service​

Deploy as a Windows Service for continuous processing:

  1. Install the service using sc create or InstallUtil
  2. Configure service account with appropriate permissions
  3. Set up logging for monitoring and troubleshooting
  4. Schedule regular document polling

Option 2: Scheduled Task​

Use Windows Task Scheduler for periodic execution:

  1. Create a scheduled task to run the application
  2. Set appropriate triggers (hourly, daily, etc.)
  3. Configure security context for Business One access
  4. Monitor execution logs

πŸ“Š Monitoring and Logging​

Implement comprehensive logging:

// Add to your service class
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(DocfloIntegrationService));

public void LogIntegrationActivity(string message, Exception ex = null)
{
if (ex == null)
{
log.Info(message);
}
else
{
log.Error(message, ex);
}
}

πŸŽ‰ Integration Complete! Your SAP Business One system is now connected to Docflo.ai and can automatically process invoice documents and create invoices in Business One based on extracted document data.

πŸ” Troubleshooting​

Common Issues​

  1. DI API Connection Errors:

    • Verify Business One server accessibility
    • Check user permissions and license availability
    • Ensure correct database server type configuration
  2. SSL Certificate Issues:

    • Verify certificate installation in Windows Certificate Store
    • Check certificate chain completeness
    • Ensure proper certificate binding
  3. API Authentication Failures:

    • Validate API key and tenant ID
    • Check network connectivity to Docflo.ai
    • Verify API endpoint URLs

πŸ“ž Support​

For technical assistance with the Business One integration:

  • Contact Docflo.ai support team
  • Consult your SAP Business One partner
  • Review SAP Business One DI API documentation