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