Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Exception;
|
||||
use FiskalyClient\errors\exceptions\FiskalyClientException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpTimeoutException;
|
||||
use FiskalyClient\FiskalyClient;
|
||||
use FiskalyClient\responses\SelfTestResponse;
|
||||
use Xentral\Components\HttpClient\Exception\ClientErrorException;
|
||||
use Xentral\Modules\FiskalyApi\Data\TechnicalSecuritySystem;
|
||||
use Xentral\Modules\FiskalyApi\Data\Client;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidCredentialsException;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidTransactionException;
|
||||
use Xentral\Modules\FiskalyApi\Exception\SmaEndpointNotFoundException;
|
||||
use Xentral\Modules\FiskalyApi\Exception\SmaEndpointNotReachableException;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Transaction;
|
||||
|
||||
/**
|
||||
* Class FiskalyApi
|
||||
*
|
||||
* @package Xentral\Modules\FiskalyApi\Service
|
||||
*/
|
||||
class FiskalyApi
|
||||
{
|
||||
/** @var string */
|
||||
private $apiKey;
|
||||
|
||||
/** @var string */
|
||||
private $apiSecret;
|
||||
|
||||
/** @var string */
|
||||
private $accessToken = null;
|
||||
|
||||
/** @var FiskalyClient */
|
||||
private $fiskalyClient;
|
||||
|
||||
const DEFAULT_SMA_ENDPOINT = 'http://localhost:8080/invoke';
|
||||
|
||||
/**
|
||||
* FiskalyApi constructor.
|
||||
*
|
||||
* @param string $smaEndpoint
|
||||
* @param string $apiKey
|
||||
* @param string $apiSecret
|
||||
* @param string $endpoint
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(string $smaEndpoint, string $apiKey, string $apiSecret, string $endpoint)
|
||||
{
|
||||
if(empty($smaEndpoint)) {
|
||||
$smaEndpoint = self::DEFAULT_SMA_ENDPOINT;
|
||||
}
|
||||
try {
|
||||
$this->fiskalyClient = FiskalyClient::createUsingCredentials(
|
||||
$smaEndpoint,
|
||||
$apiKey,
|
||||
$apiSecret,
|
||||
$endpoint
|
||||
);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
if(strpos($e->getMessage(), '404') === 0) {
|
||||
throw new SmaEndpointNotFoundException($e->getMessage());
|
||||
}
|
||||
if($e->getMessage() === 'Undefined variable: http_response_header') {
|
||||
throw new SmaEndpointNotReachableException($e->getMessage());
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
$this->apiKey = $apiKey;
|
||||
$this->apiSecret = $apiSecret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $apiKey
|
||||
* @param string $apiSecret
|
||||
*
|
||||
* @throws ClientErrorException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateAccessToken(string $apiKey, string $apiSecret): string
|
||||
{
|
||||
$result = $this->callApiPost(
|
||||
'auth',
|
||||
json_encode(
|
||||
[
|
||||
'api_key' => $apiKey,
|
||||
'api_secret' => $apiSecret,
|
||||
]
|
||||
),
|
||||
false
|
||||
);
|
||||
|
||||
return $result->access_token;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $endpoint
|
||||
* @param null $body
|
||||
* @param null $query
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callApiGet($endpoint, $body = null, $query = null)
|
||||
{
|
||||
return $this->callApi('GET', $endpoint, $body, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $endpoint
|
||||
* @param null $body
|
||||
* @param null $query
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callApiPost($endpoint, $body = null, $query = null)
|
||||
{
|
||||
return $this->callApi('POST', $endpoint, $body, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $endpoint
|
||||
* @param null $body
|
||||
* @param null $query
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callApiPut($endpoint, $body = null, $query = null)
|
||||
{
|
||||
return $this->callApi('PUT', $endpoint, $body, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $endpoint
|
||||
* @param null $body
|
||||
* @param null $query
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return mixed
|
||||
*/
|
||||
private function callApi($method, $endpoint, $body = null, $query = null)
|
||||
{
|
||||
if (!empty($body)) {
|
||||
$body = base64_encode($body);
|
||||
}
|
||||
try {
|
||||
$response = $this->fiskalyClient->request(
|
||||
$method,
|
||||
$endpoint,
|
||||
$query,
|
||||
null,
|
||||
$body
|
||||
);
|
||||
|
||||
return json_decode(base64_decode($response->getResponse()['body']));
|
||||
} catch (ClientErrorException | FiskalyHttpException $e) {
|
||||
$this->handleClientException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return SelfTestResponse
|
||||
*/
|
||||
public function selfTest(): SelfTestResponse
|
||||
{
|
||||
return $this->fiskalyClient->selfTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $e
|
||||
*
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
private function handleClientException(Exception $e): void
|
||||
{
|
||||
if ($e->getStatus() === 401 || $e->getCode() == 401) {
|
||||
throw new InvalidCredentialsException('Falsche Zugangsdaten');
|
||||
}
|
||||
if ($e->getStatus() === 403 || $e->getCode() == 403) {
|
||||
throw new InvalidCredentialsException('Nutzer nicht berechtigt');
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingApiResponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
|
||||
interface FiskalyCashPointClosingDBInterface
|
||||
{
|
||||
public function create(CashPointClosingApiResponse $cashPointClosingApiResponse): int;
|
||||
|
||||
public function update(CashPointClosingApiResponse $cashPointClosingApiResponse): void;
|
||||
|
||||
public function get(int $id): ?CashPointClosingApiResponse;
|
||||
|
||||
public function getIdByClosingId(string $closingId): ?int;
|
||||
|
||||
public function getByClosingId(string $closingId): ?CashPointClosingApiResponse;
|
||||
|
||||
public function createTransactionMapping(
|
||||
CashPointClosingApiResponse $cashPointClosing,
|
||||
TransactionReponse $transaction
|
||||
): int;
|
||||
|
||||
public function getClosingIdsByState(string $clientId, string $state): array;
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingApiResponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidArgumentException;
|
||||
|
||||
final class FiskalyCashPointClosingDBService implements FiskalyCashPointClosingDBInterface
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* CashPointClosingDBService constructor.
|
||||
*
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CashPointClosingApiResponse $cashPointClosingApiResponse
|
||||
*
|
||||
* @throws Exception
|
||||
* @return int
|
||||
*/
|
||||
public function create(CashPointClosingApiResponse $cashPointClosingApiResponse): int
|
||||
{
|
||||
$this->db->perform(
|
||||
'INSERT INTO `fiskaly_cash_point_closing`
|
||||
(`closing_id`, `client_id`, `cash_point_closing_export_id`, `state`,
|
||||
`export_creation_date`, `time_start`, `time_end`, `trx_start`, `trx_end`)
|
||||
VALUES (:closing_id, :client_id, :cash_point_closing_export_id, :state,
|
||||
:export_creation_date, :time_start, :time_end, :trx_start, :trx_end )',
|
||||
[
|
||||
'closing_id' => $cashPointClosingApiResponse->getClosingId(),
|
||||
'client_id' => $cashPointClosingApiResponse->getClientId(),
|
||||
'cash_point_closing_export_id' => $cashPointClosingApiResponse->getCashPointClosingExportId(),
|
||||
'state' => $cashPointClosingApiResponse->getState(),
|
||||
'export_creation_date' => (new Datetime('now', new DateTimeZone('UTC')))->setTimeStamp(
|
||||
$cashPointClosingApiResponse->getExportCreationDate()
|
||||
)->format('Y-m-d H:i:s'),
|
||||
'time_start' => null,
|
||||
'time_end' => null,
|
||||
'trx_start' => $cashPointClosingApiResponse->getFirstTransactionExportId(),
|
||||
'trx_end' => $cashPointClosingApiResponse->getLastTransactionExportId(),
|
||||
]
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $closingId
|
||||
*
|
||||
* @return CashPointClosingApiResponse|null
|
||||
*/
|
||||
public function getByClosingId(string $closingId): ?CashPointClosingApiResponse
|
||||
{
|
||||
$id = $this->getIdByClosingId($closingId);
|
||||
if ($id === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->get($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return CashPointClosingApiResponse|null
|
||||
*/
|
||||
public function get(int $id): ?CashPointClosingApiResponse
|
||||
{
|
||||
$row = $this->db->fetchRow(
|
||||
'SELECT * FROM `fiskaly_cash_point_closing` WHERE `id` = :id',
|
||||
['id' => $id]
|
||||
);
|
||||
if (empty($row)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CashPointClosingApiResponse::fromDbState($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $closingId
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getIdByClosingId(string $closingId): ?int
|
||||
{
|
||||
$id = $this->db->fetchValue(
|
||||
'SELECT `id` FROM `fiskaly_cash_point_closing` WHERE `closing_id` = :closing_id',
|
||||
['closing_id' => $closingId]
|
||||
);
|
||||
if ($id === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CashPointClosingApiResponse $cashPointClosing
|
||||
* @param TransactionReponse $transaction
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function createTransactionMapping(
|
||||
CashPointClosingApiResponse $cashPointClosing,
|
||||
TransactionReponse $transaction
|
||||
): int {
|
||||
$cashPointClosingId = $this->getIdByClosingId($cashPointClosing->getClosingId());
|
||||
if ($cashPointClosingId === null) {
|
||||
throw new InvalidArgumentException("cashPointClosingId {$cashPointClosing->getClosingId()} not found");
|
||||
}
|
||||
$transactionId = $transaction->getId();
|
||||
$transactionDbId = $this->getTransactionDbId($transactionId);
|
||||
if ($transactionDbId === null) {
|
||||
throw new InvalidArgumentException("Transaction {$transactionId} not found");
|
||||
}
|
||||
$this->db->perform(
|
||||
'INSERT INTO `fiskaly_cash_point_closing_transaction`
|
||||
(`fiskaly_cash_point_closing_id`, `fiskaly_transaction_id`)
|
||||
VALUES (:cash_point_closing_id, :transaction_id)',
|
||||
[
|
||||
'cash_point_closing_id' => $cashPointClosingId,
|
||||
'transaction_id' => $transactionDbId,
|
||||
]
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CashPointClosingApiResponse $cashPointClosingApiResponse
|
||||
*/
|
||||
public function update(CashPointClosingApiResponse $cashPointClosingApiResponse): void
|
||||
{
|
||||
$cashPointClosingId = $this->getIdByClosingId($cashPointClosingApiResponse->getClosingId());
|
||||
if ($cashPointClosingId === null) {
|
||||
throw new InvalidArgumentException(
|
||||
"cashPointClosingId {$cashPointClosingApiResponse->getClosingId()} not found"
|
||||
);
|
||||
}
|
||||
|
||||
$this->db->perform(
|
||||
'UPDATE `fiskaly_cash_point_closing` SET `state` = :state WHERE `id` = :id',
|
||||
['state' => $cashPointClosingApiResponse->getState(), 'id' => $cashPointClosingId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
* @param string $state
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClosingIdsByState(string $clientId, string $state): array
|
||||
{
|
||||
return $this->db->fetchCol(
|
||||
'SELECT `closing_id` FROM `fiskaly_cash_point_closing` WHERE `client_id` = :client_id AND `state` = :state',
|
||||
[
|
||||
'client_id' => $clientId,
|
||||
'state' => $state,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $transactionId
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getTransactionDbId(string $transactionId): ?int
|
||||
{
|
||||
$result = $this->db->fetchValue(
|
||||
'SELECT `id` FROM `fiskaly_transaction` WHERE `trx_id` = :trx_id',
|
||||
['trx_id' => $transactionId]
|
||||
);
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
|
||||
use Exception;
|
||||
use FiskalyClient\errors\exceptions\FiskalyClientException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpTimeoutException;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosing;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingApiResponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingApiResponseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashRegister;
|
||||
use Xentral\Modules\FiskalyApi\Data\VatDefinition;
|
||||
|
||||
class FiskalyDSFinVKApi extends FiskalyApi
|
||||
{
|
||||
/** @var string */
|
||||
private const ENDPOINT_BASE = 'https://dsfinvk.fiskaly.com/api/v0/';
|
||||
|
||||
/**
|
||||
* FiskalyDSFinVKApi constructor.
|
||||
*
|
||||
* @param string $smaEndpoint
|
||||
* @param string $apiKey
|
||||
* @param string $apiSecret
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(string $smaEndpoint, string $apiKey, string $apiSecret)
|
||||
{
|
||||
parent::__construct($smaEndpoint, $apiKey, $apiSecret, self::ENDPOINT_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return array
|
||||
*/
|
||||
public function getVatDefinitions(): array
|
||||
{
|
||||
$result = $this->callApiGet('vat_definitions');
|
||||
|
||||
return array_map([VatDefinition::class, 'fromApiResult'], $result->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return array
|
||||
*/
|
||||
public function getCashRegisters(): array
|
||||
{
|
||||
$result = $this->callApiGet('cash_registers');
|
||||
return array_map([CashRegister::class,'fromApiResult'], $result->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return CashRegister|null
|
||||
*/
|
||||
public function getCashRegister(string $clientId): ?CashRegister
|
||||
{
|
||||
$result = $this->callApiGet("cash_registers/{$clientId}");
|
||||
if(empty($result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CashRegister::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $purchaserAgencyId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*
|
||||
* @return CashRegister[]
|
||||
*/
|
||||
public function getPurchaserAgencies(?string $purchaserAgencyId = null): array
|
||||
{
|
||||
if($purchaserAgencyId === null) {
|
||||
$result = $this->callApiGet('purchaser_agencies');
|
||||
|
||||
return array_map([CashRegister::class, 'fromApiResult'] , $result->data);
|
||||
}
|
||||
|
||||
$result = $this->callApiGet("purchaser_agencies/{$purchaserAgencyId}");
|
||||
|
||||
return [
|
||||
CashRegister::fromApiResult($result)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CashRegister $cashRegister
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return CashRegister
|
||||
*/
|
||||
public function putRegister(CashRegister $cashRegister): CashRegister
|
||||
{
|
||||
$clientId = $cashRegister->getClientId();
|
||||
$body = $cashRegister->toArray();
|
||||
$result = $this->callApiPut("cash_registers/{$clientId}", json_encode($body));
|
||||
|
||||
return CashRegister::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCashPointClosings(): CashPointClosingApiResponseCollection
|
||||
{
|
||||
$result = $this->callApiGet('cash_point_closings');
|
||||
|
||||
return CashPointClosingApiResponseCollection::fromApiResult($result->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $closingId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return CashPointClosingApiResponse
|
||||
*/
|
||||
public function getCashPointClosing(string $closingId): CashPointClosingApiResponse
|
||||
{
|
||||
$result = $this->callApiGet("cash_point_closings/{$closingId}");
|
||||
|
||||
return CashPointClosingApiResponse::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $closingId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCashPointClosingDetails(string $closingId) {
|
||||
$result = $this->callApiGet("cash_point_closings/{$closingId}/details");
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CashPointClosing $cashPointClosing
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*
|
||||
* @return CashPointClosingApiResponse
|
||||
*/
|
||||
public function sendCashPointClosings(CashPointClosing $cashPointClosing): CashPointClosingApiResponse
|
||||
{
|
||||
$result = $this->callApiPut(
|
||||
"cash_point_closings/{$cashPointClosing->getClosingId()}", json_encode($cashPointClosing->toApiResult())
|
||||
);
|
||||
|
||||
return CashPointClosingApiResponse::fromApiResult($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Exception;
|
||||
use FiskalyClient\errors\exceptions\FiskalyClientException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpTimeoutException;
|
||||
|
||||
class FiskalyEReceiptApi extends FiskalyApi
|
||||
{
|
||||
/** @var string */
|
||||
private const ENDPOINT_BASE = 'https://ereceipt.fiskaly.dev/api/v0/';
|
||||
|
||||
/**
|
||||
* FiskalyEReceiptApi constructor.
|
||||
*
|
||||
* @param string $smaEndpoint
|
||||
* @param string $apiKey
|
||||
* @param string $apiSecret
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(string $smaEndpoint, string $apiKey, string $apiSecret)
|
||||
{
|
||||
parent::__construct($smaEndpoint, $apiKey, $apiSecret, self::ENDPOINT_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
* @param int $offset
|
||||
* @param string|null $tssId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return mixed
|
||||
*/
|
||||
public function listEReceipts(int $limit = 100, int $offset = 0, ?string $tssId = null)
|
||||
{
|
||||
if($tssId === null) {
|
||||
$result = $this->callApiGet("issuer/e_receipts");
|
||||
//$result = $this->callApiGet("issuer/e_receipts?limit={$limit}&offset={$offset}");
|
||||
}
|
||||
else {
|
||||
$result = $this->callApiGet("issuer/e_receipts?limit={$limit}&offset={$offset}&tss_id={$tssId}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
|
||||
use Exception;
|
||||
use FiskalyClient\errors\exceptions\FiskalyClientException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpTimeoutException;
|
||||
use Xentral\Modules\FiskalyApi\Data\Client;
|
||||
use Xentral\Modules\FiskalyApi\Data\Export;
|
||||
use Xentral\Modules\FiskalyApi\Data\TechnicalSecuritySystem;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\AmountsPerPaymentTypeCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\AmountsPerVatTypeCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\SchemaReceipt;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\SchemaStandardV1;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionRequest;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionSchema;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidArgumentException;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidTransactionException;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Transaction;
|
||||
use Xentral\Modules\FiskalyApi\UuidTool;
|
||||
|
||||
class FiskalyKassenSichVApi extends FiskalyApi
|
||||
{
|
||||
/** @var string */
|
||||
private const ENDPOINT_BASE = 'https://kassensichv.io/api/v1/';
|
||||
|
||||
/**
|
||||
* FiskalyKassenSichVApi constructor.
|
||||
*
|
||||
* @param string $smaEndpoint
|
||||
* @param string $apiKey
|
||||
* @param string $apiSecret
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct(string $smaEndpoint, string $apiKey, string $apiSecret)
|
||||
{
|
||||
parent::__construct($smaEndpoint, $apiKey, $apiSecret, self::ENDPOINT_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TechnicalSecuritySystem[]
|
||||
*/
|
||||
public function getTechnicalSecuritySystemList(): array
|
||||
{
|
||||
$result = $this->callApiGet('tss');
|
||||
|
||||
return array_map([TechnicalSecuritySystem::class, 'fromApiResult'], $result->data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $tssUuid
|
||||
*
|
||||
* @return TechnicalSecuritySystem
|
||||
*/
|
||||
public function getTechnicalSecuritySystemByUuid(string $tssUuid): TechnicalSecuritySystem
|
||||
{
|
||||
$result = $this->callApiGet("tss/{$tssUuid}");
|
||||
|
||||
return TechnicalSecuritySystem::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tssUuid
|
||||
* @param string $state
|
||||
* @param string|null $description
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TechnicalSecuritySystem
|
||||
*/
|
||||
public function changeSecuritySystem(
|
||||
string $tssUuid,
|
||||
string $state = 'INITIALIZED',
|
||||
?string $description = null
|
||||
): TechnicalSecuritySystem {
|
||||
if (!in_array($state, ['UNINITIALIZED', 'INITIALIZED', 'DISABLED'])) {
|
||||
throw new InvalidArgumentException("unknown state '{$state}'");
|
||||
}
|
||||
$body = ['state' => $state];
|
||||
if ($description !== null) {
|
||||
$body['description'] = $description;
|
||||
}
|
||||
$result = $this->callApiPut("tss/{$tssUuid}", json_encode($body));
|
||||
|
||||
return TechnicalSecuritySystem::fromApiResult($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param null|string $tssUuid
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*
|
||||
* @return Client[]
|
||||
*/
|
||||
public function getClients($tssUuid = null): array
|
||||
{
|
||||
if (empty($tssUuid)) {
|
||||
$result = $this->callApiGet("client");
|
||||
} else {
|
||||
$result = $this->callApiGet("tss/{$tssUuid}/client");
|
||||
}
|
||||
|
||||
return array_map([Client::class, 'fromApiResult'], $result->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tssUuid
|
||||
* @param string|null $clientId
|
||||
* @param string|null $exportId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*/
|
||||
public function triggerExport(string $tssUuid, ?string $clientId = null, ?string $exportId = null): Export
|
||||
{
|
||||
if ($exportId === null) {
|
||||
$exportId = UuidTool::generateUuid();
|
||||
}
|
||||
if ($clientId !== null) {
|
||||
Export::fromApiResult(
|
||||
$this->callApiPut("tss/{$tssUuid}/export/{$exportId}", '{}', ['client_id' => $clientId])
|
||||
);
|
||||
}
|
||||
|
||||
return Export::fromApiResult($this->callApiPut("tss/{$tssUuid}/export/{$exportId}", '{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tssUuid
|
||||
* @param string $serialNumber
|
||||
* @param string $clientId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return Client
|
||||
*/
|
||||
public function createClient(string $tssUuid, string $serialNumber, string $clientId): Client
|
||||
{
|
||||
$result = $this->callApiPut(
|
||||
"tss/{$tssUuid}/client/{$clientId}",
|
||||
json_encode(['serial_number' => $serialNumber])
|
||||
);
|
||||
|
||||
return Client::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $clientUuid
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function getClientByUuid($tssUuid, $clientUuid): Client
|
||||
{
|
||||
$result = $this->callApiGet("tss/{$tssUuid}/client/{$clientUuid}");
|
||||
|
||||
return Client::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $tssUuid
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponseCollection
|
||||
*/
|
||||
public function getTransactions(
|
||||
?string $tssUuid = null,
|
||||
int $offset = 0,
|
||||
int $limit = 100
|
||||
): TransactionReponseCollection {
|
||||
if ($tssUuid === null) {
|
||||
$result = $this->callApiGet("tx", null, ['offset' => $offset, 'limit' => $limit,]);
|
||||
} else {
|
||||
$result = $this->callApiGet("tss/{$tssUuid}/tx", null, ['offset' => $offset, 'limit' => $limit,]);
|
||||
}
|
||||
|
||||
return TransactionReponseCollection::fromApiResult($result->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tssUuid
|
||||
* @param string $txId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function getTransaction(string $tssUuid, string $txId): TransactionReponse
|
||||
{
|
||||
$result = $this->callApiGet("tss/{$tssUuid}/tx/{$txId}");
|
||||
|
||||
return TransactionReponse::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param TechnicalSecuritySystem $technicalSecuritySystem
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
public function uploadTransaction(
|
||||
Transaction $transaction,
|
||||
TechnicalSecuritySystem $technicalSecuritySystem
|
||||
): Transaction {
|
||||
$transaction = $this->startTransaction($transaction, $technicalSecuritySystem);
|
||||
|
||||
return $this->finishTransactionOld($transaction, $technicalSecuritySystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param TechnicalSecuritySystem $tss
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return Transaction
|
||||
*/
|
||||
public function startTransaction(Transaction $transaction, TechnicalSecuritySystem $tss)
|
||||
{
|
||||
$tssId = $tss->getUuid();
|
||||
|
||||
$body = json_encode(
|
||||
[
|
||||
'state' => 'ACTIVE',
|
||||
'client_id' => $transaction->getClientUuid(),
|
||||
]
|
||||
);
|
||||
|
||||
$result = $this->callApiPut("tss/{$tssId}/tx/" . $transaction->getUuid(), $body);
|
||||
|
||||
$transaction->setLastRevision($result->revision);
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $transactionRequest
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function createTransaction(TransactionRequest $transactionRequest): TransactionReponse
|
||||
{
|
||||
return TransactionReponse::fromApiResult(
|
||||
$this->callApiPut(
|
||||
"tss/{$transactionRequest->getTssId()}/tx/{$transactionRequest->getId()}",
|
||||
json_encode($transactionRequest->toApiResult())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $transactionRequest
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function updateTransaction(TransactionRequest $transactionRequest): TransactionReponse
|
||||
{
|
||||
return TransactionReponse::fromApiResult(
|
||||
$this->callApiPut(
|
||||
"tss/{$transactionRequest->getTssId()}/tx/{$transactionRequest->getId()}",
|
||||
json_encode($transactionRequest->toApiResult()),
|
||||
['last_revision' => $transactionRequest->getRevision()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionReponse $reponse
|
||||
* @param string $receiptType
|
||||
* @param AmountsPerVatTypeCollection $amountsPerVatTypeCollection
|
||||
* @param AmountsPerPaymentTypeCollection $amountsPerPaymentTypeCollection
|
||||
*
|
||||
* @return TransactionRequest
|
||||
*/
|
||||
public function getFinishTransactionRequest(
|
||||
TransactionReponse $reponse,
|
||||
string $receiptType,
|
||||
AmountsPerVatTypeCollection $amountsPerVatTypeCollection,
|
||||
AmountsPerPaymentTypeCollection $amountsPerPaymentTypeCollection
|
||||
): TransactionRequest {
|
||||
return (new TransactionRequest(
|
||||
'FINISHED',
|
||||
$reponse->getClientId(),
|
||||
new TransactionSchema(
|
||||
new SchemaStandardV1(
|
||||
new SchemaReceipt(
|
||||
$receiptType,
|
||||
$amountsPerVatTypeCollection,
|
||||
$amountsPerPaymentTypeCollection
|
||||
)
|
||||
)
|
||||
), $reponse->getMetaData()
|
||||
)
|
||||
)->setTssId($reponse->getTssId())
|
||||
->setId($reponse->getId())
|
||||
->setRevision($reponse->getLatestRevision());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $request
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @throws Exception
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function finishTransaction(TransactionRequest $request): TransactionReponse
|
||||
{
|
||||
$result = $this->callApiPut(
|
||||
"tss/{$request->getTssId()}/tx/{$request->getId()}",
|
||||
json_encode($request->toArray()),
|
||||
['last_revision' => $request->getRevision()]
|
||||
);
|
||||
|
||||
return TransactionReponse::fromApiResult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Transaction $transaction
|
||||
* @param TechnicalSecuritySystem $tss
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
public function finishTransactionOld(Transaction $transaction, TechnicalSecuritySystem $tss)
|
||||
{
|
||||
$tssId = $tss->getUuid();
|
||||
|
||||
$vatTypeAmounts = [];
|
||||
$paymentTypeAmounts = [];
|
||||
|
||||
foreach ($transaction->getAmountsPerVatRate() as $vatTypeAmount) {
|
||||
$vatTypeAmounts[] = [
|
||||
'vat_rate' => $vatTypeAmount->getVatType(),
|
||||
'amount' => (string)number_format($vatTypeAmount->getAmount(), 2, '.', ''),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($transaction->getAmountsPerPaymentType() as $paymentTypeAmount) {
|
||||
$paymentTypeAmounts[] = [
|
||||
'payment_type' => $paymentTypeAmount->getPaymentType(),
|
||||
'amount' => (string)number_format($paymentTypeAmount->getAmount(), 2, '.', ''),
|
||||
'currency_code' => 'EUR',
|
||||
];
|
||||
}
|
||||
|
||||
$hasOrderLineItems = count($transaction->getOrderLineItems()) > 0;
|
||||
|
||||
$body =
|
||||
[
|
||||
'state' => 'FINISHED',
|
||||
'client_id' => $transaction->getClientUuid(),
|
||||
'schema' => [
|
||||
'standard_v1' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$body['schema']['standard_v1'] = [
|
||||
'receipt' => [
|
||||
'receipt_type' => 'RECEIPT',
|
||||
'amounts_per_vat_rate' => $vatTypeAmounts,
|
||||
'amounts_per_payment_type' => $paymentTypeAmounts,
|
||||
],
|
||||
];
|
||||
if ($hasOrderLineItems) {
|
||||
foreach ($transaction->getOrderLineItems() as $orderLineItem) {
|
||||
$body['schema']['standard_v1']['receipt']['line_items'][] = [
|
||||
'quantity' => $orderLineItem->getQuantity(),
|
||||
'text' => $orderLineItem->getText(),
|
||||
'price_per_unit' => $orderLineItem->getPricePerUnit(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$transaction->isLastRevisionSet()) {
|
||||
throw new InvalidTransactionException("Transaction last_revision not set");
|
||||
}
|
||||
|
||||
$query = ['last_revision' => $transaction->getLastRevision()];
|
||||
|
||||
$uuid = $transaction->getUuid();
|
||||
|
||||
$result = $this->callApiPut("tss/{$tssId}/tx/{$uuid}", json_encode($body), $query);
|
||||
|
||||
$transaction->setLastRevision($result->revision);
|
||||
$transaction->setTransactionNumber($result->number);
|
||||
$transaction->setStartTime($result->time_start);
|
||||
$transaction->setEndTime($result->time_end);
|
||||
$transaction->setClientSerialNumber($result->client_serial_number);
|
||||
$transaction->setCertificateSerial($result->certificate_serial);
|
||||
$transaction->setSignature($result->signature->value);
|
||||
$transaction->setSignatureAlgorithm($result->signature->algorithm);
|
||||
$transaction->setSignatureCounter($result->signature->counter);
|
||||
$transaction->setPublicKey($result->signature->public_key);
|
||||
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $tssId
|
||||
* @param bool $orderIsDesc
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
* @param string $orderBy
|
||||
* @param array $states
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return array
|
||||
*/
|
||||
public function listExports(
|
||||
?string $tssId = null,
|
||||
bool $orderIsDesc = false,
|
||||
int $offset = 0,
|
||||
int $limit = 100,
|
||||
string $orderBy = 'time_request',
|
||||
array $states = []
|
||||
): array {
|
||||
$query = null;
|
||||
if ($orderIsDesc) {
|
||||
$query['order'] = 'desc';
|
||||
}
|
||||
|
||||
if (!empty($states)) {
|
||||
$query['states'] = '';
|
||||
foreach ($states as $keyState => $state) {
|
||||
$query['states'] .= ($keyState > 0 ? '&' : '') . "states%5B{$keyState}%5D={$state}";
|
||||
}
|
||||
}
|
||||
$query['order_by'] = $orderBy;
|
||||
$query['limit'] = $limit;
|
||||
$query['offset'] = $offset;
|
||||
|
||||
$endPoint = 'export';
|
||||
if ($tssId !== null) {
|
||||
$endPoint = "tss/{$tssId}/export";
|
||||
}
|
||||
$result = $this->callApiGet($endPoint, null, $query);
|
||||
|
||||
return array_map([Export::class, 'fromApiResult'], $result->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
|
||||
use FiskalyClient\errors\exceptions\FiskalyClientException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpTimeoutException;
|
||||
use Xentral\Modules\FiskalyApi\Data\BillingAddress;
|
||||
use Xentral\Modules\FiskalyApi\Data\Organisation;
|
||||
use Xentral\Modules\FiskalyApi\Data\User;
|
||||
|
||||
class FiskalyManagementApi extends FiskalyApi
|
||||
{
|
||||
|
||||
private const ENDPOINT_BASE = 'https://dashboard.fiskaly.com/api/v0/';
|
||||
|
||||
|
||||
public function __construct(string $smaEndpoint, string $apiKey, string $apiSecret)
|
||||
{
|
||||
parent::__construct($smaEndpoint, $apiKey, $apiSecret, self::ENDPOINT_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $uuId
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
*
|
||||
* @return Organisation[]
|
||||
*/
|
||||
public function getOrganisations(?string $uuId = null): array
|
||||
{
|
||||
if ($uuId === null) {
|
||||
$organisations = $this->callApiGet('organizations');
|
||||
|
||||
return array_map([Organisation::class, 'fromApiResult'], $organisations->data);
|
||||
}
|
||||
|
||||
$organisation = $this->callApiGet("organizations/{$uuId}");
|
||||
|
||||
return [Organisation::fromApiResult($organisation)];
|
||||
}
|
||||
|
||||
public function getUsers(string $organisationUuId): array
|
||||
{
|
||||
$result = $this->callApiGet("/organizations/{$organisationUuId}/users");
|
||||
|
||||
return array_map([User::class, 'fromApiResult'], $result->data);
|
||||
}
|
||||
|
||||
public function getBillingAddresses(): array
|
||||
{
|
||||
$result = $this->callApiGet('billing-addresses');
|
||||
|
||||
return array_map([BillingAddress::class, 'fromApiResult'], $result->data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponseCollection;
|
||||
|
||||
interface FiskalyPosClosingInterface
|
||||
{
|
||||
public function getNextCashPointClosingExportId(string $clientId): int;
|
||||
|
||||
public function getOpenTransactions(string $clientId): TransactionReponseCollection;
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use erpAPI;
|
||||
use Exception;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\BusinessCaseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosing;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingCashStatement;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingHead;
|
||||
use Xentral\Modules\FiskalyApi\Data\CashPointClosing\CashPointClosingPayment;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Factory\FiskalyCashPointClosingFactory;
|
||||
use Xentral\Modules\FiskalyApi\Wrapper\TaxSettingWrapper;
|
||||
|
||||
class FiskalyPosClosingService implements FiskalyPosClosingInterface
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var FiskalyCashPointClosingFactory $cashPointFactory */
|
||||
private $cashPointFactory;
|
||||
|
||||
/** @var TaxSettingWrapper $taxSettingWrapper */
|
||||
private $taxSettingWrapper;
|
||||
|
||||
/**
|
||||
* FiskalyPosClosingService constructor.
|
||||
*
|
||||
* @param Database $db
|
||||
* @param FiskalyCashPointClosingFactory $cashPointFactory
|
||||
* @param TaxSettingWrapper $taxSettingWrapper
|
||||
*/
|
||||
public function __construct(
|
||||
Database $db,
|
||||
FiskalyCashPointClosingFactory $cashPointFactory,
|
||||
TaxSettingWrapper $taxSettingWrapper
|
||||
) {
|
||||
$this->db = $db;
|
||||
$this->cashPointFactory = $cashPointFactory;
|
||||
$this->taxSettingWrapper = $taxSettingWrapper;
|
||||
}
|
||||
|
||||
public function getNextCashPointClosingExportId(string $clientId): int
|
||||
{
|
||||
return 1 + (int)$this->db->fetchValue(
|
||||
'SELECT MAX(`cash_point_closing_export_id`)
|
||||
FROM `fiskaly_cash_point_closing`
|
||||
WHERE `client_id` = :client_id',
|
||||
['client_id' => $clientId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
* @param string|null $date
|
||||
*
|
||||
* @throws Exception
|
||||
* @return TransactionReponseCollection
|
||||
*/
|
||||
public function getOpenTransactions(string $clientId, ?string $date = null): TransactionReponseCollection
|
||||
{
|
||||
$transactions = $this->db->fetchCol(
|
||||
'SELECT ft.json_response
|
||||
FROM `fiskaly_transaction` AS `ft`
|
||||
INNER JOIN `fiskaly_tranaction_mapping` AS `ftm` ON ft.id = ftm.fiskaly_transaction_id
|
||||
LEFT JOIN `fiskaly_cash_point_closing_transaction` AS `fcpct` ON ft.id = fcpct.fiskaly_transaction_id
|
||||
WHERE ft.client_id = :client_id AND fcpct.id IS NULL
|
||||
GROUP BY ft.id',
|
||||
['client_id' => $clientId]
|
||||
);
|
||||
$instance = new TransactionReponseCollection();
|
||||
foreach ($transactions as $transactionJson) {
|
||||
$instance->addTransactionResponse(TransactionReponse::fromApiResult(json_decode($transactionJson, false)));
|
||||
}
|
||||
if ($date !== null) {
|
||||
return $instance->filterDate($date);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
*
|
||||
* @throws Exception
|
||||
* @return array
|
||||
*/
|
||||
public function getOpenPointClosingDates(string $clientId): array
|
||||
{
|
||||
$openTransactions = $this->getOpenTransactions($clientId);
|
||||
|
||||
return $openTransactions->getTransactionDates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $projectId
|
||||
* @param string $date
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPosCountingExistsForDate(int $projectId, string $date): bool
|
||||
{
|
||||
return $this->db->fetchValue(
|
||||
'SELECT `id`
|
||||
FROM `pos_zaehlungen`
|
||||
WHERE `projekt` = :project_id AND DATE(`zeitstempel`) = :date
|
||||
LIMIT 1',
|
||||
[
|
||||
'project_id' => $projectId,
|
||||
'date' => $date,
|
||||
]
|
||||
) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
* @param string|null $date
|
||||
*
|
||||
* @throws Exception
|
||||
* @return CashPointClosing
|
||||
*/
|
||||
public function getNextCashPointClosing(string $clientId, ?string $date = null): CashPointClosing
|
||||
{
|
||||
if ($date === null) {
|
||||
$openTransactions = $this->getOpenTransactions(
|
||||
$clientId,
|
||||
(new DateTime('now', new DateTimeZone('UTC')))->format(
|
||||
'Y-m-d'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$openTransactions = $this->getOpenTransactions($clientId, $date);
|
||||
}
|
||||
$trxs = $openTransactions->getTrxIds();
|
||||
$firstTransaction = $openTransactions->getBoundedTransactionWithClientId($clientId, true);
|
||||
$lastLastTransaction = $openTransactions->getBoundedTransactionWithClientId($clientId, false);
|
||||
$this->cashPointFactory->setTaxNormal($this->getNormalTaxForClientId($clientId));
|
||||
$paymentCollection = $this->cashPointFactory->getPaymentTypesFromTransactionCollection($openTransactions);
|
||||
$sum = $paymentCollection->getSum();
|
||||
$cashCollection = $paymentCollection->filterByType('CASH');
|
||||
$cashSum = $cashCollection->getSum();
|
||||
|
||||
$posJournalEntries = $this->getPosJounralEntriesByTrxs($trxs);
|
||||
$posSessions = $this->getPosSessionsByTrxs($trxs);
|
||||
$businessCases = new BusinessCaseCollection();
|
||||
foreach ($posJournalEntries as $posJournalArray) {
|
||||
$businessCases = $businessCases->combine(
|
||||
$this->cashPointFactory->createBusinessCaseCollection($posJournalArray)
|
||||
);
|
||||
}
|
||||
$payment = new CashPointClosingPayment(
|
||||
$sum,
|
||||
$cashSum,
|
||||
$this->cashPointFactory->getCashAmountByCurrencyCollection($paymentCollection),
|
||||
$this->cashPointFactory->getCashPointClosingPaymentTypeCollectionByPosJournalCollection($posJournalEntries)
|
||||
);
|
||||
|
||||
$instance = new CashPointClosing(
|
||||
$clientId, $this->getNextCashPointClosingExportId($clientId),
|
||||
new CashPointClosingHead(
|
||||
new DateTime('now', new DateTimeZone('UTC')),
|
||||
$firstTransaction->getId(),
|
||||
$lastLastTransaction->getId(),
|
||||
$date === null ? null : (new DateTime($date, new DateTimeZone('UTC')))
|
||||
),
|
||||
new CashPointClosingCashStatement($businessCases, $payment),
|
||||
$this->cashPointFactory->getCashPointClosingTransactionCollection(
|
||||
$openTransactions,
|
||||
$posJournalEntries,
|
||||
$posSessions
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $trxs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getPosJounralEntriesByTrxs(array $trxs): array
|
||||
{
|
||||
return $this->db->fetchGroup(
|
||||
"SELECT ft.trx_id, pj.*
|
||||
FROM `fiskaly_transaction` AS `ft`
|
||||
INNER JOIN `fiskaly_tranaction_mapping` AS `ftm` ON ft.id = ftm.fiskaly_transaction_id
|
||||
INNER JOIN `pos_journal` AS `pj` ON ftm.document = 'pos_journal' AND ftm.document_id = pj.id
|
||||
WHERE ft.trx_id IN (:trx_ids)",
|
||||
[
|
||||
'trx_ids' => $trxs,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $trxs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getPosSessionsByTrxs(array $trxs): array
|
||||
{
|
||||
$posSessions = $this->db->fetchPairs(
|
||||
"SELECT ft.trx_id, ps.data
|
||||
FROM `fiskaly_transaction` AS `ft`
|
||||
INNER JOIN `fiskaly_tranaction_mapping` AS `ftm` ON ft.id = ftm.fiskaly_transaction_id
|
||||
INNER JOIN `pos_sessions` AS `ps` ON ftm.document = 'pos_session' AND ftm.document_id = ps.id
|
||||
WHERE ft.trx_id IN (:trx_ids)",
|
||||
[
|
||||
'trx_ids' => $trxs,
|
||||
]
|
||||
);
|
||||
|
||||
$posSessions = array_map(
|
||||
static function ($posSession) {
|
||||
return unserialize($posSession, ['allowed_classes' => false]);
|
||||
},
|
||||
$posSessions
|
||||
);
|
||||
$addressIds = [];
|
||||
$cashierIds = [];
|
||||
foreach ($posSessions as $posSession) {
|
||||
$cashierId = $posSession['kassiererId'] ?? null;
|
||||
$addressId = $posSession['addrid'] ?? null;
|
||||
if ($addressId !== null && !in_array($addressId, $addressIds, true)) {
|
||||
$addressIds[] = $addressId;
|
||||
}
|
||||
if ($cashierId !== null && !in_array($cashierId, $cashierIds, true)) {
|
||||
$cashierIds[] = $cashierId;
|
||||
}
|
||||
}
|
||||
$cashierAddresses = $this->getAddressesFromCashierIds($cashierIds);
|
||||
|
||||
$addesses = $this->getAddressesFromIds($addressIds);
|
||||
foreach ($posSessions as $positionKey => $posSession) {
|
||||
$posSessions[$positionKey]['address'] = !empty($addesses[$posSession['addrid'] ?? '']) ? reset(
|
||||
$addesses[$posSession['addrid']]
|
||||
) : null;
|
||||
}
|
||||
foreach ($posSessions as $positionKey => $posSession) {
|
||||
if (!empty($posSessions[$positionKey]['address']) || empty($posSession['kassiererId'])) {
|
||||
continue;
|
||||
}
|
||||
if (empty($cashierAddresses[$posSession['kassiererId']])) {
|
||||
continue;
|
||||
}
|
||||
$posSessions[$positionKey]['address'] = reset($cashierAddresses[$posSession['kassiererId']]);
|
||||
$posSessions[$positionKey]['addrid'] = $posSessions[$positionKey]['address']['id'];
|
||||
$posSessions[$positionKey]['addr']['name'] = $posSessions[$positionKey]['address']['name'];
|
||||
}
|
||||
|
||||
return $posSessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $addressIds
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAddressesFromIds(array $addressIds): array
|
||||
{
|
||||
if (empty($addressIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->db->fetchGroup(
|
||||
"SELECT adr.id, IF(l.iso3 IS NULL OR l.iso3 = '', IF(l.iso = 'AT', 'AUT', 'DEU'), l.iso3) AS `land_iso3`,
|
||||
adr.*
|
||||
FROM `adresse` AS `adr`
|
||||
LEFT JOIN `laender` AS `l` ON adr.land = l.iso
|
||||
WHERE adr.id IN (:address_ids)",
|
||||
['address_ids' => $addressIds]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $cashierIds
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAddressesFromCashierIds(array $cashierIds): array
|
||||
{
|
||||
if (empty($cashierIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->db->fetchGroup(
|
||||
"SELECT pk.kassenkennung,
|
||||
IF(l.iso3 IS NULL OR l.iso3 = '', IF(l.iso = 'AT', 'AUT', 'DEU'), l.iso3) AS `land_iso3`, adr.*
|
||||
FROM `adresse` AS `adr`
|
||||
INNER JOIN `pos_kassierer` AS `pk` ON adr.id = pk.adresse
|
||||
LEFT JOIN `laender` AS `l` ON adr.land = l.iso
|
||||
WHERE pk.kassenkennung IN (:cashier_ids)",
|
||||
['cashier_ids' => $cashierIds]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $clientId
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function getNormalTaxForClientId(string $clientId): float
|
||||
{
|
||||
$projectId = $this->db->fetchValue(
|
||||
'SELECT pr.id
|
||||
FROM `fiskaly_pos_mapping` AS `fpm`
|
||||
INNER JOIN `projekt` AS `pr`
|
||||
WHERE fpm.client_uuid = :client_id
|
||||
LIMIT 1',
|
||||
['client_id' => $clientId]
|
||||
);
|
||||
|
||||
return $this->taxSettingWrapper->getStandardTaxRate($projectId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
|
||||
use Aura\SqlQuery\Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\FiskalyApi\Data\Organisation;
|
||||
|
||||
final class FiskalyPosMappingService
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* FiskalyPosMappingService constructor.
|
||||
*
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function listProjects(): array
|
||||
{
|
||||
return $this->db->fetchAll(
|
||||
$this->db->select()
|
||||
->from('projekt')
|
||||
->where('geloescht = 0 AND kasse_konto > 0')
|
||||
->cols(['id', 'name', 'abkuerzung'])
|
||||
->getStatement()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function list(): array
|
||||
{
|
||||
$query = $this->db->select()
|
||||
->from('fiskaly_pos_mapping AS f')
|
||||
->cols(['f.id', 'f.tss_uuid', 'f.client_uuid', 'f.pos_id']);
|
||||
|
||||
return $this->db->fetchAll($query->getStatement(), $query->getBindValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cashierId
|
||||
*
|
||||
* @throws Exception
|
||||
* @return array
|
||||
*/
|
||||
public function getByCashierId(string $cashierId): array
|
||||
{
|
||||
$posProjectQuery = $this->db->select()
|
||||
->from('pos_kassierer AS p')
|
||||
->cols(['f.tss_uuid', 'f.client_uuid', 'f.pos_id'])
|
||||
->where('p.kassenkennung=:kennung')
|
||||
->leftJoin('fiskaly_pos_mapping AS f', 'f.pos_id = p.projekt')
|
||||
->bindValue('kennung', $cashierId);
|
||||
|
||||
return $this->db->fetchRow($posProjectQuery->getStatement(), $posProjectQuery->getBindValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cashId
|
||||
*
|
||||
* @throws Exception
|
||||
* @return array|null
|
||||
*/
|
||||
public function getByCashId(int $cashId): ?array
|
||||
{
|
||||
$query = $this->db->select()
|
||||
->from('fiskaly_pos_mapping AS f')
|
||||
->innerJoin('projekt AS p', 'f.pos_id = p.id')
|
||||
->where('p.kasse_konto=:kasse')
|
||||
->bindValue('kasse', $cashId)
|
||||
->cols(['f.tss_uuid', 'f.organization_id', 'f.client_uuid', 'p.id']);
|
||||
|
||||
return $this->db->fetchRow($query->getStatement(), $query->getBindValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fiskalyPosMappingId
|
||||
*/
|
||||
public function delete(int $fiskalyPosMappingId): void
|
||||
{
|
||||
$query = $this->db->delete()
|
||||
->from('fiskaly_pos_mapping')
|
||||
->where('id=:id')
|
||||
->bindValue('id', $fiskalyPosMappingId);
|
||||
$this->db->perform($query->getStatement(), $query->getBindValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $projectId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTssFromProjectId(int $projectId): array
|
||||
{
|
||||
$query = $this->db->select()
|
||||
->from('fiskaly_pos_mapping AS f')
|
||||
->where('pos_id=:pos_id')
|
||||
->bindValue('pos_id', $projectId)
|
||||
->cols(['f.tss_uuid', 'f.organization_id', 'f.client_uuid']);
|
||||
|
||||
return $this->db->fetchRow($query->getStatement(), $query->getBindValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $projectId
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTssIdFromProjectId(int $projectId): ?string
|
||||
{
|
||||
$query = $this->db->select()
|
||||
->from('fiskaly_pos_mapping AS f')
|
||||
->where('pos_id=:pos_id')
|
||||
->bindValue('pos_id', $projectId)
|
||||
->cols(['f.tss_uuid']);
|
||||
|
||||
$tssId = $this->db->fetchValue($query->getStatement(), $query->getBindValues());
|
||||
if ($tssId === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tssId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Organisation $organisation
|
||||
*/
|
||||
public function tryCreateOrUpdateOrganization(Organisation $organisation): void
|
||||
{
|
||||
if ($this->getOrganizationByUuId($organisation->getUuid()) === null) {
|
||||
$this->createOrganization($organisation);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->updateOrganization($organisation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return Organisation|null
|
||||
*/
|
||||
public function getOrganizationById(int $id): ?Organisation
|
||||
{
|
||||
$organizationRow = $this->db->fetchRow(
|
||||
'SELECT * FROM `fiskaly_organization` WHERE `id` = :id',
|
||||
[
|
||||
'id' => $id,
|
||||
]
|
||||
);
|
||||
if (empty($organizationRow)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getOrganizationFromDbEntry($organizationRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uuId
|
||||
*
|
||||
* @return Organisation|null
|
||||
*/
|
||||
public function getOrganizationByUuId(string $uuId): ?Organisation
|
||||
{
|
||||
$organizationRow = $this->db->fetchRow(
|
||||
'SELECT * FROM `fiskaly_organization` WHERE `fiskaly_organization_id` = :uuid',
|
||||
[
|
||||
'uuid' => $uuId,
|
||||
]
|
||||
);
|
||||
if (empty($organizationRow)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getOrganizationFromDbEntry($organizationRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $organizationRow
|
||||
*
|
||||
* @return Organisation
|
||||
*/
|
||||
private function getOrganizationFromDbEntry(array $organizationRow): Organisation
|
||||
{
|
||||
$envs = [];
|
||||
if (!empty($organizationRow['is_environment_live'])) {
|
||||
$envs[] = 'LIVE';
|
||||
}
|
||||
if (!empty($organizationRow['is_environment_test'])) {
|
||||
$envs[] = 'TEST';
|
||||
}
|
||||
$organizationRow['_id'] = $organizationRow['fiskaly_organization_id'];
|
||||
$organizationRow['_type'] = $organizationRow['type'];
|
||||
$organizationRow['_envs'] = $envs;
|
||||
if (!empty($organizationRow['gln'])) {
|
||||
$organizationRow['billing_options']['gln'] = $organizationRow['gln'];
|
||||
}
|
||||
if (!empty($organizationRow['withhold_billing'])) {
|
||||
$organizationRow['billing_options']['withhold_billing'] = $organizationRow['withhold_billing'];
|
||||
}
|
||||
if (!empty($organizationRow['bill_to_organization'])) {
|
||||
$organizationRow['billing_options']['bill_to_organization'] = $organizationRow['bill_to_organization'];
|
||||
}
|
||||
|
||||
return Organisation::fromDbState($organizationRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Organisation $organisation
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function createOrganization(Organisation $organisation): int
|
||||
{
|
||||
$query = $this->db->insert()
|
||||
->into('fiskaly_organization')
|
||||
->cols(
|
||||
[
|
||||
'fiskaly_organization_id' => $organisation->getUuid(),
|
||||
'managed_by_organization_id' => $organisation->getManagedByOrganizationId(),
|
||||
'type' => $organisation->getType(),
|
||||
'name' => $organisation->getName(),
|
||||
'display_name' => $organisation->getDisplayName(),
|
||||
'address_line1' => $organisation->getAddressLine1(),
|
||||
'address_line2' => $organisation->getAddressLine2(),
|
||||
'state' => $organisation->getState(),
|
||||
'zip' => $organisation->getZip(),
|
||||
'town' => $organisation->getTown(),
|
||||
'tax_number' => $organisation->getTaxNumber(),
|
||||
'vat_id' => $organisation->getVatId(),
|
||||
'economy_id' => $organisation->getEconomyId(),
|
||||
'country_code' => $organisation->getCountryCode(),
|
||||
'is_environment_live' => (int)in_array('LIVE', $organisation->getEnvs()),
|
||||
'is_environment_test' => (int)in_array('TEST', $organisation->getEnvs()),
|
||||
]
|
||||
);
|
||||
$this->db->perform(
|
||||
$query->getStatement(),
|
||||
$query->getBindValues()
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Organisation $organisation
|
||||
*/
|
||||
public function updateOrganization(Organisation $organisation): void
|
||||
{
|
||||
$query = $this->db->update()
|
||||
->table('fiskaly_organization')
|
||||
->where('fiskaly_organization_id=:fiskaly_organization_id')
|
||||
->bindValue('fiskaly_organization_id', $organisation->getUuid())
|
||||
->cols(
|
||||
[
|
||||
'managed_by_organization_id' => $organisation->getManagedByOrganizationId(),
|
||||
'type' => $organisation->getType(),
|
||||
'name' => $organisation->getName(),
|
||||
'display_name' => $organisation->getDisplayName(),
|
||||
'address_line1' => $organisation->getAddressLine1(),
|
||||
'address_line2' => $organisation->getAddressLine2(),
|
||||
'state' => $organisation->getState(),
|
||||
'zip' => $organisation->getZip(),
|
||||
'town' => $organisation->getTown(),
|
||||
'tax_number' => $organisation->getTaxNumber(),
|
||||
'vat_id' => $organisation->getVatId(),
|
||||
'economy_id' => $organisation->getEconomyId(),
|
||||
'country_code' => $organisation->getCountryCode(),
|
||||
'is_environment_live' => (int)in_array('LIVE', $organisation->getEnvs()),
|
||||
'is_environment_test' => (int)in_array('TEST', $organisation->getEnvs()),
|
||||
]
|
||||
);
|
||||
$this->db->perform(
|
||||
$query->getStatement(),
|
||||
$query->getBindValues()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $projectId
|
||||
* @param string $tseUuid
|
||||
* @param string $tseDescription
|
||||
* @param string $clientUuid
|
||||
* @param string $clientDescription
|
||||
* @param bool|null $istTestEnvironment
|
||||
* @param string|null $organizationId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function create(
|
||||
int $projectId,
|
||||
string $tseUuid,
|
||||
string $tseDescription,
|
||||
string $clientUuid,
|
||||
string $clientDescription,
|
||||
?bool $istTestEnvironment = null,
|
||||
?string $organizationId = null
|
||||
): int {
|
||||
$query = $this->db->insert()
|
||||
->into('fiskaly_pos_mapping')
|
||||
->cols(
|
||||
[
|
||||
'pos_id' => $projectId,
|
||||
'tss_uuid' => $tseUuid,
|
||||
'tss_description' => $tseDescription,
|
||||
'client_uuid' => $clientUuid,
|
||||
'client_description' => $clientDescription,
|
||||
'is_test_environment' => $istTestEnvironment === null ? null : (int)$istTestEnvironment,
|
||||
'organization_id' => $organizationId ?? null,
|
||||
]
|
||||
);
|
||||
$this->db->perform(
|
||||
$query->getStatement(),
|
||||
$query->getBindValues()
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fiskalyPosMappingId
|
||||
* @param int $projectId
|
||||
* @param string $tseUuid
|
||||
* @param string $tseDescription
|
||||
* @param string $clientUuid
|
||||
* @param string $clientDescription
|
||||
* @param bool|null $istTestEnvironment
|
||||
* @param string|null $organizationId
|
||||
*/
|
||||
public function update(
|
||||
int $fiskalyPosMappingId,
|
||||
int $projectId,
|
||||
string $tseUuid,
|
||||
string $tseDescription,
|
||||
string $clientUuid,
|
||||
string $clientDescription,
|
||||
?bool $istTestEnvironment = null,
|
||||
?string $organizationId = null
|
||||
): void {
|
||||
$query = $this->db->update()
|
||||
->table('fiskaly_pos_mapping')
|
||||
->where('id=:id')
|
||||
->bindValue('id', $fiskalyPosMappingId)
|
||||
->cols(
|
||||
[
|
||||
'pos_id' => $projectId,
|
||||
'tss_uuid' => $tseUuid,
|
||||
'tss_description' => $tseDescription,
|
||||
'client_uuid' => $clientUuid,
|
||||
'client_description' => $clientDescription,
|
||||
'is_test_environment' => $istTestEnvironment === null ? null : (int)$istTestEnvironment,
|
||||
'organization_id' => $organizationId ?? null,
|
||||
]
|
||||
);
|
||||
$this->db->perform(
|
||||
$query->getStatement(),
|
||||
$query->getBindValues()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Aura\SqlQuery\Exception;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Transaction;
|
||||
|
||||
class FiskalyTransactionCacheService
|
||||
{
|
||||
/** @var array */
|
||||
private $transactions = [];
|
||||
|
||||
/** @var array $transactionResponse */
|
||||
private $transactionResponse = [];
|
||||
|
||||
/** @var array $documentMappings */
|
||||
private $documentMappings = [];
|
||||
|
||||
/** @var array $error */
|
||||
private $error = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $document
|
||||
* @param int $documentId
|
||||
*/
|
||||
public function addDocumentMapping(int $id, string $document, int $documentId): void
|
||||
{
|
||||
$this->documentMappings[$id][] = ['document' => $document, 'document_id' => $documentId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDocumentMappings(int $id): array
|
||||
{
|
||||
return $this->documentMappings[$id] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param Transaction $transaction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function put(int $id, Transaction $transaction): void
|
||||
{
|
||||
$this->transactions[$id] = $transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param TransactionReponse $transactionResponse
|
||||
*/
|
||||
public function putTransactionResponse(int $id, TransactionReponse $transactionResponse): void
|
||||
{
|
||||
$this->transactionResponse[$id] = TransactionReponse::fromDbState($transactionResponse->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function getTransactionResponse(int $id): TransactionReponse
|
||||
{
|
||||
return TransactionReponse::fromDbState($this->transactionResponse[$id]->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $errorMessage
|
||||
* @param string $sma
|
||||
*/
|
||||
public function putErrorMessage(int $id, string $errorMessage, string $sma): void
|
||||
{
|
||||
$this->error[$id] = ['sma' => $sma, 'error_message' => $errorMessage];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getErrorMessage(int $id): ?array
|
||||
{
|
||||
if (!isset($this->error[$id])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->error[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
public function get(int $id): Transaction
|
||||
{
|
||||
return $this->transactions[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*/
|
||||
public function reset(int $id): void
|
||||
{
|
||||
if (isset($this->documentMappings[$id])) {
|
||||
unset($this->documentMappings[$id]);
|
||||
}
|
||||
if (isset($this->transactionResponse[$id])) {
|
||||
unset($this->transactionResponse[$id]);
|
||||
}
|
||||
if (isset($this->error[$id])) {
|
||||
unset($this->error[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTransaction(int $id): bool
|
||||
{
|
||||
return array_key_exists($id, $this->transactionResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasError(int $id): bool
|
||||
{
|
||||
return array_key_exists($id, $this->error);
|
||||
}
|
||||
|
||||
public function getNextOpenKey(): int
|
||||
{
|
||||
if (empty($this->transactionResponse)) {
|
||||
return 0;
|
||||
}
|
||||
$keys = array_diff(range(0, count($this->transactionResponse)), array_keys($this->transactionResponse));
|
||||
|
||||
return reset($keys);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Xentral\Modules\FiskalyApi\Data\Export;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionRequest;
|
||||
|
||||
interface FiskalyTransactionPosSessionInterface
|
||||
{
|
||||
public function get(string $trxId): ?array;
|
||||
|
||||
public function insertTransactions(TransactionReponseCollection $transactionResponseCollection): void;
|
||||
|
||||
public function getTransactionIdFromTrxId(string $trxId): ?int;
|
||||
|
||||
public function tryMapDocument(string $trxId, string $document, int $documentId): int;
|
||||
|
||||
public function create(
|
||||
?TransactionRequest $request,
|
||||
?TransactionReponse $response
|
||||
): int;
|
||||
|
||||
public function update(
|
||||
int $fiskalyTransactionPosSessionId,
|
||||
TransactionRequest $request,
|
||||
TransactionReponse $response
|
||||
): void;
|
||||
|
||||
public function createOrUpdateExport(Export $export): void;
|
||||
|
||||
public function updateExport(Export $export): void;
|
||||
|
||||
public function getExportUrlsNotInDms(string $tssId): array;
|
||||
|
||||
public function getUuIdsByState(string $state, ?string $tssId = null): array;
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Datetime;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\FiskalyApi\Data\Export;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionRequest;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidArgumentException;
|
||||
use Xentral\Modules\FiskalyApi\Exception\InvalidTransactionException;
|
||||
|
||||
final class FiskalyTransactionPosSessionService implements FiskalyTransactionPosSessionInterface
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* FiskalyTransactionPosSessionService constructor.
|
||||
*
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $trxId
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function get(string $trxId): ?array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT * FROM `fiskaly_transaction` WHERE `trx_id` = :trx_id',
|
||||
['trx_id' => $trxId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionReponseCollection $transactionResponseCollection
|
||||
*/
|
||||
public function insertTransactions(TransactionReponseCollection $transactionResponseCollection): void
|
||||
{
|
||||
foreach ($transactionResponseCollection as $transactionResponse) {
|
||||
$trxId = $transactionResponse->getId();
|
||||
if (!empty($this->get($trxId))) {
|
||||
continue;
|
||||
}
|
||||
$this->create(null, $transactionResponse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $trxId
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getTransactionIdFromTrxId(string $trxId): ?int
|
||||
{
|
||||
$fiskalyTransactionId = $this->db->fetchValue(
|
||||
'SELECT `id` FROM `fiskaly_transaction` WHERE `trx_id` = :trx_id',
|
||||
[
|
||||
'trx_id' => $trxId,
|
||||
]
|
||||
);
|
||||
|
||||
return $fiskalyTransactionId === false ? null : (int)$fiskalyTransactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $document
|
||||
* @param int $documentId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTransactionFromDocument(string $document, int $documentId): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT ft.*
|
||||
FROM `fiskaly_transaction` AS `ft`
|
||||
INNER JOIN `fiskaly_tranaction_mapping` AS `ftm` ON ft.id = ftm.fiskaly_transaction_id
|
||||
WHERE ftm.document = :document AND ftm.document_id = :document_id',
|
||||
[
|
||||
'document' => $document,
|
||||
'document_id' => $documentId,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $trxId
|
||||
* @param string $document
|
||||
* @param int $documentId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function tryMapDocument(string $trxId, string $document, int $documentId): int
|
||||
{
|
||||
$fiskalyTransactionId = $this->getTransactionIdFromTrxId($trxId);
|
||||
$mappingId = $fiskalyTransactionId === null ? false : $this->db->fetchValue(
|
||||
'SELECT ftm.id
|
||||
FROM `fiskaly_tranaction_mapping` AS `ftm`
|
||||
WHERE ftm.fiskaly_transaction_id = :fiskaly_transaction_id
|
||||
AND ftm.document = :document
|
||||
AND ftm.document_id = :document_id',
|
||||
[
|
||||
'fiskaly_transaction_id' => $fiskalyTransactionId,
|
||||
'document' => $document,
|
||||
'document_id' => $documentId,
|
||||
]
|
||||
);
|
||||
if ($mappingId !== false) {
|
||||
return (int)$mappingId;
|
||||
}
|
||||
$this->db->perform(
|
||||
'INSERT INTO `fiskaly_tranaction_mapping` (`fiskaly_transaction_id`, `document`, `document_id`)
|
||||
VALUES (:fiskaly_transaction_id, :document, :document_id)',
|
||||
[
|
||||
'fiskaly_transaction_id' => $fiskalyTransactionId,
|
||||
'document' => $document,
|
||||
'document_id' => $documentId,
|
||||
]
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest|null $request
|
||||
* @param TransactionReponse|null $response
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function create(
|
||||
?TransactionRequest $request,
|
||||
?TransactionReponse $response
|
||||
): int {
|
||||
if ($request === null && $response === null) {
|
||||
throw new InvalidArgumentException('response or request required');
|
||||
}
|
||||
$trxId = $request === null ? $response->getId() : $request->getId();
|
||||
if (!empty($this->get($trxId))) {
|
||||
throw new InvalidTransactionException('Transaction already exists');
|
||||
}
|
||||
$this->db->perform(
|
||||
'INSERT INTO `fiskaly_transaction`
|
||||
(`tss_id`, `client_id`, `trx_id`, `state`,
|
||||
`time_start`, `time_end`, `json_request`, `json_response`)
|
||||
VALUES (:tss_id, :client_id, :trx_id, :state,
|
||||
NULL, NULL, :json_request, :json_response)',
|
||||
[
|
||||
'tss_id' => $request === null ? $response->getTssId() : $request->getTssId(),
|
||||
'client_id' => $request === null ? $response->getClientId() : $request->getClientId(),
|
||||
'trx_id' => $trxId,
|
||||
'state' => $response === null ? null : $response->getState(),
|
||||
'json_request' => $request === null ? null : json_encode($request->toArray()),
|
||||
'json_response' => $response === null ? null : json_encode($response->toArray()),
|
||||
]
|
||||
);
|
||||
|
||||
$fiskalyTransactionId = $this->db->lastInsertId();
|
||||
if ($response === null) {
|
||||
return $fiskalyTransactionId;
|
||||
}
|
||||
if ($response->getTimeStart() !== null) {
|
||||
$this->db->perform(
|
||||
'UPDATE `fiskaly_transaction`
|
||||
SET `time_start` = FROM_UNIXTIME(:time_start)
|
||||
WHERE `id` = :id',
|
||||
[
|
||||
'time_start' => $response->getTimeStart()->getTimestamp(),
|
||||
'id' => $fiskalyTransactionId,
|
||||
]
|
||||
);
|
||||
}
|
||||
if ($response->getTimeEnd() !== null) {
|
||||
$this->db->perform(
|
||||
'UPDATE `fiskaly_transaction`
|
||||
SET `time_end` = FROM_UNIXTIME(:time_end)
|
||||
WHERE `id` = :id',
|
||||
[
|
||||
'time_end' => $response->getTimeEnd()->getTimestamp(),
|
||||
'id' => $fiskalyTransactionId,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $fiskalyTransactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $fiskalyTransactionPosSessionId
|
||||
* @param TransactionRequest $request
|
||||
* @param TransactionReponse $response
|
||||
*/
|
||||
public function update(
|
||||
int $fiskalyTransactionPosSessionId,
|
||||
TransactionRequest $request,
|
||||
TransactionReponse $response
|
||||
): void {
|
||||
if ($response->getTimeEnd() === null) {
|
||||
$this->db->perform(
|
||||
'UPDATE `fiskaly_transaction`
|
||||
SET `state` = :state,
|
||||
`time_end` = NULL,
|
||||
`json_request` = :json_request,
|
||||
`json_response` = :json_response
|
||||
WHERE `id` = :id',
|
||||
[
|
||||
'state' => $response->getState(),
|
||||
'json_request' => json_encode($request->toApiResult()),
|
||||
'json_response' => json_encode($response->toApiResult()),
|
||||
'id' => $fiskalyTransactionPosSessionId,
|
||||
]
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->db->perform(
|
||||
'UPDATE `fiskaly_transaction`
|
||||
SET `state` = :state,
|
||||
`time_end` = FROM_UNIXTIME(:time_end),
|
||||
`json_request` = :json_request,
|
||||
`json_response` = :json_response
|
||||
WHERE `id` = :id',
|
||||
[
|
||||
'state' => $response->getState(),
|
||||
'time_end' => $response->getTimeEnd()->getTimestamp(),
|
||||
'json_request' => json_encode($request->toApiResult()),
|
||||
'json_response' => json_encode($response->toApiResult()),
|
||||
'id' => $fiskalyTransactionPosSessionId,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Export $export
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createOrUpdateExport(Export $export): void
|
||||
{
|
||||
if ($this->getExportIdFromUuid($export->getUuId()) === null) {
|
||||
$this->createExport($export);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->updateExport($export);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Export $export
|
||||
*
|
||||
* @throws Exception
|
||||
* @return int
|
||||
*/
|
||||
public function createExport(Export $export): int
|
||||
{
|
||||
$this->db->perform(
|
||||
'INSERT INTO `fiskaly_kassensichv_export`
|
||||
(`uuid`, `type`, `env`, `tssid`, `state`, `href`, `time_request`, `time_start`, `time_end`)
|
||||
VALUES (:uuid, :type, :env, :tssid, :state, :href, :time_request, :time_start, :time_end)',
|
||||
[
|
||||
'uuid' => $export->getUuId(),
|
||||
'type' => $export->getType(),
|
||||
'env' => $export->getEnv(),
|
||||
'tssid' => $export->getTssId(),
|
||||
'state' => $export->getState(),
|
||||
'href' => $export->getHref(),
|
||||
'time_request' => $export->getTimeRequest() === null ? null : (new Datetime(
|
||||
'now',
|
||||
new DateTimeZone('UTC')
|
||||
))->setTimeStamp(
|
||||
$export->getTimeRequest()
|
||||
)->format('Y-m-d H:i:s'),
|
||||
'time_start' => $export->getTimeStart() === null ? null : (new Datetime(
|
||||
'now', new DateTimeZone('UTC')
|
||||
))->setTimeStamp($export->getTimeStart())
|
||||
->format('Y-m-d H:i:s'),
|
||||
'time_end' => $export->getTimeEnd() === null ? null : (new Datetime(
|
||||
'now', new DateTimeZone('UTC')
|
||||
))->setTimeStamp($export->getTimeEnd())
|
||||
->format('Y-m-d H:i:s'),
|
||||
]
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Export $export
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function updateExport(Export $export): void
|
||||
{
|
||||
$this->db->perform(
|
||||
'UPDATE `fiskaly_kassensichv_export`
|
||||
SET `state` = :state,
|
||||
`href` = :href,
|
||||
`time_request` = :time_request,
|
||||
`time_start` = :time_start,
|
||||
`time_end` = :time_end
|
||||
WHERE `uuid` = :uuid',
|
||||
[
|
||||
'uuid' => $export->getUuId(),
|
||||
'state' => $export->getState(),
|
||||
'href' => $export->getHref(),
|
||||
'time_request' => (new Datetime('now', new DateTimeZone('UTC')))->setTimeStamp(
|
||||
$export->getTimeRequest()
|
||||
)->format('Y-m-d H:i:s'),
|
||||
'time_start' => (new Datetime('now', new DateTimeZone('UTC')))->setTimeStamp($export->getTimeStart())
|
||||
->format('Y-m-d H:i:s'),
|
||||
'time_end' => (new Datetime('now', new DateTimeZone('UTC')))->setTimeStamp($export->getTimeEnd())
|
||||
->format('Y-m-d H:i:s'),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tssId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExportUrlsNotInDms(string $tssId): array
|
||||
{
|
||||
return $this->db->fetchAll(
|
||||
"SELECT fke.id, fke.href, fke.uuid
|
||||
FROM `fiskaly_kassensichv_export` AS `fke`
|
||||
LEFT JOIN `datei_stichwoerter` AS `ds` ON fke.id = ds.parameter AND ds.objekt = 'fiskaly_kassensichv_export'
|
||||
WHERE `fke`.state = 'COMPLETED' AND fke.tssid = :tssid AND ds.id IS NULL",
|
||||
['tssid' => $tssId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $state
|
||||
* @param string|null $tssId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUuIdsByState(string $state, ?string $tssId = null): array
|
||||
{
|
||||
if ($tssId === null) {
|
||||
return $this->db->fetchCol(
|
||||
'SELECT `uuid` FROM `fiskaly_kassensichv_export` WHERE `state` = :state',
|
||||
[
|
||||
'state' => $state,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $this->db->fetchCol(
|
||||
'SELECT `uuid` FROM `fiskaly_kassensichv_export` WHERE `tssid` = :tssid AND `state` = :state',
|
||||
[
|
||||
'tssid' => $tssId,
|
||||
'state' => $state,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uuid
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getExportIdFromUuid(string $uuid): ?int
|
||||
{
|
||||
$id = $this->db->fetchValue(
|
||||
'SELECT `id` FROM `fiskaly_kassensichv_export` WHERE `uuid` = :uuid',
|
||||
['uuid' => $uuid]
|
||||
);
|
||||
|
||||
return $id === false ? null : (int)$id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\FiskalyApi\Service;
|
||||
|
||||
use Aura\SqlQuery\Exception;
|
||||
use FiskalyClient\errors\exceptions\FiskalyClientException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpException;
|
||||
use FiskalyClient\errors\exceptions\FiskalyHttpTimeoutException;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\FiskalyApi\Data\TechnicalSecuritySystem;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\AmountsPerPaymentType;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\AmountsPerPaymentTypeCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\AmountsPerVatType;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\AmountsPerVatTypeCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponse;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionReponseCollection;
|
||||
use Xentral\Modules\FiskalyApi\Data\Transaction\TransactionRequest;
|
||||
use Xentral\Modules\FiskalyApi\Factory\FiskalyApiFactory;
|
||||
use Xentral\Modules\FiskalyApi\Factory\FiskalyTransactionFactory;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Payment\CashPayment;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Payment\NonCashPayment;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Payment\OrderLineItem;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\Transaction;
|
||||
use Xentral\Modules\FiskalyApi\Transaction\VatAmount\BaseVatAmount;
|
||||
|
||||
class FiskalyTransferService
|
||||
{
|
||||
/** @var FiskalyKassenSichVApi */
|
||||
private $fiskalyApi;
|
||||
|
||||
/** @var FiskalyTransactionFactory $transactionFactory */
|
||||
private $transactionFactory;
|
||||
|
||||
/** @var Database */
|
||||
private $database;
|
||||
|
||||
/** @var FiskalyApiFactory $fiskalyApiFactory */
|
||||
private $fiskalyApiFactory;
|
||||
|
||||
/**
|
||||
* FiskalyTransferService constructor.
|
||||
*
|
||||
* @param FiskalyApiFactory $fiskalyApiFactory
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(
|
||||
FiskalyApiFactory $fiskalyApiFactory,
|
||||
FiskalyTransactionFactory $transactionFactory,
|
||||
Database $database
|
||||
) {
|
||||
$this->fiskalyApiFactory = $fiskalyApiFactory;
|
||||
$this->transactionFactory = $transactionFactory;
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $organizationId
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function loadOrganization(string $organizationId): self
|
||||
{
|
||||
$this->fiskalyApi = $this->fiskalyApiFactory->createFiskalyKassenSichVApiFromSystemSettings($organizationId);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $cashierId
|
||||
* @param bool $incoming
|
||||
* @param float $amount
|
||||
* @param bool $isCash
|
||||
*
|
||||
* @throws Exception
|
||||
* @return Transaction
|
||||
*
|
||||
* @depracated
|
||||
*/
|
||||
public function createTransactionFromSingleJournal(
|
||||
int $cashierId,
|
||||
bool $incoming,
|
||||
float $amount,
|
||||
bool $isCash
|
||||
): Transaction {
|
||||
$posProjectQuery = $this->database->select()
|
||||
->from('pos_kassierer AS p')
|
||||
->cols(['f.tss_uuid', 'f.client_uuid'])
|
||||
->where('p.kassenkennung=:kennung')
|
||||
->leftJoin('fiskaly_pos_mapping AS f', 'f.pos_id = p.projekt')
|
||||
->bindValue('kennung', $cashierId);
|
||||
$result = $this->database->fetchRow($posProjectQuery->getStatement(), $posProjectQuery->getBindValues());
|
||||
$tssUuid = $result['tss_uuid'];
|
||||
$clientId = $result['client_uuid'];
|
||||
$tssDescription = $result['tss_description'];
|
||||
|
||||
if ($isCash) {
|
||||
$paymentTypePayment = new CashPayment($amount);
|
||||
} else {
|
||||
$paymentTypePayment = new NonCashPayment($amount);
|
||||
}
|
||||
|
||||
$vat = (float)0;
|
||||
$sum = $amount * ($incoming ? 1 : -1);
|
||||
$vatTypePayment = BaseVatAmount::fromPercentage($vat, $sum);
|
||||
|
||||
|
||||
$transaction = new Transaction([$paymentTypePayment], [$vatTypePayment], [], $clientId);
|
||||
|
||||
$tss = new TechnicalSecuritySystem($tssUuid, $tssDescription);
|
||||
|
||||
return $this->fiskalyApi->uploadTransaction($transaction, $tss);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $transactionRequest
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function startTransaction(TransactionRequest $transactionRequest): TransactionReponse
|
||||
{
|
||||
return $this->fiskalyApi->createTransaction($transactionRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $tssUuid
|
||||
* @param int $offset
|
||||
* @param int $limit
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponseCollection
|
||||
*/
|
||||
public function getTransactions(
|
||||
?string $tssUuid = null,
|
||||
int $offset = 0,
|
||||
int $limit = 100
|
||||
): TransactionReponseCollection {
|
||||
return $this->fiskalyApi->getTransactions($tssUuid, $offset, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionRequest $transactionRequest
|
||||
*
|
||||
* @throws FiskalyClientException
|
||||
* @throws FiskalyHttpException
|
||||
* @throws FiskalyHttpTimeoutException
|
||||
* @return TransactionReponse
|
||||
*/
|
||||
public function updateTransaction(TransactionRequest $transactionRequest): TransactionReponse
|
||||
{
|
||||
return $this->fiskalyApi->updateTransaction($transactionRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionReponse $transactionResponse
|
||||
* @param bool $incoming
|
||||
* @param float $value
|
||||
* @param bool $isTraining
|
||||
*
|
||||
* @return TransactionRequest
|
||||
*/
|
||||
public function createTransactionRequestFromPosCounting(
|
||||
TransactionReponse $transactionResponse,
|
||||
bool $incoming,
|
||||
float $value,
|
||||
bool $isTraining = false
|
||||
): TransactionRequest {
|
||||
$negativeMultiplier = $incoming ? 1 : -1;
|
||||
$vatTypePayment = BaseVatAmount::fromPercentage(0, $value);
|
||||
$amountsPerVatTypeCollection = new AmountsPerVatTypeCollection(
|
||||
[
|
||||
new AmountsPerVatType(
|
||||
$vatTypePayment->getVatType(), number_format($negativeMultiplier * $value, 2, '.', '')
|
||||
),
|
||||
]
|
||||
);
|
||||
$amountsPerPaymentTypeCollection = new AmountsPerPaymentTypeCollection(
|
||||
[new AmountsPerPaymentType('CASH', number_format($negativeMultiplier * $value, 2, '.', ''), 'EUR')]
|
||||
);
|
||||
$receiptType = $isTraining ? 'TRAINING' : 'TRANSFER';
|
||||
|
||||
return $this->fiskalyApi->getFinishTransactionRequest(
|
||||
$transactionResponse,
|
||||
!empty($posSession['training']) ? 'TRAINING' : $receiptType,
|
||||
$amountsPerVatTypeCollection,
|
||||
$amountsPerPaymentTypeCollection
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionReponse $transactionResponse
|
||||
* @param array $posSession
|
||||
*
|
||||
* @return TransactionRequest
|
||||
*/
|
||||
public function createFinishTransactionFromPosSession(
|
||||
TransactionReponse $transactionResponse,
|
||||
array $posSession
|
||||
): TransactionRequest {
|
||||
$amountsPerVatTypeCollection = new AmountsPerVatTypeCollection();
|
||||
$amountsPerPaymentTypeCollection = new AmountsPerPaymentTypeCollection();
|
||||
$paymentType = $posSession['ptype'];
|
||||
$receiptType = 'RECEIPT';
|
||||
$type = $posSession['rtype'];
|
||||
if (in_array($type, ['einlage', 'entnahme'])) {
|
||||
$type = 'TRANSFER';
|
||||
}
|
||||
$negativeMultiplier = 1;
|
||||
if ($type === 'entnahme' || in_array($posSession['cmd'], ['stornieren', 'teilstornieren'])) {
|
||||
$negativeMultiplier = -1;
|
||||
}
|
||||
$tip = isset($posSession['tip']) ? (float)round(str_replace(',', '.', $posSession['tip']), 2) : 0.;
|
||||
$amount = (float)$posSession['soll'] * $negativeMultiplier;
|
||||
if ($paymentType === 'bar') {
|
||||
$amountsPerPaymentTypeCollection->addPaymentType(
|
||||
new AmountsPerPaymentType('CASH', number_format($amount + $tip, 2, '.', ''), 'EUR')
|
||||
);
|
||||
} else {
|
||||
$amountsPerPaymentTypeCollection->addPaymentType(
|
||||
new AmountsPerPaymentType('NON_CASH', number_format($amount, 2, '.', ''), 'EUR')
|
||||
);
|
||||
if ($tip > 0) {
|
||||
$amountsPerPaymentTypeCollection->addPaymentType(
|
||||
new AmountsPerPaymentType('CASH', number_format($tip, 2, '.', ''), 'EUR')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($posSession['wk'] as $position) {
|
||||
$vat = str_replace('%', '', $position['tax']);
|
||||
$vat = (float)str_replace(',', '.', $vat);
|
||||
$sum = (float)str_replace(',', '.', $position['preis']) * (float)str_replace(',', '.', $position['amount'])
|
||||
* (1 - (float)str_replace(',', '.', $position['rabatt']) / 100);
|
||||
$vatTypePayment = BaseVatAmount::fromPercentage($vat, $sum);
|
||||
$amountsPerVatTypeCollection->combine(
|
||||
new AmountsPerVatTypeCollection(
|
||||
[
|
||||
new AmountsPerVatType($vatTypePayment->getVatType(), number_format($sum, 2, '.', '')),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
if ($tip > 0) {
|
||||
$vatTypePayment = BaseVatAmount::fromPercentage(0, $tip);
|
||||
$amountsPerVatTypeCollection->combine(
|
||||
new AmountsPerVatTypeCollection(
|
||||
[
|
||||
new AmountsPerVatType($vatTypePayment->getVatType(), number_format($tip, 2, '.', '')),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->fiskalyApi->getFinishTransactionRequest(
|
||||
$transactionResponse,
|
||||
!empty($posSession['training']) ? 'TRAINING' : $receiptType,
|
||||
$amountsPerVatTypeCollection,
|
||||
$amountsPerPaymentTypeCollection
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $posSession
|
||||
*
|
||||
* @throws Exception
|
||||
* @return Transaction
|
||||
*
|
||||
* @depracated
|
||||
*/
|
||||
public function transferPosSession($posSession): Transaction
|
||||
{
|
||||
$cashierId = $posSession['kassiererId'];
|
||||
$result = $this->transactionFactory->getClientAndTssInfoFromCashierId((string)$cashierId);
|
||||
$tssUuid = $result['tss_uuid'];
|
||||
$clientId = $result['client_uuid'];
|
||||
$tssDescription = $result['tss_description'];
|
||||
$paymentType = $posSession['ptype'];
|
||||
|
||||
$negativeMultiplier = 1;
|
||||
if (in_array($posSession['cmd'], ['stornieren', 'teilstornieren'])) {
|
||||
$negativeMultiplier = -1;
|
||||
}
|
||||
$tip = isset($posSession['tip']) ? (float)round(str_replace(',', '.', $posSession['tip']), 2) : 0.;
|
||||
$amount = (float)$posSession['soll'] * $negativeMultiplier;
|
||||
$paymentTypePayments = [];
|
||||
if ($paymentType === 'bar') {
|
||||
$paymentTypePayments[] = new CashPayment($amount + $tip);
|
||||
} else {
|
||||
$paymentTypePayments[] = new NonCashPayment($amount);
|
||||
if ($tip > 0) {
|
||||
$paymentTypePayments[] = new CashPayment($tip);
|
||||
}
|
||||
}
|
||||
|
||||
$vatTypeAmounts = [];
|
||||
$oderLineItems = [];
|
||||
|
||||
foreach ($posSession['wk'] as $position) {
|
||||
$vat = str_replace('%', '', $position['tax']);
|
||||
$vat = (float)str_replace(',', '.', $vat);
|
||||
$sum = (float)str_replace(',', '.', $position['preis']);
|
||||
$amount = (float)$position['amount'];
|
||||
$oderLineItems[] = new OrderLineItem($amount, (string)$position['artikel'], $sum * $negativeMultiplier);
|
||||
$sum *= $amount * $negativeMultiplier;
|
||||
$vatTypePayment = BaseVatAmount::fromPercentage($vat, $sum);
|
||||
|
||||
$vatTypeClass = get_class($vatTypePayment);
|
||||
/** @var BaseVatAmount $vatTypeAmount */
|
||||
$cachedVatTypePayment = $vatTypeAmounts[$vatTypeClass] ?? null;
|
||||
if (empty($cachedVatTypePayment)) {
|
||||
$vatTypeAmounts[$vatTypeClass] = $vatTypePayment;
|
||||
} else {
|
||||
$cachedVatTypePayment->add($sum);
|
||||
}
|
||||
}
|
||||
if ($tip > 0) {
|
||||
$vatTypePayment = BaseVatAmount::fromPercentage(0., $tip);
|
||||
$vatTypeClass = get_class($vatTypePayment);
|
||||
/** @var BaseVatAmount $vatTypeAmount */
|
||||
$cachedVatTypePayment = $vatTypeAmounts[$vatTypeClass] ?? null;
|
||||
if ($cachedVatTypePayment === null) {
|
||||
$vatTypeAmounts[$vatTypeClass] = $vatTypePayment;
|
||||
} else {
|
||||
$cachedVatTypePayment->add($tip);
|
||||
}
|
||||
$oderLineItems[] = new OrderLineItem(1, 'Trinkgeld', $tip);
|
||||
}
|
||||
|
||||
$transaction = new Transaction($paymentTypePayments, array_values($vatTypeAmounts), $oderLineItems, $clientId);
|
||||
|
||||
$tss = new TechnicalSecuritySystem($tssUuid, $tssDescription);
|
||||
|
||||
return $this->fiskalyApi->uploadTransaction($transaction, $tss);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user