Files
kupshop/bundles/External/ZNZBundle/Synchronizers/SupplySynchronizer.php
2025-08-02 16:30:27 +02:00

220 lines
7.6 KiB
PHP

<?php
declare(strict_types=1);
namespace External\ZNZBundle\Synchronizers;
use External\ZNZBundle\Exception\ZNZException;
use KupShop\KupShopBundle\Util\Database\QueryHint;
use KupShop\StoresBundle\Utils\StoresInStore;
use KupShop\SynchronizationBundle\Exception\RabbitRetryMessageException;
use Query\Operator;
class SupplySynchronizer extends BaseSynchronizer
{
public static string $typeItems = 'stores_item';
protected static string $type = 'supply';
protected static bool $logging = false;
/** @required */
public StoresInStore $storesInStore;
public static function getHandledTables(): array
{
return [
'ProduktDostupnost' => 'processSupplyItem',
];
}
public static function getIdField(): ?string
{
return 'IdProdukt';
}
public function processSupplyItem(array $item): void
{
if ($this->isDeleteMessage($item) && !empty($item['meta']['unique_id'])) {
$parts = explode('-', $item['meta']['unique_id']);
$item['IdProdukt'] = (int) $parts[0];
$item['IdSklad'] = $parts[1] ?? null;
}
if (!$this->isDeleteMessage($item) && !$this->isMessageWithStoreRequiredValid($item)) {
return;
}
[$productId, $variationId] = $this->znzUtil->getProductMapping($item['IdProdukt']);
if (!$productId) {
throw new RabbitRetryMessageException();
}
if (!empty($item['IdSklad'])) {
$variationId = $this->znzUtil->getProductVariationIdWithStoreCheck($productId, $variationId, $item['IdSklad'] ?? null);
}
if (!$variationId && $this->hasProductVariations($productId)) {
throw new ZNZException(
sprintf('Invalid stock data for product "%s" (%s): trying to update product stock while the product has variations', $item['IdProdukt'], $productId)
);
}
// pokud prijde delete, tak nastavim sklad na 0
if ($this->isDeleteMessage($item)) {
// pokud neprislo IdSklad, tak smaznu vsechnu skladovost konkretniho produktu
if (empty($item['IdSklad'])) {
sqlQueryBuilder()
->delete('stores_items')
->where(Operator::equalsNullable(
[
'id_product' => $productId,
'id_variation' => $variationId,
]
))->execute();
return;
}
$item['Dostupnost'] = 0;
}
$maxId = $this->znzUtil->getZNZMaxId((int) $item['IdProdukt'], 'ProduktDostupnost', $item['IdSklad']);
// je to stara zmena, ktera nas uz nezajima, protoze mame novejsi data
if ($maxId && $maxId >= $item['meta']['id_change']) {
return;
}
QueryHint::withRouteToMaster(function () use ($item, $productId, $variationId) {
// vytvorim si mapping, ke kteremu bude ukladat data o moznosti objednani od dodavatele
if (!$this->isDeleteMessage($item) && !$this->znzUtil->getMapping(static::$typeItems, $item['IdPorovnej'])) {
$this->znzUtil->createMapping(
static::$typeItems,
$item['IdPorovnej'],
$this->getStoreItemId(
$this->getStoreId($item['IdSklad']),
$productId,
$variationId
)
);
}
// aktualizuju skladovou zasobu
$this->storesInStore->updateStoreItem(
[
'id_store' => $this->getStoreId($item['IdSklad']),
'id_product' => $productId,
'id_variation' => $variationId,
'quantity' => $item['Dostupnost'],
],
false
);
});
$this->znzUtil->updateZNZMaxId((int) $item['IdProdukt'], 'ProduktDostupnost', $item['IdSklad'], $item['meta']['id_change']);
// pokud je to delete, tak chci prepocitat sklad hned, at tam treba nezustava posledni kus 5 minut nez se spusti prepocet skladu
if ($item['Dostupnost'] <= 0) {
$this->znzUtil->recalculateStores(productIds: [$productId], withVariationsInStoreRecalculate: false);
}
if (!$this->isDeleteMessage($item)) {
$supplierData = [];
if (!empty($item['DobaDodani']) && !empty($item['PrahObjednani']) && !empty($item['ZemeObjednani'])) {
$supplierData = [
'dobaDodani' => $item['DobaDodani'],
'prahObjednani' => $item['PrahObjednani'],
'zemeObjednani' => $item['ZemeObjednani'],
'dnyVTydnu' => $item['DnyVTydnu'],
];
}
sqlQueryBuilder()
->update('znz_stores_items')
->directValues(['data' => json_encode(['supplier' => $supplierData])])
->where(Operator::equals(['id_znz' => $item['IdPorovnej']]))
->execute();
}
}
private function hasProductVariations(int $productId): bool
{
static $hasVariationsCache = [];
if (($hasVariationsCache[$productId] ?? null) !== null) {
return $hasVariationsCache[$productId];
}
$variationId = sqlQueryBuilder()
->select('id')
->from('products_variations')
->where(Operator::equals(['id_product' => $productId]))
->execute()->fetchOne();
return $hasVariationsCache[$productId] = (bool) $variationId;
}
private function getStoreItemId(int $storeId, int $productId, ?int $variationId): int
{
$storeItemId = sqlQueryBuilder()
->select('id')
->from('stores_items')
->andWhere(Operator::equalsNullable(
[
'id_product' => $productId,
'id_variation' => $variationId,
'id_store' => $storeId,
])
)
->execute()
->fetchOne();
if (!$storeItemId) {
$storeItemId = sqlGetConnection()->transactional(function () use ($storeId, $productId, $variationId) {
sqlQueryBuilder()
->insert('stores_items')
->directValues(
[
'id_store' => $storeId,
'id_product' => $productId,
'id_variation' => $variationId,
'quantity' => 0,
]
)
->execute();
return (int) sqlInsertId();
});
}
return $storeItemId;
}
private function getStoreId(string $znzId): int
{
static $storesCache = [];
if (!($storesCache[$znzId] ?? false)) {
if (!($storeId = $this->znzUtil->getMapping('store', $znzId))) {
$storeId = sqlGetConnection()->transactional(function () use ($znzId) {
sqlQueryBuilder()
->insert('stores')
->directValues(
[
'name' => $znzId,
'figure' => 'N',
]
)->execute();
return (int) sqlInsertId();
});
$this->znzUtil->createMapping('store', $znzId, $storeId);
}
$storesCache[$znzId] = $storeId;
}
return $storesCache[$znzId];
}
}