All extensions that are used to convert data are summarized under Converter Scripts in xentral. Included are among others:
-
File import and export
-
Data transfer via the Transfer module in many formats (CSV, XML, PDF, EDI)
-
Data exchange via store interfaces
-
Document import and data migration
Xentral provides many connectors to external services. These are mostly implemented via a proprietary API interface and therefore cannot be changed. However, data can be exported from the system at various points in xentral and made available to other services. The possibilities for output formats are almost unlimited, but incoming messages are mostly dependent on correct interpretation of the data structures and therefore only conditionally adaptable.
A tool for processing outgoing and incoming data is the Transmissions module (CSV/XML/EDI/PDF). There are several input and output formats available, but you can define your own formats. The most flexible solution for data transfer of lists offers the CSV format. Since the columns can be clearly assigned by the column label, this format can also react flexibly to incoming data.
With help of the integrated Template-Engine Smarty can flexibly adapt character-based output structures of data formats such as CSV, XML or JSON to the customer's needs. All that is required is to copy the existing target format into a text file and fill it with the appropriate variables. These can still be reformatted in the process.
Document data is located e.g. in the smarty variable $beleg and is addressed in the usual PHP object notation, e.g. $beleg->customer number or $beleg->belegnr. The naming is similar to the naming of database column names. Access to more complex structures is also possible depending on the document. So in the delivery bill the underlying order can be accessed: $voucher->order->delivery date.
Shortened example with fictitious format
<?xml version="1.0" encoding="utf-8"?>
<lieferschein>
{* Beleg-Stammdaten -------------------------------- *}
<status>{$beleg->status}</status>
<datum>{$beleg->datum|date_format:'%d.%m.%Y'}</datum>
<belegnummer>{$beleg->belegnr}</belegnr>
<kundennummer>{$beleg->kundennummer}</kundennummer>
<kundenname>{$beleg->name}</kundenname>
{* Zugriff auf Daten des zugrundeliegenden Auftrages *}
<lieferdatum>{$beleg->auftrag->lieferdatum}</lieferdatum>
{* Weitere Felder entfernt *}
{* Positionen - Beginn ------------------------------ *}
<positionen>
{* Schleife durchläuft alle verfügbaren Positionen *}
{foreach from=$beleg->positionen key=keypos item=position}
<position>
<artikelnummer>{$position->nummer}</artikelnummer>
<artikelbezeichnung>{$position->bezeichnung}</artikelbezeichnung>
<artikelbeschreibung>{$position->beschreibung}</artikelbeschreibung>
<menge>{$position->menge}</menge>
<preis>{$position->preis}</preis>
<ean>{$position->articledata->ean}</ean>
{* Weitere Felder entfernt *}
</position>
{/foreach}
</positionen>
{* Positionen - Ende -------------------------------- *}
</lieferschein>
Beyond the transmitted data, additional data, e.g. from the article master data, can also be accessed. These can be referenced via the object articledata, as represented by the EAN in the above example. All columns of the article table can be accessed via this notation, e.g. also the free fields of the article, which may be assigned with other values in the order itself.
Date values can be output as requested:
-
International date format (ISO 8601):
{$beleg->datum|date_format:'%Y-%m-%d'}
-
German date format (DIN 5008):
{$beleg->datum|date_format:'%d.%m.%Y'}
Numbers can be formatted as required. Here at the example of the column "Quantity":
-
Output without decimal places (as integer):
{$position->menge|string_format:'%d'}
-
Output with one decimal place:
{$position->menge|string_format:'%.1f'}
-
Output with two decimal places:
{$position->menge|string_format:'%.2f'}
For some output formats, the text lengths for output texts must be formatted. For this purpose use the
-
Fixed length output with preceding blanks:
{$position->nummer|string_format:'%10s'}, Ausgabe: " abcdef"
-
Fixed length output with preceding zeros:
{$position->nummer|string_format:'%010s'}, Ausgabe: "0000abcdef"
Basically, all formats can be output that are available through Function sprintf in PHP.
The following is a summary of some examples that enable the output of various delivery note formats in EDI, CSV and XML formats. The column labels (CSV), tag names (XML) and EDI specifications are to be checked. Other output formats such as JSON are conceivable.
The following is a detailed example of exporting a delivery note in various formats.
Format EDI
- wird ergänzt -
Format CSV
"tracking";"beleg_status";"beleg_datum";"beleg_lieferdatum";"beleg_tatsaechlicheslieferdatum";"beleg_versandart";"beleg_zahlungsweise";"beleg_belegnr";"beleg_hauptbelegnr";"beleg_kundennummer";"beleg_name";"beleg_abteilung";"beleg_unterabteilung";"beleg_adresszusatz";"beleg_ansprechpartner";"beleg_telefon";"beleg_email";"beleg_land";"beleg_strasse";"beleg_plz";"beleg_ort";"beleg_projekt";"beleg_aktion";"beleg_internebemerkung";"beleg_internebezeichnung";"beleg_freitext";"beleg_ihrebestellnummer";"beleg_lieferbedingung";"beleg_art";"artikel_nummer";"artikel_bezeichnung";"artikel_beschreibung";"artikel_menge";"artikel_preis";"artikel_rabatt";"artikel_waehrung";"artikel_lieferdatum";"artikel_sort";"artikel_umsatzsteuer";"artikel_einheit";"artikel_zolltarifnummer";"artikel_herkunftsland";"artikel_artikelnummerkunde";"wunschlieferdatum";"artikel_freifeld1";"artikel_freifeld2";"artikel_freifeld3";"artikel_freifeld40"
{foreach from=$beleg->positionen key=keyrow item=position}{* Positionen *}\
"{$position->tracking}";\
"{$beleg->status}";\
"{$beleg->datum|date_format:'%d.%m.%Y'}";\
"{$beleg->lieferdatum}";\
"{$beleg->tatsaechlicheslieferdatum}";\
"{$beleg->versandart}";\
"{$beleg->zahlungsweise}";\
"{$beleg->belegnr}";\
"{$beleg->hauptbelegnr}";\
"{$beleg->kundennummer}";\
"{$beleg->name}";\
"{$beleg->abteilung}";\
"{$beleg->unterabteilung}";\
"{$beleg->adresszusatz}";\
"{$beleg->ansprechpartner}";\
"{$beleg->telefon}";\
"{$beleg->email}";\
"{$beleg->land}";\
"{$beleg->strasse}";\
"{$beleg->plz}";\
"{$beleg->ort}";\
"{$beleg->projekt}";\
"{$beleg->aktion}";\
"{$beleg->internebemerkung}";\
"{$beleg->internebezeichnung}";\
"{$beleg->freitext}";\
"{$beleg->ihrebestellnummer}";\
"{$beleg->lieferbedingung}";\
"{$beleg->art}";\
"{$position->nummer}";\
"{$position->bezeichnung}";\
"{$position->beschreibung}";\
"{$position->menge|string_format:'%.2f'}";\
"{$position->preis}";\
"{$position->rabatt}";\
"{$position->waehrung}";\
"{$position->lieferdatum}";\
"{$position->sort}";\
"{$position->umsatzsteuer}";\
"{$position->einheit}";\
"{$position->zolltarifnummer}";\
"{$position->herkunftsland}";\
"{$position->artikelnummerkunde}";\
"{$beleg->auftrag->lieferdatum}";\
"{$position->freifeld1}";\
"{$position->freifeld2}";\
"{$position->freifeld3}";\
{* Ggf. weitere Freifelder *}";\
"{$position->freifeld40}";\
{/foreach}
Format XML
<?xml version="1.0" encoding="utf-8"?>
<lieferschein>
<status>{$beleg->status}</status>
<datum>{$beleg->datum|date_format:'%d.%m.%Y'}</datum>
<lieferdatum>{$beleg->lieferdatum}</lieferdatum>
<tatsaechlicheslieferdatum>{$beleg->tatsaechlicheslieferdatum}</tatsaechlicheslieferdatum>
<versandart>{$beleg->versandart}</versandart>
<zahlungsweise>{$beleg->zahlungsweise}</zahlungsweise>
<belegnr>{$beleg->belegnr}</belegnr>
<hauptbelegnr>{$beleg->hauptbelegnr}</hauptbelegnr>
<kundennummer>{$beleg->kundennummer}</kundennummer>
<name>{$beleg->name}</name>
<abteilung>{$beleg->abteilung}</abteilung>
<unterabteilung>{$beleg->unterabteilung}</unterabteilung>
<adresszusatz>{$beleg->adresszusatz}</adresszusatz>
<ansprechpartner>{$beleg->ansprechpartner}</ansprechpartner>
<telefon>{$beleg->telefon}</telefon>
<email>{$beleg->email}</email>
<land>{$beleg->land}</land>
<strasse>{$beleg->strasse}</strasse>
<plz>{$beleg->plz}</plz>
<ort>{$beleg->ort}</ort>
<projekt>{$beleg->projekt}</projekt>
<aktion>{$beleg->aktion}</aktion>
<internebemerkung>{$beleg->internebemerkung}</internebemerkung>
<internebezeichnung>{$beleg->internebezeichnung}</internebezeichnung>
<freitext>{$beleg->freitext}</freitext>
<ihrebestellnummer>{$beleg->ihrebestellnummer}</ihrebestellnummer>
<lieferbedingung>{$beleg->lieferbedingung}</lieferbedingung>
<art>{$beleg->art}</art>
<positionen>
{foreach from=$beleg->positionen key=keypos item=position}
<position>
<nummer>{$position->nummer}</nummer>
<bezeichnung>{$position->bezeichnung}</bezeichnung>
<beschreibung>{$position->beschreibung}</beschreibung>
<menge>{$position->menge}</menge>
<preis>{$position->preis}</preis>
<rabatt>{$position->rabatt}</rabatt>
<waehrung>{$position->waehrung}</waehrung>
<lieferdatum>{$position->lieferdatum}</lieferdatum>
<sort>{$position->sort}</sort>
<umsatzsteuer>{$position->umsatzsteuer}</umsatzsteuer>
<einheit>{$position->einheit}</einheit>
<zolltarifnummer>{$position->zolltarifnummer}</zolltarifnummer>
<herkunftsland>{$position->herkunftsland}</herkunftsland>
<artikelnummerkunde>{$position->artikelnummerkunde}</artikelnummerkunde>
<freifeld1>{$position->freifeld1}</freifeld1>
<freifeld2>{$position->freifeld2}</freifeld2>
<freifeld3>{$position->freifeld3}</freifeld3>
{* Ggf. weitere Freifelder *}
<freifeld40>{$position->freifeld40}</freifeld40>
</position>
{/foreach}
</positionen>
{if $beleg->versand}
{foreach from=$beleg->versand key=keyversand item=tracking} {* Versand *}
<tracking>{$tracking->tracking}</tracking>
{/foreach}
{/if}
</lieferschein>
The following examples are shown as a supplement to the above code examples, which only take into account specifics related to BBDs in an item.
Format CSV
- wird ergänzt -
Format XML
- wird ergänzt -
The following examples are given as a supplement to the above code examples, taking into account only the specifics related to batches in a position.
Format CSV
- wird ergänzt -
Format XML
- wird ergänzt -
The following examples are shown as a supplement to the above code examples, which only take into account the specifics related to BBDs and batches in an item.
Format CSV
- wird ergänzt -
Format XML
- wird ergänzt -
Store interfaces (so-called "importers") can be created customer-specific or overloaded. Already in the import process data can be changed or enriched, also the more complex conversion of data structures is possible.
At xentral, great importance is attached to the connection as well as the automatic processing of orders in online stores. Therefore xentral offers interfaces to various stores. Although almost every store has an interface, sometimes problems with the compatibility of the interfaces to be connected occur. An importer, which serves as an adapter between xentral and the online stores interface, can be developed. This acts as middleware and translates the data of the orders from the store into a format from which xentral can in turn create orders. Conversely, the importer is also responsible for transferring the order status, item data and stock figures to the store.
The process within the importer always follows a fixed pattern. If, for example, orders are to be collected from the store, the system first checks whether there is a connection to the store interface. Then it is determined whether or how many orders are to be collected from the store. If there are orders to be picked up, they are imported and then marked as "In process" in the store. When items are imported from or exported to the store, a similar scheme applies. First, a check is made to see if there is a connection between xentral and the store, then the data is transferred.

