diff --git a/README.md b/README.md index 5680df4..9c150fc 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,28 @@ Raiffeisenbank for Stormware Pohoda Configuration ------------- -```````````` -`EASE_LOGGER`=syslog|console -`CERT_FILE`='RAIFF_CERT.p12' -`CERT_PASS`=CertPass -`XIBMCLIENTID`=PwX4bOQLWGiuoErv6I -`ACCOUNT_NUMBER`=666666666 -`ACCOUNT_CURRENCY`=CZK - -`STATEMENT_IMPORT_SCOPE`=last_two_months -`TRANSACTION_IMPORT_SCOPE`=yesterday - -`API_DEBUG`=True -`APP_DEBUG`=True -`STATEMENT_LINE`=ADDITIONAL - -`POHODA_ICO`=12345678 -`POHODA_URL`=http://10.11.25.25:10010 -`POHODA_USERNAME`=mServerXXX -`POHODA_PASSWORD`=mServerXXX -`POHODA_TIMEOUT`=60 -`POHODA_COMPRESS`=false -`POHODA_DEBUG`=true -`POHODA_BANK_IDS`=RB - - - +```env +EASE_LOGGER=syslog|console +CERT_FILE='RAIFF_CERT.p12' +CERT_PASS=CertPass +XIBMCLIENTID=PwX4bOQLWGiuoErv6I +ACCOUNT_NUMBER=666666666 +ACCOUNT_CURRENCY=CZK + +STATEMENT_IMPORT_SCOPE=last_two_months +TRANSACTION_IMPORT_SCOPE=yesterday + +API_DEBUG=True +APP_DEBUG=True +STATEMENT_LINE=ADDITIONAL + +POHODA_ICO=12345678 +POHODA_URL=http://10.11.25.25:10010 +POHODA_USERNAME=mServerXXX +POHODA_PASSWORD=mServerXXX +POHODA_TIMEOUT=60 +POHODA_COMPRESS=false +POHODA_DEBUG=true +POHODA_BANK_IDS=RB +``` diff --git a/composer.json b/composer.json index 238725a..271f846 100644 --- a/composer.json +++ b/composer.json @@ -11,9 +11,10 @@ "minimum-stability": "dev", "bin": ["src/pohoda-raiffeisenbank-transactions.php", "src/pohoda-raiffeisenbank-setup.php"], "require": { + "vgrem/php-spo": "^3", "vitexsoftware/pohoda-connector": "dev-main", "vitexsoftware/rbczpremiumapi": "dev-main", - "spojenet/pohoda-sql": "dev-master" + "spojenet/pohoda-sql": "dev-main" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 4c747e3..c401f8e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "651243b951839e2d545405da094ed0e6", + "content-hash": "d516f6e5549082b5e9617e439fa9d342", "packages": [ { "name": "doctrine/instantiator", @@ -154,6 +154,70 @@ ], "time": "2024-02-05T12:02:27+00:00" }, + { + "name": "firebase/php-jwt", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "500501c2ce893c824c801da135d02661199f60c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5", + "reference": "500501c2ce893c824c801da135d02661199f60c5", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.10.1" + }, + "time": "2024-05-18T18:05:11+00:00" + }, { "name": "fpdo/fluentpdo", "version": "dev-master", @@ -1390,16 +1454,16 @@ }, { "name": "spojenet/pohoda-sql", - "version": "dev-master", + "version": "dev-main", "source": { "type": "git", "url": "https://github.com/Spoje-NET/PohodaSQL.git", - "reference": "b95999832498063439c0725777afc6e30aa28b5e" + "reference": "8e0b40ab66a07dbcfdc176891bff684a7d9a97d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spoje-NET/PohodaSQL/zipball/b95999832498063439c0725777afc6e30aa28b5e", - "reference": "b95999832498063439c0725777afc6e30aa28b5e", + "url": "https://api.github.com/repos/Spoje-NET/PohodaSQL/zipball/8e0b40ab66a07dbcfdc176891bff684a7d9a97d7", + "reference": "8e0b40ab66a07dbcfdc176891bff684a7d9a97d7", "shasum": "" }, "require": { @@ -1408,6 +1472,7 @@ "require-dev": { "phpunit/phpunit": "^9.5" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -1427,9 +1492,9 @@ "description": "Management Library for PohodaSQL", "support": { "issues": "https://github.com/Spoje-NET/PohodaSQL/issues", - "source": "https://github.com/Spoje-NET/PohodaSQL/tree/master" + "source": "https://github.com/Spoje-NET/PohodaSQL/tree/main" }, - "time": "2023-12-08T15:54:33+00:00" + "time": "2024-06-13T20:14:48+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1717,6 +1782,80 @@ ], "time": "2024-06-02T19:15:21+00:00" }, + { + "name": "vgrem/php-spo", + "version": "v3.1.2", + "source": { + "type": "git", + "url": "https://github.com/vgrem/phpSPO.git", + "reference": "a3b445bde0d3a45ef71a2a0be6f9baf00864588e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vgrem/phpSPO/zipball/a3b445bde0d3a45ef71a2a0be6f9baf00864588e", + "reference": "a3b445bde0d3a45ef71a2a0be6f9baf00864588e", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-simplexml": "*", + "firebase/php-jwt": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Office365\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Vadim Gremyachev", + "email": "vvgrem@gmail.com" + } + ], + "description": "PHP Office 365 library. It allows to performs CRUD operations against Office 365 resources via an REST/OData based API", + "keywords": [ + "Office365", + "api", + "curl", + "microsoftgraph", + "rest" + ], + "support": { + "issues": "https://github.com/vgrem/phpSPO/issues", + "source": "https://github.com/vgrem/phpSPO/tree/v3.1.2" + }, + "funding": [ + { + "url": "https://paypal.me/ossvgrem", + "type": "custom" + }, + { + "url": "https://github.com/[user1", + "type": "github" + }, + { + "url": "https://github.com/user2", + "type": "github" + }, + { + "url": "https://github.com/vgrem] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g.", + "type": "github" + } + ], + "time": "2024-04-21T18:46:47+00:00" + }, { "name": "vitexsoftware/ease-core", "version": "dev-main", @@ -1842,12 +1981,12 @@ "source": { "type": "git", "url": "https://github.com/VitexSoftware/PHP-Pohoda-Connector.git", - "reference": "331548aae638f32547c69d8d29177638dd01c129" + "reference": "ae618999c359831b06b1dcfc01c7532e7824bbdc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/VitexSoftware/PHP-Pohoda-Connector/zipball/331548aae638f32547c69d8d29177638dd01c129", - "reference": "331548aae638f32547c69d8d29177638dd01c129", + "url": "https://api.github.com/repos/VitexSoftware/PHP-Pohoda-Connector/zipball/ae618999c359831b06b1dcfc01c7532e7824bbdc", + "reference": "ae618999c359831b06b1dcfc01c7532e7824bbdc", "shasum": "" }, "require": { @@ -2809,7 +2948,7 @@ "type": "patreon" } ], - "time": "2024-06-12T13:23:38+00:00" + "time": "2024-06-13T20:41:29+00:00" }, { "name": "vitexsoftware/rbczpremiumapi", @@ -2883,12 +3022,12 @@ "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "2f5294676c802a62b0549f6bc8983f14294ce369" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/2f5294676c802a62b0549f6bc8983f14294ce369", - "reference": "2f5294676c802a62b0549f6bc8983f14294ce369", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -2928,7 +3067,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.x" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -2936,7 +3075,7 @@ "type": "tidelift" } ], - "time": "2024-02-10T11:10:03+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "nikic/php-parser", @@ -2944,12 +3083,12 @@ "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "daaadc3bae458908aa477b90a8932e7da9253f22" + "reference": "3ef0811e45ba7e91fb0f066af5af7d52c3b24469" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/daaadc3bae458908aa477b90a8932e7da9253f22", - "reference": "daaadc3bae458908aa477b90a8932e7da9253f22", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3ef0811e45ba7e91fb0f066af5af7d52c3b24469", + "reference": "3ef0811e45ba7e91fb0f066af5af7d52c3b24469", "shasum": "" }, "require": { @@ -2995,7 +3134,7 @@ "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/master" }, - "time": "2024-06-03T06:24:19+00:00" + "time": "2024-06-12T18:31:58+00:00" }, { "name": "phar-io/manifest", diff --git a/src/Pohoda/RaiffeisenBank/PohodaBankClient.php b/src/Pohoda/RaiffeisenBank/PohodaBankClient.php index 3e99d90..28be5ba 100644 --- a/src/Pohoda/RaiffeisenBank/PohodaBankClient.php +++ b/src/Pohoda/RaiffeisenBank/PohodaBankClient.php @@ -43,6 +43,12 @@ abstract class PohodaBankClient extends \mServer\Bank */ public static $dateFormat = 'Y-m-d'; + /** + * + * @var Response + */ + public $response; + /** * * @var @@ -214,6 +220,9 @@ public function ensureKSExists($conSym) */ public function insertTransactionToPohoda($success) { + $producedId = ''; + $producedNumber = ''; + $producedAction = ''; if ($this->checkForTransactionPresence() === false) { try { $cache = $this->getData(); @@ -223,9 +232,19 @@ public function insertTransactionToPohoda($success) if ($this->commit()) { $success++; } + if (property_exists($this->response, 'producedDetails') && is_array($this->response->producedDetails)) { + $producedId = $this->response->producedDetails['id']; + $producedNumber = $this->response->producedDetails['number']; + $producedAction = $this->response->producedDetails['actionType']; + } else { + echo ''; + } } catch (\Pohoda\Exception $exc) { + $producedId = 'n/a'; + $producedNumber = 'n/a'; + $producedAction = 'n/a'; } - $this->addStatusMessage('New entry ', $result ? 'success' : 'error'); // TODO: Parse response for docID + $this->addStatusMessage('#' . $producedId . ' ' . $producedAction . ' ' . $producedNumber, $result ? 'success' : 'error'); // TODO: Parse response for docID } else { $this->addStatusMessage('Record with remoteNumber ' . 'TODO' . ' already present in Pohoda', 'warning'); } diff --git a/src/Pohoda/RaiffeisenBank/Statementor.php b/src/Pohoda/RaiffeisenBank/Statementor.php index 74e57c4..cb61602 100644 --- a/src/Pohoda/RaiffeisenBank/Statementor.php +++ b/src/Pohoda/RaiffeisenBank/Statementor.php @@ -34,6 +34,18 @@ class Statementor extends PohodaBankClient */ public $account; + /** + * Downloaded XML statements + * @var array + */ + private $statementsXML = []; + + /** + * Downloaded PDF statements + * @var array + */ + private $statementsPDF = []; + /** * * @param string $bankAccount @@ -59,32 +71,41 @@ public function __construct($bankAccount, $options = []) */ public function import() { - $statementsXML = $this->obtainer->download($this->statementsDir, $this->obtainer->getStatements(), 'xml'); -// $statementsPDF = $this->obtainer->download($this->statementsDir, $this->obtainer->getStatements(), 'pdf'); + $inserted = []; + $this->statementsXML = $this->obtainer->download($this->statementsDir, $this->obtainer->getStatements(), 'xml'); + $this->statementsPDF = $this->obtainer->download($this->statementsDir, $this->obtainer->getStatements(), 'pdf'); $this->account = \Ease\Shared::cfg('POHODA_BANK_IDS', 'RB'); //TODO!!! $success = 0; - foreach ($statementsXML as $pos => $statement) { + foreach ($this->statementsXML as $pos => $statement) { $statementXML = new \SimpleXMLElement(file_get_contents($statement)); + $statementNumberLong = current((array) $statementXML->BkToCstmrStmt->Stmt->Id); foreach ($statementXML->BkToCstmrStmt->Stmt->Ntry as $entry) { $this->dataReset(); $this->setData($this->entryToPohoda($entry)); list($statementNumber, $statementYear, ) = explode('_', $pos); $this->setDataValue('statementNumber', ['statementNumber' => $statementNumber . '/' . $statementYear]); - $this->setDataValue('account', current((array) $entry->NtryRef)); +// $this->setDataValue('account', current((array) $entry->NtryRef)); // $this->setDataValue('vypisCisDokl', $statementXML->BkToCstmrStmt->Stmt->Id); // $this->setDataValue('cisSouhrnne', $statementXML->BkToCstmrStmt->Stmt->LglSeqNb); $success = $this->insertTransactionToPohoda($success); - if ($success && property_exists($this->response, 'producedDetails')) { - $inserted[$this->response->producedDetails['id']] = $this->response->producedDetails; + if (property_exists($this->response, 'producedDetails') && is_array($this->response->producedDetails)) { + if (array_key_exists('id', $this->response->producedDetails)) { + $inserted[$this->response->producedDetails['id']] = $this->response->producedDetails; + } else { + echo ''; // WTF? + } } } - $this->addStatusMessage('Import done. ' . $success . ' of ' . count($statementsXML) . ' imported'); + $this->addStatusMessage($statementNumberLong . ' Import done. ' . $success . ' of ' . count($this->statementsXML) . ' imported'); return $inserted; } } /** - * Parse Ntry element into \Pohoda\Banka data + * Parse Ntry element and convert into \Pohoda\Banka data + * + * @see https://cbaonline.cz/upload/1425-standard-xml-cba-listopad-2020.pdf + * @see https://www.stormware.cz/xml/schema/version_2/bank.xsd * * @param SimpleXMLElement $entry * @@ -92,6 +113,7 @@ public function import() */ public function entryToPohoda($entry) { + $data['symPar'] = current((array) $entry->NtryRef); $data['intNote'] = 'Import Job ' . \Ease\Shared::cfg('JOB_ID', 'n/a'); $data['note'] = 'Imported by ' . \Ease\Shared::AppName() . ' ' . \Ease\Shared::AppVersion(); $data['datePayment'] = current((array) $entry->BookgDt->DtTm); @@ -110,22 +132,39 @@ public function entryToPohoda($entry) if (property_exists($entry->NtryDtls->TxDtls, 'AddtlTxInf')) { $data['text'] = current((array) $entry->NtryDtls->TxDtls->AddtlTxInf); } +// if ($entry->NtryDtls->TxDtls->Refs->MsgId) { +// $data['numberMovement'] = current((array) $entry->NtryDtls->TxDtls->Refs->MsgId); +// } if ($entry->NtryDtls->TxDtls->Refs->InstrId) { + // ZPS: Platební titul, + // SEPA: Identifikace platby Dříve i pro TPS: Konstantní symbol $data['symConst'] = current((array) $entry->NtryDtls->TxDtls->Refs->InstrId); } if (property_exists($entry->NtryDtls->TxDtls->Refs, 'EndToEndId')) { + // ZPS: Klientská reference, + // SEPA: Reference, Karetní operace: Číslo dobíjeného mobilu, případně číslo faktury, Klientská reference Dříve i pro TPS: Variabilní symbol $data['symVar'] = current((array) $entry->NtryDtls->TxDtls->Refs->EndToEndId); } $paymentAccount = []; if (property_exists($entry->NtryDtls->TxDtls, 'RltdPties')) { if (property_exists($entry->NtryDtls->TxDtls->RltdPties, 'DbtrAcct')) { $paymentAccount['accountNo'] = current((array) $entry->NtryDtls->TxDtls->RltdPties->DbtrAcct->Id->Othr->Id); - } - if (property_exists($entry->NtryDtls->TxDtls->RltdPties, 'DbtrAcct')) { + $data['partnerIdentity'] = [//"address", "addressLinkToAddress", "extId", "id", "shipToAddress" 'address' => [// "VATPayerType", "city", "company", "country", "dic", "division", "email", "fax", "icDph", "ico", "mobilPhone", "name", "phone", "street", "zip" 'name' => current((array) $entry->NtryDtls->TxDtls->RltdPties->DbtrAcct->Nm)]]; } + + if (property_exists($entry->NtryDtls->TxDtls->RltdPties, 'CdtrAcct')) { + $paymentAccount['accountNo'] = current((array) $entry->NtryDtls->TxDtls->RltdPties->CdtrAcct->Id->Othr->Id); + $data['partnerIdentity'] = [ + 'address' => [ + 'name' => current((array) $entry->NtryDtls->TxDtls->RltdPties->CdtrAcct->Nm) + ] + ]; + } + } else { + echo ''; // No Related party ? } if (property_exists($entry->NtryDtls->TxDtls, 'RltdAgts')) { if (property_exists($entry->NtryDtls->TxDtls->RltdAgts->DbtrAgt, 'FinInstnId')) { @@ -137,7 +176,9 @@ public function entryToPohoda($entry) // $data['paymentAccount'] = current((array) $paymentAccount['accountNo']); // } // accountNo, bankCode - $data['paymentAccount'] = $paymentAccount; + if (empty($paymentAccount) === false) { + $data['paymentAccount'] = $paymentAccount; + } } } // @@ -167,6 +208,10 @@ function setScope($scope) $this->since = new \DateTime("first day of last month"); $this->until = new \DateTime("last day of last month"); break; + case 'last_week': + $this->since = new \DateTime("first day of last week"); + $this->until = new \DateTime("last day of last week"); + break; case 'last_two_months': $this->since = (new \DateTime("first day of last month"))->modify('-1 month'); $this->until = (new \DateTime("last day of last month")); @@ -217,4 +262,22 @@ function setScope($scope) $this->until = $this->until->setTime(0, 0); } } + + /** + * List of downloaded PDF statements + * @return array + */ + public function getPdfStatements() + { + return $this->statementsPDF; + } + + /** + * List of downloaded XML statements + * @return array + */ + public function getXmlStatements() + { + return $this->statementsXML; + } } diff --git a/src/pohoda-raiffeisenbank-statements.php b/src/pohoda-raiffeisenbank-statements.php index fcf6abc..a92768a 100644 --- a/src/pohoda-raiffeisenbank-statements.php +++ b/src/pohoda-raiffeisenbank-statements.php @@ -10,6 +10,10 @@ namespace Pohoda\RaiffeisenBank; use SpojeNet\PohodaSQL\Agenda; +use Ease\Shared; +use Office365\Runtime\Auth\ClientCredential; +use Office365\Runtime\Auth\UserCredentials; +use Office365\SharePoint\ClientContext; require_once('../vendor/autoload.php'); @@ -41,11 +45,36 @@ // +$pdfs = $engine->getPdfStatements(); + +if (Shared::cfg('OFFICE365_USERNAME', false) && Shared::cfg('OFFICE365_PASSWORD', false)) { + $credentials = new UserCredentials(Shared::cfg('OFFICE365_USERNAME'), Shared::cfg('OFFICE365_PASSWORD')); +} else { + $credentials = new ClientCredential(Shared::cfg('OFFICE365_CLIENTID'), Shared::cfg('OFFICE365_CLSECRET')); +} + +$ctx = (new ClientContext('https://' . Shared::cfg('OFFICE365_TENANT') . '.sharepoint.com/sites/' . Shared::cfg('OFFICE365_SITE')))->withCredentials($credentials); +$targetFolder = $ctx->getWeb()->getFolderByServerRelativeUrl('Sdilene dokumenty/Banky'); + +foreach ($pdfs as $filename) { + $uploadFile = $targetFolder->uploadFile(basename($filename), file_get_contents($filename)); + try { + $ctx->executeQuery(); + } catch (Exception $exc) { + fwrite(fopen('php://stderr', 'wb'), $exc->getMessage() . PHP_EOL); + exit(1); + } + $fileUrl = $ctx->getBaseUrl() . '/_layouts/15/download.aspx?SourceUrl=' . urlencode($uploadFile->getServerRelativeUrl()); +} + + + $doc = new \SpojeNet\PohodaSQL\DOC(); -$doc->setDataValue('RelAgID', Agenda::BANK); //Bank +$doc->setDataValue('RelAgID', \SpojeNet\PohodaSQL\Agenda::BANK); //Bank foreach ($inserted as $id => $importInfo) { - $url = \Ease\Shared::cfg('DOWNLOAD_LINK_PREFIX') . $id; - $result = $doc->urlAttachment('Sharepoint', $url, $id); - $doc->addStatusMessage($importInfo['number'] . ' URL', is_null($result) ? 'error' : 'success'); + $statement = current($pdfs); + //$url = \Ease\Shared::cfg('DOWNLOAD_LINK_PREFIX') . urlencode(basename($statement)); + $result = $doc->urlAttachment($id, $fileUrl, basename($statement)); + $doc->addStatusMessage($importInfo['number'] . ' ' . $fileUrl, is_null($result) ? 'error' : 'success'); }