MasterData
Allgemeine Informationen
Bei jedem Stammdatenimport können die zu importierenden Daten aufbereitet werden.
Für die Aufbereitung der Stammdaten stehen vordefinierte UserExits zur Verfügung, die für diesen Zweck genutzt werden können.
UserExit Verzeichnis
Das UserExit-Verzeichnis für die Nutzung ist immer \htdocs\repository\client.server.net\UserExits\MasterData\tabellenname.php
Ein Beispiel für die Aufbereitung der Lieferantenstammdaten wäre also z.B.:\htdocs\repository\localhost\UserExits\MasterData\creditors.php
UserExit Funktion
In der UserExit Datei der jeweiligen Tabelle muss eine Function mit einem bestimmten Namen vorhanden sein.
Der Name dieser Funktion leitet sich aus dem technischen Tabellennamen ab. Hier der Rumpf für das oben angegebene Beispiel:
<?php
function Enhance_Creditors(array $data): ?array
{
return $data;
}
In der Variablen $data ist eine Zeile mit allen Spalten enthalten. Diese Zeile kann nun nach den jeweiligen Anforderungen überprüft und aufbereitet werden.
Sollen z.B. die Leerzeichen einer IBAN entfernt werden und das Land mit Großbuchstaben dargestellt werden, so könnte folgende Funktion genutzt werden:
<?php
function Enhance_Creditors(array $data): ?array
{
// Enhance IBAN
if (isset($data['IBAN'])) {
$data['IBAN'] = strtoupper(str_replace(' ', '', $data['IBAN']));
}
return $data;
}
Wie in diesem Beispiel zu sehen ist, wird auf die Daten der Spalte IBAN auf das Array $data['IBAN']
zugegriffen.
Die Leerzeichen dieser IBAN werden mit der Funktion str_replace(' ', '', $data['IBAN'])
ersetzt und mit der umschließenden Funktion strtoupper()
werden alle Buchstaben in Großbuchstaben gewandelt.
Aus dem Wert De12 1234 5678 9012 3456 78
würde mit dieser Funktion der Wert DE12123456789012345678
erzeugt werden.
Ausnahmen definieren
Soll ein ganzer Datensatz nicht importiert werden, dann muss lediglich der Wert null
zurückgegeben werden. Soll also zum Beispiel der Datensatz nicht importiert werden, wenn keine IBAN angegeben wurde so wäre folgende Erweiterung möglich:
<?php
function Enhance_Creditors(array $data): ?array
{
// Enhance IBAN
if (isset($data['IBAN'])) {
$data['IBAN'] = strtoupper(str_replace(' ', '', $data['IBAN']));
if ($data['IBAN'] === '') {
return null; // verhindert den Import des gesammten Datensatzes
}
}
return $data;
}
creditors.php
Die Lieferantenstammdaten müssen i.d.R. noch normalisiert werden.
Dazu gibt es im Invoice Template einen ausgelieferten UserExit, der diese Aufgabe übernimmt.
Zu den Normalisierungsschritten gehört
- das Setzen einer Firmennummer, wenn keine Firmennummer angegeben wurde
- das Entfernen der Leerzeichen in IBANs
- das Entfernen der Leerzeichen in Umsatzsteueridentifikationsnummern
- das Entfernen von Sonderzeichen in Telefon und Faxnummern
- das Setzen der BLZ auf Basis der IBAN, sofern keine BLZ angeben ist
- das Setzen des Kontos auf Basis der IBAN, sofern keine Konto angeben ist
- die Umschlüsselung von Länderkennzeichen
Hier das ausgelieferte Skript, welches beliebig auf die Anforderungen des Kunden angepasst werden kann:
<?php
function Enhance_Creditors($data){
$logger = Logger::getLogger("main");
// Enhance Company Id
if(isset($data['CompanyId']) == false) $data['CompanyId'] = "1000";
// Enhance IBAN
if(isset($data['IBAN'])){
$data['IBAN'] = strtoupper(str_replace(" ","",$data['IBAN']));
}
// Enhance EU Tax Id
$data['EUTaxId'] = strtoupper(str_replace(" ","",$data['EUTaxId']));
// Enhance Phone
if(isset($data['Phone'])) {
$data['Phone'] = str_replace(" ", "", $data['Phone']);
$data['Phone'] = str_replace("-", "", $data['Phone']);
$data['Phone'] = str_replace("/", "", $data['Phone']);
$data['Phone'] = str_replace("(", "", $data['Phone']);
$data['Phone'] = str_replace(")", "", $data['Phone']);
$data['Phone'] = str_replace("\\", "", $data['Phone']);
}
// Enhance Fax
if(isset($data['Fax'])) {
$data['Fax'] = str_replace(" ", "", $data['Fax']);
$data['Fax'] = str_replace("-", "", $data['Fax']);
$data['Fax'] = str_replace("/", "", $data['Fax']);
$data['Fax'] = str_replace("(", "", $data['Fax']);
$data['Fax'] = str_replace(")", "", $data['Fax']);
$data['Fax'] = str_replace("\\", "", $data['Fax']);
}
// Enhance Bank Code if IBAN is German
if(isset($data['BankCode']) && $data['IBAN']){
if($data['BankCode'] == "" && substr($data['IBAN'],0,2) == "DE"){
$data['BankCode'] = substr($data['IBAN'],4,8);
}
}
// Enhance Bank Account if IBAN is German
if(isset($data['BankAccount']) && $data['IBAN']){
if($data['BankAccount'] == "" && substr($data['IBAN'],0,2) == "DE"){
$data['BankAccount'] = ltrim(substr($data['IBAN'],12,10),"0");
}
}
// Enhance Bank Account delete leading Zeros
if(isset($data['BankAccount'])){
$data['BankAccount'] = ltrim($data['BankAccount'],"0");
}
// Enhance Country Code if EU Tax Id is set or IBAN is set
if($data['CountryCode'] == "" && $data['EUTaxId'] != ""){
$data['CountryCode'] = substr($data['EUTaxId'],0,2);
} elseif($data['CountryCode'] == "" && isset($data['IBAN'])){
$data['CountryCode'] = substr($data['IBAN'],0,2);
} elseif($data['CountryCode'] == "D"){
$data['CountryCode'] = "DE";
} elseif($data['CountryCode'] == "A"){
$data['CountryCode'] = "AT";
} elseif($data['CountryCode'] == "B"){
$data['CountryCode'] = "BE";
} elseif($data['CountryCode'] == "E"){
$data['CountryCode'] = "ES";
} elseif($data['CountryCode'] == "F"){
$data['CountryCode'] = "FR";
} elseif($data['CountryCode'] == "I"){
$data['CountryCode'] = "IT";
} elseif($data['CountryCode'] == "IRL"){
$data['CountryCode'] = "IE";
} elseif($data['CountryCode'] == "USA"){
$data['CountryCode'] = "US";
} elseif($data['CountryCode'] == "S"){
$data['CountryCode'] = "SE";
}
return $data;
}
Stammdaten per RFC aus SAP holen
Voraussetzungen:
- Es werden folgende Dateien benötigt: php_sapnwrfc.dll, sapnwrfc.dll
- Es wird ein SAP RFC Baustein je Stammdaten Tabelle benötigt, welcher Stammdaten liefert
- Es wird ein SAP Benutzer benötigt
- Es werden die SAP Daten benötigt: Host, SysNr, Client, User, password
Vorbereitung:
- SAP DLLs im Squeeze/php/ext ablegen
- In der php.ini extension=php_sapnwrfc.dll hinzufügen
- In der Squeeze/repository/config/clients/<serverFQDN>.json erweitern:
"sap": {
"host": "host",
"instance": "01",
"sysid": "C01",
"client": "940",
"user": "xyz",
"password": "geheim"
}
- Unter Squeeze/repository/<ServerFQDN>/UserExits/MasterData eine Datei "SapRfcUpdateSettings.json" anlegen und füllen mit:
{
"tables": [
{
"tableid": "1",
"query": "ZZDEX_MASTERDATA",
"variant": " "
}
]
}
- Den folgenden UserExit anlegen:
<?php
use Dompdf\Exception;
use Squeeze\xTools;
// Disable xdebug to prevent CLI hanging
if(extension_loaded('xdebug')) {
xdebug_disable();
}
// Alle Fehler ausser E_NOTICE melden
error_reporting(E_ALL ^ E_NOTICE);
ini_set('display_errors',true);
ini_set("log_errors", true);
ini_set("error_log", dirname(__DIR__) . "/logs/php-error.log");
//ini_set("memory_limit", "-1");
ini_set('max_execution_time', "1200");
require_once __DIR__ . '/../../../../htdocs/bootstrap/app.php';
session_start();
if($argv[2] == '') $argv[2] = '80';
$_SESSION['SERVER_NAME'] = $argv[1];
$_SESSION['SERVER_PORT'] = $argv[2];
$logger = Logger::getLogger("main");
// ==============================================
// start time
// ==============================================
$time_start = xTools::microtime_float();
$logger->info("Refresh master data for client ". $argv[1]. " running on port " . $argv[2]);
// ==============================================
// Get Root directory
// ==============================================
$dirSep = DIRECTORY_SEPARATOR;
$config = new \Squeeze\SqueezeConfig();
$repoRoot = $config->get('repository.root');
// ==============================================
// Get defined Table Settings
// ==============================================
$settingsFile = $repoRoot . 'UserExits' . $dirSep . 'MasterData' . $dirSep . 'SapRfcUpdateSettings.json';
$logger->debug($settingsFile);
if(file_exists($settingsFile)){
$settings = json_decode(file_get_contents($settingsFile));
$logger->debug($settings);
foreach($settings->tables as $table){
$logger->debug($table);
//$tableResult = refreshMasterDataTableViaRfc($logger, $table, $config, $_SESSION['SERVER_NAME'], $_SESSION['SERVER_PORT']);
$tableResult = MasterDataRefreshForSapRfc($table->tableid, $table->query,$table->variant);
}
} else {
throw(new \Exception('SapRfcUpdateSettings.json does not exist.', 400));
}
// end time
$time_end = xTools::microtime_float();
$time = $time_end - $time_start;
$logger->info("Refresh master data for client ". $argv[1]. " finished");
/**
* @param Logger $logger
* @param stdClass $table
* @param \Squeeze\SqueezeConfig $config
* @param string $host
* @param string $port
* @return \Squeeze\xReturnObject
*/
function refreshMasterDataTableViaRfc(Logger $logger, $table, \Squeeze\SqueezeConfig $config, $host, $port){
try {
$curl = curl_init();
$headers = array("Content-Type:multipart/form-data");
$options = array(
CURLOPT_URL => 'http://' . $host . ':' . $port . "/api/MasterDataRefreshViaSapRfc",
CURLOPT_HEADER => true,
CURLOPT_POST => 1,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true
); // cURL options
curl_setopt_array($curl, $options);
$args['tableid'] = $table->tableid;
$args['sapRfcQuery'] = $table->query;
$args['sapRfcVariant'] = $table->variant;
curl_setopt($curl, CURLOPT_POSTFIELDS, $args);
$logger->debug($curl);
curl_exec($curl);
if(!curl_errno($curl))
{
$info = curl_getinfo($curl);
if ($info['http_code'] == 200) {
$logger->debug('MasterData Refresh successful');
} else {
$msg = curl_error($curl);
$logger->error($msg);
}
}
else
{
$msg = curl_error($curl);
$logger->error($msg);
}
curl_close($curl);
} catch (Exception $e) {
$logger = Logger::getLogger("main");
$logger->error($e->getMessage());
return new \Squeeze\xReturnObject(false, 500, $e->getMessage(), null);
}
}
function MasterDataRefreshForSapRfc($tableId, $queryName, $queryVariant ) {
try {
$logger = Logger::getLogger("main");
$logger->info("Start");
ini_set("memory_limit", "-1");
// ================================================
// Check if tableid is defined
// ================================================
if ($tableId == "") {
$msg = __FUNCTION__ . ' ' . "No Table ID defined!";
$logger->error($msg);
return false;
}
// ================================================
// Check if sapRfcQuery is defined
// ================================================
if ($queryName == "") {
$msg = __FUNCTION__ . ' ' . "No Query defined!";
$logger->error($msg);
return false;
}
// ================================================
// Check if sapRfcVariant is defined
// ================================================
if ($queryVariant == "") {
$msg = __FUNCTION__ . ' ' . "No Variant defined!";
$logger->error($msg);
return false;
}
$uuid = uniqid();
$dirSep = DIRECTORY_SEPARATOR;
// ================================================
// Get Master Data Table object
// ================================================
$xMasterDataTable = new \Squeeze\xMasterDataTable();
if(strtoupper($queryName) == 'MD_VENDOR') {
$xMasterDataTable->loadByName('creditors');
}
elseif(strtoupper($queryName) == 'ZZDEX_MASTERDATA') {
$xMasterDataTable->loadByName('creditors');
}
elseif(strtoupper($queryName) == 'MD_PO') {
$xMasterDataTable->loadByName('orders');
}
// ================================================
// Get Configuration value
// ================================================
$config = new \Squeeze\SqueezeConfig();
$sapConfig = [
'ashost' => $config->get('sap.host'),
'sysnr' => $config->get('sap.instance'),
'sysid' => $config->get('sap.sysid'),
'lang' => 'DE',
'client' => $config->get('sap.client'),
'user' => $config->get('sap.user'),
'passwd' => $config->get('sap.password'),
'trace' => '1'
];
$logger->debug($sapConfig);
$c = new \SAPNWRFC\Connection($sapConfig);
$f = $c->getFunction('ZZDEX_MASTERDATA');
//$result = $f->invoke([
// 'I_WORKSPACE' => 'G',
// 'I_USERGROUP' => '/QUERY',
// 'I_QUERY' => $queryName,
// 'I_VARIANT' => $queryVariant
//]);
$result = $f->invoke();
if($result['RETURN']['TYPE'] == 'E'){
$msg = __FUNCTION__ . ' ' . $result['RETURN']['MESSAGE'];
$code = 400;
$logger = \Logger::getLogger("main");
$logger->error(__CLASS__ ." ". __FUNCTION__ ." ". $msg);
return false;
}
// ============================================
// Check if a userexit script for this table exists
// ============================================
//$root = \Squeeze\xTools::getRoot();
$repo = $config->get('repository.root');
$userExit = xTools::buildAbsolutePath($repo, 'UserExits' . $dirSep . 'MasterData', true);
if (file_exists($userExit . $xMasterDataTable->getName() . '.php')) {
//$logger->info("File for User Exit of table " . $xMasterDataTable->getName() . " found.");
include_once($userExit . $xMasterDataTable->getName() . '.php');
} else {
//$logger->info("No UserExit for table " . $xMasterDataTable->getName() . " defined.");
}
$resultTable = array();
$i = 0;
if(strtoupper($queryName) == 'MD_VENDOR') {
foreach ($result['E_DATA'] as $line) {
$cells = explode(';', $line['LINE']);
$newLine['CreditorId'] = $cells[0];
$newLine['CompanyId'] = $cells[1];
$newLine['Name1'] = $cells[2];
$newLine['Name2'] = $cells[3];
$newLine['Name3'] = $cells[4];
$newLine['Phone'] = $cells[5];
$newLine['Street'] = $cells[6];
$newLine['CountryCode'] = $cells[7];
$newLine['Zip'] = $cells[8];
$newLine['City'] = $cells[9];
$newLine['EUTaxId'] = $cells[10];
$newLine['NationalTaxId'] = $cells[11];
$newLine['BankCountry'] = $cells[12];
$newLine['BankCode'] = $cells[13];
$newLine['BankAccount'] = $cells[14];
$newLine['IBAN'] = $cells[15];
$logger->debug($newLine);
if (function_exists('Enhance_' . $xMasterDataTable->getName())) {
$newLine = call_user_func('Enhance_' . $xMasterDataTable->getName(), $newLine);
}
$resultTable[] = $newLine;
}
}
elseif(strtoupper($queryName) == 'ZZDEX_MASTERDATA') {
foreach ($result['ET_VND'] as $line) {
//0 1 2 3 4 5 6 7 8 9 10 -11 12 13 14 15 16 17 18 19
//Kreditor BuKr. Land Name1 Name2 ??? Name Ort Ortsteil Postfach PLZ-Postf. PLZ Region Suchbegr. Straße USt-Id.Nr SteuerNr. IBAN Adresse ??? ??? ???
$logger->debug($line);
$cells = explode(';', $line['']);
$logger->debug($cells);
$newLine['CreditorId'] = ltrim( $cells[0], '0' );
$newLine['CompanyId'] = $cells[1];
$newLine['Name1'] = $cells[3];
$newLine['Name2'] = $cells[4];
//$newLine['Name3'] = $cells[4];
//$newLine['Phone'] = $cells[5];
$newLine['Street'] = $cells[14];
$newLine['CountryCode'] = $cells[2];
$newLine['Zip'] = $cells[11];
$newLine['City'] = $cells[7];
$newLine['EUTaxId'] = $cells[15];
$newLine['NationalTaxId'] = $cells[16];
//$newLine['BankCountry'] = $cells[12];
//$newLine['BankCode'] = $cells[13];
//$newLine['BankAccount'] = $cells[14];
$newLine['IBAN'] = $cells[17];
//$logger->debug($newLine);
if (function_exists('Enhance_' . $xMasterDataTable->getName())) {
$newLine = call_user_func('Enhance_' . $xMasterDataTable->getName(), $newLine);
}
$resultTable[] = $newLine;
}
}
elseif(strtoupper($queryName) == 'MD_PO') {
foreach ($result['E_DATA'] as $line) {
$cells = explode(';', $line['LINE']);
$newLine['CompanyId'] = $cells[0];
$newLine['CreditorId'] = $cells[1];
$newLine['OrderNumber'] = $cells[2];
$newLine['OrderItem'] = $cells[3];
$newLine['Material'] = $cells[4];
$newLine['Description'] = $cells[5];
$newLine['Quantity'] = $cells[6];
//$newLine['Quantity'] = $cells[7];
$newLine['QuantityUnit'] = $cells[8];
$newLine['PriceUnit'] = $cells[9];
//$newLine['Client'] = $cells[10];
$newLine['NetAmount'] = $cells[11];
$newLine['Currency'] = $cells[12];
//$logger->debug($newLine);
if (function_exists('Enhance_' . $xMasterDataTable->getName())) {
$newLine = call_user_func('Enhance_' . $xMasterDataTable->getName(), $newLine);
}
$resultTable[] = $newLine;
}
}
$result = $xMasterDataTable->createNewTempTable($uuid);
//$logger->info("xMasterDataTable created");
if ($result->isSuccess() == false) {
$logger->error($result->getMessage());
return false;
}
//$logger->info("DB Created");
$result = $xMasterDataTable->insertData($uuid, $resultTable);
$resultTable = null;
if ($result->isSuccess() == false) {
$logger->error($result->getMessage());
return false;
}
//$logger->info("DB Insert done");
$result = $xMasterDataTable->dropTableAndReplace();
if ($result->isSuccess() == false) {
$logger->error($result->getMessage());
return false;
}
//$logger->info("Drop and replace done.");
// ============================================
// Check if a UserExit script saving the document exists
// ============================================
$repo = $config->get('repository.root');
$userExit = xTools::buildAbsolutePath($repo, 'UserExits/MasterData/', true);
if (file_exists($userExit . 'AfterMasterDataRefresh.php')) {
//$logger->info("File for UserExit AfterMasterDataRefresh found.");
include_once($userExit . 'AfterMasterDataRefresh.php');
$userExitResult = call_user_func('AfterMasterDataRefresh', $xMasterDataTable);
} else {
//$logger->info("No UserExit for AfterMasterDataRefresh defined.");
}
$logger->info(json_encode($xMasterDataTable));
} catch (\Exception $e) {
xTools::handleException($e);
$code = 500;
$msg = $e->getMessage();
$logger = \Logger::getLogger("main");
$logger->error(__CLASS__ ." ". __FUNCTION__ ." ". $msg);
return false;
}
}
Anpassen des Mappings im UserExit in Zeilen 219ff.
Aufruf per "D:\EASY\Squeeze\php\php.exe" RefreshMasterDataViaRfc.php ServerFQDN 80
Es wird im UserExit Ordner oder teilweise im Windows/System32 eine *.trc Datei erzeugt, welche die Verbindungsdaten wiedergibt.
https://github.com/gkralik/php7-sapnwrfc/releases