Files
kupshop/bundles/KupShop/POSBundle/Util/PosPaymentUtil.php
2025-08-02 16:30:27 +02:00

305 lines
12 KiB
PHP

<?php
namespace KupShop\POSBundle\Util;
use FilipSedivy\EET\Utils\UUID;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\ContentBundle\View\Exception\ValidationException;
use KupShop\GraphQLBundle\ApiPos\Types\Collection\PosOrderPaymentsCollection;
use KupShop\GraphQLBundle\ApiPos\Types\Collection\PosPaymentCollection;
use KupShop\GraphQLBundle\ApiPos\Types\Order\PosOrderResponse;
use KupShop\GraphQLBundle\ApiPos\Types\Payments\PosConfirmPayment;
use KupShop\GraphQLBundle\ApiPos\Types\Payments\PosOrderPayment;
use KupShop\GraphQLBundle\ApiPos\Types\Payments\PosPayment;
use KupShop\GraphQLBundle\ApiPos\Types\Payments\PosPaymentsStatusUpdate;
use KupShop\GraphQLBundle\ApiShared\ApiUtil;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\PosContext;
use KupShop\KupShopBundle\Util\Database\QueryHint;
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
use KupShop\OrderingBundle\Exception\OrderingException;
use KupShop\POSBundle\Event\PosOrderEvent;
use KupShop\WarehouseBundle\Util\StoreItemWorker;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class PosPaymentUtil
{
use \DatabaseCommunication;
public function __construct(
protected PosUtil $posUtil,
protected CurrencyContext $currencyContext,
protected PosContext $posContext,
protected EventDispatcherInterface $eventDispatcher,
protected ?PosWarehouseUtil $posWarehouseUtil = null,
protected ?StoreItemWorker $storeItemWorker = null,
) {
}
public function getOrderPayments(int $idOrder, ?\Order $order = null): PosOrderPaymentsCollection
{
if (!$order) {
$order = new \Order();
$order->createFromDB($idOrder);
}
$payments = [];
foreach ($order->getPaymentsArray() as $payment) {
$payments[] = new PosOrderPayment(payment: $payment);
}
return new PosOrderPaymentsCollection(
items: $payments
);
}
public function setPaymentStatus(int $idOrder, array $payments): PosPaymentsStatusUpdate
{
$order = new \Order();
$order->createFromDB($idOrder);
$classPayment = $order->getDeliveryType()->getPayment();
$classPayment->setOrder($order);
foreach ($payments as $payment) {
if (in_array($payment->status, [\Payment::STATUS_PENDING, \Payment::STATUS_FINISHED, \Payment::STATUS_CREATED, \Payment::STATUS_STORNO])) {
$classPayment->setStatus($payment->status, $payment->uuid);
if ($payment->terminal ?? false) {
if (!$classPayment->paymentId) {
throw new ValidationException('Proběhlo potvrzení platby, ale není nastaveno ID platby');
}
sqlQueryBuilder()
->update('order_payments')
->set('payment_data', 'JSON_MERGE(payment_data, :terminalData)')
->setParameter('terminalData', json_encode(['terminal' => $payment->terminal]))
->andWhere(Operator::equals(['id' => $classPayment->paymentId]))
->execute();
}
}
}
$results = [];
$paymentArray = null;
$isPaid = null;
$remainingPayment = null;
QueryHint::withRouteToMaster(function () use ($order, &$paymentArray, &$isPaid, &$remainingPayment) {
$paymentArray = $order->getPaymentsArray();
$isPaid = $order->isPaid();
$remainingPayment = $order->getRemainingPayment();
});
foreach ($paymentArray as $payment) {
$results[] = new PosOrderPayment(payment: $payment);
}
return new PosPaymentsStatusUpdate(
isPaid: $isPaid,
remainingPayment: $remainingPayment,
posOrderPayments: new PosOrderPaymentsCollection($results),
);
}
public function confirmPayment(int $idOrder, string $uuidPayment, string $paidPrice, string $methodPayment, ?string $terminal = null): PosConfirmPayment
{
$order = new \Order();
$order->createFromDB($idOrder);
$order->fetchItems();
$exceptionMessage = null;
if ($uuidPayment != 'UNDEFINED') {
$classPayment = $order->getDeliveryType()->getPayment();
$classPayment->setOrder($order);
// Pokud spadne generování unikátního čísla faktury, tak se nesmí zahodit nic co se stane po přepnutí statusu platby
try {
$classPayment->setStatus(\Payment::STATUS_FINISHED, $uuidPayment);
} catch (OrderingException $exception) {
$exceptionMessage = $exception->getMessage();
}
if ($terminal) {
if (!$classPayment->paymentId) {
throw new ValidationException('Proběhlo potvrzení platby, ale není nastaveno ID platby');
}
sqlQueryBuilder()
->update('order_payments')
->set('payment_data', 'JSON_MERGE(payment_data, :terminalData)')
->setParameter('terminalData', json_encode(['terminal' => $terminal]))
->andWhere(Operator::equals(['id' => $classPayment->paymentId]))
->execute();
}
}
if ($order->isPaid()) {
$this->eventDispatcher->dispatch(new PosOrderEvent($order->getPurchaseState(), $this->posContext->getActiveId(), $order), PosOrderEvent::ORDER_PAID_BY_POS);
}
if ($order->isPaid() && toDecimal($paidPrice)->isZero() && !$order->getDeliveryId()) {
$this->changeOrderDelivery(
posId: $this->posContext->getActiveId(),
order: $order,
paymentMethod: $methodPayment,
);
}
return new PosConfirmPayment(
posOrderResponse: new PosOrderResponse(
idOrder: $order->id,
noOrder: $order['order_no'],
isPaid: $order->isPaid(),
remainingPayment: $order->getRemainingPayment(),
status: $order->status,
uuidPayment: $uuidPayment
),
saved: true,
exception: $exceptionMessage
);
}
public function createNonOrderPayment(int $idPos, string $price, string $note, $method): int
{
global $adminID;
return sqlGetConnection()->transactional(function () use ($adminID, $idPos, $price, $note, $method) {
$this->insertSQL('order_payments', [
'price' => $price,
'method' => $method,
'note' => $note,
'date' => date('Y-m-d H:i:s'),
'admin' => !empty($adminID) ? $adminID : null,
]);
$id_payment = sqlInsertId();
$this->insertSQL('pos_payments_relation', [
'id_pos' => $idPos,
'id_payment' => $id_payment,
]);
return 200;
});
}
public function getPosPaymentList(int $idPos, int $pageDivide, int $page, ?string $method = null): PosPaymentCollection
{
return QueryHint::withRouteToMaster(function () use ($idPos, $pageDivide, $page, $method) {
$qb = $this->getQueryPaymentLoader($idPos, $pageDivide, $page);
$next = $this->getQueryPaymentLoader($idPos, $pageDivide, $page + 1)->execute()->fetchAll();
if (!empty($method)) {
$qb->where('pos.method = ?')->setParameter(0, $method);
}
$qb2 = sqlQueryBuilder()
->select('SUM(price) as in_pos')
->from('order_payments', 'op')
->andWhere(Operator::orX(
Operator::equals(['op.method' => \Payment::METHOD_CASH]),
Operator::equals(['op.method' => \Payment::METHOD_CASH_INSERTION]),
Operator::equals(['op.method' => \Payment::METHOD_CASH_SELECTION]),
Operator::equals(['op.method' => \Payment::METHOD_EET_INVOICE_CASH]),
))
->leftJoin('op', 'pos_payments_relation', 'ppr', 'ppr.id_payment = op.id')
->andWhere(Operator::equals(['op.status' => \Payment::STATUS_FINISHED]))
->andWhere('ppr.id_pos = :pos_id')
->setParameter('pos_id', $idPos);
$dataCollection = [];
$baseCurrencySymbol = $this->currencyContext->getDefault()->getSymbol();
foreach ($qb->execute()->fetchAll() as $row) {
$dataCollection[] = new PosPayment(
$row['id'],
ApiUtil::prepareDateTimeFromDB($row['date'])->format('H:i:s d.m.Y'),
$row['id_order'],
$row['price'],
'',
\Pos::METHODS[$row['method']],
$row['note'],
$row['admin'],
$row['currency_symbol'] ?: $baseCurrencySymbol
);
}
return new PosPaymentCollection(
$dataCollection,
sqlFetchArray($qb2->execute())['in_pos'],
$page,
($page - 1) > 0,
count($next) > 0
);
});
}
private function getQueryPaymentLoader($idPos, $pageDivide, $page)
{
$qb = sqlQueryBuilder()
->select('pos.id', 'pos.note', 'pos.price', 'a.login as admin', 'pos.method', 'pos.id_order', 'date')
->from('order_payments', 'pos')
->leftJoin('pos', 'admins', 'a', 'pos.admin=a.id')
->leftJoin('pos', 'pos_payments_relation', 'ppr', 'ppr.id_payment = pos.id')
->andWhere('ppr.id_pos = :pos_id')
->andWhere(Operator::equals(['pos.status' => \Payment::STATUS_FINISHED]))
->orderBy('pos.date', 'DESC')
->setParameter('pos_id', $idPos)
->setMaxResults($pageDivide)
->setFirstResult($pageDivide * ($page - 1));
if (findModule(\Modules::CURRENCIES)) {
$qb->addSelect('c.symbol as currency_symbol')
->leftJoin('pos', 'orders', 'o', 'o.id=pos.id_order')
->leftJoin('o', 'currencies', 'c', 'o.currency=c.id');
} else {
$qb->addSelect('null as currency_symbol');
}
return $qb;
}
public function changeOrderDelivery(int $posId, \Order $order, string $paymentMethod): void
{
$deliveryType = $this->posUtil->getPosDeliveryType($posId, $paymentMethod);
$order->id_delivery = $deliveryType['id'];
$order->delivery_type = $deliveryType['name'];
$order->updateSQL('orders', ['id_delivery' => $deliveryType['id'], 'delivery_type' => $deliveryType['name']], ['id' => $order->id]);
}
public function createOrderPayment(\Order $order, PurchaseState $purchaseState, bool $purchase = false): ?string
{
$methodPayment = $purchaseState->getCustomData('methodPayment');
if ($methodPayment == 'UNDEFINED' || $methodPayment == 'INVOICE') {
return null;
}
$paidPrice = $purchaseState->getCustomData('paidPrice');
try {
return sqlGetConnection()->transactional(function () use ($order, $paidPrice, $methodPayment) {
$idPos = $this->posContext->getActiveId();
$this->changeOrderDelivery(posId: $idPos, order: $order, paymentMethod: $methodPayment);
$classPayment = $order->getDeliveryType()->getPayment();
$classPayment->setOrder($order);
$uuid = UUID::v4();
$classPayment->createPayment($uuid, toDecimal($paidPrice));
if ($classPayment->paymentId) {
sqlQueryBuilder()
->insert('pos_payments_relation')
->values([
'id_pos' => $idPos,
'id_payment' => $classPayment->paymentId,
])
->execute();
return $uuid;
}
return null;
});
} catch (\Exception|\Throwable $e) {
$id = addActivityLog(ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_COMMUNICATION, "POS API: vytvoření platby selhalo s chybou: {$e->getMessage()}");
$order->logHistory("<a href=\"javascript:nw('reportActivities', '{$id}', '');\"> Vytvoření platby z pokladny skončilo s chybou.</a>");
throw $e;
}
}
}