diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a83542b --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +REALPAD_USERNAME=realpad +REALPAD_PASSWORD=realpad diff --git a/.gitignore b/.gitignore index a67d42b..587d495 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ composer.phar /vendor/ - -# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control -# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file -# composer.lock +/nbproject/private/ +/.env +/composer.lock +/nbproject/ +/tests/.phpunit.result.cache +.vscode/settings.json diff --git a/README.md b/README.md index 96629c6..aaa5e8f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,181 @@ -# Realpad-Takeout -Realpad Takeout API Client to back up the data stored in the system. +# Realpad Takeout API Client for PHP + +//for real estate developer IT / reporting departments, integrators, and +other 3rd parties// + +This document describes how to use the Realpad Takeout API to back up the data stored in the +system. Both structured data (such as lists of customers, deals, etc) and files uploaded to the +CRM (unit plans, contract scans, ...) can be automatically retrieved this way. You are +encouraged to implement an automatic backup system that will download the data from our +server at any frequency you prefer, and use the data as a source for a reporting solution, or any +other purpose. + +## Request + +First of all please contact to obtain the credentials to use the +Takeout API endpoints. You will perform POST requests over HTTPS and then store the +resulting data. Most of the Takeout endpoints have just 2 parameters, both required: `login`` and +`password`. + +Example call in cURL: + +```shell +curl \ +--data "login=...&password=..." \ +--output customers.xls \ +https://cms.realpad.eu/ws/v10/list-excel-customers +``` + +### Response + +#### Endpoints with XML payload + +**list-resources** +The root element is ``, sub-elements look like this: + +```xml + +``` + +● uid is the unique identifier of this resource, by which it can be retrieved using +get-projects. +● content-type is the MIME type of the file, resolved when uploaded to the system (it’s +the best guess). +● file-name is the original file name when it was uploaded to the system. +● size is the file size in bytes. +● crc is the CRC32 checksum of the file. + +This endpoint will always return all the resources. It’s up to your system to determine which +ones you haven’t downloaded yet. You may rely on UID as the unique identifier to distinguish +between the files. You can fetch resources using HTTP GET by retrieving a URL in the following +form: `` +Example call in cURL: + +```shell +curl \ +--output cached_resource \ +https://cms.realpad.eu/resource/bd5563ae-abc... +``` + +## Endpoints with a binary payload + +All of these endpoints return a single Excel file with a .xls extension, containing all the relevant +data stored in our system. These endpoints behave just like get-resource, in that the HTTP +headers contain a reasonable file name (e.g. when running from a web browser). +If an extra parameter xlsx is sent with a non-empty value, Takeout API will instead provide the +data in the Excel newer .xlsx format. + +**list-excel-customers** +The last column contains the unique customer ID from the Realpad database. + +**list-excel-products** +The last columns contain the unique unit ID, numeric ID of the unit type, numeric ID of the unit +availability, unique project ID and deal ID from the Realpad database. See the appendix for the +unit type and availability enums. + +**list-excel-business-cases** +The last column contains the unique Deal ID from the Realpad database. + +**list-excel-projects** +The last column contains the unique project ID from the Realpad database. + +**list-excel-deal-documents** +The last three columns contain the unique document ID, customer ID, and sales agent ID from +the Realpad database. The first column is the relevant deal ID. + +**list-excel-payments-prescribed** +The last column contains the unique payment ID from the Realpad database. The second +column is the relevant deal ID. + +**list-excel-payments-incoming** +The first column contains the unique incoming payment ID from the Realpad database. The +second column is the relevant Deal ID. + +**list-excel-additional-products** +The last columns contain the additional product ID, its type ID, and the ID of the associated +prescribed payment from the Realpad database. The first column is the relevant deal ID. + +**list-excel-inspections** +Among the columns, there are those representing the deal ID and inspection ID from the +Realpad database. + +**list-excel-defects** +Accepts an additional optional parameter mode. By default all the Deal Warranty Claim Defects +are returned. Certain developers will also see the Communal Areas Defects here by default. If +mode is specified, other Defects can be returned. Available modes are: DEAL_DEFECTS, +DEAL_DEFECTS_COMMUNAL_AREA, DEAL_DEFECTS_COMBINED, INSPECTION_DEFECTS, +INSPECTION_DEFECTS_COMMUNAL_AREA, INSPECTION_DEFECTS_COMBINED. +The last column contains the unique defect ID from the Realpad database. The second column +is the relevant deal ID. + +**list-excel-tasks** +The last columns contain the task ID, customer ID, and sales agent ID from the Realpad +database. + +**list-excel-events** +The last columns contain the event ID, customer ID, unit, and project ID from the Realpad +database. + +**list-excel-sales-status** +The last column contains the unit ID from the Realpad database. + +**list-excel-unit-history** +Accepts an additional required parameter unitid, which has to be a valid unit Realpad database +ID obtained from some other endpoint. +The first column contains the timestamp of when the given unit started containing the data on +the given row. The second column contains the name of the user who caused that data to be +recorded. + +**list-excel-invoices** +Accepts several additional optional parameters: +● `filter_status`` - if left empty, invoices in all statuses are sent. 1 - new invoices. 2 - +invoices in Review #1. 3 - invoices in Review #2. 4 - invoices in approval. 5 - fully +approved invoices. 6 - fully rejected invoices. +●`filter_groupcompany` - if left empty, invoices from all the group companies are sent. If +Realpad database IDs of group companies are provided (as a comma-separated list), +then only invoices from these companies are sent. +● `filter_issued_from`` - specify a date in the 2019-12-31 format to only send invoices +issues after that date. +● `filter_issued_to` - specify a date in the 2019-12-31 format to only send invoices issues +before that date. +The initial set of columns describes the Invoice itself, and the last set of columns contains the +data of its Lines. + +## Appendix + +Unit status enumeration +● 0 - free. +● 1 - pre-reserved. +● 2 - reserved. +● 3 - sold. +● 4 - not for sale. +● 5 - delayed. + +Unit type enumeration +● 1 - flat. +● 2 - parking. +● 3 - cellar. +● 4 - outdoor parking. +● 5 - garage. +● 6 - commercial space. +● 7 - family house. +● 8 - land. +● 9 - atelier. +● 10 - office. +● 11 - art workshop. +● 12 - non-residential unit. +● 13 - motorbike parking. +● 14 - creative workshop. +● 15 - townhouse. +● 16 - utility room. +● 17 - condominium. +● 18 - storage. +● 19 - apartment. +● 20 - accommodation unit. +● 21 - bike stand. +● 22 - communal area. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..fbe1c1a --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "spojenet/realpad-takeout", + "description": "Realpad Takeout API Client", + "type": "library", + "require": { + "vitexsoftware/ease-core": "dev-main" + }, + "require-dev": { + "phpunit/phpunit": "10.5.x-dev" + }, + "license": "MIT", + "autoload": { + "psr-4": { + "SpojeNet\\Realpad\\": "src/" + } + }, + "authors": [ + { + "name": "CyberVitexus", + "email": "info@vitexsoftware.cz" + } + ], + "minimum-stability": "dev" +} diff --git a/examples/listexcelcustomers.php b/examples/listexcelcustomers.php new file mode 100644 index 0000000..03ca716 --- /dev/null +++ b/examples/listexcelcustomers.php @@ -0,0 +1,13 @@ +getResources(); + +print_r($resources); + diff --git a/src/ApiClient.php b/src/ApiClient.php new file mode 100644 index 0000000..6d098c0 --- /dev/null +++ b/src/ApiClient.php @@ -0,0 +1,148 @@ + + * @copyright 2023 SpojeNetIT s.r.o. + */ + +declare(strict_types=1); + +namespace SpojeNet\Realpad; + +/** + * Connect to TakeOut + * + * @author vitex + */ +class ApiClient extends \Ease\Molecule +{ + /** + * RealPad URI + * @var string + */ + public $baseEndpoint = 'https://cms.realpad.eu/'; + + private $debug; + + private $curl; + + private $timeout; + + /** + * RealPad Data obtainer + */ + public function __construct() + { + $this->apiUsername = \Ease\Shared::cfg('REALPAD_USERNAME'); + $this->apiPassword = \Ease\Shared::cfg('REALPAD_PASSWORD'); + } + + /** + * Inicializace CURL + * + * @return boolean Online Status + */ + public function curlInit() + { + if ($this->offline === false) { + $this->curl = \curl_init(); // create curl resource + \curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); // return content as a string from curl_exec + \curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true); // follow redirects + \curl_setopt($this->curl, CURLOPT_HTTPAUTH, true); // HTTP authentication + \curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, true); + \curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false); + \curl_setopt($this->curl, CURLOPT_VERBOSE, ($this->debug === true)); // For debugging + if (empty($this->authSessionId)) { + \curl_setopt( + $this->curl, + CURLOPT_USERPWD, + $this->user . ':' . $this->password + ); // set username and password + } + if (!is_null($this->timeout)) { + \curl_setopt($this->curl, CURLOPT_HTTPHEADER, [ + 'Connection: Keep-Alive', + 'Keep-Alive: ' . $this->timeout + ]); + \curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout); + } + + \curl_setopt($this->curl, CURLOPT_USERAGENT, 'RealpadTakeout v' . \Ease\Shared::appVer() . ' https://github.com/Spoje-NET/Realpad-Takeout'); + } + return !$this->offline; + } + + /** + * Vykonej HTTP požadavek + * + * @param string $url URL požadavku + * @param string $method HTTP Method GET|POST|PUT|OPTIONS|DELETE + * @param string $format požadovaný formát komunikace + * + * @return int HTTP Response CODE + */ + public function doCurlRequest($url, $method, $format = null) + { + if (is_null($format)) { + $format = $this->format; + } + curl_setopt($this->curl, CURLOPT_URL, $url); +// Nastavení samotné operace + curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, strtoupper($method)); +//Vždy nastavíme byť i prázná postdata jako ochranu před chybou 411 + curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->postFields); + $httpHeaders = $this->defaultHttpHeaders; + $formats = Formats::bySuffix(); + if (!isset($httpHeaders['Accept'])) { + $httpHeaders['Accept'] = $formats[$format]['content-type']; + } + if (!isset($httpHeaders['Content-Type'])) { + $httpHeaders['Content-Type'] = $formats[$format]['content-type']; + } + + array_walk($httpHeaders, function (&$value, $header) { + $value = $header . ': ' . $value; + }); + curl_setopt($this->curl, CURLOPT_HTTPHEADER, $httpHeaders); +// Proveď samotnou operaci + $this->lastCurlResponse = curl_exec($this->curl); + $this->curlInfo = curl_getinfo($this->curl); + $this->curlInfo['when'] = microtime(); + $this->responseFormat = $this->contentTypeToResponseFormat(strval($this->curlInfo['content_type']), $url); + $this->lastResponseCode = $this->curlInfo['http_code']; + $this->lastCurlError = curl_error($this->curl); + if (strlen($this->lastCurlError)) { + $msg = sprintf('Curl Error (HTTP %d): %s', $this->lastResponseCode, $this->lastCurlError); + $this->addStatusMessage($msg, 'error'); + if ($this->throwException) { + throw new Exception($msg, $this); + } + } + + if ($this->debug === true) { + $this->saveDebugFiles(); + } + return $this->lastResponseCode; + } + + /** + * Realpad server disconnect. + */ + public function disconnect() + { + if (is_resource($this->curl)) { + curl_close($this->curl); + } + $this->curl = null; + } + + /** + * + */ + public function __destruct() + { + $this->disconnect(); + } +}