OpenXE/classes/Modules/FiskalyApi/Service/FiskalyPosClosingService.php
2021-05-21 08:49:41 +02:00

325 lines
11 KiB
PHP

<?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);
}
}