334 lines
11 KiB
PHP
334 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace External\HannahBundle\Util;
|
|
|
|
use KupShop\CatalogBundle\ProductList\ProductCollection;
|
|
use KupShop\CatalogBundle\ProductList\ProductList;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
use KupShop\StoresBundle\Utils\StoresInStore;
|
|
use Query\Operator;
|
|
use Query\Product;
|
|
|
|
class ProductUtil
|
|
{
|
|
public const FLAG_LAST_SIZES = 'LS';
|
|
public const LABEL_CODE_LAST_SIZES = 'LS';
|
|
|
|
public function __construct(
|
|
private StoresInStore $storesInStore,
|
|
private ProductList $productList,
|
|
) {
|
|
}
|
|
|
|
public function getDateIncrement(array $products, bool $min = false): int
|
|
{
|
|
$increment = $min ? null : 0;
|
|
|
|
$items = $this->getProducts($products);
|
|
foreach ($items as $product) {
|
|
$productDeliveryTime = $this->getDeliveryTimeByProduct($product);
|
|
|
|
if ($increment === null) {
|
|
$increment = $productDeliveryTime;
|
|
}
|
|
|
|
if ($min) {
|
|
$increment = min(
|
|
$increment,
|
|
$productDeliveryTime
|
|
);
|
|
|
|
continue;
|
|
}
|
|
|
|
$increment = max(
|
|
$increment,
|
|
$productDeliveryTime
|
|
);
|
|
}
|
|
|
|
return $increment;
|
|
}
|
|
|
|
public function getStoreDeliveryTime(array $products, int $storeId): int
|
|
{
|
|
$deliveryTime = 0;
|
|
|
|
$items = $this->getProducts($products);
|
|
// nactu deliveryTime pro konkretni prodejnu
|
|
foreach ($items as $product) {
|
|
$deliveryTime = max(
|
|
$this->getDeliveryTimeByProduct($product, $storeId),
|
|
$deliveryTime
|
|
);
|
|
}
|
|
|
|
// kontroluju oteviraci dobu prodejny
|
|
if ($openingHours = $this->getStoreOpeningHours($storeId)) {
|
|
[$open, $closed] = $openingHours;
|
|
|
|
$closedDateTime = new \DateTime(
|
|
$this->getCurrentDateTime()->format('Y-m-d '.$closed)
|
|
);
|
|
|
|
// Pokud je prodejna uz zavrena, tak pridam jeste jeden den, protoze to udelaji az dalsi den
|
|
if ($this->getCurrentDateTime() > $closedDateTime) {
|
|
++$deliveryTime;
|
|
}
|
|
} else {
|
|
++$deliveryTime;
|
|
}
|
|
|
|
return $deliveryTime;
|
|
}
|
|
|
|
public function getLabelByCode(string $code): ?int
|
|
{
|
|
$labelId = sqlQueryBuilder()
|
|
->select('id')
|
|
->from('labels')
|
|
->where(Operator::equals(['code' => $code]))
|
|
->execute()->fetchOne();
|
|
|
|
return $labelId ?: null;
|
|
}
|
|
|
|
private function getDeliveryTimeByProduct(\Product $product, ?int $storeId = null): int
|
|
{
|
|
$dbcfg = \Settings::getDefault();
|
|
// pouziju nastaveni z administrace
|
|
$config = $dbcfg->outdoorconcept['deliveryTime'] ?? [];
|
|
|
|
// nactu si skladovosti
|
|
[$inStore, $inStoreSellers, $inStoreSelectedStore] = $this->getInStores($product->storesInStore ?? [], $storeId);
|
|
|
|
$deliveryTime = 0;
|
|
|
|
if ($product->isVirtual()) {
|
|
return $deliveryTime;
|
|
}
|
|
|
|
if ($storeId) {
|
|
$afternoonIncrement = null;
|
|
// pokud ze mam skladem na vybranem skladu (prodejne), tak je vyzednuti v podstate ihned
|
|
if ($inStoreSelectedStore > 0) {
|
|
$deliveryTime = 0;
|
|
// pokud nemam skladem na prodejne, ale mam skladem na hlavnim skladu, tak se musi na prodejnu zavest z centraly
|
|
} elseif ($inStore > 0) {
|
|
$deliveryTime = $config['seller']['inMainStore']['days'] ?? 2;
|
|
$afternoonIncrement = $config['seller']['inMainStore']['afternoonIncrement'] ?? 0;
|
|
} else {
|
|
// neni skladem na prodejne, ani na centrale, ale je skladem na jine prodejne, takze budeme prevazet odtamtud
|
|
$deliveryTime = $config['seller']['inOtherStore']['days'] ?? 4;
|
|
$afternoonIncrement = $config['seller']['inOtherStore']['afternoonIncrement'] ?? 0;
|
|
}
|
|
|
|
// pokud je nastaveny afternoon increment a zaroven je po 12 hodine, takze je odpoledne, tak potrebuju jeste
|
|
// navysit datum doruceni o tenhle afternoon increment
|
|
if ($afternoonIncrement && $this->getCurrentDateTime()->format('H') >= 12) {
|
|
$deliveryTime += $afternoonIncrement;
|
|
}
|
|
} else {
|
|
// Pokud nemam na centrale, ale mam skladem na nejake prodejne, tak muzu expedovat pres prodejnu, ale je potreba pridat dalsi dny
|
|
// k dnum doruceni na dopravci
|
|
if ($inStore <= 0 && $inStoreSellers > 0) {
|
|
$deliveryTime = $config['seller']['carrier']['inSellerStoreIncrement'] ?? 2;
|
|
}
|
|
}
|
|
|
|
return (int) $deliveryTime;
|
|
}
|
|
|
|
private function getStoreOpeningHours(int $storeId): ?array
|
|
{
|
|
static $storesCache;
|
|
|
|
if (!$storesCache) {
|
|
$qb = sqlQueryBuilder()
|
|
->select('s.id, s.name, dtd.data')
|
|
->from('stores', 's')
|
|
->join('s', 'delivery_type_delivery', 'dtd', 's.id_delivery = dtd.id');
|
|
|
|
foreach ($qb->execute() as $item) {
|
|
$storesCache[$item['id']] = [
|
|
'name' => $item['name'],
|
|
'data' => json_decode($item['data'] ?? '', true) ?? [],
|
|
];
|
|
}
|
|
}
|
|
|
|
$currentDay = $this->getCurrentDateTime()->format('N');
|
|
|
|
if ($store = ($storesCache[$storeId] ?? false)) {
|
|
if ($openingHours = ($store['data']['opening_hours'] ?? false)) {
|
|
$currentDayOpeningHours = array_filter($openingHours[$currentDay] ?? []);
|
|
if (!empty($currentDayOpeningHours)) {
|
|
return $currentDayOpeningHours;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function getCurrentDateTime(): \DateTime
|
|
{
|
|
static $dateTime;
|
|
|
|
if (!$dateTime) {
|
|
$dateTime = new \DateTime();
|
|
}
|
|
|
|
return $dateTime;
|
|
}
|
|
|
|
/**
|
|
* Funkce, ktera vrati skladovosti.
|
|
*
|
|
* Vraci:
|
|
* [hlavni sklad, sklad prodejen, sklad vybrane prodejny]
|
|
*/
|
|
private function getInStores(array $storesValues, ?int $selectedStoreId = null): array
|
|
{
|
|
$stores = $this->storesInStore->getStores();
|
|
$sellers = $this->getSellers();
|
|
|
|
$inStore = 0;
|
|
$inStoreStores = 0;
|
|
$inStoreSelectedStore = 0;
|
|
|
|
foreach ($storesValues as $storeId => $data) {
|
|
if (!($store = ($stores[$storeId] ?? false))) {
|
|
continue;
|
|
}
|
|
|
|
if ($selectedStoreId && $storeId == $selectedStoreId) {
|
|
$inStoreSelectedStore += $data['in_store'];
|
|
}
|
|
|
|
$seller = null;
|
|
foreach ($sellers as $tmpSeller) {
|
|
if ($tmpSeller['id_store'] == $storeId) {
|
|
$seller = $tmpSeller;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// pokud je sklad prodejna, tak ma nastaveny id_delivery
|
|
if ($store['id_delivery'] || $seller) {
|
|
// pokud neni prodejna aktivni, tak jeji sklad ignoruju
|
|
if ($seller['figure'] !== 'N') {
|
|
$inStoreStores += $data['in_store'];
|
|
}
|
|
} else {
|
|
$inStore += $data['in_store'];
|
|
}
|
|
}
|
|
|
|
return [$inStore, $inStoreStores, $inStoreSelectedStore];
|
|
}
|
|
|
|
public function generateLastSizesFlag(): void
|
|
{
|
|
$ids = array_map(
|
|
fn ($x) => $x['id'],
|
|
sqlQueryBuilder()
|
|
->select('p.id')
|
|
->from('products', 'p')
|
|
->leftJoin('p', 'products_variations', 'pv', 'pv.id_product = p.id')
|
|
->leftJoin('pv', 'products_variations_combination', 'pvc', 'pvc.id_variation = pv.id')
|
|
->leftJoin('pvc', 'products_variations_choices_values', 'pvcv', 'pvc.id_value = pvcv.id')
|
|
->where('pv.in_store > 0 AND pv.figure = \'Y\' AND pvcv.value NOT IN (\'UNI\', \'nezadáno\', \'-\')')
|
|
->groupBy('p.id')
|
|
->having('COUNT(pv.id) = 1')
|
|
->execute()->fetchAllAssociative()
|
|
);
|
|
|
|
sqlGetConnection()->transactional(function () {
|
|
$labelId = $this->getLabelByCode(self::LABEL_CODE_LAST_SIZES);
|
|
if (!$labelId) {
|
|
return;
|
|
}
|
|
|
|
sqlQueryBuilder()
|
|
->delete('product_labels_relation')
|
|
->andWhere(Operator::equals(['id_label' => $labelId]))
|
|
->execute();
|
|
|
|
sqlQuery('INSERT IGNORE INTO product_labels_relation (id_label, id_product)
|
|
SELECT '.$labelId.' as id_label, p.id as id_product FROM products p
|
|
LEFT JOIN products_variations pv ON pv.id_product = p.id
|
|
LEFT JOIN products_variations_combination pvc ON pvc.id_variation = pv.id
|
|
LEFT JOIN products_variations_choices_values pvcv on pvc.id_value = pvcv.id
|
|
WHERE pv.in_store > 0 AND pv.figure = \'Y\'
|
|
AND pvcv.value NOT IN (\'UNI\', \'nezadáno\', \'-\')
|
|
GROUP BY p.id
|
|
HAVING COUNT(pv.id) = 1');
|
|
});
|
|
|
|
// TODO: deprecated campaign - remove me after some time
|
|
sqlGetConnection()->transactional(function () use ($ids) {
|
|
sqlQueryBuilder()
|
|
->update('products')
|
|
->set('campaign', 'REMOVE_FROM_SET(:flag, campaign)')
|
|
->setParameter('flag', self::FLAG_LAST_SIZES)
|
|
->execute();
|
|
|
|
sqlQueryBuilder()
|
|
->update('products')
|
|
->set('campaign', 'ADD_TO_SET(:flag, campaign)')
|
|
->where(Operator::inIntArray($ids, 'id'))
|
|
->setParameter('flag', self::FLAG_LAST_SIZES)
|
|
->execute();
|
|
});
|
|
}
|
|
|
|
public function getProductVat(int $productId): float
|
|
{
|
|
$vatId = sqlQueryBuilder()
|
|
->select('vat')
|
|
->from('products')
|
|
->where(Operator::equals(['id' => $productId]))
|
|
->execute()->fetchOne();
|
|
|
|
return (float) getVat($vatId);
|
|
}
|
|
|
|
private function getProducts(array $products): ProductCollection
|
|
{
|
|
static $productsCache = [];
|
|
|
|
$cacheKey = md5(serialize($products));
|
|
|
|
if (!($productsCache[$cacheKey] ?? false)) {
|
|
$productList = clone $this->productList;
|
|
$productList->setVariationsAsResult(true);
|
|
$collection = $productList->andSpec(Product::productsAndVariationsIds($products))
|
|
->getProducts();
|
|
|
|
$collection->fetchStoresInStore();
|
|
|
|
$productsCache[$cacheKey] = $collection;
|
|
}
|
|
|
|
return $productsCache[$cacheKey];
|
|
}
|
|
|
|
private function getSellers(): array
|
|
{
|
|
static $sellersAll;
|
|
|
|
if (!$sellersAll) {
|
|
$sellersAll = Mapping::mapKeys(sqlQueryBuilder()
|
|
->select('id, id_store, figure')
|
|
->from('sellers')
|
|
->execute()
|
|
->fetchAllAssociative(), fn ($k, $v) => [$v['id'], $v]);
|
|
}
|
|
|
|
return $sellersAll;
|
|
}
|
|
}
|