mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-12-26 14:40:29 +01:00
1257 lines
32 KiB
PHP
1257 lines
32 KiB
PHP
<?php
|
|
/**
|
|
* Created by PhpStorm.
|
|
* User: schmitt
|
|
* Date: 13.8.18
|
|
* Time: 11:32
|
|
*/
|
|
|
|
|
|
class PayoneSftpConnection
|
|
{
|
|
/** @var resource $connection*/
|
|
private $connection;
|
|
/** @var resource $sftp*/
|
|
private $sftp;
|
|
/** @var string */
|
|
private $subdir = '';
|
|
|
|
/**
|
|
* UebertragungenSFTPConnection constructor.
|
|
*
|
|
* @param string $host
|
|
* @param int $port
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public function __construct($host, $port=22)
|
|
{
|
|
if(empty($port))
|
|
{
|
|
$port = 22;
|
|
}
|
|
|
|
if(!function_exists('ssh2_connect'))
|
|
{
|
|
throw new Exception('SSH2 not installed');
|
|
}
|
|
$this->connection = @ssh2_connect($host, $port);
|
|
if (! $this->connection){
|
|
throw new Exception("Could not connect to $host on port $port.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $username
|
|
* @param string $password
|
|
*
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function login($username, $password)
|
|
{
|
|
if (! @ssh2_auth_password($this->connection, $username, $password)){
|
|
throw new Exception(
|
|
'Could not authenticate with username '.$username .
|
|
(empty($password)?' and no password': ' and password ***.')
|
|
);
|
|
}
|
|
$this->sftp = @ssh2_sftp($this->connection);
|
|
if (! $this->sftp){
|
|
throw new Exception("Could not initialize SFTP subsystem.");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param string $local_file
|
|
* @param string $remote_file
|
|
*
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function uploadFile($local_file, $remote_file)
|
|
{
|
|
$sftp = $this->sftp;
|
|
$stream = @fopen('ssh2.sftp://'.(int)$sftp.$remote_file, 'w');
|
|
if (! $stream){
|
|
throw new Exception("Could not open file: $remote_file");
|
|
}
|
|
$data_to_send = @file_get_contents($local_file);
|
|
if ($data_to_send === false){
|
|
throw new Exception("Could not open local file: $local_file.");
|
|
}
|
|
if (@fwrite($stream, $data_to_send) === false){
|
|
throw new Exception("Could not send data from file: $local_file.");
|
|
}
|
|
@fclose($stream);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param string $remote_file
|
|
*
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
public function scanFilesystem($remote_file) {
|
|
$sftp = $this->sftp;
|
|
$dir = 'ssh2.sftp://'.(int)$sftp.$remote_file;
|
|
$tempArray = array();
|
|
$handle = @opendir($dir);
|
|
if(empty($handle))
|
|
{
|
|
throw new Exception("Could not read dir: $remote_file.");
|
|
}
|
|
if(!empty($handle)){
|
|
// List all the files
|
|
while (false !== ($file = readdir($handle))) {
|
|
if(substr("$file", 0, 1) !== '.'){
|
|
if(is_dir($file)){
|
|
// $tempArray[$file] = $this->scanFilesystem("$dir/$file");
|
|
}else{
|
|
$tempArray[] = $file;
|
|
}
|
|
}
|
|
}
|
|
closedir($handle);
|
|
}
|
|
return $tempArray;
|
|
}
|
|
|
|
/**
|
|
* @param string $remote_file
|
|
* @param string $local_file
|
|
*
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function receiveFile($remote_file, $local_file)
|
|
{
|
|
$sftp = $this->sftp;
|
|
$stream = @fopen('ssh2.sftp://'.(int)$sftp.$remote_file, 'r');
|
|
if (! $stream){
|
|
throw new Exception("Could not open file: $remote_file");
|
|
}
|
|
$contents = '';
|
|
$size = filesize('ssh2.sftp://'.(int)$sftp.$remote_file);
|
|
if($size <= 0 || $size > 8192) {
|
|
$size = 8192;
|
|
}
|
|
while(($content = fread($stream, $size)) !== false) {
|
|
$contents .= $content;
|
|
if($content ==='') {
|
|
break;
|
|
}
|
|
}
|
|
file_put_contents ($local_file, $contents);
|
|
@fclose($stream);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param $remote_file
|
|
*
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function deleteFile($remote_file){
|
|
$sftp = $this->sftp;
|
|
if(!@unlink('ssh2.sftp://'.(int)$sftp.$remote_file)) {
|
|
throw new Exception("Could not delete file: $remote_file");
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* sofortimport for 'payone'
|
|
* import from .csv file at specified location
|
|
*
|
|
* Class payone
|
|
*/
|
|
class payone
|
|
{
|
|
const COL_DIVIDER = ";";
|
|
const DEFAULT_N_DAYS = 5;
|
|
const DEFAULT_FILE = '/tmp/default-payone.csv';
|
|
|
|
/** @var Application $app */
|
|
private $app;
|
|
private $requiredHeaders = ['payment', 'currency', 'timestamp', 'txid'];
|
|
private $optionalHeaders = ['reference', 'clearingtype', 'clearingsubtype', 'userid'];
|
|
|
|
/**
|
|
* the file handle
|
|
*
|
|
* @var resource
|
|
*/
|
|
private $handle = null;
|
|
|
|
/**
|
|
* just as fallback, if all fails
|
|
* on delete, the file handle should
|
|
* be closed
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
$this->closeFile();
|
|
}
|
|
|
|
protected function FTPisDir(&$conn_id, $dir ) {
|
|
$akt = ftp_pwd( $conn_id );
|
|
if ( @ftp_chdir( $conn_id, $dir ) ) {
|
|
@ftp_chdir( $conn_id, $akt );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param string $file
|
|
* @param bool $ssl
|
|
* @param string $url
|
|
* @param int $port
|
|
* @param string $username
|
|
* @param string $pw
|
|
* @param string $subdir
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function FTPDeleteFile($file, $ssl, $url, $port, $username, $pw, $subdir = '')
|
|
{
|
|
$aktfolder = '';
|
|
if(empty($port)) {
|
|
$port = 21;
|
|
}
|
|
if($ssl) {
|
|
$conn_id = ftp_ssl_connect($url, $port);
|
|
}
|
|
else{
|
|
$conn_id = ftp_connect($url, $port);
|
|
}
|
|
if(!$conn_id) {
|
|
$this->fehler[] = 'FTP-Verbindung fehlgeschlagen zu '.$url.':'.$port;
|
|
return false;
|
|
}
|
|
$login_result = ftp_login($conn_id, $username, $pw);
|
|
if($login_result) {
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.') {
|
|
$aktfolder = @ftp_pwd($conn_id);
|
|
if(!@ftp_chdir($conn_id, $subdir)) {
|
|
ftp_close($conn_id);
|
|
$this->fehler[] = $subdir.' auf FTP-Server nicht gefunden';
|
|
return false;
|
|
}
|
|
}
|
|
if (ftp_delete($conn_id, $file)) {
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && !empty($aktfolder))
|
|
{
|
|
@ftp_chdir($conn_id, $aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
$this->fehler[] = 'FTP-Login fehlgeschlagen zu '.$url.':'.$port.' mit User '.$username;
|
|
}
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param bool $ssl
|
|
* @param string $url
|
|
* @param string $port
|
|
* @param string $username
|
|
* @param string $pw
|
|
* @param string $subdir
|
|
* @param string $prefix
|
|
*
|
|
* @return array|bool
|
|
*/
|
|
protected function FTPGetFileList($ssl, $url, $port, $username, $pw, $subdir = '', $prefix = '')
|
|
{
|
|
if(empty($port)) {
|
|
$port = 21;
|
|
}
|
|
$port = (int)$port;
|
|
$aktfolder = '';
|
|
if($ssl) {
|
|
$conn_id = ftp_ssl_connect($url, $port);
|
|
}
|
|
else{
|
|
$conn_id = ftp_connect($url, $port);
|
|
}
|
|
if(!$conn_id) {
|
|
$this->fehler[] = 'Verbindung fehlgeschlagen zu '.$url .':'. $port;
|
|
return false;
|
|
}
|
|
$login_result = ftp_login($conn_id, $username, $pw);
|
|
if($login_result) {
|
|
ftp_pasv($conn_id, true);
|
|
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.') {
|
|
$aktfolder = ftp_pwd($conn_id);
|
|
if(!@ftp_chdir($conn_id, $subdir)) {
|
|
$this->fehler[] = $subdir.' nicht gefunden';
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
}
|
|
$Liste = ftp_nlist($conn_id, "-dF ".".");
|
|
if(empty($Liste) || count($Liste)<= 1) {
|
|
$Liste = ftp_nlist($conn_id, ".");
|
|
}
|
|
if($Liste !== false) {
|
|
if(!empty($Liste)) {
|
|
foreach($Liste as $k => $v) {
|
|
if(strpos($v,'./') === 0) {
|
|
$v = substr($v, 2);
|
|
$Liste[$k] = $v;
|
|
}
|
|
if($v === '' || $v === '.' || $v === '..') {
|
|
unset($Liste[$k]);
|
|
continue;
|
|
}
|
|
if($prefix != '') {
|
|
if(stripos($v, $prefix) !== 0) {
|
|
unset($Liste[$k]);
|
|
continue;
|
|
}
|
|
if(substr($v, -1) === '/' || (strpos($v,'.') === false && $this->FTPisDir($conn_id, $v))) {
|
|
unset($Liste[$k]);
|
|
continue;
|
|
}
|
|
}
|
|
if(substr($v,-1) === '/' && $this->FTPisDir($conn_id, $v)) {
|
|
unset($Liste[$k]);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.') {
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return $Liste;
|
|
}
|
|
$this->fehler[] = "Fehler beim Lesen von $url $subdir";
|
|
}
|
|
else{
|
|
$this->fehler[] = 'FTP Login fehlgeschlagen fehlgeschlagen';
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param string $from
|
|
* @param string $to
|
|
* @param bool $ssl
|
|
* @param string $url
|
|
* @param string $port
|
|
* @param string $username
|
|
* @param string $pw
|
|
* @param string $subdir
|
|
* @param string $prefix
|
|
* @param bool $binary
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function GetFile($from, $to, $ssl, $url, $port, $username, $pw, $subdir = '', $prefix = '', $binary = false)
|
|
{
|
|
$aktfolder = '';
|
|
$this->wasdir = false;
|
|
if($from === '' || strpos($from,'.') === 0) {
|
|
$this->fehler[] = 'ungültiger Dateiname '.$from;
|
|
return false;
|
|
}
|
|
if($to === '' || strpos($to,'.') === 0) {
|
|
$this->fehler[] = 'ungültiger Dateiname '.$to;
|
|
return false;
|
|
}
|
|
if(empty($port)) {
|
|
$port = 21;
|
|
}
|
|
$port = (int)$port;
|
|
if($ssl) {
|
|
$conn_id = ftp_ssl_connect($url, $port);
|
|
}
|
|
else{
|
|
$conn_id = ftp_connect($url, $port);
|
|
}
|
|
if(!$conn_id) {
|
|
$this->fehler[] = 'Verbindung fehlgeschlagen zu '.$url.':'.$port;
|
|
return false;
|
|
}
|
|
$login_result = ftp_login($conn_id, $username, $pw);
|
|
if($login_result) {
|
|
ftp_pasv($conn_id, true);
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.')
|
|
{
|
|
$aktfolder = @ftp_pwd($conn_id);
|
|
if(!@ftp_chdir($conn_id, $subdir))
|
|
{
|
|
$this->fehler[] = $from." nicht gefunden";
|
|
}
|
|
}
|
|
$Liste = ftp_nlist($conn_id, '.');
|
|
if($Liste !== false)
|
|
{
|
|
if(!empty($Liste))
|
|
{
|
|
foreach($Liste as $k => $v) {
|
|
if($v == $from) {
|
|
if(@ftp_get($conn_id, $to, $from, ($binary?FTP_BINARY:FTP_ASCII)))
|
|
{
|
|
if(file_exists($to)) {
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && $aktfolder != '') {
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return true;
|
|
}
|
|
if(@ftp_chdir($conn_id, $from))
|
|
{
|
|
@ftp_chdir($conn_id,'..');
|
|
$this->wasdir = true;
|
|
}
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && $aktfolder != '')
|
|
{
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
if(@ftp_chdir($conn_id, $from)) {
|
|
@ftp_chdir($conn_id,'..');
|
|
$this->wasdir = true;
|
|
}
|
|
else{
|
|
$this->fehler[] = "Fehler beim Herunterladen von " . $from;
|
|
}
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && $aktfolder != '') {
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
}
|
|
if(!empty($from) && strpos($from, '..') === false && $from[0] !== '.'
|
|
&& @ftp_get($conn_id, $to, $from, ($binary?FTP_BINARY:FTP_ASCII))) {
|
|
if(file_exists($to)) {
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && $aktfolder != '') {
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return true;
|
|
}
|
|
if(@ftp_chdir($conn_id, $from)) {
|
|
@ftp_chdir($conn_id,'..');
|
|
$this->wasdir = true;
|
|
}
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && $aktfolder != '') {
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
}
|
|
$this->fehler[] = $from.' nicht gefunden';
|
|
}
|
|
}
|
|
else{
|
|
$this->fehler[] = "Fehler beim Lesen von $url $subdir";
|
|
}
|
|
}
|
|
else{
|
|
$this->fehler[] = 'Login fehlgeschlagen';
|
|
}
|
|
if(!empty($subdir) && $subdir !== '/' && $subdir !== '.' && $aktfolder != '')
|
|
{
|
|
@ftp_chdir($conn_id,$aktfolder);
|
|
}
|
|
ftp_close($conn_id);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Import a .csv file from payone
|
|
* all options in $config array are optional.
|
|
* The default number of days is 5 (DEFAULT_N_DAYS)
|
|
* and the default file name is stored in
|
|
* '/tmp/payone.csv' (DEFAULT_FILE)
|
|
*
|
|
* The arguments 'PATH' or 'FILE' set the used file,
|
|
* the keys 'API_DAYS', 'DAYS' or 'TAGE' override the
|
|
* number of days to look into the past
|
|
*
|
|
* @param array $config
|
|
* @param Application $app
|
|
*
|
|
* @return array|string
|
|
* @throws Exception
|
|
*/
|
|
function Import($config, $app)
|
|
{
|
|
$this->app = $app;
|
|
$csv = [];
|
|
$index = [];
|
|
$description = [];
|
|
$config = (array) $config;
|
|
|
|
try {
|
|
$filename = $this->getFileName($config);
|
|
}
|
|
catch (Exception $e) {
|
|
$filename = '';
|
|
}
|
|
list($ftphost, $ftpport, $ftpuser, $ftppassword, $ftpdebug, $ftpssl, $ftpsubdir, $sftp) = $this->getFtp($config);
|
|
|
|
if($ftphost) {
|
|
|
|
if(empty($filename)){
|
|
$filename = date('YmdHis').'.csv';
|
|
}
|
|
//$folder = $this->app->erp->GetTMP(). rtrim($this->app->Conf->WFuserdata,'/').'/payone';
|
|
$folder = $this->app->erp->GetTMP().'payone';
|
|
if(!file_exists($folder)) {
|
|
if(!mkdir($folder) && !is_dir($folder)) {
|
|
$this->app->erp->LogFile($folder.' konnte nicht erstellt werden');
|
|
}
|
|
}
|
|
$folder = $folder.'/'.$this->app->Conf->WFdbname;
|
|
if(!file_exists($folder)) {
|
|
if(!mkdir($folder) && !is_dir($folder)) {
|
|
$this->app->erp->LogFile($folder.' konnte nicht erstellt werden');
|
|
}
|
|
}
|
|
$tofile = $folder.'/'.$filename;
|
|
|
|
|
|
if($ftphost) {
|
|
if($sftp) {
|
|
try {
|
|
$connection = new PayoneSftpConnection($ftphost, $ftpport);
|
|
}
|
|
catch(Exception $e) {
|
|
$this->fehler[] = $e->getMessage();
|
|
$this->app->erp->LogFile($e->getMessage());
|
|
return '';
|
|
}
|
|
try {
|
|
$connection->login($ftpuser, $ftppassword);
|
|
}
|
|
catch(Exception $e) {
|
|
$this->fehler[] = $e->getMessage();
|
|
$this->app->erp->LogFile($e->getMessage());
|
|
return '';
|
|
}
|
|
$list = $connection->scanFilesystem($ftpsubdir);
|
|
}
|
|
else {
|
|
$list = $this->FTPGetFileList($ftpssl, $ftphost, $ftpport, $ftpuser, $ftppassword, $ftpsubdir);
|
|
}
|
|
if(!empty($list)) {
|
|
foreach($list as $file) {
|
|
$fullFile = $file;
|
|
if(!empty($ftpsubdir)) {
|
|
$fullFile = rtrim($ftpsubdir,'/').'/'.$file;
|
|
}
|
|
if($sftp) {
|
|
try {
|
|
$getFile = $connection->receiveFile($fullFile, $tofile);
|
|
}
|
|
catch (Exception $e) {
|
|
$this->fehler[] = $e->getMessage();
|
|
$this->app->erp->LogFile($e->getMessage());
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
$getFile = $this->GetFile($file, $tofile, $ftpssl, $ftphost, $ftpport, $ftpuser, $ftppassword, $ftpsubdir);
|
|
}
|
|
|
|
if($getFile) {
|
|
if(!$ftpdebug) {
|
|
if($sftp){
|
|
try {
|
|
$slash = '';
|
|
if(trim($this->subdir) !== '' && substr($this->subdir,-1) !== '/'){
|
|
$slash = '/';
|
|
}
|
|
$connection->deleteFile($this->subdir.$slash.$file);
|
|
}
|
|
catch (Exception $e) {
|
|
$this->fehler[] = $e->getMessage();
|
|
$this->app->erp->LogFile($e->getMessage());
|
|
}
|
|
}
|
|
else{
|
|
$this->FTPDeleteFile($file, $ftpssl, $ftphost, $ftpport, $ftpuser, $ftppassword, $ftpsubdir);
|
|
}
|
|
}
|
|
$filename = $tofile;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!empty($filename)){
|
|
$this->openFile($filename);
|
|
}
|
|
$completeHeader = [];
|
|
while(!feof($this->handle)) {
|
|
|
|
$line = $this->readCSVLineAsArray();
|
|
|
|
if (!$line || (is_array($line) && count($line) <= 1 && empty(reset($line)))) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Use the first line as header.
|
|
* Extract some index numbers defined in $requiredHeaders array into $index;
|
|
* Extract some index numbers defined in $descriptionHeaders array into $description;
|
|
*/
|
|
if (empty($index)) {
|
|
$index = $this->extractIndexFromHeaderLine($line, $this->requiredHeaders);
|
|
$description = $this->extractIndexFromHeaderLine($line, $this->optionalHeaders);
|
|
$completeHeader = array_flip($line);
|
|
$completeHeader = array_change_key_case($completeHeader, CASE_LOWER);
|
|
try {
|
|
$this->checkMissingArrayValues($index, $this->requiredHeaders);
|
|
}
|
|
catch(Exception $e) {
|
|
if($ftpdebug) {
|
|
$this->app->erp->LogFile(['Error '.$e->getMessage().' in File '.$filename, $line]);
|
|
}
|
|
throw new Exception($e->getMessage().' in File '.$filename, $e->getCode());
|
|
}
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$data = $this->getDataOfInterest($line, $index);
|
|
}
|
|
catch (Exception $e) {
|
|
if($ftpdebug) {
|
|
$this->app->erp->LogFile(['Error '.$e->getMessage().' in File '.$filename, $line]);
|
|
}
|
|
throw new Exception($e->getMessage().' in File '.$filename, $e->getCode());
|
|
}
|
|
|
|
try {
|
|
$desc = $this->getDataOfInterest($line, $description);
|
|
}
|
|
catch (Exception $e) {
|
|
if($ftpdebug) {
|
|
$this->app->erp->LogFile(['Error '.$e->getMessage().' in File '.$filename, $line]);
|
|
}
|
|
throw new Exception($e->getMessage().' in File '.$filename, $e->getCode());
|
|
}
|
|
|
|
/*
|
|
* The timestamp is in the range, of [today - Number-of-days, today]
|
|
* so, import this line
|
|
*/
|
|
$data = array_merge($data, $desc);
|
|
foreach($completeHeader as $col => $indHeader) {
|
|
if(!isset($data[$col]) && !empty($line[$indHeader])) {
|
|
$data[$col] = $line[$indHeader];
|
|
}
|
|
}
|
|
$csv[] = $data;
|
|
}
|
|
|
|
$this->closeFile();
|
|
if ($this->getDeleteFlag($config)) {
|
|
/*
|
|
* Note: there are no checks about the given file!
|
|
* At this point the file exists and is readable
|
|
* other checks aren't performed so this method
|
|
* may deletes necessary files!
|
|
*/
|
|
@unlink($filename);
|
|
}
|
|
|
|
$header = array_unique(array_values(array_merge($index, $description, array_keys($completeHeader))));
|
|
$header = implode(self::COL_DIVIDER, $header);
|
|
|
|
$csv = array_map([$this, 'implodeCSVLine'], $csv);
|
|
|
|
$csv = array_merge([$header], $csv);
|
|
$csv = implode("\n", $csv);
|
|
|
|
return $csv;
|
|
}
|
|
|
|
/**
|
|
* copied and modified from 'ImportKontoauszug' in 'class.erpapi'
|
|
* used case "stripe" in switch statement
|
|
*
|
|
* @param string $csv the csv 'file' to import
|
|
* @param int $konto the konto id
|
|
* @param $app
|
|
* @return array($inserted, $duplicate);
|
|
* @throws Exception
|
|
*/
|
|
public function ImportKontoauszug($csv, $konto, $app)
|
|
{
|
|
$this->app = $app;
|
|
$inserted = 0;
|
|
$duplicate = 0;
|
|
|
|
if (!is_string($csv)) {
|
|
$type = gettype($csv);
|
|
throw new Exception(sprintf(
|
|
'Expected csv as string, got \'%s\'', $type
|
|
));
|
|
}
|
|
|
|
// fix values
|
|
$gebuehr = 0;
|
|
$gegenkonto = "";
|
|
$stamp = time();
|
|
$userName = $this->app->User->GetName();
|
|
$userName = $this->app->DB->real_escape_string($userName);
|
|
|
|
$csv = $this->explodeCSVLines($csv);
|
|
if (empty($csv)) {
|
|
return array($inserted, $duplicate);
|
|
}
|
|
$count = count($csv);
|
|
|
|
$csv = array_map([$this, 'explodeCSVLine'], $csv);
|
|
|
|
$header = $this->extractIndexFromHeaderLine($csv[0], $this->requiredHeaders);
|
|
try {
|
|
$this->checkMissingArrayValues($header, $this->requiredHeaders);
|
|
}
|
|
catch(Exception $e) {
|
|
$this->app->erp->LogFile(['Error '.$e->getMessage(), $header]);
|
|
throw new Exception($e->getMessage().' in '.json_encode($header), $e->getCode());
|
|
}
|
|
|
|
$completeHeader = array_flip($csv[0]);
|
|
$completeHeader = array_change_key_case($completeHeader, CASE_LOWER);
|
|
/*
|
|
* skip first row -> 'header' line
|
|
*/
|
|
for ($i = 1; $i < $count; $i++) {
|
|
if(count($csv[$i]) <= 1) {
|
|
continue;
|
|
}
|
|
$data = $this->getDataOfInterest($csv[$i], $header);
|
|
|
|
/*
|
|
* translate / extract
|
|
*/
|
|
$betrag = $data['payment'];
|
|
$vorgang = $data['txid'];
|
|
$buchung = $data['timestamp'];
|
|
$waehrung = $data['currency'];
|
|
|
|
foreach(
|
|
[
|
|
'reference',
|
|
'clearingtype',
|
|
'clearingsubtype',
|
|
'userid',
|
|
'customerid',
|
|
'email',
|
|
'company',
|
|
'firstname',
|
|
'lastname',
|
|
'street',
|
|
'zip',
|
|
'city',
|
|
'country'
|
|
] as $col) {
|
|
if(!empty($data[$col])) {
|
|
$vorgang .= ' '.$data[$col];
|
|
}
|
|
elseif(!empty($completeHeader[$col]) && !empty($csv[$i][$completeHeader[$col]])) {
|
|
$vorgang .= ' '.$csv[$i][$completeHeader[$col]];
|
|
}
|
|
}
|
|
// free some unnecessary memory
|
|
unset($csv[$i], $data);
|
|
|
|
|
|
$buchung = $this->app->DB->real_escape_string( $buchung);
|
|
$buchung = str_replace('"','', $buchung);
|
|
$buchung = explode(' ', $buchung);
|
|
$buchung = $buchung[0];
|
|
if(is_numeric($buchung) && (String)(int)$buchung === (String)$buchung) {
|
|
$buchung = date('Y-m-d', $buchung);
|
|
}
|
|
|
|
$vorgangUtf8 = iconv('UTF-8', 'UTF-8', $vorgang);
|
|
if(md5($vorgangUtf8) !== md5($vorgang)){
|
|
$vorgang = utf8_encode($vorgang);
|
|
}
|
|
$vorgang = $this->app->DB->real_escape_string($vorgang);
|
|
$vorgang = str_replace('"','',$vorgang);
|
|
|
|
$betrag = $this->app->DB->real_escape_string( $betrag);
|
|
|
|
$waehrung = $this->app->DB->real_escape_string($waehrung);
|
|
|
|
// haben vs. soll
|
|
list($haben, $soll) = str_replace(',','.', $betrag) > 0
|
|
? array($betrag, "")
|
|
: array("", $betrag);
|
|
|
|
// hash over some values
|
|
$pruefsumme = md5(serialize(array($buchung, $vorgang, $soll, $haben, $waehrung)));
|
|
|
|
$sql = "SELECT id FROM kontoauszuege WHERE buchung='$buchung' AND konto='$konto' AND pruefsumme='$pruefsumme' LIMIT 1";
|
|
$check = $app->DB->Select($sql);
|
|
if($check > 0) {
|
|
$duplicate++;
|
|
continue;
|
|
}
|
|
$soll = str_replace(',','.', $soll);
|
|
$haben = str_replace(',','.', $haben);
|
|
$sql = "INSERT INTO kontoauszuege (
|
|
konto,
|
|
buchung,
|
|
vorgang,
|
|
soll,
|
|
haben,
|
|
gebuehr,
|
|
waehrung,
|
|
fertig,
|
|
bearbeiter,
|
|
pruefsumme,
|
|
importgroup,
|
|
originalbuchung,
|
|
originalvorgang,
|
|
originalsoll,
|
|
originalhaben,
|
|
originalgebuehr,
|
|
originalwaehrung,
|
|
gegenkonto
|
|
) VALUE (
|
|
'$konto',
|
|
'$buchung',
|
|
'$vorgang',
|
|
'$soll',
|
|
'$haben',
|
|
'$gebuehr',
|
|
'$waehrung',
|
|
0,
|
|
'".$userName."',
|
|
'$pruefsumme',
|
|
'$stamp',
|
|
'$buchung',
|
|
'$vorgang',
|
|
'$soll',
|
|
'$haben',
|
|
'$gebuehr',
|
|
'$waehrung',
|
|
'$gegenkonto')";
|
|
|
|
$app->DB->Insert($sql);
|
|
$newid = $app->DB->GetInsertID();
|
|
$app->DB->Update("UPDATE kontoauszuege SET sort='$newid' WHERE id='$newid' LIMIT 1");
|
|
$inserted++;
|
|
}
|
|
|
|
return array($inserted, $duplicate);
|
|
}
|
|
|
|
/**
|
|
* @param string $filename
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
private function openFile($filename)
|
|
{
|
|
/*
|
|
* If the open fails, an error of level E_WARNING is generated.
|
|
*/
|
|
$handle = @fopen($filename,"r");
|
|
if (!$handle) {
|
|
throw new Exception(sprintf(
|
|
'Die Datei \'%s\' kann nicht geöffnet werden!', $filename
|
|
));
|
|
}
|
|
$this->handle = $handle;
|
|
}
|
|
|
|
/**
|
|
* Close File if Handle open
|
|
*/
|
|
private function closeFile()
|
|
{
|
|
if ($this->handle) {
|
|
fclose($this->handle);
|
|
$this->handle = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read one line, explode and trim all values.
|
|
*
|
|
* @return array
|
|
*/
|
|
private function readCSVLineAsArray()
|
|
{
|
|
$line = fgets($this->handle);
|
|
$line = trim($line);
|
|
$line = explode(self::COL_DIVIDER, $line);
|
|
|
|
$line = array_map('trim', $line);
|
|
$line = array_map([$this, 'trimDoubleQuotes'], $line);
|
|
|
|
return $line;
|
|
}
|
|
|
|
/**
|
|
* get the number of days as positive integer,
|
|
* they can be set via the config keys
|
|
* 'API_DAYS', 'DAYS' or 'TAGE'
|
|
*
|
|
* @param $config
|
|
* @return int
|
|
*/
|
|
private function getFirstDate($config)
|
|
{
|
|
$days = self::DEFAULT_N_DAYS;
|
|
if (array_key_exists('API_DAYS', $config) && is_numeric($config['API_DAYS'])) {
|
|
$days = $config['API_DAYS'];
|
|
} elseif (array_key_exists('DAYS', $config) && is_numeric($config['DAYS'])) {
|
|
$days = $config['DAYS'];
|
|
} elseif (array_key_exists('TAGE', $config) && is_numeric($config['TAGE'])) {
|
|
$days = $config['TAGE'];
|
|
}
|
|
|
|
if (!is_numeric($days)) {
|
|
$days = self::DEFAULT_N_DAYS;
|
|
}
|
|
|
|
$days = abs($days);
|
|
|
|
$time = time();
|
|
if ($days > 0) {
|
|
$time = strtotime("-{$days} days", $time);
|
|
}
|
|
|
|
// var_dump($time, date('Y-m-d', $time), $days);
|
|
|
|
return $time;
|
|
}
|
|
|
|
/**
|
|
* @param array $config
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getFtp($config)
|
|
{
|
|
$host = '';
|
|
$user = '';
|
|
$port = '';
|
|
$pw = '';
|
|
$debug = '';
|
|
$subdir = '';
|
|
$ssl = false;
|
|
$sftp = false;
|
|
if(!empty($config['SFTP'])) {
|
|
$sftp = true;
|
|
}
|
|
if(!empty($config['FTP_HOST'])) {
|
|
$host = $config['FTP_HOST'];
|
|
}
|
|
if(!empty($config['FTP_USERNAME'])) {
|
|
$user = $config['FTP_USERNAME'];
|
|
}
|
|
if(!empty($config['FTP_PASSWORD'])) {
|
|
$pw = $config['FTP_PASSWORD'];
|
|
}
|
|
if(!empty($config['FTP_PORT'])) {
|
|
$port = $config['FTP_PORT'];
|
|
}
|
|
if(!empty($config['FTP_DEBUG'])) {
|
|
$debug = $config['FTP_DEBUG'];
|
|
}
|
|
if(!empty($config['FTP_SSL'])) {
|
|
$ssl = $config['FTP_SSL'];
|
|
}
|
|
if(!empty($config['FTP_SUBDIR'])) {
|
|
$subdir = $config['FTP_SUBDIR'];
|
|
$this->subdir = $subdir;
|
|
}
|
|
return [$host, $port, $user, $pw, $debug, $ssl, $subdir, $sftp];
|
|
}
|
|
|
|
/**
|
|
* should we delete the given file after processing?
|
|
* default is false
|
|
*
|
|
* @param $config
|
|
* @return bool
|
|
*/
|
|
private function getDeleteFlag($config)
|
|
{
|
|
$delete = array_key_exists('API_DELETE', $config)
|
|
? $config['API_DELETE']
|
|
: false;
|
|
$delete = array_key_exists('DELETE', $config)
|
|
? $config['DELETE']
|
|
: $delete;
|
|
$delete = array_key_exists('RM', $config)
|
|
? $config['RM']
|
|
: $delete;
|
|
|
|
return (bool)$delete;
|
|
}
|
|
|
|
/**
|
|
* grab the file name, throw an exception
|
|
* if not present, invalid or not readable
|
|
*
|
|
* @param $config
|
|
* @return string the file name
|
|
* @throws Exception
|
|
*/
|
|
private function getFileName($config)
|
|
{
|
|
/*
|
|
* allow 'PATH' or 'FILE' as key to the .csv file
|
|
* as fallback if that keys aren't present use
|
|
* the value behind 'DEFAULT_FILE'
|
|
*/
|
|
|
|
$file = self::DEFAULT_FILE;
|
|
if (array_key_exists('PATH', $config)) {
|
|
$file = (string) $config['PATH'];
|
|
}
|
|
if (array_key_exists('FILE', $config)) {
|
|
$file = (string) $config['FILE'];
|
|
}
|
|
|
|
if (!$file) {
|
|
throw new Exception('Keine .csv Datei angegeben');
|
|
}
|
|
|
|
if (!$this->endsWith($file, '.csv')) {
|
|
throw new Exception('Keine .csv Datei angegeben');
|
|
}
|
|
if (strpos($file, '..')) {
|
|
throw new Exception(sprintf(
|
|
'Der Dateiname \'%s\' ist nicht erlaubt.', $file
|
|
));
|
|
}
|
|
if (!file_exists($file) || !is_file($file)) {
|
|
throw new Exception(
|
|
sprintf('Die Datei \'%s\' existiert nicht!', $file
|
|
));
|
|
}
|
|
if (!is_readable($file)) {
|
|
throw new Exception(sprintf(
|
|
'Die Datei \'%s\' kann nicht gelesen werden!', $file
|
|
));
|
|
}
|
|
return (string) $file;
|
|
}
|
|
|
|
/**
|
|
* @param array $line
|
|
*
|
|
* @return string
|
|
*/
|
|
private function implodeCSVLine($line)
|
|
{
|
|
return implode(self::COL_DIVIDER, $line);
|
|
}
|
|
|
|
/**
|
|
* @param array|string $lines
|
|
*
|
|
* @return array|false
|
|
*/
|
|
private function explodeCSVLines($lines)
|
|
{
|
|
if (is_array($lines)) {
|
|
$lines = implode("\n", $lines);
|
|
}
|
|
|
|
$lines = preg_split("/(\r\n)+|(\n|\r)+/", $lines);
|
|
|
|
return $lines;
|
|
}
|
|
|
|
/**
|
|
* @param string $line
|
|
*
|
|
* @return array
|
|
*/
|
|
private function explodeCSVLine($line)
|
|
{
|
|
$line = str_replace('"','', $line);
|
|
$line = explode(self::COL_DIVIDER, $line);
|
|
|
|
return $line;
|
|
}
|
|
|
|
/**
|
|
* @param string $haystack
|
|
* @param string $needle
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function endsWith($haystack, $needle)
|
|
{
|
|
$length = strlen($needle);
|
|
|
|
return $length === 0 ||
|
|
(substr($haystack, -$length) === $needle);
|
|
}
|
|
|
|
/**
|
|
* Should we append the line?
|
|
*
|
|
* compare the date from given line with
|
|
* the first allowed date
|
|
*
|
|
* @see https://stackoverflow.com/a/3847762 for date comparison
|
|
* @param array|string|int|bool $date
|
|
* @param int $firstDate timestamp
|
|
* @return bool
|
|
*/
|
|
private function timestampInRangeOfInterest($date, $firstDate)
|
|
{
|
|
if (is_array($date)) {
|
|
$date = $date['timestamp'];
|
|
}
|
|
if(!is_numeric($date) && (String)(int)$date !== (String)$date) {
|
|
$date = strtotime($date);
|
|
}
|
|
if (!$date || $date == -1) {
|
|
/*
|
|
* Returns a timestamp on success, FALSE otherwise.
|
|
* Previous to PHP 5.1.0, this function would return -1 on failure.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
return $date >= $firstDate;
|
|
}
|
|
|
|
/**
|
|
* Read the header line and return
|
|
* an array with index => name pairs like:
|
|
*
|
|
* array (
|
|
* 2 => 'timestamp',
|
|
* 4 => 'payment',
|
|
* 7 => 'currency',
|
|
* )
|
|
*
|
|
* @param array $line
|
|
* @param array $required the required values
|
|
* @return array
|
|
*/
|
|
private function extractIndexFromHeaderLine($line, $required)
|
|
{
|
|
$tmp = array_flip($line);
|
|
$tmp = array_change_key_case($tmp, CASE_LOWER);
|
|
|
|
$index = array_flip($required);
|
|
$index = array_intersect_key($tmp, $index);
|
|
|
|
$index = array_flip($index);
|
|
ksort($index);
|
|
|
|
return $index;
|
|
}
|
|
|
|
/**
|
|
* Remove the double quotes from each value
|
|
*
|
|
* @param string $line
|
|
* @return string
|
|
*/
|
|
private function trimDoubleQuotes($line)
|
|
{
|
|
return trim($line, '"');
|
|
}
|
|
|
|
/**
|
|
* Throw an exception, is one or more required array values
|
|
* are missing.
|
|
*
|
|
* @param array $array
|
|
* @param array $required
|
|
* @throws Exception
|
|
*/
|
|
private function checkMissingArrayValues($array, $required)
|
|
{
|
|
foreach ($required as $req) {
|
|
if (!in_array($req, $array)) {
|
|
throw new Exception(sprintf(
|
|
'Could not find column \'%s\' in header line.', $req
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract the data of interest.
|
|
*
|
|
* $indexes is required as array with
|
|
* index => name values like:
|
|
*
|
|
* array (
|
|
* 2 => 'timestamp',
|
|
* 4 => 'payment',
|
|
* 7 => 'currency',
|
|
* )
|
|
*
|
|
* returns an array like:
|
|
*
|
|
* array (
|
|
* 'timestamp' => '2019-01-02 14:29:31',
|
|
* 'payment' => '189,90',
|
|
* 'currency' => 'EUR',
|
|
* )
|
|
*
|
|
* @param array $line
|
|
* @param array $indexes
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
private function getDataOfInterest($line, $indexes)
|
|
{
|
|
$tmp = array_intersect_key($line, $indexes);
|
|
ksort($tmp);
|
|
|
|
/*
|
|
* Check if an required index is missing.
|
|
*/
|
|
$missed = array_diff_key($indexes, $line);
|
|
if (!empty($missed)) {
|
|
$cols = count($missed) < 2 ? 'column' : 'columns';
|
|
$missed = implode(', ', $missed);
|
|
throw new Exception(sprintf(
|
|
'Could not find %s \'%s\'', $cols, $missed
|
|
));
|
|
}
|
|
|
|
/*
|
|
* Merge column name with it's value
|
|
*/
|
|
$tmp = array_combine($indexes, $tmp);
|
|
|
|
return $tmp;
|
|
}
|
|
}
|
|
|