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, << {$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, << {$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, << {$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, << {$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, << {$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, << {$this->lastArchiveID} LIMIT {$this->sqlLimit} SQL , $refType ); } /** * @return DocuvitaSolutionsTemplateData */ private function generateCreditTemplates() { $columnNames = $this->generateUniqColumnNames(['gutschrift', 'pdfarchiv', 'projekt', 'adresse']); return $this->createTemplatesFromSQL($this->templateCredit, << {$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 = << $objectXml 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 Ö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; } }