828 lines
31 KiB
PHP
828 lines
31 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace External\PompoBundle\DRS\Synchronizer;
|
|
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
|
use External\PompoBundle\Util\Configuration;
|
|
use KupShop\BonusProgramBundle\Event\UserBonusPointsUpdatedEvent;
|
|
use KupShop\BonusProgramBundle\Utils\BonusComputer;
|
|
use KupShop\I18nBundle\Entity\Currency;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Query\JsonOperator;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Database\QueryHint;
|
|
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
|
|
use KupShop\KupShopBundle\Util\StringUtil;
|
|
use KupShop\OrderingBundle\Entity\Order\OrderItem;
|
|
use Query\Operator;
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
use Symfony\Contracts\Service\Attribute\Required;
|
|
|
|
/**
|
|
* Synchronizuje objednavky z pokladen do e-shopu - uctenky, vypocita body zakaznika.
|
|
*/
|
|
class POSOrderSynchronizer extends AbstractSynchronizer
|
|
{
|
|
/** @required */
|
|
public Configuration $configuration;
|
|
/** @required */
|
|
public EventDispatcherInterface $eventDispatcher;
|
|
|
|
public ?BonusComputer $bonusComputer = null;
|
|
/** @required */
|
|
public SentryLogger $sentryLogger;
|
|
|
|
protected static $type = 'pos_order';
|
|
|
|
private ?int $timestamp = null;
|
|
|
|
public function process(): void
|
|
{
|
|
// nacist posledni timestamp
|
|
$this->timestamp = $this->getLastTimestamp();
|
|
|
|
// zpracovat objednavky
|
|
foreach ($this->getItems() as $index => $item) {
|
|
try {
|
|
$this->processItem($item);
|
|
} catch (UniqueConstraintViolationException $e) {
|
|
}
|
|
|
|
$this->timestamp = max((int) ($item['header']['TimeStamp'] ?? 0), 0);
|
|
if (($index % 500) == 0) {
|
|
$this->setLastTimestamp($this->timestamp);
|
|
}
|
|
}
|
|
|
|
// ulozit timestamp
|
|
if ($this->timestamp) {
|
|
$this->setLastTimestamp($this->timestamp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kvuli doimportovani starych objednavek z prodejen.
|
|
*/
|
|
public function processWithCustomTimestamp(int $timestamp, callable $timestampUpdate): void
|
|
{
|
|
$this->timestamp = $timestamp;
|
|
|
|
// zpracovat objednavky
|
|
foreach ($this->getItems() as $index => $item) {
|
|
try {
|
|
$this->processItem($item);
|
|
} catch (UniqueConstraintViolationException $e) {
|
|
}
|
|
|
|
$this->timestamp = max((int) ($item['header']['TimeStamp'] ?? 0), 0);
|
|
if (($index % 500) == 0) {
|
|
$timestampUpdate($this->timestamp);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function processOrder(array $order, bool $force = true): void
|
|
{
|
|
if ($force) {
|
|
$order['header']['force'] = true;
|
|
}
|
|
|
|
$this->processItem($order);
|
|
}
|
|
|
|
public function processSingleItem(int $drsId, bool $force = false): void
|
|
{
|
|
if ($order = $this->drsApi->getStoreOrder($drsId)) {
|
|
if ($force) {
|
|
$order['header']['force'] = true;
|
|
}
|
|
|
|
$this->processItem($order);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zpracuje prodejku z DRSu. Bud vytvori novou objednavku, nebo aktualizuje jiz existujici objednavku na eshopu (rezervace).
|
|
*/
|
|
protected function processItem(array $item, bool $isOldOrder = false): void
|
|
{
|
|
if (!$isOldOrder && (empty($item['header']['DocumentNumber']) || (($item['header']['DocumentTypeDesc'] ?? '') !== 'SALE'))) {
|
|
return;
|
|
}
|
|
|
|
$orderNumber = $item['header']['Description'] ?? '';
|
|
$description = explode('ESHOP:', $orderNumber);
|
|
if ($description[1] ?? false) {
|
|
$orderNumber = $description[1];
|
|
}
|
|
|
|
// Pokud Je tam jen text nebo mezery, tak to necham fallbacknout na DocumentNumber
|
|
if (!preg_match('~[0-9]+~', $orderNumber) || strpos($orderNumber, ' ') !== false) {
|
|
$orderNumber = null;
|
|
}
|
|
|
|
$oldReceiptOrderNumber = 'P'.$item['header']['DocumentNumber'];
|
|
if (empty($orderNumber)) {
|
|
$dateCreated = new \DateTime($item['header']['RecCreated']);
|
|
$orderNumber = 'P'.$dateCreated->format('ymd').'-'.$item['header']['DocumentNumber'];
|
|
}
|
|
|
|
// Objednavka z nejhracky, skipuju
|
|
if ($isOldOrder && (StringUtil::startsWith($orderNumber, '2') || StringUtil::startsWith($orderNumber, '4'))) {
|
|
return;
|
|
}
|
|
|
|
if (($item['data']['InvoiceCountryCode'] ?? 'CZ') === 'SK') {
|
|
$item['header']['CurrencyISOCode'] = 'EUR';
|
|
}
|
|
|
|
// mena objednavky
|
|
$currency = $this->getOrderCurrency($item['header']['CurrencyISOCode'] ?? '');
|
|
|
|
$orX = [Operator::equals(['order_no' => $orderNumber])];
|
|
if (StringUtil::startsWith($oldReceiptOrderNumber, 'P')) {
|
|
$orX[] = Operator::equals(['order_no' => $oldReceiptOrderNumber]);
|
|
}
|
|
|
|
$foundOrder = sqlQueryBuilder()
|
|
->select('id, order_no')
|
|
->from('orders')
|
|
->andWhere(
|
|
Operator::orX($orX)
|
|
)
|
|
->execute()->fetchAssociative();
|
|
|
|
$orderId = $foundOrder['id'] ?? null;
|
|
|
|
// import starych objednavek - muzou tam prijit novy objednavky, ktery vznikly uz u nas... a ty chci preskocit
|
|
if ($isOldOrder && (StringUtil::startsWith($orderNumber, '122') || StringUtil::startsWith($orderNumber, '322'))) {
|
|
return;
|
|
}
|
|
|
|
// objednavku uz mame v e-shopu, protoze je to rezervace provedena na e-shopu
|
|
if ($orderId) {
|
|
// pokud je to stara objednavak (ze stareho eshopu)
|
|
if ($isOldOrder) {
|
|
$this->updateOldOrder((int) $orderId, $item);
|
|
} else {
|
|
$this->updateShopOrder((int) $orderId, $item, $currency);
|
|
// Zpetna aktualizace, aby se opravili cisla objednavek
|
|
if ($foundOrder['order_no'] !== $orderNumber) {
|
|
$this->updateOrderNumber((int) $orderId, $orderNumber);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Vytvorim objednavku
|
|
sqlGetConnection()->transactional(function () use ($item, $orderNumber, $currency, $isOldOrder) {
|
|
// Nactu spravny deliveryType
|
|
$deliveryTypeName = strip_tags($item['data']['NameDelivery'] ?? '').' - '.strip_tags($item['data']['NamePayment'] ?? '');
|
|
$deliveryType = $this->getDeliveryType();
|
|
if ($isOldOrder) {
|
|
$deliveryType = null;
|
|
}
|
|
|
|
if ($deliveryType) {
|
|
$deliveryTypeName = $deliveryType->name ?? '';
|
|
}
|
|
|
|
// Najdu uzivatele
|
|
$userId = null;
|
|
$userName = '';
|
|
$userSurname = '';
|
|
if (!empty($item['data']['InvoiceName'])) {
|
|
$nameParts = explode(' ', $item['data']['InvoiceName']);
|
|
$userName = $nameParts[0];
|
|
unset($nameParts[0]);
|
|
$userSurname = implode(' ', $nameParts);
|
|
}
|
|
|
|
if ($item['customer']['CustomerNumber'] ?? false) {
|
|
if ($user = $this->getUser((string) $item['customer']['CustomerNumber'])) {
|
|
$userId = $user->id;
|
|
$userName = $user->name ?? $userName;
|
|
$userSurname = $user->surname ?? $userSurname;
|
|
}
|
|
}
|
|
|
|
if (empty($userName) && empty($userSurname)) {
|
|
$userName = 'Prodej';
|
|
$userSurname = 'prodejna ('.$item['header']['DocumentNumber'].')';
|
|
}
|
|
|
|
// Stav objednavky
|
|
$status = $this->getOrderStatus($item, $isOldOrder);
|
|
$dateHandle = null;
|
|
// datum vyrizeni
|
|
if ($status == $this->configuration->getOrderFinalStatus()) {
|
|
try {
|
|
$dateHandle = (new \DateTime($item['data']['DueDate'] ?? 'now'))->format('Y-m-d H:i:s');
|
|
} catch (\Throwable $e) {
|
|
$dateHandle = (new \DateTime())->format('Y-m-d H:i:s');
|
|
}
|
|
}
|
|
|
|
$sellerId = $this->getSellerByBranchId($item['header']['SourceBranch'] ?? '');
|
|
|
|
// Vytvorim objednavku
|
|
sqlQueryBuilder()
|
|
->insert('orders')
|
|
->directValues(
|
|
[
|
|
'id_user' => $userId,
|
|
'id_language' => $currency->getId() === 'EUR' ? 'sk' : 'cs',
|
|
'order_no' => $orderNumber,
|
|
'date_created' => $item['header']['RecCreated'],
|
|
'date_accept' => $dateHandle,
|
|
'date_handle' => $dateHandle,
|
|
'date_updated' => $item['header']['RecCreated'],
|
|
'currency' => $currency->getId(),
|
|
'status' => $status,
|
|
'status_storno' => ($item['header']['StornoDocument'] === '1' || ($item['data']['Status_WEB_objednavky'] ?? null) == 80) ? 1 : 0,
|
|
'status_payed' => 1,
|
|
'id_delivery' => $deliveryType ? $deliveryType->id : null,
|
|
'delivery_type' => $deliveryTypeName,
|
|
'flags' => $isOldOrder ? 'O' : 'NP',
|
|
'invoice_email' => $item['data']['Email'] ?? '',
|
|
'invoice_name' => $userName,
|
|
'invoice_surname' => $userSurname,
|
|
'invoice_country' => $item['data']['InvoiceCountryCode'] ?? 'CZ',
|
|
'note_admin' => json_encode([
|
|
'transactionPrintout' => $item['data']['TransactionPrintout'] ?? null,
|
|
'delivery_data' => [
|
|
'seller_id' => $sellerId,
|
|
],
|
|
]),
|
|
]
|
|
)->execute();
|
|
|
|
$orderId = (int) sqlInsertId();
|
|
|
|
// Vytvarim mapping do tabulky
|
|
sqlQueryBuilder()
|
|
->insert('drs_orders')
|
|
->directValues(
|
|
[
|
|
'id_drs' => $orderNumber,
|
|
'id_order' => $orderId,
|
|
'data' => json_encode(
|
|
[
|
|
'originalDocumentNumber' => $item['header']['DocumentNumber'],
|
|
'type' => 'sale',
|
|
'hasReceipt' => 1,
|
|
'completed' => 1,
|
|
'recId' => $item['header']['TimeStamp'],
|
|
]
|
|
),
|
|
]
|
|
)->execute();
|
|
|
|
// insertnu polozky objednavky
|
|
$this->insertShopOrderItems($orderId, $item, $currency);
|
|
|
|
$order = new \Order();
|
|
$order->createFromDB($orderId);
|
|
|
|
// ulozim k objednavce kupon, ktery v ni byl pouzit
|
|
$this->setOrderCoupon($order, $item, $sellerId);
|
|
|
|
// zavolam recalculate na objednavce, aby sedela total price
|
|
$order->recalculate();
|
|
if ($isOldOrder) {
|
|
$order->logHistory('Stará objednávka');
|
|
} else {
|
|
$order->logHistory('[DRS] Nákup přes pokladnu');
|
|
}
|
|
|
|
// zavolam update order, ktery prida body za objednavku
|
|
$this->updateShopOrder($orderId, $item, $currency, true);
|
|
|
|
return $order;
|
|
});
|
|
}
|
|
|
|
protected function getItems(): iterable
|
|
{
|
|
if ($this->timestamp === null) {
|
|
// od roku 2020
|
|
$this->timestamp = 38096494;
|
|
}
|
|
|
|
do {
|
|
$hasData = false;
|
|
foreach ($this->drsApi->getStoreOrders($this->timestamp) as $item) {
|
|
$hasData = true;
|
|
yield $item;
|
|
}
|
|
} while ($hasData === true);
|
|
}
|
|
|
|
private function getOrderStatus(array $item, bool $isOldOrder): int
|
|
{
|
|
if ($isOldOrder) {
|
|
switch ($item['data']['Status_WEB_objednavky'] ?? null) {
|
|
case null:
|
|
case '':
|
|
return 0;
|
|
case 10:
|
|
// Přijatá
|
|
return 1;
|
|
case 40:
|
|
// Zpracovává se
|
|
return 2;
|
|
case 50:
|
|
// Vyřízeno
|
|
return 4;
|
|
case 60:
|
|
// Připraveno k osobnímu odběru
|
|
return 5;
|
|
case 70:
|
|
// ukončená
|
|
return 6;
|
|
case 80:
|
|
// stornovaná
|
|
return 6;
|
|
case 90:
|
|
// převoz
|
|
return 3;
|
|
case 91:
|
|
// nekompletní
|
|
return 7;
|
|
}
|
|
}
|
|
|
|
return $this->configuration->getOrderFinalStatus();
|
|
}
|
|
|
|
private function getSellerByBranchId(string $branchId): ?int
|
|
{
|
|
$sellerId = sqlQueryBuilder()
|
|
->select('id')
|
|
->from('sellers')
|
|
->where(
|
|
Operator::equals(
|
|
[
|
|
JsonOperator::value('data', 'branchId') => $branchId,
|
|
]
|
|
)
|
|
)->execute()->fetchOne();
|
|
|
|
if (!$sellerId) {
|
|
return null;
|
|
}
|
|
|
|
return (int) $sellerId;
|
|
}
|
|
|
|
private function getUser(string $customerId): ?\User
|
|
{
|
|
$data = sqlQueryBuilder()
|
|
->select('u.*')
|
|
->from('drs_users', 'du')
|
|
->join('du', 'users', 'u', 'du.id_user = u.id')
|
|
->where(Operator::equals(['du.id_drs' => $customerId]))
|
|
->execute()->fetchAssociative();
|
|
|
|
if (!$data) {
|
|
return null;
|
|
}
|
|
|
|
$user = new \User();
|
|
$user->loadData($data);
|
|
|
|
return $user;
|
|
}
|
|
|
|
private function getDeliveryType(): ?\DeliveryType
|
|
{
|
|
foreach (\DeliveryType::getAll() as $deliveryType) {
|
|
if ($deliveryType->getDelivery() instanceof \OdberNaProdejne && $deliveryType->getPayment() instanceof \Hotovost) {
|
|
return $deliveryType;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje uz existujici objednavku v e-shopu.
|
|
*
|
|
* $isNewOrder - znamená, zda je to nákup vytvořený na prodejně a nebo rezervace vytvořená přes e-shop
|
|
*/
|
|
private function updateShopOrder(int $orderId, array $item, Currency $currency, bool $isNewOrder = false): void
|
|
{
|
|
$order = \Order::get($orderId);
|
|
// objednavka uz byla sesynchronizovana z pokladny
|
|
if ($order->getData('drsPos') && !($item['header']['force'] ?? false)) {
|
|
return;
|
|
}
|
|
|
|
$orderDateHandle = $order->date_handle;
|
|
|
|
// Tohle nedelam u objednavek, ktere importuju primo z DRSu (prodejky). Tuhle cast musim volat pouze u rezervaci, ktere byly
|
|
// vytvoreny pres e-shop
|
|
if (!$isNewOrder) {
|
|
// ulozim uctenku
|
|
$order->setData('transactionPrintout', $item['data']['TransactionPrintout'] ?? null);
|
|
|
|
// pokud objednavka nema uzivatele, ale na prodejne nakoupila na zakaznikou kartu, tak potrebuju k objednavce uzivatele pridat
|
|
if (!$order->id_user && !empty($item['customer']['CustomerNumber'])) {
|
|
if ($user = $this->getUser((string) $item['customer']['CustomerNumber'])) {
|
|
sqlQueryBuilder()
|
|
->update('orders')
|
|
->directValues(
|
|
[
|
|
'id_user' => $user->id,
|
|
]
|
|
)
|
|
->where(Operator::equals(['id' => $order->id]))
|
|
->execute();
|
|
|
|
$order->id_user = $user->id;
|
|
}
|
|
}
|
|
|
|
// ulozit poukaz, ktery byl pouzity na pokladne
|
|
$this->setOrderCoupon($order, $item);
|
|
|
|
// aktualizovat polozky objednavky podle dat v prodejce
|
|
$this->updateShopOrderItems($order, $item, $currency);
|
|
|
|
// zaloguju s cim to bylo sparovane
|
|
$order->logHistory(
|
|
sprintf('[DRS] Objednávka byla spárovaná s prodejkou %s', $item['header']['DocumentNumber'] ?? '')
|
|
);
|
|
|
|
// prepnu objednavku do finalniho stavu, pokud v nem jeste neni a neni to prave vytvorena objednavka
|
|
$order->changeStatus(
|
|
$this->configuration->getOrderFinalStatus(),
|
|
null,
|
|
false
|
|
);
|
|
}
|
|
|
|
// Starsim objednavkam body nechceme pricitat, protoze to jsou historicke objednavky, ktere jsme jen doimportovali do shopu
|
|
// pokud je to ale stara objednavka, ktera jeste nebyla vyrizena, tak tam ty body pricist chceme
|
|
if ($this->bonusComputer && $order->date_created > (new \DateTime('2022-10-05 00:00:00')) || (($order->getFlags()['O'] ?? false) && !$orderDateHandle) || ($item['header']['forcePoints'] ?? false)) {
|
|
$this->bonusComputer->checkBonusPointsEarningDiscount($order);
|
|
$this->bonusComputer->updateBonusPoints($order);
|
|
|
|
$discountsToDelete = array_map(fn ($action) => $action['id_order_discount'], $order->getPurchaseState()->getActions('bonus_points') ?: []);
|
|
$discounts = $order->getData('discounts');
|
|
if ($discounts && is_null($discounts['extra_bonus_points'] ?? null)) {
|
|
sqlQueryBuilder()
|
|
->delete('order_discounts_orders')
|
|
->where(Operator::equals(['id_order' => $orderId]))
|
|
->andWhere(Operator::inIntArray($discountsToDelete, 'id_order_discount'))
|
|
->execute();
|
|
}
|
|
|
|
$points = $this->bonusComputer->countBonusPoints($order->getPurchaseState());
|
|
|
|
$received = sqlQueryBuilder()
|
|
->select('bp.id')
|
|
->from('bonus_points', 'bp')
|
|
->andWhere(Operator::equals(['bp.id_order' => $order->id, 'bp.id_user' => $order->id_user]))
|
|
->andWhere('bp.points > 0')
|
|
->sendToMaster()
|
|
->execute()->fetchOne();
|
|
if ($received) {
|
|
// body byly pripsany standardne (inactive) - potrebujeme je smazat
|
|
// a pridat pomoci setOrderBonusPoints, aby byly Aktivní a s jinou poznamkou
|
|
sqlQueryBuilder()->delete('bonus_points')->where(Operator::equals(['id' => $received]))->execute();
|
|
} else {
|
|
// body nebyly pripsany, protoze viz engine/bundles/External/PompoBundle/BonusProgram/Utils/BonusComputer.php:32
|
|
}
|
|
$this->setOrderBonusPoints($order, $points, $isNewOrder);
|
|
}
|
|
|
|
// oznacim si, ze jsem objednavku uz sesynchronizoval z pokladny
|
|
$order->setData('drsPos', 1);
|
|
}
|
|
|
|
public function updateShopOrderItems(\Order $order, array $drsOrder, Currency $currency): void
|
|
{
|
|
$diff = $order->getTotalPrice()->getPriceWithVat()->sub($this->getDRSOrderTotalPrice($drsOrder, $currency))->abs()->asFloat();
|
|
// nothing changed so do not process update
|
|
if ($diff < 0.1 && empty($drsOrder['header']['force'])) {
|
|
return;
|
|
}
|
|
|
|
sqlGetConnection()->transactional(function () use ($order, $drsOrder, $currency) {
|
|
$currentItems = $order->getItems();
|
|
|
|
sqlQueryBuilder()
|
|
->delete('order_items')
|
|
->where(Operator::equals(['id_order' => $order->id]))
|
|
->execute();
|
|
|
|
$this->insertShopOrderItems($order->id, $drsOrder, $currency, $currentItems);
|
|
$order->recalculate();
|
|
|
|
$order->logHistory('[DRS] Byla provedena aktualizace položek podle prodejky '.($drsOrder['header']['DocumentNumber'] ?? ''));
|
|
});
|
|
|
|
// reload order items because items of order changed
|
|
$order->setPurchaseState(null);
|
|
$order->items = [];
|
|
QueryHint::withRouteToMaster(fn () => $order->fetchItems());
|
|
}
|
|
|
|
/**
|
|
* @param OrderItem[]|null $currentItems
|
|
*/
|
|
public function insertShopOrderItems(int $orderId, array $drsOrder, Currency $currency, ?array $currentItems = null): void
|
|
{
|
|
foreach ($drsOrder['items'] ?? [] as $orderItem) {
|
|
// polozka s 0 ks me nezajima
|
|
if (((float) $orderItem['Amount']) == 0) {
|
|
continue;
|
|
}
|
|
|
|
$info = $this->getPOSOrderItemInfo($orderItem, $currency);
|
|
|
|
$note = [
|
|
'isPOSItem' => true,
|
|
'isPOSDiscounted' => $this->isItemDiscounted($info),
|
|
];
|
|
|
|
// ulozim puvodni cenu a celkovou slevu do poznamky, aby se ta sleva zobrazila i v adminu
|
|
if ($this->isItemDiscounted($info)) {
|
|
$note['priceWithoutDiscounts'] = $info['originalPiecePriceWithVat']->asFloat();
|
|
$note['totalDiscount'] = $info['originalPiecePriceWithVat']->sub($info['piecePriceWithVat'])->asFloat();
|
|
}
|
|
|
|
$orderItemId = null;
|
|
// pokud znam aktualni polozku, tak z ni budu chtit zachovat udaje (id polozky, notu)
|
|
if ($currentItem = ($currentItems[$orderItem['EshopItemID']] ?? null)) {
|
|
$orderItemId = $currentItem->getId();
|
|
$note = array_merge($note, $currentItem->getNote());
|
|
}
|
|
|
|
$note = json_encode($note);
|
|
|
|
$insertData = [
|
|
'id_order' => $orderId,
|
|
'id_product' => $info['productId'],
|
|
'pieces' => $info['pieces'],
|
|
'piece_price' => $info['price']->div($info['pieces']),
|
|
'total_price' => $info['price'],
|
|
'tax' => $orderItem['VATPercent'] ?? getAdminVat()['value'],
|
|
'descr' => $orderItem['Description'] ?? 'Položka',
|
|
'note' => $note,
|
|
];
|
|
|
|
if ($orderItemId) {
|
|
$insertData['id'] = $orderItemId;
|
|
}
|
|
|
|
sqlQueryBuilder()
|
|
->insert('order_items')
|
|
->directValues($insertData)
|
|
->execute();
|
|
}
|
|
}
|
|
|
|
private function getDRSOrderTotalPrice(array $drsOrder, Currency $currency): \Decimal
|
|
{
|
|
$totalPrice = \DecimalConstants::zero();
|
|
foreach ($drsOrder['items'] ?? [] as $orderItem) {
|
|
// polozka s 0 ks me nezajima
|
|
if (((float) $orderItem['Amount']) == 0) {
|
|
continue;
|
|
}
|
|
|
|
$info = $this->getPOSOrderItemInfo($orderItem, $currency);
|
|
|
|
$totalPrice = $totalPrice->add($info['priceWithVat']);
|
|
}
|
|
|
|
return $totalPrice;
|
|
}
|
|
|
|
/**
|
|
* Teoreticky se bude moct jednou smazat, protoze to slouzi pouze k doimportovani starych objednavek.
|
|
*
|
|
* Aktualizace staré objednavky v e-shopu.
|
|
*/
|
|
private function updateOldOrder(int $orderId, array $item): void
|
|
{
|
|
sqlQueryBuilder()
|
|
->update('orders')
|
|
->directValues(
|
|
[
|
|
'invoice_email' => $item['data']['Email'] ?? '',
|
|
'status' => $this->getOrderStatus($item, true),
|
|
'status_storno' => ($item['header']['StornoDocument'] === '1' || ($item['data']['Status_WEB_objednavky'] ?? null) == 80) ? 1 : 0,
|
|
]
|
|
)
|
|
->andWhere(Operator::equals(['id' => $orderId]))
|
|
->andWhere(Operator::findInSet(['O'], 'flags'))
|
|
->execute();
|
|
}
|
|
|
|
private function updateOrderNumber(int $orderId, string $orderNumber): void
|
|
{
|
|
sqlQueryBuilder()
|
|
->update('orders')
|
|
->directValues(
|
|
[
|
|
'order_no' => $orderNumber,
|
|
]
|
|
)
|
|
->where(Operator::equals(['id' => $orderId]))
|
|
->execute();
|
|
}
|
|
|
|
/**
|
|
* Nastavi objednavce body podle obsahu objednavky.
|
|
*/
|
|
private function setOrderBonusPoints(\Order $order, \Decimal $points, bool $isNewOrder = false): void
|
|
{
|
|
if (!$order->id_user) {
|
|
return;
|
|
}
|
|
|
|
sqlQueryBuilder()
|
|
->insert('bonus_points')
|
|
->directValues(
|
|
[
|
|
'id_user' => $order->id_user,
|
|
'points' => $points,
|
|
'date_created' => (new \DateTime())->format('Y-m-d H:i:s'),
|
|
'note' => $isNewOrder ? 'Body za nákup na prodejně' : 'Body za vyzvednutou rezervaci',
|
|
'status' => 'active',
|
|
'id_order' => $order->id,
|
|
]
|
|
)->execute();
|
|
|
|
$this->eventDispatcher->dispatch(
|
|
new UserBonusPointsUpdatedEvent($order->id_user)
|
|
);
|
|
|
|
$order->logHistory(sprintf('Za tuto objednávku bylo na účet uživatele přičteno %s bodů', $points->asFloat()));
|
|
}
|
|
|
|
/**
|
|
* Ulozi k objednavce pouzitou slevu / kupon, a u kuponu prida objednavku, ve ktery byl uplatnen.
|
|
*/
|
|
public function setOrderCoupon(\Order $order, array $item, ?int $sellerId = null): void
|
|
{
|
|
$coupons = $item['data']['ShopVoucherNumber'] ?? [];
|
|
if (empty($coupons)) {
|
|
return;
|
|
}
|
|
|
|
$orderId = $order->id;
|
|
|
|
foreach ($coupons as $coupon) {
|
|
try {
|
|
$foundCoupon = sqlQueryBuilder()
|
|
->select('id, id_discount')
|
|
->from('discounts_coupons')
|
|
->where(
|
|
Operator::equals(
|
|
[
|
|
'code' => $coupon,
|
|
'used' => 'Y',
|
|
]
|
|
)
|
|
)->execute()->fetchAssociative();
|
|
|
|
if ($foundCoupon) {
|
|
// aktualizuje u kuponu, ze byl pouziti v tehle objednavce
|
|
sqlQueryBuilder()
|
|
->update('discounts_coupons')
|
|
->directValues(
|
|
[
|
|
'id_order_used' => $orderId,
|
|
]
|
|
)
|
|
->set('data', 'JSON_SET(COALESCE(data, "{}"), "$.sellerId", :sellerId)')
|
|
->where(Operator::equals(['id' => $foundCoupon['id']]))
|
|
->setParameter('sellerId', $sellerId)
|
|
->execute();
|
|
|
|
$usedOrderDiscountId = sqlQueryBuilder()
|
|
->select('id_order_discount')
|
|
->from('order_discounts_triggers')
|
|
->where(
|
|
Operator::equals(['JSON_VALUE(data, \'$.generate_coupon\')' => $foundCoupon['id_discount']])
|
|
)
|
|
->execute()->fetchOne();
|
|
|
|
// ulozim k objednavce, ze tam byla uplatnena sleva
|
|
if ($usedOrderDiscountId) {
|
|
sqlQueryBuilder()
|
|
->insert('order_discounts_orders')
|
|
->directValues(
|
|
[
|
|
'id_order' => $orderId,
|
|
'id_order_discount' => $usedOrderDiscountId,
|
|
]
|
|
)->execute();
|
|
}
|
|
|
|
// zaloguju do historie objednavky info o tom, ktery pouakz byl uplatneny
|
|
$order->logHistory(
|
|
comment: '[DRS] V objednávce byl uplatněný poukaz: '.$coupon
|
|
);
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$this->sentryLogger->captureException($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Vrati informace o produktu v DRS prodejce, abych to byl schopny vlozit do nasi objednavky.
|
|
*/
|
|
private function getPOSOrderItemInfo(array $item, Currency $currency): array
|
|
{
|
|
$productId = (int) sqlQueryBuilder()
|
|
->select('id')
|
|
->from('products')
|
|
->where(Operator::equals(['code' => $item['VirtualArticleNumber'] ?? null]))
|
|
->execute()->fetchOne();
|
|
|
|
if (!$productId) {
|
|
// Kdyz nenajdu produkt, tak to naparuju na produkt, aby tam byla aspon nejaka vazba kvuli pocitani bodu
|
|
$productId = $this->getDummyProductId((string) ($item['VirtualArticleNumber'] ?? ''));
|
|
if (in_array($productId, ['995003', '995001', '995002'])) {
|
|
$productId = null;
|
|
}
|
|
}
|
|
|
|
$vat = toDecimal((float) ($item['VATPercent'] ?? getAdminVat()['value']));
|
|
|
|
$roundPrice = function (\Decimal $value, bool $round = true) use ($currency): \Decimal {
|
|
if (!$round) {
|
|
return $value;
|
|
}
|
|
|
|
return roundPrice($value, -1, 'DB', null, $currency);
|
|
};
|
|
|
|
$originalPriceWithVat = $roundPrice(toDecimal((float) $item['OriginalRetailPriceWithVAT'] ?? 0), false);
|
|
$priceWithVat = $roundPrice(toDecimal((float) $item['RetailPriceWithVAT'] ?? 0), false);
|
|
$originalPrice = $originalPriceWithVat->removeVat($vat);
|
|
$price = $priceWithVat->removeVat($vat);
|
|
$pieces = toDecimal((float) $item['Amount'] ?? 1);
|
|
|
|
return [
|
|
'productId' => $productId,
|
|
'originalPrice' => $originalPrice,
|
|
'originalPiecePrice' => $originalPrice->div($pieces),
|
|
'originalPriceWithVat' => $originalPriceWithVat,
|
|
'originalPiecePriceWithVat' => $originalPriceWithVat->div($pieces),
|
|
'price' => $price,
|
|
'piecePrice' => $price->div($pieces),
|
|
'priceWithVat' => $priceWithVat,
|
|
'piecePriceWithVat' => $priceWithVat->div($pieces),
|
|
'pieces' => $pieces,
|
|
'vat' => $vat,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Nektere produkty nemusi byt na e-shopu, takze jsou na e-shopu vytvorene "Dummy" produkty, ktere tyhle produkty reprezentuji. Je to primarne
|
|
* kvuli pocitani bodu, abych to mel jednodussi.
|
|
*/
|
|
private function getDummyProductId(string $code): int
|
|
{
|
|
// pokud je to lego produkt
|
|
if (StringUtil::startsWith($code, '22')) {
|
|
return 45476;
|
|
}
|
|
|
|
return 45475;
|
|
}
|
|
|
|
#[Required]
|
|
public function setBonusComputer(?BonusComputer $bonusComputer = null): void
|
|
{
|
|
$this->bonusComputer = $bonusComputer;
|
|
}
|
|
|
|
private function isItemDiscounted(array $info): bool
|
|
{
|
|
if ($info['piecePriceWithVat']->lowerThan($info['originalPiecePriceWithVat'])) {
|
|
$diff = $info['originalPiecePriceWithVat']->sub($info['piecePriceWithVat']);
|
|
if (!$diff->lowerThanOrEqual(toDecimal(0.1))) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function getOrderCurrency(string $currency): Currency
|
|
{
|
|
return Contexts::get(CurrencyContext::class)->getOrDefault($currency);
|
|
}
|
|
}
|