It is necessary to follow the given structure when writing an importer. Some points must be specified in advance:
-
Name
By convention, the class name is composed of "Shopimporter_XXX". In this example, a special importer for Shopware is being developed, which is why the class is also named Shopimporter_Shopwarespecial. The file itself corresponds to the class name in lower case and is stored in the www/pages/ directory of the xentral installation, for example /var/home/www/html/xentral/www/pages/shopimporter_shopwarespecial.php.
-
Database entry
If a new importer is added to the system via the interface, a new entry is created in the database in the "Shopexport" table. However, since this importer is a custom-made product, the name of the importer itself must be entered in this table in this case. It is important that the module name corresponds to the name of the PHP file.
-
ShopimporterBase
All internal importers are based on the "ShopimporterBase" class. The class contains system-relevant functions that ensure the execution of the importer. In principle, every importer should be derived from the ShopimporterBase base class to ensure that these functions are actually present:
class Shopimporter_Shopwarespecial extends ShopimporterBase {
-
Boilerplate
Two variables are relevant for the use of the importers:
-
$app contains the application xentrals itself and is passed in the constructor. The importer can, without any additional actions, use the core functionality of xentral.
-
$internal specifies whether the action handlers for the object need to be initialized. This makes it possible to instantiate and use the object otherwise
/** * @var bool */ public $intern = false;
/** * @var Application */ public $app;
-
-
Konstruktor
Two parameters are passed in Konstruktor; $app and $internal are stored directly in the object to be addressed later if required. The first logic in the module is to check whether action handlers should be registered for the module.
There are several action handlers that can be registered - in this example we will use the basic functions that every store importer should contain. It is recommended to use the function names as given to ensure compatibility. A list of the functions and the role they play in the importer follows later.
public function __construct($app, $intern = false) { $this->app=$app; $this->intern = true; if($intern) { return; } $this->app->ActionHandlerInit($this); $this->app->ActionHandler('auth','ImportAuth'); $this->app->ActionHandler('sendlist','ImportSendList'); $this->app->ActionHandler('sendlistlager','ImportSendListLager'); $this->app->ActionHandler('getauftraegeanzahl','ImportGetAuftraegeAnzahl'); $this->app->ActionHandler('getauftrag','ImportGetAuftrag'); $this->app->ActionHandler('deleteauftrag','ImportDeleteAuftrag'); $this->app->ActionHandler('updateauftrag','ImportUpdateAuftrag'); $this->app->DefaultActionHandler('list'); $this->app->ActionHandlerListen($app); }
-
EinstellungenStruktur
Since each store interface has its own setting options, it is necessary to give the importer these setting options via the EinstellungenStruktur() function. Fields passed here will be displayed in the importer's interface form. If necessary, additional fields can also be created.
public function EinstellungenStruktur() { return array( 'felder'=>array( 'APIUser'=>array('typ'=>'text','bezeichnung'=>'{|API User:','size'=>40), 'APIKey'=>array('typ'=>'text','bezeichnung'=>'{|API Key|}:','size'=>40), 'APIUrl'=>array('typ'=>'text','bezeichnung'=>'{|API Url|}:','size'=>40), )); }
-
getKonfig
The last function is getKonfig(). This passes the data from the settings and the store importer ID to the object. There are instructions for each function in the $data parameter. This function is called by the system when the object is initialized.
public function getKonfig($shopid, $data) { $this->shopid = $shopid; $this->data = $data; $einstellungen = $this->app->DB->Select("SELECT einstellungen_json FROM shopexport WHERE id = '$shopid' LIMIT 1"); if($einstellungen){ $einstellungen = json_decode($einstellungen,true); $this->apiUser = $einstellungen['felder']['APIUser']; $this->apiKey = $einstellungen['felder']['APIKey']; $this->apiUrl = $einstellungen['felder']['APIUrl']; } }
After the basics for the importer are in place, it comes to the actual connection to Shopware. It is possible to outsource the complete communication to a separate class or, in the case of Shopware, to copy the class from the documentation. For simplicity, however, a short function that returns the response of the API is sufficient for this example:
protected function call($endpoint, $method, $data=array(),$params=array()){ $queryString = ''; if (!empty($params)) { $queryString = '?'.http_build_query($params); } $url = $this->apiUrl.$endpoint.$queryString; $dataString = json_encode($data); $curl = curl_init(); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false); curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($curl, CURLOPT_USERPWD, $this->apiUser . ':' . $this->apiKey); curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=utf-8']); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($curl, CURLOPT_POSTFIELDS, $dataString); return curl_exec($curl); }
All requirements must be met: First, the ImportAuth() function must be used. In the external importer this function is necessary for authentication from xentral to the importer, in internal importers the function checks the connection call to the API. Since this function is called every time the importer is to be put into action, there is an underlying request or mechanism here to create a token. The important thing here is to return success to indicate that the importer is ready to accept instructions and pass them on to the Shop API.
public function ImportAuth()
{
$params = ['limit' => 1];
$responseJson =$this->call('articles','GET',null, $params);
$response = json_decode($responseJson,true);
if($response['success']){
return 'success';
}
return '';
}
The ImportSendListLager() function is added in addition. If the option for transferring stock numbers is activated in the import settings, this function leads to a synchronization of the stocks in the store with the stocks in xentral. In this example, this is done via the item number. It should be noted that for this function, the data parameter of the Importer object has the data for the item to be updated.
public function ImportSendListLager()
{
$updatedArticles = 0;
$tmp = $this->CatchRemoteCommand('data');
foreach ($tmp as $article){
$nummer = $article['nummer'];
$lageranzahl = $article['anzahl_lager'];
$updateInStock = [
'mainDetail' => [
'inStock' => $lageranzahl
]
];
$params = [
'useNumberAsId'=>true
];
$result = $this->call('articles/'.$nummer, 'PUT',$updateInStock, $params);
if($result['success']){
$updatedArticles++;
}
}
return $updatedArticles;
}
The ImportSendList() function is a version of ImportSendListLager(). This function transfers items to the store or updates their data. So item properties or images can be transferred to the store. It depends on the respective store interface how and which data can be transferred.
Care should be taken to separate the two functions, as ImportSendList is only called in two cases:
-
Manual item export
-
Export of the item via the item transfer in the importer
$articleData = [ 'name' => $name, 'lastStock' => $laststock, 'tax' => $tax, // alternativ 'taxId' => 1, 'supplier' => $supplier, // alternativ 'supplierId' => 2, 'description' => $description, 'descriptionLong' => $description_long, 'keywords' => $keywords, 'metaTitle' => $metatitle, 'highlight' => $topseller, 'mainDetail' => [ 'number' => $articleNumber, 'active' => $active, 'ean' => $ean, 'weight' => $weight, 'width' => $width, 'len' => $length, 'height' => $height, 'supplierNumber' => $supplierNumber, 'inStock' => $lageranzahl, 'prices' => $prices ] ];
The ImportGetAuftraegeAnzahl function determines how many open orders are available in the store and need to be picked up. It is conceivable that the orders can be picked up via the status, from a certain order number or also via the order date. Depending on the store interface, not all options can be realized.
public function ImportGetAuftraegeAnzahl()
{
$result = $this->getOrders($this->data);
return count($result['data']);
}
The most important basic function of all importers is to fetch and translate the orders from the store to the shopping cart format of xentral. After the order is fetched from the store API, the first step is to transfer the customer's address data to the shopping cart:
$cart['anrede'] = 'herr';
$cart['strasse'] = $order['data']['billing']['street'];
$cart['plz'] = $order['data']['billing']['zipCode'];
$cart['ort'] = $order['data']['billing']['city'];
$cart['land'] = $order['data']['billing']['country']['iso'];
$cart['email'] = $order['data']['customer']['email'];
$cart['name'] = $order['data']['billing']['firstName'] . ' ' . $order['data']['billing']['lastName'];
If there is a deviating delivery address, this must also be transferred and the field deviating delivery address must be filled with true to ensure that the order arrives at the correct recipient. In addition, there are important data that are important for further processing.
$cart['auftrag'] = $orderFromCollection['id'];
$cart['gesamtsumme'] = $orderFromCollection['invoiceAmount'];
$cart['transaktionsnummer'] = $orderFromCollection['transactionId'];
$cart['onlinebestellnummer'] = $orderFromCollection['number'];
...
$cart['zahlungsweise'] = $order['data']['payment']['name'];
$cart['lieferung'] = $order['data']['dispatch']['name'];
$cart['bestelldatum'] = substr($order['data']['orderTime'],0,10);
$cart['versandkostennetto'] = $order['data']['invoiceShippingNet'];
Total amount:
The order total serves as a safeguard against an incorrectly assembled shopping cart. If item data is not passed correctly or there is a problem with taxation, the order total differs from the cart total and the auto-shipping option is disabled for the order. This ensures that no shipment leaves the warehouse that does not match the order.
Transaction number:
The transaction number is relevant for the automation of accounting and helps to allocate paid orders.
Order:
In this field the unique identification of the order for the store is transferred. Not every store interface allows to change the shipping status of an order via the order number. Occasionally an internal ID is required, which can be passed at this point. The value passed here will be referred to later when a change in the order status is transmitted from the store.
Online order number:
The order number is to be entered in this field. This facilitates the assignment and in case of queries, an order from the store can be directly assigned to an order in xentral.
Payment method:
Transferring the payment method allows you to enable or disable settings such as autoship, fast lane, or invoice creation specific to certain payment methods in xentral.
Delivery:
If the delivery method is transferred, a matching can be done in xentral, similar to the payment method.
Gross shipping costs:
When placing an order, the shipping costs can be transferred like an item in the shopping cart. By this special field the forwarding expenses are booked however on the, in the Importer adjusted, postage item and transferred into the order.
Order date: The order date shows the date the order was received.
foreach ($order['data']['details'] as $article)
{
$articlearray[] = [
'articleid' => $article['articleNumber'],
'name' => $article['articleName'],
'price' => $article['price'],
'quantity' => $article['quantity']
];
}
...
$orderData =[
'id' => $cart['auftrag'],
'warenkorb' => base64_encode(serialize($cart)),
'warenkorbjson' => base64_encode(json_encode($cart))
];
When the required basic data of the order has been transferred to the shopping cart, the individual items are to be added to the shopping cart. The item number is transferred in the articleid field. The other fields contain their respective equivalent from the order item.
After the shopping cart has been filled with all data from the order, the function returns the data.
The ImportDeleteAuftrag function is responsible for marking an open purchase order as "In process". The ImportUpdateAuftrag partner function marks the order as "Shipped".
public function ImportDeleteAuftrag()
{
$orderId = $this->data['auftrag'];
$update = [
'orderStatusId' => 1
];
$this->call('orders/'.$orderId, 'PUT',$update);
return true;
}
The entire code of the described example is listed below for your own use. This serves as a starting point for the development - without guarantee for correctness or expected functionality.
<?php
class Shopimporter_Shopwarespecial extends ShopimporterBase
{
/**
* @var bool
*/
public $intern = false;
/**
* @var Application
*/
public $app;
/** @var string*/
public $apiUrl;
/** @var string*/
public $apiKey;
/** @var string*/
public $apiUser;
/** @var int */
public $shopid;
/** @var mixed */
public $data;
/**
* Shopimporter_Shopwarespecial constructor.
*
* @param $app
* @param bool $intern
*/
public function __construct($app, $intern = false)
{
$this->app=$app;
$this->intern = true;
if($intern)
{
return;
}
$this->app->ActionHandlerInit($this);
$this->app->ActionHandler('auth','ImportAuth');
$this->app->ActionHandler('sendlist','ImportSendList');
$this->app->ActionHandler('sendlistlager','ImportSendListLager');
$this->app->ActionHandler('getauftraegeanzahl','ImportGetAuftraegeAnzahl');
$this->app->ActionHandler('getauftrag','ImportGetAuftrag');
$this->app->ActionHandler('deleteauftrag','ImportDeleteAuftrag');
$this->app->ActionHandler('updateauftrag','ImportUpdateAuftrag');
$this->app->DefaultActionHandler('list');
$this->app->ActionHandlerListen($app);
}
/**
* @return array
*/
public function EinstellungenStruktur()
{
return
array(
'felder'=>array(
'APIUser'=>array('typ'=>'text','bezeichnung'=>'{|API User:','size'=>40),
'APIKey'=>array('typ'=>'text','bezeichnung'=>'{|API Key|}:','size'=>40),
'APIUrl'=>array('typ'=>'text','bezeichnung'=>'{|API Url|}:','size'=>40),
));
}
/**
* @param $shopid
* @param $data
*/
public function getKonfig($shopid, $data)
{
$this->shopid = $shopid;
$this->data = $data;
$einstellungen = $this->app->DB->Select("SELECT einstellungen_json FROM shopexport WHERE id = '$shopid' LIMIT 1");
if($einstellungen){
$einstellungen = json_decode($einstellungen,true);
$this->apiUser = $einstellungen['felder']['APIUser'];
$this->apiKey = $einstellungen['felder']['APIKey'];
$this->apiUrl = $einstellungen['felder']['APIUrl'];
}
}
/**
* @param $endpoint
* @param $method
* @param array $data
* @param array $params
*
* @return bool|string
*/
protected function call($endpoint, $method, $data=array(),$params=array()){
$queryString = '';
if (!empty($params)) {
$queryString = '?'.http_build_query($params);
}
$url = $this->apiUrl.$endpoint.$queryString;
$dataString = json_encode($data);
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($curl, CURLOPT_USERPWD, $this->apiUser . ':' . $this->apiKey);
curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=utf-8']);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($curl, CURLOPT_POSTFIELDS, $dataString);
return curl_exec($curl);
}
/**
* @return int
*/
public function ImportSendListLager()
{
$updatedArticles = 0;
$tmp = $this->CatchRemoteCommand('data');
foreach ($tmp as $article){
$nummer = $article['nummer'];
$lageranzahl = $article['anzahl_lager'];
$updateInStock = [
'mainDetail' => [
'inStock' => $lageranzahl
]
];
$params = [
'useNumberAsId'=>true
];
$result = $this->call('articles/'.$nummer, 'PUT',$updateInStock, $params);
if($result['success']){
$updatedArticles++;
}
}
return $updatedArticles;
}
/**
* @return int|string
*/
public function ImportSendList()
{
$tmp = $this->CatchRemoteCommand('data');
$successCount = 0;
foreach ($tmp as $article){
$name = $article['name_de'];
$articleNumber = $article['nummer'];
$active = !$article['inaktiv'];
$length = $article['laenge'];
$width = $article['breite'];
$height = $article['hoehe'];
$weight = $article['gewicht'];
$tax = $article['steuersatz'];
$ean = $article['ean'];
$lageranzahl = $article['anzahl_lager'];
$laststock = $article['restmenge'];
$supplier = $article['hersteller'];
$supplierNumber = $article['herstellernummer'];
$description = $article['metadescription_de'];
$description_long = htmlspecialchars_decode($article['uebersicht_de']);
$keywords = $article['metakeywords_de'];
$metatitle = $article['metatitle_de'];
$preis = $article['bruttopreis'];
$topseller=0;
if($article['topseller'])
{
$topseller=1;
}
$prices[] = [
'customerGroupKey' => 'EK',
'price' => $preis
];
$articleData = [
'name' => $name,
'lastStock' => $laststock,
'tax' => $tax, // alternativ 'taxId' => 1,
'supplier' => $supplier, // alternativ 'supplierId' => 2,
'description' => $description,
'descriptionLong' => $description_long,
'keywords' => $keywords,
'metaTitle' => $metatitle,
'highlight' => $topseller,
'mainDetail' => [
'number' => $articleNumber,
'active' => $active,
'ean' => $ean,
'weight' => $weight,
'width' => $width,
'len' => $length,
'height' => $height,
'supplierNumber' => $supplierNumber,
'inStock' => $lageranzahl,
'prices' => $prices
]
];
$params = [
'useNumberAsId' => true
];
$responseJson = $this->call('articles/'.$articleNumber, 'GET',null,$params);
$response = json_decode($responseJson,true);
if(empty($response['data']['id'])){
$responseJson = $this->call('articles', 'POST' ,$articleData);
}else{
$responseJson = $this->call('articles/'.$articleNumber, 'PUT' ,$articleData,$params);
}
$response = json_decode($responseJson,true);
if($response['success']){
$successCount++;
}else{
return 'error: '.print_r($response,true);
}
}
return $successCount;
}
/**
* @return int
*/
public function ImportGetAuftraegeAnzahl()
{
$result = $this->getOrders($this->data);
return count($result['data']);
}
/**
* @return bool
*/
public function ImportUpdateAuftrag()
{
$orderId = $this->data['auftrag'];
$update = [
'orderStatusId' => 2
];
$this->call('orders/'.$orderId, 'PUT',$update);
return true;
}
/**
* @return bool
*/
public function ImportDeleteAuftrag()
{
$orderId = $this->data['auftrag'];
$update = [
'orderStatusId' => 1
];
$this->call('orders/'.$orderId, 'PUT',$update);
return true;
}
/**
* @param $data
*
* @return array
*/
protected function getOrders($data){
$toNumber = $data['bis_nummer'];
if(empty($toNumber)){
$filter[] = [
'property' => 'status',
'value' => 0,
];
}else{
$filter[] = [
'property' => 'number',
'expression' => '<=',
'value' => $toNumber,
];
}
$params = [
'filter' => $filter
];
$resultJson = $this->call('orders', 'GET', null, $params);
return json_decode($resultJson,true);
}
/**
* @return array
*/
public function ImportGetAuftrag()
{
$result = $this->getOrders($this->data);
$allOrders = [];
foreach ($result['data'] as $orderFromCollection){
$cart = [];
$cart['auftrag'] = $orderFromCollection['id'];
$cart['gesamtsumme'] = $orderFromCollection['invoiceAmount'];
$cart['transaktionsnummer'] = $orderFromCollection['transactionId'];
$cart['onlinebestellnummer'] = $orderFromCollection['number'];
$orderJson = $this->call('orders/'.$orderFromCollection['id'],'GET');
$order = json_decode($orderJson,true);
$cart['anrede'] = 'herr';
$cart['strasse'] = $order['data']['billing']['street'];
$cart['plz'] = $order['data']['billing']['zipCode'];
$cart['ort'] = $order['data']['billing']['city'];
$cart['land'] = $order['data']['billing']['country']['iso'];
$cart['email'] = $order['data']['customer']['email'];
$cart['name'] = $order['data']['billing']['firstName'] . ' ' . $order['data']['billing']['lastName'];
$cart['zahlungsweise'] = $order['data']['payment']['name'];
$cart['lieferung'] = $order['data']['dispatch']['name'];
$cart['bestelldatum'] = substr($order['data']['orderTime'],0,10);
$cart['versandkostennetto'] = $order['data']['invoiceShippingNet'];
$articlearray = [];
foreach ($order['data']['details'] as $article)
{
$articlearray[] = [
'articleid' => $article['articleNumber'],
'name' => $article['articleName'],
'price' => $article['price'],
'quantity' => $article['quantity']
];
}
$cart['articlelist']=$articlearray;
$orderData =[
'id' => $cart['auftrag'],
'warenkorb' => base64_encode(serialize($cart)),
'warenkorbjson' => base64_encode(json_encode($cart))
];
$allOrders[] = $orderData;
}
return $allOrders;
}
/**
* @return string
*/
public function ImportAuth()
{
$params = ['limit' => 1];
$responseJson =$this->call('articles','GET',null, $params);
$response = json_decode($responseJson,true);
if($response['success']){
return 'success';
}
return '';
}
}
EDI messages are divided into lines of code in which precisely defined data is transmitted.
The UNB line defines, among other things, the recipient and sender. A unique identifier for the communication partner must be entered here. There are various possibilities for this:
-
The quasi-standard here is a GLN (Global Location Number, formerly also ILN) from the GS1 System (formerly EAN System), which is managed in Germany by GS1.de. However, this GLN UNB ID type "14" is only one of many possibilities.
-
For a quick start and if the interface is developed by yourself, the type "9" can also be chosen as UNB ID. There the DUNS number (also D-U-N-S, short for Data Universal Numbering System) is expected, which can be queried at https://www.upik.de/upik_suche.cgi. All corporations are automatically assigned a DUNS number after registration with the relevant authorities.
-
The phone number with international code can be used as UNB ID type "12".
With EDI/EDIFACT almost everything is transmitted with code lists. For this, there are some sources that explain the format and show how to handle it:
-
A good introduction can be found at https://www.stylusstudio.com/edifact/.
-
A somewhat more complicated, but very comprehensive introduction can be found at https://service.unece.org/trade/untdid/d96a/trmd/orders_t.htm.
The data transfer is done e.g. with SFTP exclusively via keyfile (ssh pub-key), alternatively AS2 is used. If required, xentral will provide an AS2 server, for this contact the sales department or the partner account manager at xentral.
The EDI file is a plain text file, ideally encoded in UTF8; however, this is not mandatory in the standard. If a file with only one line is sent, the EDI can replace punctuation ' (single quotation mark, is defined in the UNA segment) with an LF or CR/LF.
Certain segments may occur more than once in EDI and may then have different meanings, e.g. data in the header and at item level.
Some segments were combined into an ORDER to show how an ORDER would be parsed.
BGM+220+4HQ7EBLG+9' --> Bestellung mit PO Nummer 4HQ7EBLG,
--> die 9 am Schluss bedeutet es werden die EAN/GS1 Codelisten verwendet
DTM+137:20190107:102' --> Belegdatum im Format 102
DTM+63:20190111:102' --> Lieferdatum spätestens im format 102
DTM+64:20190107:102' --> Lieferdatum frühstens im Format 102
Benötigt wird das GLN Verzeichnis aus dem Vendor-Central, damit die Lieferadressen zugeordnet werden können.
NAD+BY+5450534000017::9' --> Käufer, GLN Codiert
NAD+SU+4016428000009::9' --> Lieferant, hier wird die eigene UNB-ID wieder angegeben
NAD+DP+5450534002325::9+++++++DE' --> Lieferadresse mit Land
NAD+IV+5450534005838::9++AMAZON EU SARL:NIEDERLASSUNG DEUTSCHLAND+MARCEL-BREUER-STR. 12+MUENCHEN++80807+DE'
--> Rechnungsadresse als GLN und Klartext muss in der INVOICE Message auch komplett
--> im Klartext übertragen werden.
In the following (from LIN) the positions begin. Between NAD and LIN further lines are possible, e.g. RFF (reference in each case to the current element) or CUX (here the currency is defined).
Each LIN defines the beginning of a new position (LIN+1, LIN+2, LIN+3 etc.):
LIN+1++4016428352115:EN' --> Artikel Pos 1 mit Artikel EAN, EN=Datenherkunft EAN, könnte evtl. auch SA sein
QTY+21:52' --> Pos Menge in Stück
PRI+AAA:55.9' --> Preis pro Stück netto
This is followed by framework data, which is primarily used for validation: With UMS+S the sum block begins, which enables a validation with the help of different sums.
If several documents of a UNB transmission are contained, these now follow by a UNH or BGM, separated with the document header.