OpenXE/classes/Modules/DocuvitaSolutionsApi/DocuvitaSolutionsApiService.php

772 lines
24 KiB
PHP
Raw Normal View History

2021-05-21 08:49:41 +02:00
<?php
namespace Xentral\Modules\DocuvitaSolutionsApi;
use Application;
use CURLFile;
use RuntimeException;
use Xentral\Components\Database\Database;
use Xentral\Core\DependencyInjection\ServiceContainer;
use Xentral\Modules\DocuvitaSolutionsApi\Data\DocuvitaSolutionsTemplateData;
use ZipArchive;
class DocuvitaSolutionsApiService
{
const KEY_LAST_ARCHIVE_ID = 'docuvita_solutions_last_archive_id';
const KEY_LAST_FILE_ID = 'docuvita_solutions_last_file_id';
/** @var ServiceContainer */
private $container;
/**
* @var Database
*/
private $db;
/**
* @var string
*/
private $username;
/**
* @var string
*/
private $endpoint;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $syncStart;
/**
* @var string
*/
private $systemReference;
/**
* @var string
*/
private $templateAddress;
/**
* @var string
*/
private $templateProject;
/**
* @var string
*/
private $templateOffer;
/**
* @var string
*/
private $templateInvoice;
/**
* @var string
*/
private $templateCredit;
/**
* @var string
*/
private $templateDeliveryNote;
/**
* @var string
*/
private $templateOrder;
/** @var string */
private $templateOrderIn;
/** @var string */
private $templateVerbindlichkeit;
/** @var Application */
private $app;
/** @var int */
private $lastArchiveID;
/** @var int */
private $lastFileID;
/** @var string[] */
private $missingFiles = [];
/** @var int */
private $sqlLimit = 100;
/**
* DocuvitaSolutionsApiService constructor.
*
* @param Application $app
* @param string $username
* @param string $password
* @param string $endpoint
* @param string $syncStart
* @param string $systemReference
* @param string $templateAddress
* @param string $templateProject
* @param string $templateOffer
* @param string $templateInvoice
* @param string $templateCredit
* @param string $templateDeliveryNote
* @param string $templateOrder
* @param string $templateOrderIn
* @param string $templateVerbindlichkeit
*/
public function __construct(
$app,
$username,
$password,
$endpoint,
$syncStart,
$systemReference,
$templateAddress,
$templateProject,
$templateOffer,
$templateInvoice,
$templateCredit,
$templateDeliveryNote,
$templateOrder,
$templateOrderIn,
$templateVerbindlichkeit
) {
$this->app = $app;
$this->container = $app->Container;
$this->db = $this->container->get('Database');
$this->username = $username;
$this->password = $password;
$this->endpoint = $endpoint;
$this->syncStart = $syncStart;
$this->systemReference = $systemReference;
$this->templateAddress = $templateAddress;
$this->templateProject = $templateProject;
$this->templateOffer = $templateOffer;
$this->templateInvoice = $templateInvoice;
$this->templateCredit = $templateCredit;
$this->templateDeliveryNote = $templateDeliveryNote;
$this->templateOrder = $templateOrder;
$this->templateOrderIn = $templateOrderIn;
$this->templateVerbindlichkeit = $templateVerbindlichkeit;
$this->lastArchiveID = $this->app->erp->GetKonfiguration(self::KEY_LAST_ARCHIVE_ID);
if (empty($this->lastArchiveID)) {
$this->lastArchiveID = -1;
} else {
$this->lastArchiveID = (int)$this->lastArchiveID;
}
$this->lastFileID = $this->app->erp->GetKonfiguration(self::KEY_LAST_FILE_ID);
if (empty($this->lastFileID)) {
$this->lastFileID = -1;
} else {
$this->lastFileID = (int)$this->lastFileID;
}
}
/**
* @param string $uploadGuid
* @param string $filePath
* @param string $fileName
*
* @return array
*/
public function uploadFile($uploadGuid, $filePath, $fileName)
{
$curl = curl_init("{$this->endpoint}/fileupload?guid={$uploadGuid}&format=json");
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath, null, $fileName),
],
]);
$response = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$this->checkCode($code, $response);
return json_decode($response);
}
public function sendFileAsString($fileName, $content, $uploadGuid)
{
$curl = curl_init("{$this->endpoint}/fileupload?guid={$uploadGuid}&format=json");
$boundary = '------------------------' . uniqid();
$body = "--$boundary\r\n"
. "Content-Disposition: form-data; name=\"file\"; filename=\"$fileName\"\r\n"
. "Content-Type: application/pdf\r\n\r\n"
. "$content\r\n"
. "--$boundary--\r\n";
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Length: ' . (strlen($body)),
"Content-Type: multipart/form-data; boundary=$boundary",
'Transfer-Encoding: chunked',
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$this->checkCode($code, $response);
return json_decode($response);
}
private function log($message, $dump = '')
{
$this->app->erp->LogFile($message, $dump, 'docuvitasolutions');
}
/**
* @return void
*/
public function export()
{
$this->log('starting export');
$dataObjects = [
$this->generateAddressTemplates(),
$this->generateProjectTemplates(),
$this->generateOfferTemplates(),
$this->generateInvoiceTemplates(),
$this->generateCreditTemplates(),
$this->generateDeliveryNoteTemplates(),
$this->generateOrderTemplates(),
$this->generateOrderInTemplates(),
$this->generateVerbindlichkeitTemplates(),
];
if (!empty($this->missingFiles)) {
$this->log("missing files", implode("\n", $this->missingFiles));
}
for ($i = 0, $count = count($dataObjects); $i < $count; $i++) {
if (count($dataObjects[$i]->getQueryResult()) === 0) {
unset($dataObjects[$i]);
}
}
if (count($dataObjects) === 0) {
$this->log('no data left to export');
$this->app->erp->SetKonfigurationValue(self::KEY_LAST_ARCHIVE_ID, $this->lastArchiveID);
return;
}
$zipFilePath = $this->generateZipFromTemplates($dataObjects);
if (!file_exists($zipFilePath)) {
throw new RuntimeException('Fehler beim Erstellen der ZIP');
}
$uploadGuid = $this->generateImportSessionGuid();
$this->uploadFile($uploadGuid, $zipFilePath, 'upload.zip');
unlink($zipFilePath);
// Save uploaded objects
foreach ($dataObjects as $templateObject) {
/** @var DocuvitaSolutionsTemplateData $templateObject */
if (in_array($templateObject->getRefType(), ['adresse', 'projekt'])) {
$sqlValues = [];
$idIdentifier = "{$templateObject->getRefType()}___id";
$type = $this->db->escapeString($templateObject->getRefType());
/** @var DocuvitaSolutionsTemplateData $templateObject */
foreach ($templateObject->getQueryResult() as $queryResult) {
$id = $this->db->escapeInt($queryResult[$idIdentifier]);
$sqlValues[] = "({$type}, {$id})";
}
$sql = implode(',', $sqlValues);
$this->db->perform("INSERT INTO docuvitasolutions_export (ref_type, ref_id) VALUES {$sql}");
} else {
foreach ($templateObject->getQueryResult() as $queryResult) {
if (in_array('pdfarchiv___id', array_keys($queryResult))) {
$this->lastArchiveID = max($this->lastArchiveID, (int)$queryResult['pdfarchiv___id']);
} else if (in_array('datei_stichwoerter___id', array_keys($queryResult))) {
$this->lastFileID = max($this->lastFileID, (int)$queryResult['datei_stichwoerter___id']);
}
}
}
}
$this->app->erp->SetKonfigurationValue(self::KEY_LAST_ARCHIVE_ID, $this->lastArchiveID);
$this->app->erp->SetKonfigurationValue(self::KEY_LAST_FILE_ID, $this->lastFileID);
$this->log('export finished');
}
private function generateUniqColumnNames($tableNames)
{
$mappingTable = include(__DIR__ . '/database_mapping/database_mapping.php');
$mapping = [];
foreach ($tableNames as $tableName) {
$mappingElement = $mappingTable[$tableName];
if ($mappingElement == null) {
throw new RuntimeException("mapping for {$tableName} not found");
}
$mapping = array_merge($mapping, $mappingTable[$tableName]);
}
$lines = [];
foreach ($mapping as $templateName => $databaseName) {
$databaseName = $this->db->escapeIdentifier($databaseName);
$templateName = $this->db->escapeIdentifier($templateName);
$lines[] = "{$databaseName} AS {$templateName}";
}
$sql = implode(',', $lines);
return $sql;
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateProjectTemplates()
{
$columnNames = $this->generateUniqColumnNames(['adresse', 'projekt']);
return $this->createTemplatesFromSQL($this->templateProject,
"SELECT $columnNames FROM projekt LEFT JOIN adresse ON projekt.kunde = adresse.id WHERE projekt.id NOT IN (SELECT e.ref_id FROM docuvitasolutions_export as e WHERE e.ref_type = \"projekt\") LIMIT {$this->sqlLimit}",
'projekt'
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateAddressTemplates()
{
$columnNames = $this->generateUniqColumnNames(['adresse']);
return $this->createTemplatesFromSQL($this->templateAddress,
"SELECT $columnNames FROM adresse WHERE adresse.id NOT IN (SELECT e.ref_id FROM docuvitasolutions_export as e WHERE e.ref_type = \"adresse\") LIMIT {$this->sqlLimit}",
'adresse'
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateInvoiceTemplates()
{
$columnNames = $this->generateUniqColumnNames(['rechnung', 'pdfarchiv', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateInvoice, <<<SQL
SELECT $columnNames
FROM rechnung
INNER JOIN pdfarchiv ON pdfarchiv.table_id = rechnung.id
LEFT JOIN projekt ON rechnung.projekt = projekt.id
LEFT JOIN adresse ON rechnung.adresse = adresse.id
WHERE pdfarchiv.table_name = "rechnung"
AND pdfarchiv.keinhintergrund = 0
AND pdfarchiv.id > {$this->lastArchiveID}
LIMIT {$this->sqlLimit}
SQL
,
'rechnung'
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateOrderTemplates()
{
$refType = 'auftrag';
$columnNames = $this->generateUniqColumnNames([$refType, 'pdfarchiv', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateOrder, <<<SQL
SELECT $columnNames
FROM {$refType}
INNER JOIN pdfarchiv ON pdfarchiv.table_id = {$refType}.id
LEFT JOIN projekt ON {$refType}.projekt = projekt.id
LEFT JOIN adresse ON {$refType}.adresse = adresse.id
WHERE pdfarchiv.table_name = "{$refType}"
AND pdfarchiv.keinhintergrund = 0
AND pdfarchiv.id > {$this->lastArchiveID}
LIMIT {$this->sqlLimit}
SQL
,
$refType
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateOfferTemplates()
{
$refType = 'angebot';
$columnNames = $this->generateUniqColumnNames([$refType, 'pdfarchiv', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateOffer, <<<SQL
SELECT $columnNames
FROM {$refType}
INNER JOIN pdfarchiv ON pdfarchiv.table_id = {$refType}.id
LEFT JOIN projekt ON {$refType}.projekt = projekt.id
LEFT JOIN adresse ON {$refType}.adresse = adresse.id
WHERE pdfarchiv.table_name = "{$refType}"
AND pdfarchiv.keinhintergrund = 0
AND pdfarchiv.id > {$this->lastArchiveID}
LIMIT {$this->sqlLimit}
SQL
,
$refType
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateOrderInTemplates()
{
$refType = 'bestellung';
$columnNames = $this->generateUniqColumnNames([$refType, 'pdfarchiv', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateOrderIn, <<<SQL
SELECT $columnNames
FROM {$refType}
INNER JOIN pdfarchiv ON pdfarchiv.table_id = {$refType}.id
LEFT JOIN projekt ON {$refType}.projekt = projekt.id
LEFT JOIN adresse ON {$refType}.adresse = adresse.id
WHERE pdfarchiv.table_name = "{$refType}"
AND pdfarchiv.keinhintergrund = 0
AND pdfarchiv.id > {$this->lastArchiveID}
LIMIT {$this->sqlLimit}
SQL
,
$refType
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateVerbindlichkeitTemplates()
{
$refType = 'verbindlichkeit';
$columnNames = $this->generateUniqColumnNames([$refType, 'datei_stichwoerter', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateVerbindlichkeit, <<<SQL
SELECT $columnNames
FROM {$refType}
INNER JOIN datei_stichwoerter ON datei_stichwoerter.parameter = {$refType}.id
LEFT JOIN projekt ON {$refType}.projekt = projekt.id
LEFT JOIN adresse ON {$refType}.adresse = adresse.id
WHERE datei_stichwoerter.objekt = "{$refType}"
AND datei_stichwoerter.id > {$this->lastFileID}
LIMIT {$this->sqlLimit}
SQL
,
$refType
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateDeliveryNoteTemplates()
{
$refType = 'lieferschein';
$columnNames = $this->generateUniqColumnNames([$refType, 'pdfarchiv', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateDeliveryNote, <<<SQL
SELECT $columnNames
FROM {$refType}
INNER JOIN pdfarchiv ON pdfarchiv.table_id = {$refType}.id
LEFT JOIN projekt ON {$refType}.projekt = projekt.id
LEFT JOIN adresse ON {$refType}.adresse = adresse.id
WHERE pdfarchiv.table_name = "{$refType}"
AND pdfarchiv.keinhintergrund = 0
AND pdfarchiv.id > {$this->lastArchiveID}
LIMIT {$this->sqlLimit}
SQL
,
$refType
);
}
/**
* @return DocuvitaSolutionsTemplateData
*/
private function generateCreditTemplates()
{
$columnNames = $this->generateUniqColumnNames(['gutschrift', 'pdfarchiv', 'projekt', 'adresse']);
return $this->createTemplatesFromSQL($this->templateCredit, <<<SQL
SELECT $columnNames
FROM gutschrift
INNER JOIN pdfarchiv ON pdfarchiv.table_id = gutschrift.id
LEFT JOIN projekt ON gutschrift.projekt = projekt.id
LEFT JOIN adresse ON gutschrift.adresse = adresse.id
WHERE pdfarchiv.table_name = "gutschrift"
AND pdfarchiv.keinhintergrund = 0
AND pdfarchiv.id > {$this->lastArchiveID}
LIMIT {$this->sqlLimit}
SQL
,
'gutschrift'
);
}
/**
* @param string $projectTemplate
* @param string $query
* @param string $refType
*
* @return DocuvitaSolutionsTemplateData
*/
private function createTemplatesFromSQL($projectTemplate, $query, $refType)
{
if ($this->sqlLimit === 0 || empty(trim($projectTemplate))) {
return new DocuvitaSolutionsTemplateData('', [], $refType);
}
/** @var Application $app */
$objects = $this->db->fetchAll($query);
$realObjects = [];
$data = '';
foreach ($objects as $object) {
if (in_array('pdfarchiv___table_id', array_keys($object))) {
$tableID = $object['pdfarchiv___table_id'];
$path = implode(str_split($tableID), '/');
$file = "{$this->app->Conf->WFuserdata}/pdfarchiv/{$this->app->Conf->WFdbname}/{$object['pdfarchiv___doctype']}/{$path}/{$object['pdfarchiv___dateiname']}";
if (!file_exists($file)) {
$this->missingFiles[] = $file;
continue;
};
} else {
if ($refType == 'verbindlichkeit') {
$fileID = $object['datei_stichwoerter___datei'];
$path = $this->getDMSPath($fileID);
if (!file_exists($path)) {
$this->missingFiles = $path;
continue;
}
}
}
$realObjects[] = $object;
$data .= $this->prepareTemplate($projectTemplate, $object);
}
$this->sqlLimit -= count($realObjects);
return new DocuvitaSolutionsTemplateData($data, $realObjects, $refType);
}
/**
* @param int $fileID
*
* @return string
*/
private function getDMSPath($fileID)
{
$path = $this->app->erp->GetDMSPath($fileID);
$path = $path . "/{$fileID}";
return $path;
}
/**
* @param $template
* @param $row
*
* @return mixed
*/
private function prepareTemplate($template, $row)
{
foreach ($row as $key => $value) {
$key = str_replace('___', ':', strtoupper($key));
$template = str_ireplace('{' . $key . '}', htmlspecialchars($value), $template);
}
return $template;
}
/**
* @param string $importName
*
* @return
*/
public function generateImportSessionGuid($importName = 'Xentral Import')
{
$guid = $this->generateGUID();
$json = json_encode([
'BatchImportGuid' => $guid,
//TODO give meaningful name
'ImportName' => $importName,
"ImportTagName" => "Xentral",
'StartImportAfterUpload' => 'true',
'UserName' => $this->username,
'Password' => $this->password,
'SystemReference' => $this->systemReference,
]);
$curl = curl_init("$this->endpoint/importapi_uploadimportset/?format=json");
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($json),
],
CURLOPT_POSTFIELDS => $json,
CURLOPT_RETURNTRANSFER => true,
]);
$resultString = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$this->checkCode($code, $resultString);
$result = json_decode($resultString);
return $result->UploadGuid;
}
/**
*
*/
public function testLogin()
{
$query = json_encode([
'UserName' => $this->username,
'UserPassword' => $this->password,
'SystemReferenceId' => $this->systemReference,
// 'PerformIntegratedUserLogin' => true,
'SessionType' => 1,
'LogoutExistingSession' => false,
]);
$curl = curl_init("$this->endpoint/userlogin");
curl_setopt_array($curl, [
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $query,
]);
$resultString = curl_exec($curl);
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
$result = json_decode($resultString);
$this->checkCode($code, $resultString);
return $result->UploadGuid;
}
/**
* @return string
*/
private function generateGUID()
{
return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535),
mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
}
/**
* @param $code
* @param $result
*/
private function checkCode($code, $result)
{
switch ($code){
case 0: throw new RuntimeException("Server nicht erreichbar");
case 401:
case 403: throw new RuntimeException('Zugangsdaten ungültig');
}
// check if 2xx http code
if (((int)$code / 100) !== 2) {
throw new RuntimeException("HTTP exception $code: $result");
}
}
/**
* @param DocuvitaSolutionsTemplateData[] $templateObjects
*
* @return string
*/
public function generateZipFromTemplates($templateObjects)
{
$objectXml = implode('', array_map(function ($template) {
/** @var DocuvitaSolutionsTemplateData $template */
return $template->getTemplate();
}, $templateObjects));
$data =
<<<EOF
<?xml version="1.0" encoding="utf-8"?>
<import>
<data>
$objectXml
</data>
</import>
EOF;
$zipPath = $this->app->getTmpFolder() . 'docuvita.zip';
$zipFile = new ZipArchive();
if ($error = $zipFile->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException("Fehler beim &Ouml;ffnen der Zip-Datei ({$error})");
}
foreach ($templateObjects as $templateObject) {
if (in_array($templateObject->getRefType(),
['rechnung', 'gutschrift', 'auftrag', 'lieferschein', 'bestellung', 'angebot'])) {
foreach ($templateObject->getQueryResult() as $queryResult) {
$tableID = $queryResult['pdfarchiv___table_id'];
$path = implode(str_split($tableID), '/');
$file = "{$this->app->Conf->WFuserdata}/pdfarchiv/{$this->app->Conf->WFdbname}/{$queryResult['pdfarchiv___doctype']}/{$path}/{$queryResult['pdfarchiv___dateiname']}";
$zipFile->addFile($file, $queryResult['pdfarchiv___dateiname']);
}
} else {
if ($templateObject->getRefType() == 'verbindlichkeit') {
foreach ($templateObject->getQueryResult() as $queryResult) {
$fileID = $queryResult['datei_stichwoerter___datei'];
$path = $this->getDMSPath($fileID);
$zipFile->addFile($path, $queryResult['datei_stichwoerter___datei']);
}
}
}
}
$zipFile->addFromString('meta.dvImport', $data);
$zipFile->close();
return $zipPath;
}
}